要求

你的 Slim 应用程序的路由和中间件被赋予一个 PSR-7 请求对象,代表你的网络服务器接收到的当前 HTTP 请求。请求对象实现PSR-7 ServerRequestInterface,您可以使用它检查和操作 HTTP 请求方法、标头和正文。

如何获取请求对象

PSR-7 请求对象被注入到你的 Slim 应用程序路由中作为路由回调的第一个参数,如下所示:

<?php
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\Factory\AppFactory;

require __DIR__ . '/../vendor/autoload.php';

$app = AppFactory::create();

$app->get('/hello', function (Request $request, Response $response) {
    $response->getBody()->write('Hello World');
    return $response;
});

$app->run();
图 1:将 PSR-7 请求注入应用程序路由回调。

PSR-7 请求对象作为可调用中间件的第一个参数注入到你的 Slim 应用中间件中 ,如下所示:

<?php

use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
use Slim\Factory\AppFactory;

require __DIR__ . '/../vendor/autoload.php';

$app = AppFactory::create();

$app->add(function (Request $request, RequestHandler $handler) {
   return $handler->handle($request);
});

// ...define app routes...

$app->run();
图 2:将 PSR-7 请求注入应用程序中间件。

请求方法

每个 HTTP 请求都有一个方法,通常是以下方法之一:

  • 得到
  • 邮政
  • 删除
  • 修补
  • 选项

您可以使用适当命名的 Request 对象方法检查 HTTP 请求的方法getMethod()

$method = $request->getMethod();

可以伪造或覆盖HTTP 请求方法。例如,如果您需要使用仅支持或请求PUT的传统 Web 浏览器来模拟请求,这将很有用GETPOST

小心!
要启用请求方法覆盖,必须将方法覆盖中间件注入到您的应用程序中。

有两种方法可以覆盖 HTTP 请求方法。您可以在请求的正文中包含一个 METHOD参数POSTHTTP 请求必须使用 application/x-www-form-urlencoded内容类型。

POST /path HTTP/1.1
Host: example.com
Content-type: application/x-www-form-urlencoded
Content-length: 22

data=value&_METHOD=PUT
图 3:使用 _METHOD 参数覆盖 HTTP 方法。

您还可以使用自定义 HTTP 请求标头覆盖 HTTP 请求方法 X-Http-Method-Override这适用于任何 HTTP 请求内容类型。

POST /path HTTP/1.1
Host: example.com
Content-type: application/json
Content-length: 16
X-Http-Method-Override: PUT

{"data":"value"}
图 4:使用 X-Http-Method-Override 标头覆盖 HTTP 方法。

请求 URI

