ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Hyperf 请求对象(Request Object)完整指南:PSR-7 标准实现、输入预处理与生命周期事件

Hyperf 请求对象(Request Object)完整指南:PSR-7 标准实现、输入预处理与生命周期事件 后端微服务【免费下载链接】hyperf A coroutine framework that focuses on hyperspeed and flexibility. Building microservice or middleware with ease.项目地址https://gitcode.com/gh_mirrors/hy/hyperf点击查看免费下载导读本文围绕 Hyperf 框架的请求对象Request Object展开该对象完全基于 PSR-7 标准实现由hyperf/http-message组件提供底层支撑并经由Hyperf\HttpServer\Request代理对象对外提供丰富的便捷方法。你将掌握如何在 Hyperf 中通过依赖注入获取请求对象、读取路由参数、获取 Path/URL/Method、处理表单与 JSON 输入、读取 Cookie 与上传文件以及如何借助enable_request_lifecycle配置监听请求生命周期事件。文末结合当前仓库源码说明底层实现原理方便你深入理解框架内部机制。一、PSR-7 标准与不可变机制Request Object在 Hyperf 中完整实现了 PSR-7。该类同时实现Psr\Http\Message\ServerRequestInterface与Swow\Psr7\Message\ServerRequestPlusInterface。注意PSR-7 标准为Request设计了不可变immutable机制。所有以with开头的方法如withHeader()、withQueryParams()、withAttribute()的返回值都是一个新的对象不会修改原对象的值。以 src/http-message/src/Server/Request.php 中的withCookieParams()为例其内部通过clone $this生成副本后再修改属性并返回。因此在编写代码时若要保留修改结果必须将返回值重新赋值例如$request $request-withHeader(X-Foo, bar)。二、安装与适用场景该组件完全独立适用于任意 PHP 框架项目安装命令如下composer require hyperf/http-message如果在其他框架中使用仅支持 PSR-7 标准定义的 API具体细节以 PSR-7 规范为准本文档所描述的便捷方法仅在 Hyperf 框架内生效。三、获取 Request 对象在 Hyperf 中通过依赖注入Dependency Injection即可获得Hyperf\HttpServer\Request实例。你只需在控制器方法中注入Hyperf\HttpServer\Contract\RequestInterfacedeclare(strict_types1); namespace App\Controller; use Hyperf\HttpServer\Contract\RequestInterface; use Hyperf\HttpServer\Annotation\AutoController; #[AutoController] class IndexController { public function info(RequestInterface $request) { // ... } }底层原理这里注入的实际上是一个代理对象proxy object它代理的是当前请求对应的PSR-7 Request Object。也就是说该对象只能在onRequest生命周期内访问。查看 src/http-server/src/Request.php 可以发现代理类内部通过RequestContext::get()获取真正的 PSR-7 请求见 getRequest()再通过call()方法将getMethod()、getUri()、getQueryParams()等 PSR-7 方法调用转发给底层请求对象而all()、input()、route()等 Hyperf 特有方法则在代理层直接实现。3.1 依赖注入与路由参数如果你想通过控制器方法的参数直接获取路由参数只需在依赖之后声明相应参数即可框架会自动注入匹配的参数。例如定义如下路由// 注解模式 #[GetMapping(path: /user/{id:\d})] // 配置模式 use Hyperf\HttpServer\Router\Router; Router::addRoute([GET, HEAD], /user/{id:\d}, [\App\Controller\IndexController::class, user]);那么可以通过在方法参数中声明$id来获取Query参数iddeclare(strict_types1); namespace App\Controller; use Hyperf\HttpServer\Contract\RequestInterface; use Hyperf\HttpServer\Annotation\AutoController; #[AutoController] class IndexController { public function info(RequestInterface $request, int $id) { // ... } }除了依赖注入还可以通过route()方法访问路由参数declare(strict_types1); namespace App\Controller; use Hyperf\HttpServer\Contract\RequestInterface; use Hyperf\HttpServer\Annotation\AutoController; #[AutoController] class IndexController { public function info(RequestInterface $request) { // 存在则返回不存在则返回默认值 null $id $request-route(id); // 存在则返回不存在则返回默认值 0 $id $request-route(id, 0); // ... } }从源码看route()的实现src/http-server/src/Request.php#L73-L81是从请求属性中取出Dispatched对象再从$route-params中按 key 取值未命中时返回默认值。3.2 请求 Path 与 Method除了 PSR-7 标准提供的 API 外Hyperf\HttpServer\Contract\RequestInterface还提供了丰富的请求查看方法。完整的方法清单见 src/http-server/src/Contract/RequestInterface.php。获取请求 Pathpath()方法返回请求的 path 信息。例如请求地址为http://domain.com/foo/bar?baz1则path()返回foo/bar$uri $request-path();is(...$patterns)方法用于判断请求 path 是否匹配指定规则支持*通配符if ($request-is(user/*)) { // ... }从实现上看is()内部使用Str::is()对decodedPath()即rawurldecode($this-path())进行模式匹配因此路径中的 URL 编码字符会被正确解码后参与匹配。获取请求 URL使用url()或fullUrl()获取完整的请求URL。url()返回不含Query Parameters的URLfullUrl()则包含Query Parameters// 不含 query parameters $url $request-url(); // 包含 query parameters $url $request-fullUrl();获取请求 MethodgetMethod()返回请求的HTTP方法。也可以使用isMethod(string $method)校验请求方法是否匹配指定规则$method $request-getMethod(); if ($request-isMethod(post)) { // ... }isMethod()的实现会对传入参数做strtoupper()归一化后与getMethod()比较见 src/http-server/src/Request.php#L318-L321因此大小写混写也能正确匹配。3.3 PSR-7 请求及其方法hyperf/http-message组件本身即 PSR-7 标准的实现因此可以通过注入的Request Object直接调用 PSR-7 相关方法。如果在注入时声明为 PSR-7 标准的Psr\Http\Message\ServerRequestInterface接口框架会自动将其转换为Hyperf\HttpServer\Request对象其能力与Hyperf\HttpServer\Contract\RequestInterface等价。建议注入时使用Hyperf\HttpServer\Contract\RequestInterface以便在 IDE 中获得 Hyperf 特有方法的自动补全。PSR-7 核心方法包括均可在代理对象上直接调用最终转发给底层 PSR-7 请求消息通用方法getProtocolVersion()、getHeaders()、hasHeader($name)、getHeader($name)、getHeaderLine($name)、getBody()等请求方法getRequestTarget()、getMethod()、getUri()、withUri()服务端请求方法getServerParams()、getCookieParams()、getQueryParams()、getUploadedFiles()、getParsedBody()、getAttributes()、getAttribute($name, $default)等。四、输入预处理与归一化4.1 获取输入获取所有输入使用all()方法以array形式获取全部输入数据$all $request-all();从源码看src/http-server/src/Request.php#L560-L572all()返回的数据由parsedBody解析后的请求体通常来自 POST 表单或 JSON与queryParamsQuery 参数合并而成且结果会缓存在协程上下文http.request.parsedData中同一次请求内多次调用不会重复解析。获取特定输入值通过input(string $key, $default null)和inputs(array $keys, $default null): array获取一个或多个任意形式的输入值// 存在则返回不存在则返回 null $name $request-input(name); // 存在则返回不存在则返回默认值 Hyperf $name $request-input(name, Hyperf);如果表单数据是数组形式可以使用dot语法访问$name $request-input(products.0.name); $names $request-input(products.*.name);input()与inputs()的取值均基于data_get()辅助函数来自hyperf/collection因此天然支持点号嵌套与*通配遍历。从 Query String 获取输入input、inputs可以获取整个请求的输入数据包括Query Parameters而query(?string $key null, $default null)只能从 query string 中获取数据// 存在则返回不存在则返回 null $name $request-query(name); // 存在则返回不存在则返回默认值 Hyperf $name $request-query(name, Hyperf); // 不传参数时以关联数组形式返回全部 Query 参数 $name $request-query();获取 JSON 输入信息如果请求Body的数据格式为JSON只要Request Object的Content-TypeHeader 正确设置为application/json就可以通过input(string $key, $default null)访问JSON数据甚至可以使用dot语法读取JSON数组// 存在则返回不存在则返回 null $name $request-input(user.name); // 存在则返回不存在则返回默认值 Hyperf $name $request-input(user.name, Hyperf); // 以数组形式返回全部 Json 数据 $name $request-all();底层原理JSON 的解析发生在请求对象构建阶段。在 src/http-message/src/Server/Request.php#L467-L493 的normalizeParsedBody()中框架会根据Content-Type头自动去除;后的 charset 等参数查找对应的解析器。默认解析器 Parser 支持以下四种 Content-TypeContent-Type解析器application/jsonJsonParsertext/jsonJsonParserapplication/xmlXmlParsertext/xmlXmlParser若 JSON 字符串非法框架会抛出Hyperf\HttpMessage\Exception\BadRequestHttpExceptionHTTP 400 异常。你也可以通过实现Hyperf\HttpMessage\Server\RequestParserInterface并注册到容器中来扩展自定义 Content-Type 的解析器见 getParser()。判断输入值是否存在使用has($keys)判断请求中是否存在某值存在返回true不存在返回false。$keys可以是字符串也可以是包含多个字符串的数组只有全部存在时才返回true// 只检查一个值 if ($request-has(name)) { // ... } // 同时检查多个值 if ($request-has([name, email])) { // ... }该判断基于Arr::has()对合并后的输入数据做 key 存在性检查同样支持点号嵌套语法如has(products.0.name)。4.2 Cookies从请求获取 Cookies使用getCookieParams()获取请求的全部Cookies返回关联数组$cookies $request-getCookieParams();如果需要获取某个Cookie的值可以通过cookie(string $key, $default null)方法// 存在则返回不存在则返回 null $name $request-cookie(name); // 存在则返回不存在则返回默认值 Hyperf $name $request-cookie(name, Hyperf);此外还可以使用hasCookie(string $key): bool判断 Cookie 是否已设置源码见 src/http-server/src/Request.php#L300-L303。在底层Swoole 请求的cookie数据会在 loadFromSwooleRequest() 中被直接转存为cookieParams。4.3 文件上传获取上传文件使用file(string $key, $default): ?Hyperf\HttpMessage\Upload\UploadedFile获取请求中的上传文件对象。若存在则返回Hyperf\HttpMessage\Upload\UploadedFile实例。该类继承自 PHP 的SplFileInfo并提供了多种文件交互方法// 存在则返回 Hyperf\HttpMessage\Upload\UploadedFile 对象不存在则返回 null $file $request-file(photo);检查文件是否存在使用hasFile(string $key): bool确认请求中是否存在某文件if ($request-hasFile(photo)) { // ... }验证上传是否成功除了检查文件是否存在还可以通过isValid(): bool验证上传文件是否有效if ($request-file(photo)-isValid()) { // ... }从源码看src/http-message/src/Upload/UploadedFile.php#L91-L96isValid()要求上传错误码为UPLOAD_ERR_OK且is_uploaded_file()校验通过即文件确实经由 HTTP 上传而非伪造路径。文件路径与扩展名UploadedFile类还包含访问文件完整路径和扩展名的方法。getExtension()依据文件内容对应的客户端原始文件名后缀确定扩展名该扩展名可能与客户端提供的扩展名不同// 该路径是上传文件的临时路径 $path $request-file(photo)-getPath(); // 由于 Swoole 上传文件的 tmp_name 不保留原始文件名 // 此方法已被重写用于获取原始文件名的后缀 $extension $request-file(photo)-getExtension();具体实现见 src/http-message/src/Upload/UploadedFile.php#L71-L76getExtension()取getClientFilename()客户端原始文件名按.切分后的最后一段。该类还提供getSize()、getError()、getClientFilename()、getClientMediaType()、getStream()等 PSR-7 标准方法以及getMimeType()基于mime_content_type()探测临时文件真实 MIME 类型和toArray()等便捷方法。保存上传文件上传文件在永久保存前存放于临时位置若不保存请求结束后文件将被删除因此需要将其持久化。使用moveTo(string $targetPath): void将临时文件移动到$targetPath$file $request-file(photo); $file-moveTo(/foo/bar.jpg); // 通过 isMoved(): bool 判断文件是否已移动 if ($file-isMoved()) { // ... }底层实现src/http-message/src/Upload/UploadedFile.php#L154-L169会根据运行环境选择移动方式CLISwoole/Swow 协程环境下使用rename()常规 PHP-FPM 环境下使用move_uploaded_file()。移动失败会抛出RuntimeException同一文件重复调用moveTo()也会因为isMoved()为真而抛异常。五、请求生命周期相关事件如果在服务配置中启用了enable_request_lifecycle每个请求都会触发以下三个事件便于你在请求到达、处理完成、协程销毁等关键节点挂载监听逻辑。5.1 配置示例以下配置省略了其他无关代码?php declare(strict_types1); use Hyperf\Server\Event; use Hyperf\Server\Server; use Hyperf\Server\ServerInterface; return [ servers [ [ name http, type ServerInterface::SERVER_HTTP, host 0.0.0.0, port 9501, sock_type SWOOLE_SOCK_TCP, callbacks [ Event::ON_REQUEST [Hyperf\HttpServer\Server::class, onRequest], ], options [ // 是否启用请求生命周期事件 enable_request_lifecycle false, ], ], ], ];将enable_request_lifecycle设置为true即可开启请求生命周期事件。5.2 事件列表三个事件类均位于 src/http-server/src/Event 目录Hyperf\HttpServer\Event\RequestReceived请求被接收时触发。对应事件类定义见 src/http-server/src/Event/RequestReceived.php可用于请求到达时的日志记录、指标采集等。Hyperf\HttpServer\Event\RequestHandled请求处理完成时触发事件类见 src/http-server/src/Event/RequestHandled.php适合在响应生成后做统一的后置处理。Hyperf\HttpServer\Event\RequestTerminated承载当前请求的协程被销毁时触发事件类见 src/http-server/src/Event/RequestTerminated.php适合做协程级资源清理。参考仓库中的tracer组件通过RequestTraceListener监听这些生命周期事件来记录请求链路见 src/tracer/src/Listener/RequestTraceListener.php说明该机制已被官方组件用于实际业务场景。rpc-server组件也定义了同名的生命周期事件见 src/rpc-server/src/Event可一并参考。六、小结与最佳实践注入接口而非实现类始终注入Hyperf\HttpServer\Contract\RequestInterface既可获得 IDE 自动补全又保持与底层 PSR-7 实现的解耦理解代理机制RequestInterface注入的是代理对象只在onRequest生命周期内有效不可跨请求协程保存使用牢记不可变性PSR-7 的with*方法返回新对象使用后需重新赋值统一入口读取输入input()/all()合并了 Query 参数与解析后的请求体表单、JSON、XML配合dot语法可高效处理嵌套结构JSON 解析依赖 Content-Type确保客户端请求头Content-Type: application/json正确设置否则 JSON 体不会被解析及时保存上传文件临时文件在请求结束后会被清理务必在请求内调用moveTo()持久化按需开启生命周期事件默认enable_request_lifecycle为false仅在需要监听RequestReceived/RequestHandled/RequestTerminated时开启可避免不必要的开销。赞分享后端微服务【免费下载链接】hyperf A coroutine framework that focuses on hyperspeed and flexibility. Building microservice or middleware with ease.项目地址https://gitcode.com/gh_mirrors/hy/hyperf点击查看免费下载相关推荐Flask 请求与响应对象Request 与 Response完全指南从信封隐喻到 WSGI 请求生命周期Flask 请求与响应对象Request 与 Response完全指南从信封隐喻到 WSGI 请求生命周期 导读 本文围绕 Flask 框架中最基础也最核人工智能AI 应用AI AgentHyperf 事件机制完全指南PSR-14 事件分发、监听器注册与生命周期事件实战Hyperf 事件机制完全指南PSR 14 事件分发、监听器注册与生命周期事件实战 Hyperf 的事件机制基于 PSR 14 标准实现通过将事件触发方业后端Web框架微服务RPC框架异步编程Yii 2 应用对象Application深入解析配置属性、请求事件与生命周期Yii 2 应用对象Application深入解析配置属性、请求事件与生命周期 本篇技术指南以官方指南 structure applications ht后端Web框架上一篇Typhoon OCR 1.5 2B 8位模型部署指南本地服务器搭建与Docker容器化方案下一篇Closure Compiler单元测试集成确保优化过程的代码质量创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表