您的控制器或端点需要一种访问活动的 PSR-7 请求和生成响应的方法。在本节中,我们将介绍在 MVC 设置中使用请求/响应的方法。
注意 中间件和原生 PSR-15 处理器可以直接接收 PSR-7 对象。
获取用户请求的最快方法是进行 Psr\Http\Message\ServerRequestInterface
方法注入。
namespace App\Endpoint\Web;
use Psr\Http\Message\ServerRequestInterface;
use Spiral\Core\Container\SingletonInterface;
class HomeController implements SingletonInterface
{
public function index(ServerRequestInterface $request): void
{
dump($request->getHeaders());
}
}
警告 不允许在单例中使用
Psr\Http\Message\ServerRequestInterface
作为构造函数注入。
一旦获得请求,您就可以使用它来调用 PSR-7 标准 中可用的所有读取方法。
或者,您可以使用上下文管理器 Spiral\Http\Request\InputManager
,该管理器可以存储在单例服务/控制器中,并且始终指向当前的用户请求。此对象提供了几个用户友好的方法来读取传入数据。
namespace App\Endpoint\Web;
use Spiral\Core\Container\SingletonInterface;
use Spiral\Http\Request\InputManager;
class HomeController implements SingletonInterface
{
private InputManager $input;
public function __construct(InputManager $input)
{
$this->input = $input;
}
public function index(): void
{
dump($this->input->query->all());
}
}
您还可以通过 Spiral\Prototype\Traits\PrototypeTrait
访问 Spiral\Http\Request\InputManager
。
namespace App\Endpoint\Web;
use Spiral\Prototype\Traits\PrototypeTrait;
class HomeController
{
use PrototypeTrait;
public function index(): void
{
// $this->request is alias to $this->input
dump($this->request->data->all());
}
}
警告 建议避免直接访问
Psr\Http\Message\ServerRequestInterface
和Spiral\Http\Request\InputManager
,除非必要,请改用请求过滤器。
您可以使用 Spiral\Http\Request\InputManager
通过其名称(嵌套结构允许使用点符号)访问完整的输入数据数组或任何特定字段。每个输入结构都使用 Spiral\Http\Request\InputBag
类表示,该类具有一组常用方法。让我们以访问查询为例:
/** @var \Spiral\Http\Request\InputManager $input */
// Get instance of QueryBag associated with query data
dump($input->query);
// Get all query params as array
dump($input->query->all());
// Count of query params
dump($input->query->count());
// Check if parameter "name" is presented in query
dump($input->query->has('name'));
// Get value for parameter "name"
dump($input->query->get('name'));
// Both get and has methods support dot notation for nested structures
dump($input->query->has('name.subName'));
dump($input->query->get('name.subName'));
// Fetch only given query params (no dot notation allowed), only existed records will be returned
dump($input->query->fetch(["name", "nameB"]);
// Fetch only given query params (no dot notation allowed), non existed records will be filled with `null`
dump($input->query->fetch(['name', 'nameB'], true, null);
// In addition query get method has short alias in input manager
dump($input->query('name'));
警告 在 Input bag 具有
null
值的键时,new \Spiral\Http\Request\InputBag(['name' => null]);
,方法$input->query->has('name')
将返回true
,但isset($input->query['name'])
将返回false
。
我们可以使用 Spiral\Http\Request\InputManager
中的 'headers' 输入包和 header
方法来访问输入标头。 Spiral\Http\Request\HeadersBag
有一些我们必须提及的补充:
Spiral\Http\Request\HeadersBag
将自动规范化请求的标头名称/** @var \Spiral\Http\Request\InputManager $sinput */
// Get all headers as array
dump($input->headers->all());
// Will be normalized into "Accept"
dump($input->headers->get('accept'));
// Return Accept header as array of values
dump($input->headers->get('accept', false));
dump($input->header('accept'));
/** @var \Spiral\Http\Request\InputManager $input */
dump($input->cookies->all());
dump($input->cookie('name'));
/** @var \Spiral\Http\Request\InputManager $input */
dump($input->server->all());
dump($input->server('name'));
注意
Spiral\Http\Request\ServerBag
将自动规范化所有请求的服务器值。这样就可以在不使用所有大写字母作为名称的情况下获取值:
/** @var \Spiral\Http\Request\InputManager $input */
dump($input->server('SERVER_PORT'));
dump($input->server('server-port'));
/** @var \Spiral\Http\Request\InputManager $input */
dump($input->data->all());
dump($input->data('name'));
// An alias
dump($input->post('name'));
如果要从 POST 数据中读取值,然后从 Query 中读取值,只需使用 input
方法。
dump($input->input('name'));
dump($input->attributes->all());
dump($input->attribute('name'));
要获取上传文件的列表或单个文件,请使用 files
包和 file
方法。每个上传的文件实例都使用 Psr\Http\Message\UploadedFileInterface
表示,它是 PSR7 的一部分。
/** @var \Spiral\Http\Request\InputManager $input */
dump($this->input->files->all());
dump($this->input->file('upload'));
注意 根据 PSR,所有文件都组织成一个逻辑层次结构,这与 php 处理上传文件的默认方式不同。您可以使用点符号来访问嵌套的文件实例。
除了数据方法和 InputBags 之外,Spiral\Http\Request\InputManager
还提供了一组方法来读取活动请求的各种属性。
/** @var \Spiral\Http\Request\InputManager $input */
//Request Uri path, will always include leading /
dump($input->path());
//Active request Uri instance
dump($input->uri());
//GET, POST, PUT...
dump($input->method());
//Check if connection made over https
dump($input->isSecure());
//Check request headers to verify that request made over ajax
dump($input->isAjax());
//Check is request expects application/json as response (Accept: application/json)
dump($input->isJsonExpected());
//Receive client ip address (this method uses _SERVER value and may not be correct in some cases).
dump($input->remoteAddress());
要访问 Spiral\Http\Request\InputBag
而不使用 __get
:
/** @var \Spiral\Http\Request\InputManager $input */
dump($input->bag('data')->all());
使用 Spiral\Bootloader\Http\HttpBootloader
,您可以添加自己的输入包。
例如,我们需要接收作为 Symfony\Bridge\PsrHttpMessage\Factory\UploadedFile
对象的上传文件。让我们创建一个输入包类。
namespace App\Http\Request;
use Spiral\Http\Request\InputBag;
use Symfony\Bridge\PsrHttpMessage\Factory\UploadedFile;
final class FilesBag extends InputBag
{
public function __construct(array $data, string $prefix = '')
{
foreach ($data as $name => $file) {
$data[$name] = new UploadedFile($file, fn(): string => $this->getTemporaryPath());
}
parent::__construct($data, $prefix);
}
protected function getTemporaryPath(): string
{
return \tempnam(\sys_get_temp_dir(), \uniqid('symfony', true));
}
}
之后,通过 HttpBootloader
中的 addInputBag
方法添加创建的 FilesBag
。
namespace App\Application\Bootloader;
use App\Http\Request\FilesBag;
use Spiral\Boot\Bootloader\Bootloader;
use Spiral\Bootloader\Http\HttpBootloader;
class AppBootloader extends Bootloader
{
public function init(HttpBootloader $http): void
{
$http->addInputBag('symfonyFiles', [
'class' => FilesBag::class,
'source' => 'getUploadedFiles',
'alias' => 'symfony-file'
]);
}
}
Spiral\Http\Request\InputManager
的方法没有 get
前缀。其原因是位于外部包 spiral/filters
中,该包需要通过 Spiral\Filters\InputInterface
提供数据源:
namespace Spiral\Filters;
// ...
interface InputInterface
{
public function withPrefix(string $prefix, bool $add = true): InputInterface;
public function getValue(string $source, string $name = null);
}
您可以通过 Spiral\Filters\InputInterface
的短符号来调用 Spiral\Http\Request\InputManager
方法。这两种方法将产生相同的数据集。
use Spiral\Filters\InputInterface;
use Spiral\Http\Request\InputManager;
public function index(InputInterface $inputSource, InputManager $inputManager): void
{
dump($inputManager->query('name'));
dump($inputSource->getValue('query', 'name'));
dump($inputManager->path());
dump($inputSource->getValue('path'));
}
这种方法用于将传入数据映射到请求过滤器。
警告 您必须激活
Spiral\Bootloader\Security\FiltersBootloader
才能访问Spiral\Filters\InputInterface
。
您可以从控制器返回实例 Psr\Http\Message\ResponseInterface
,它将直接发送给用户。
namespace App\Endpoint\Web;
use Nyholm\Psr7\Response;
use Psr\Http\Message\ResponseInterface;
class HomeController
{
public function index(): ResponseInterface
{
$response = new Response(200);
$response->getBody()->write("hello world");
return $response;
}
}
默认启用的 PSR-15 处理器为您提供了根据返回的字符串或输出缓冲区的内容自动生成响应的能力:
namespace App\Endpoint\Web;
class HomeController
{
public function index(): string
{
return "hello world";
}
}
与以下内容相同:
namespace App\Endpoint\Web;
class HomeController
{
public function index(): void
{
echo "hello world";
}
}
注意 我们建议仅在开发过程中使用输出缓冲区来显示调试信息。坚持严格的返回类型。
默认的 PSR-15 还支持将转换为 JSON 的数组和 JsonSerializable
响应:
namespace App\Endpoint\Web;
class HomeController
{
public function index(): array
{
return [
'status' => 200,
'data' => ['some' => 'json']
];
}
}
从手动创建响应中抽象出来的正确方法是使用 Psr\Http\Message\ResponseFactoryInterface
:
namespace App\Endpoint\Web;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
class HomeController
{
public function index(ResponseFactoryInterface $responseFactory): ResponseInterface
{
$response = $responseFactory->createResponse(200);
$response->getBody()->write("hello world");
return $response;
}
}
要生成更复杂的响应,请使用 ResponseFactoryInterface
包装器 Spiral\Http\ResponseWrapper
,它添加了许多用于更简单的响应生成的方法:
namespace App\Endpoint\Web;
use Psr\Http\Message\ResponseInterface;
use Spiral\Http\ResponseWrapper;
class HomeController
{
public function index(ResponseWrapper $response): ResponseInterface
{
return $response->attachment(
__FILE__,
'controller.php'
)->withAddedHeader('Key', 'value');
}
}
您还可以通过 PrototypeTrait
和属性 response
访问包装器:
namespace App\Endpoint\Web;
use Psr\Http\Message\ResponseInterface;
use Spiral\Prototype\Traits\PrototypeTrait;
class HomeController
{
Use PrototypeTrait;
public function index(): ResponseInterface
{
// temporary redirect
return $this->response->redirect('https://google.com', 307);
}
}
要创建 HTML 响应:
public function index(): ResponseInterface
{
return $this->response->html('hello world');
}
要创建 application/json
响应:
public function index(): ResponseInterface
{
return $this->response->json(
['something' => 123],
200
);
}
要发送附件:
public function index(): ResponseInterface
{
return $this->response->attachment(__FILE__, 'name.php');
}
注意 您还可以使用
Psr\Http\Message\StreamInterface
作为第一个参数,并指定您的 MIME 类型作为第三个选项。