每个 HTTP 请求都有一个 URI,用于标识所请求的应用程序资源。HTTP 请求 URI 有几个部分:

  • 方案(例如httphttps
  • 主机(例如example.com
  • 端口(例如80443
  • 路径(例如/users/1
  • 查询字符串(例如sort=created&dir=asc

您可以使用其方法获取 PSR-7 请求对象的URI 对象getUri()

$uri = $request->getUri();

PSR-7 请求对象的 URI 本身是一个对象,它提供以下方法来检查 HTTP 请求的 URL 部分:

  • 获取方案()
  • 获取权限()
  • 获取用户信息()
  • 获取主机()
  • 获取端口()
  • 获取路径()
  • getQuery() (返回完整的查询字符串,例如a=1&b=2
  • 获取片段()

您可以使用 将查询参数作为 Request 对象上的关联数组获取getQueryParams()

请求标头

每个 HTTP 请求都有标头。这些是描述 HTTP 请求但在请求正文中不可见的元数据。Slim 的 PSR-7 Request 对象提供了多种方法来检查其标头。

获取所有标题

您可以使用 PSR-7 请求对象的getHeaders()方法将所有 HTTP 请求标头作为关联数组获取。生成的关联数组的键是标头名称,其值本身是其各自标头名称的字符串值的数字数组。

$headers = $request->getHeaders();
foreach ($headers as $name => $values) {
    echo $name . ": " . implode(", ", $values);
}
图 5:获取并迭代所有 HTTP 请求标头作为关联数组。

获取一个标题

您可以使用 PSR-7 请求对象的方法获取单个标头的值getHeader($name)这将返回给定标头名称的值数组。请记住,一个 HTTP 标头可能有多个值!

$headerValueArray = $request->getHeader('Accept');
图 6:获取特定 HTTP 标头的值。

您还可以使用 PSR-7 请求对象的getHeaderLine($name)方法获取一个以逗号分隔的字符串,其中包含给定标头的所有值。与方法不同 getHeader($name),此方法返回逗号分隔的字符串。

$headerValueString = $request->getHeaderLine('Accept');
图 7:获取单个标头的值作为逗号分隔的字符串。

检测标头

您可以使用 PSR-7 请求对象的方法测试标头是否存在 hasHeader($name)

if ($request->hasHeader('Accept')) {
    // Do something
}
图 8:检测特定 HTTP 请求标头的存在。

请求体

每个 HTTP 请求都有一个主体。如果您正在构建一个使用 JSON 或 XML 数据的 Slim 应用程序,您可以使用 PSR-7 Request 对象的 getParsedBody()方法将 HTTP 请求主体解析为原生 PHP 格式。请注意,正文解析因 PSR-7 实现而异。

根据您安装的 PSR-7 实现,您可能需要实现中间件以解析传入的输入。以下是解析传入输入的示例JSON

<?php

use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface as RequestHandler;

class JsonBodyParserMiddleware implements MiddlewareInterface
{
    public function process(Request $request, RequestHandler $handler): Response
    {
        $contentType = $request->getHeaderLine('Content-Type');

        if (strstr($contentType, 'application/json')) {
            $contents = json_decode(file_get_contents('php://input'), true);
            if (json_last_error() === JSON_ERROR_NONE) {
                $request = $request->withParsedBody($contents);
            }
        }

        return $handler->handle($request);
    }
}
$parsedBody = $request->getParsedBody();
图 9:将 HTTP 请求主体解析为原生 PHP 格式

从技术上讲,PSR-7 请求对象将 HTTP 请求主体表示为Psr\Http\Message\StreamInterface. StreamInterface您可以使用 PSR-7 Request 对象的 方法获取 HTTP 请求主体实例getBody()getBody()如果传入的 HTTP 请求大小未知或对于可用内存而言太大,则该方法更可取。

$body = $request->getBody();
图 10:获取 HTTP 请求正文

结果Psr\Http\Message\StreamInterface实例提供了以下方法来读取和迭代其底层 PHP resource

  • 获取大小()
  • 告诉()
  • eof()
  • 可搜索()
  • 寻找()
  • 倒带()
  • 是可写的()
  • 写($字符串)
  • 可读()
  • 阅读($长度)
  • 获取内容()
  • 获取元数据($key = null)

上传的文件

文件上传$_FILES可从 Request 对象的 getUploadedFiles()方法获得。这将返回一个以元素名称为键的数组 input

$files = $request->getUploadedFiles();
图 11:获取上传文件

数组中的每个对象$files都是以下方法的实例 Psr\Http\Message\UploadedFileInterface并支持以下方法:

  • 获取流()
  • 移动到($targetPath)
  • 获取大小()
  • 获取错误()
  • 获取客户端文件名()
  • getClientMediaType()

请参阅说明书,了解如何使用 POST 表单上传文件。

请求帮手

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

检测 XHR 请求

您可以通过检查标头是否使用请求的方法来检测X-Requested-WithXHRXMLHttpRequest请求getHeaderLine()

POST /path HTTP/1.1
Host: example.com
Content-type: application/x-www-form-urlencoded
Content-length: 7
X-Requested-With: XMLHttpRequest

foo=bar
图 13:XHR 请求示例。
if ($request->getHeaderLine('X-Requested-With') === 'XMLHttpRequest') {
    // Do something
}

内容类型

您可以使用 Request 对象的getHeaderLine()方法获取 HTTP 请求内容类型。

$contentType = $request->getHeaderLine('Content-Type');

内容长度

您可以使用 Request 对象的方法获取 HTTP 请求内容长度getHeaderLine()

$length = $request->getHeaderLine('Content-Length');

服务器参数

要获取与传入请求环境相关的数据,您需要使用getServerParams().

例如,要获取单个服务器参数:

$params = $request->getServerParams();
$authorization = $params['HTTP_AUTHORIZATION'] ?? null;

POST 参数

如果请求方法是POST并且Content-Typeapplication/x-www-form-urlencoded or ,您可以按如下方式multipart/form-data检索所有参数:POST

// Get all POST parameters
$params = (array)$request->getParsedBody();

// Get a single POST parameter
$foo = $params['foo'];

路由对象

有时在中间件中,您需要路由参数。

在此示例中,我们首先检查用户是否已登录,其次检查用户是否有权查看他们尝试查看的特定视频。

$app
  ->get('/course/{id}', Video::class . ':watch')
  ->add(PermissionMiddleware::class);
<?php

use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
use Slim\Routing\RouteContext;

class PermissionMiddleware
{
    public function __invoke(Request $request, RequestHandler $handler)
    {
        $routeContext = RouteContext::fromRequest($request);
        $route = $routeContext->getRoute();
        
        $courseId = $route->getArgument('id');
        
        // do permission logic...

        return $handler->handle($request);
    }
}

从路由中获取基本路径

要从路由中获取基本路径,只需执行以下操作:

<?php

use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\Factory\AppFactory;
use Slim\Routing\RouteContext;

require __DIR__ . '/../vendor/autoload.php';

$app = AppFactory::create();

$app->get('/', function(Request $request, Response $response) {
    $routeContext = RouteContext::fromRequest($request);
    $basePath = $routeContext->getBasePath();
    // ...

    return $response;
});

属性

使用 PSR-7 可以将对象/值注入请求对象以进行进一步处理。在您的应用程序中,中间件通常需要将信息传递给您的路由闭包,而这样做的方法是通过属性将其添加到请求对象。

例如,在您的请求对象上设置一个值。

use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\RequestHandlerInterface as RequestHandler;

$app->add(function (Request $request, RequestHandler $handler) {
    // Add the session storage to your request as [READ-ONLY]
    $request = $request->withAttribute('session', $_SESSION);
    
    return $handler->handle($request);
});

例如,如何检索值。

use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;

$app->get('/test', function (Request $request, Response $response) {
    // Get the session from the request
    $session = $request->getAttribute('session');
    
    $response->getBody()->write('Yay, ' . $session['name']);
    
    return $response;
});

请求对象也有批量功能。$request->getAttributes()$request->withAttributes()