HTTP 请求

最后更新于:2022-04-01 22:33:48

# HTTP 请求 Slim 应用程序的路由和中间件给出了一个 PSR 7 请求对象,它表示当前的 HTTP 请求是由 Web 服务器 接收到的。该请求对象遵循 [PSR 7 服务器请求接口(ServerRequestInterface )](http://www.php-fig.org/psr/psr-7/#3-2-1-psr-http-message-serverrequestinterface) 实现,因此你可以检查和操作该 HTTP 请求对象的方法、头和体。 ## 如何获取请求对象 该 PSR 7 请求对象作为路由回调的第一个参数注入到你的 Slim 应用程序的路由中,像这样: ``` get('/foo', function (ServerRequestInterface $request, ResponseInterface $response) { // 使用 PSR 7 $request 对象 return $response; }); $app->run(); ``` Figure 1: 将 PSR 7 请求注入到应用程序的路由回调中。 该 PSR 7 请求对象作为中间件 callable 的第一个参数注入到 Slim 应用程序的_中间件_中,像这样: ``` 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` or `https`) * Host (e.g. `example.com`) * Port (e.g. `80` or `443`) * 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); }); ```
';