HTTP 请求
Slim 应用程序的路由和中间件给出了一个 PSR 7 请求对象,它表示当前的 HTTP 请求是由 Web 服务器 接收到的。该请求对象遵循 PSR 7 服务器请求接口(ServerRequestInterface ) 实现,因此你可以检查和操作该 HTTP 请求对象的方法、头和体。
如何获取请求对象
该 PSR 7 请求对象作为路由回调的第一个参数注入到你的 Slim 应用程序的路由中,像这样:
<?php
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
$app = new \Slim\App;
$app->get('/foo', function (ServerRequestInterface $request, ResponseInterface $response) {
// 使用 PSR 7 $request 对象
return $response;
});
$app->run();
Figure 1: 将 PSR 7 请求注入到应用程序的路由回调中。
该 PSR 7 请求对象作为中间件 callable 的第一个参数注入到 Slim 应用程序的中间件中,像这样:
<?php
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
$app = new \Slim\App;
$app->add(function (ServerRequestInterface $request, ResponseInterface $response, callable $next) {
// Use the PSR 7 $request object
return $next($request, $response);
});
// Define app routes...
$app->run();
Figure 2: 注入 PSR 7 请求到应用程序中间件
请求的方法
每个 HTTP 请求都有相应的方法,通常是这些中的一个:
- GET
- POST
- PUT
- DELETE
- HEAD
- PATCH
- OPTIONS
你可以恰当地使用 getMethod()
请求对象方法来检查 HTTP 请求方法。
$method = $request->getMethod();
由于这是一个常见的功能,Slim 的内置 PSR 7 实现方法也提供了这些专有方法,它们返回 true
或 false
。
$request->isGet()
$request->isPost()
$request->isPut()
$request->isDelete()
$request->isHead()
$request->isPatch()
$request->isOptions()
还可以伪造或覆写这些 HTTP 请求方法。这非常有用,例如你需要在一个只支持 GET
或 POST
请求的传统浏览器中模拟一个 PUT
请求。
有两种方法来覆写 HTTP 请求方法。你可以在一个 POST
请求体中引入(include)一个 _METHOD
参数。该 HTTP 请求必须使用 application/x-www-form-urlencoded
内容类型(content type)。
POST /path HTTP/1.1
Host: example.com
Content-type: application/x-www-form-urlencoded
Content-length: 22
data=value&_METHOD=PUT
Figure 3: 使用 _METHOD 参数覆写 HTTP 请求。
你也可以使用自定义的 X-Http-Method-Override
HTTP请求头来覆写 HTTP 请求方法。这个方式可以用于任意 HTTP 请求内容类型(content type)。
POST /path HTTP/1.1
Host: example.com
Content-type: application/json
Content-length: 16
X-Http-Method-Override: PUT
{"data":"value"}
Figure 4: 使用 X-Http-Method-Override 请求头覆写 HTTP 方法。
你可以使用 PSR 7 请求对象的方法 getOriginalMethod()
来提取原有 (不是覆写后的)的 HTTP 方法。
请求 URI
每个 HTTP 请求都有一个识别被请求的应用程序资源的 URI 。HTTP 请求 URI 分为这几部分:
- Scheme (e.g.
http
orhttps
) - Host (e.g.
example.com
) - Port (e.g.
80
or443
) - Path (e.g.
/users/1
) - Query string (e.g.
sort=created&dir=asc
)
你可以使用 getUri()
方法来提取 PSR 7 请求对象的 URI :
$uri = $request->getUri();
PSR 7 请求对象的 URI 本身就是一个对象,提供了以下方法来检查 HTTP 请求的 URL :
getScheme()
getAuthority()
getUserInfo()
getHost()
getPort()
getPath()
getBasePath()
getQuery()
<small>(返回整个查询字符串,e.g.a=1&b=2
)</small>getFragment()
getBaseUrl()
基准路径如果你的 Slim 应用程序的前端控制器放置在文件根目录的物理子目录中,你可以使用 URI 对象的 getBasePath()
方法来提取 HTTP 请求的物理基准路径(相对于文件根目录)。如果 Slim 应用程序安装在文件根目录的最上层目录中,它将返回一个空字符串。
请求头
每个 HTTP 请求都有请求头。这些元数据描述了 HTTP 请求,但在请求体中不可见。Slim 的 PSR 7 请求对象提供了几个检查请求头的方法。
获取所有请求头
使用 PSR 7 请求对象的getHeaders()
方法提取所有 HTTP 请求头并放入一个关联数组中。此关联数组的键值是请求头的名称,其值是各请求头对应的由字符串值组成的数值数组。
$headers = $request->getHeaders();
foreach ($headers as $name => $values) {
echo $name . ": " . implode(", ", $values);
}
Figure 5: 提取并迭代所有 HTTP 请求头作为一个关联数组。
获取单个请求头
你可以使用 PSR 7 请求对象的 getHeader($name)
方法获取一个单独的请求头的值。它将返回一个由指定请求头名称对应的值组成的数组。记住,单独的请求头可不一定只有一个值。
$headerValueArray = $request->getHeader('Accept');
Figure 6: 获取指定 HTTP 请求的值。
你同样可以使用 PSR 7 请求对象的 getHeaderLine($name)
方法提取指定请求头的值,并以逗号分隔。这不同于 getHeader($name)
方法,这个方法返回一个由逗号分隔的字符串。
$headerValueString = $request->getHeaderLine('Accept');
Figure 7: 获取单个请求头的值,返回逗号分隔的字符串。
检测请求头
使用 PSR 7 请求对象的 hasHeader($name)
方法检查某请求头是否存在。
if ($request->hasHeader('Accept')) {
// Do something
}
Figure 8: Detect presence of a specific HTTP request header.
请求体
每个 HTTP 请求都有一个请求体。如果你的 Slim 应用程序是通过 JSON 或 XML 数据进行通信的,你可以使用 PSR 7 请求对象的 getParsedBody()
方法将 HTTP 请求体解析成原生 PHP 格式。Slim 可以解析 JSON, XML, 和 URL-encoded 数据,开箱即用。
$parsedBody = $request->getParsedBody();
Figure 9: Parse HTTP request body into native PHP format
- JSON 请求通过
json_decode($input)
转换成 PHP 对象。 - XML 请求通过
simplexml_load_string($input)
转换成SimpleXMLElement
。 - URL-encoded 请求通过
parse_str($input)
转换成 PHP 数组。
从技术上说,Slim 的 PSR 7 请求对象将 HTTP 请求体表示为 \Psr\Http\Message\StreamInterface
的一个实例。你可以通过使用 PSR 7 请求对象的 getBody()
方法获取 HTTP 请求体的 StreamInterface
实例。该 getBody()
方法在处理未知大小或对于可用内存来说太大的 HTTP 请求时,是个很好的方法。
$body = $request->getBody();
Figure 10: 获取 HTTP 请求体
生成的 \Psr\Http\Message\StreamInterface
实例提供了以下方法来读取或迭代未知的 资源(resource)
。
getSize()
tell()
eof()
isSeekable()
seek()
rewind()
isWritable()
write($string)
isReadable()
read($length)
getContents()
getMetadata($key = null)
请求助手/Request Helpers
Slim 的 PSR 7 请求实现方法提供了额外的专有方法来帮助你进一步检查 HTTP 请求。
检测 XHR 请求
你可以使用请求对象的 isXhr()
方法来检测 XHR 请求。该方法检测 X-Requested-With
请求头,并确保它的值是 XMLHttpRequest
。
POST /path HTTP/1.1
Host: example.com
Content-type: application/x-www-form-urlencoded
Content-length: 7
X-Requested-With: XMLHttpRequest
foo=bar
Figure 11: XHR 请求示例.
if ($request->isXhr()) {
// Do something
}
内容类型/Content Type
你可以使用请求对象的 getContentType()
方法提取 HTTP 请求的内容类型。它将通过 HTTP 客户端返回 Content-Type
头的完整值。
$contentType = $request->getContentType();
媒体类型/Media Type
你或许不会想要完整的 Content-Type
头。加入说你只想要媒体类型?你可以使用响应对象的 getMediaType()
方法取得 HTTP 请求的媒体类型。
$mediaType = $request->getMediaType();
可以使用请求对象的 getMediaTypeParams()
方法,以关联数组的形式获取附加的没提类型参数。
$mediaParams = $request->getMediaTypeParams();
字符集/Character Set
HTTP 请求字符集是最常用的媒体类型参数之一。请求对象提供了一个专用的方法来获取这个媒体类型参数。
$charset = $request->getContentCharset();
内容长度/Content Length
使用请求对象的 getContentLength()
方法获取 HTTP 请求的内容长度。
$length = $request->getContentLength();
路由对象/Route Object
有时在中间件中你需要路由的参数。
在这个例子中,我们首先检查用户是否登录,然后检查用户有否有权限浏览他们正试图浏览的视频。
$app->get('/course/{id}', Video::class.":watch")->add(Permission::class)->add(Auth::class);
//.. In the Permission Class's Invoke
/** @var $route \Slim\Route */
$route = $request->getAttribute('route');
$courseId = $route->getArgument('id');
媒体类型解析
如果 HTTP 请求的媒体类型被识别,将通过 $request->getParsedBody()
解析成为结构化的可用数据。通常是解析成一个数组,或者是 XML 媒体类型的对象。
以下媒体类型是可识别和解析的:
- application/x-www-form-urlencoded’
- application/json
- application/xml & text/xml
如果你想要用 Slim 来解析其它媒体类型,那么你可以自行解析原生的 HTTP 请求体,或者新注册一个媒体解析器。媒体解析器都是简单的 callable ,它接收一个 $input
字符串并返回一个解析后的对象或数组。
在应用程序或者路由中间件中注册新的媒体解析器。注意,你必须在首次尝试访问解析后的 HTTP 请求体前注册该解析器。
例如,要自动解析带有 text/javascript
内容类型的 JSON,你可以像这样在中间件中注册媒体解析器:
// 添加中间件
$app->add(function ($request, $response, $next) {
// add media parser
$request->registerMediaTypeParser(
"text/javascript",
function ($input) {
return json_decode($input, true);
}
);
return $next($request, $response);
});