HTTP 请求

    该 PSR 7 请求对象作为路由回调的第一个参数注入到你的 Slim 应用程序的路由中,像这样:

    Figure 1: 将 PSR 7 请求注入到应用程序的路由回调中。

    该 PSR 7 请求对象作为中间件 callable 的第一个参数注入到 Slim 应用程序的中间件中,像这样:

    1. use Psr\Http\Message\ServerRequestInterface;
    2. use Psr\Http\Message\ResponseInterface;
    3. $app = new \Slim\App;
    4. $app->add(function (ServerRequestInterface $request, ResponseInterface $response, callable $next) {
    5. // Use the PSR 7 $request object
    6. });
    7. // Define app routes...
    8. $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 方法。

    每个 HTTP 请求都有一个识别被请求的应用程序资源的 URI 。HTTP 请求 URI 分为这几部分:

    • Scheme (e.g. http or https)
    • Host (e.g. )
    • 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();
    
    • getScheme()
    • getAuthority()
    • 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) 方法获取一个单独的请求头的值。它将返回一个由指定请求头名称对应的值组成的数组。记住,单独的请求头可不一定只有一个值。

    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 请求体的 实例。该 getBody() 方法在处理未知大小或对于可用内存来说太大的 HTTP 请求时,是个很好的方法。
    $body = $request->getBody();
    

    Figure 10: 获取 HTTP 请求体

    • getSize()
    • tell()
    • eof()
    • isSeekable()
    • seek()
    • rewind()
    • isWritable()
    • write($string)
    • isReadable()
    • read($length)
    • getContents()
    • getMetadata($key = null)

    请求助手/Request Helpers

    Slim 的 PSR 7 请求实现方法提供了额外的专有方法来帮助你进一步检查 HTTP 请求。

    你可以使用请求对象的 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 头的完整值。

    媒体类型/Media Type

    你或许不会想要完整的 Content-Type 头。加入说你只想要媒体类型?你可以使用响应对象的 getMediaType()方法取得 HTTP 请求的媒体类型。

    $mediaType = $request->getMediaType();
    

    可以使用请求对象的 getMediaTypeParams() 方法,以关联数组的形式获取附加的没提类型参数。

    $mediaParams = $request->getMediaTypeParams();
    

    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

    例如,要自动解析带有 内容类型的 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);
    });