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 实现方法也提供了这些专有方法,它们返回 truefalse

  • $request->isGet()
  • $request->isPost()
  • $request->isPut()
  • $request->isDelete()
  • $request->isHead()
  • $request->isPatch()
  • $request->isOptions()

还可以伪造或覆写这些 HTTP 请求方法。这非常有用,例如你需要在一个只支持 GETPOST 请求的传统浏览器中模拟一个 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() (返回整个查询字符串,e.g. a=1&b=2)
  • 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');

媒体类型解析

Slim looks as the request’s media type and if it recognises it, will parse it into structured data available via $request->getParsedBody(). This is usually an array, but is an object for XML media types.

The following media types are recognised and parsed:

  • application/x-www-form-urlencoded’
  • application/json
  • application/xml & text/xml

If you want Slim to parse contend from a a different media type then you need to either parse the raw body yourself or register a new media parser. Media parsers are simply callables that accept an $input string and return a parsed object or array.

Register a new media parser in an application or route middleware. Note that you must register the parser before you try to access the parsed body for the first time.

For example, to automatically parse JSON that is sent with a text/javascript content type, you register a media type parser in middleware like this:

// Add the middleware
$app->add(function ($request, $response, $next) {
    // add media parser
    $request->registerMediaTypeParser(
        "text/javascript",
        function ($input) {
            return json_decode($input, true);
        }
    );
    
    return $next($request, $response);
});