Request 对象详解

Request 对象是 Viswoole 框架对 Swoole HTTP 请求的面向对象封装,提供了丰富的参数获取、请求头操作、文件处理等能力。它实现了 RequestInterface 接口,并支持通过魔术方法访问底层 Swoole 请求对象的属性和方法。

获取 Request 对象

在控制器中注入

php
use Viswoole\HttpServer\Facade\Request;

public static function index(): array
{
    // 使用 Facade 静态调用
    $method = Request::getMethod();
    $ip = Request::ip();

    return ['method' => $method, 'ip' => $ip];
}

通过方法参数注入

php
use Viswoole\HttpServer\Request;

public static function data(Request $request): array
{
    return [
        'uri' => (string)$request->getUri(),
        'method' => $request->getMethod()
    ];
}

核心方法

获取参数

get() - GET 查询参数

从 URL 查询字符串(?key=value)中获取参数:

php
/**
 * 获取单个参数
 *
 * @param string|null $key 参数名,null 返回全部
 * @param mixed $default 默认值
 * @return mixed
 */
Request::get(?string $key = null, mixed $default = null): mixed

示例:

php
// URL: /search?keyword=php&category=web&page=1

// 获取单个参数
$keyword = Request::get('keyword');        // "php"
$category = Request::get('category');      // "web"

// 带默认值
$page = Request::get('page', 1);           // 1
$sort = Request::get('sort', 'created_at'); // "created_at"

// 获取全部 GET 参数
$all = Request::get();                     // ['keyword' => 'php', ...]

post() - POST 请求体参数

从 POST 请求体中获取参数(支持 JSON 和表单数据):

php
/**
 * @param string|null $key 参数名,null 返回全部
 * @param mixed $default 默认值
 * @return mixed
 */
Request::post(?string $key = null, mixed $default = null): mixed

示例:

php
// Content-Type: application/json
// Body: { "name": "张三", "email": "test@example.com" }

$name = Request::post('name');              // "张三"
$email = Request::post('email');            // "test@example.com"

// 获取全部 POST 数据
$data = Request::post();                    // ['name' => '张三', ...]

注意:Content-Typeapplication/json 时,框架会自动解析 JSON 并填充到 post 数据中。

param() - 智能参数获取

根据当前请求方法自动选择从 GET 或 POST 获取参数:

php
/**
 * GET 请求 → 从查询参数获取
 * 其他请求 → 从 POST 获取
 *
 * @param string|null $key 参数名
 * @param mixed $default 默认值
 * @param array|string|null $filter 额外过滤器
 * @return mixed
 */
Request::param(?string $key = null, mixed $default = null, array|string|null $filter = null): mixed

示例:

php
// GET /user?id=100 → 从 GET 获取
$id = Request::param('id');

// POST /user (Body: { "id": 100 }) → 从 POST 获取
$id = Request::param('id');

params() - 批量参数获取

一次性获取多个参数:

php
/**
 * @param array|string|null $rule 取值规则
 *   - string: 取单个字段
 *   - [key1, key2]: 取多个字段
 *   - [key => default]: 取多个并设置默认值
 *   - null: 取全部参数
 * @param bool $isShowNull 是否保留 null 值的字段
 * @return array
 */
Request::params(array|string|null $rule = null, bool $isShowNull = true): array

示例:

php
// 获取全部参数
$all = Request::params();
// ['keyword' => 'php', 'page' => '1', 'size' => '10']

// 获取指定字段
$data = Request::params(['keyword', 'page', 'size']);
// ['keyword' => 'php', 'page' => '1', 'size' => '10']

// 获取字段并设置默认值
$data = Request::params([
    'keyword' => '',
    'page' => 1,
    'size' => 10,
    'sort' => 'created_at'
]);
// ['keyword' => 'php', 'page' => '1', 'size' => '10', 'sort' => 'created_at']

// 保留 null 值(默认行为)
$data = Request::params(null, true); // 保留 null
// 移除 null 值
$data = Request::params(null, false); // 移除 null

请求头操作

getHeader() - 获取请求头

php
/**
 * @param string|null $key 标头名称(不区分大小写)
 * @param mixed $default 默认值
 * @return array|string|null key 为 null 时返回数组,否则返回字符串
 */
Request::getHeader(?string $key = null, mixed $default = null): array|string|null

示例:

php
// 获取单个标头(返回字符串)
$contentType = Request::getHeader('content-type');     // "application/json"
$authorization = Request::getHeader('Authorization');  // "Bearer xxxxx"

// 获取全部标头(返回关联数组)
$headers = Request::getHeader();
// [
//     'host' => 'localhost:9501',
//     'content-type' => 'application/json',
//     'authorization' => 'Bearer xxxxx'
// ]

// 带默认值
$accept = Request::getHeader('X-Custom', 'default-value');

注意: 所有标头键名统一转换为小写。

hasHeader() - 检查标头是否存在

php
if (Request::hasHeader('authorization')) {
    // 存在 Authorization 标头
}

setHeader() - 设置/修改请求头

php
/**
 * @param string $name 标头名称
 * @param string $value 标头值
 * @return RequestInterface 支持链式调用
 */
Request::setHeader(string $name, string $value): RequestInterface

请求信息

基础信息方法

php
// 获取 HTTP 方法(GET、POST、PUT、DELETE 等)
Request::getMethod(): string

// 获取客户端 IP 地址
// 优先读取 X-Real-IP 标头,回退到 remote_addr
Request::ip(): string

// 获取完整 URI 对象
Request::getUri(): Uri

// 获取请求路径(不含查询参数)
// 优先 path_info,回退到 request_uri
Request::target(): string
// 或使用别名
Request::getPath(): string

// 判断是否为 HTTPS 请求
Request::https(): bool

// 获取 HTTP 协议版本(如 "1.1"、"2")
Request::getProtocolVersion(): string

使用示例:

php
public static function info(): array
{
    return [
        'method' => Request::getMethod(),           // "POST"
        'ip' => Request::ip(),                      // "192.168.1.100"
        'uri' => (string)Request::getUri(),         // "http://example.com/api/user?page=1"
        'path' => Request::getPath(),               // "/api/user"
        'is_https' => Request::https(),             // true/false
        'protocol' => Request::getProtocolVersion() // "1.1"
    ];
}

URI 对象

getUri() 返回的 Uri 对象提供对 URL 各组成部分的访问:

php
$uri = Request::getUri();

$uri->getScheme();    // "http" 或 "https"
$uri->getHost();      // "example.com"
$uri->getPort();      // 8080 或 null
$uri->getPath();      // "/api/user"
$uri->getQuery();     // "page=1&size=10"
$uri->getUserInfo();  // "user:pass" 或 null
$uri->__toString();   // 完整 URI 字符串
php
/**
 * 获取 Cookie 值
 *
 * @param string|null $key Cookie 名称
 * @param mixed $default 默认值
 * @return mixed
 */
Request::cookie(?string $key = null, mixed $default = null): mixed

示例:

php
// 获取单个 Cookie
$sessionId = Request::cookie('session_id');
$token = Request::cookie('token', ''); // 默认空字符串

// 获取全部 Cookie
$cookies = Request::cookie();
// ['session_id' => 'abc123', 'token' => 'xyz789']

Server 信息

php
/**
 * 获取 $_SERVER 等价信息
 *
 * @param string|null $key 键名
 * @param mixed $default 默认值
 * @return mixed
 */
Request::getServer(?string $key = null, mixed $default = null): mixed

常用 Server 字段:

php
// 请求时间戳(浮点数)
$requestTime = Request::getServer('request_time_float');

// 请求 URI(含查询参数)
$requestUri = Request::getServer('request_uri');

// PATH_INFO
$pathInfo = Request::getServer('path_info');

// 远程地址
$remoteAddr = Request::getServer('remote_addr');

// 远程端口
$remotePort = Request::getServer('remote_port');

// 服务器软件
$serverSoftware = Request::getServer('server_software');

文件上传

php
/**
 * 获取上传文件
 *
 * @param string|null $key 表单字段名
 * @return UploadedFile|UploadedFile[]|array|null
 */
Request::files(?string $key = null): UploadedFile|array|null

详细用法参见 文件上传处理

其他实用方法

判断响应类型

php
// 判断客户端是否期望 JSON 响应
if (Request::isJson()) {
    return $response->json($data);
}

// 获取客户端期望的资源类型别名
$type = Request::getAcceptType();
// 可能返回: 'json', 'html', 'text', 'xml', 'image', '*'

// 支持的类型常量
Request::ACCEPT_TYPE = [
    'html' => 'text/html,application/xhtml+xml,*/*',
    'json' => 'application/json,text/x-json,text/json',
    'image' => 'image/png,image/jpg,...',
    // ...
];

获取原始请求数据

php
// 获取原始请求体(等价于 php://input)
$rawContent = Request::getContent();

// 获取完整 HTTP 报文(含 Header 和 Body)
// 注意:HTTP2 模式下不可用
$rawData = Request::getData();

操作请求参数

php
/**
 * 合并写入请求参数
 *
 * @param array $params 要添加的参数
 * @param string $type 写入目标: 'get'|'post'|'auto'
 */
Request::addParams(array $params, string $type = 'auto'): void

示例:

php
// 添加 GET 参数
Request::addParams(['forced_param' => 'value'], 'get');

// 添加 POST 参数
Request::addParams(['extra_field' => 'data'], 'post');

// 根据当前请求方法自动选择
Request::addParams(['timestamp' => time()], 'auto');

XSS 过滤机制

默认过滤行为

框架默认对所有字符串类型的输入应用 htmlspecialchars 过滤,防止 XSS 攻击:

php
// 用户输入: <script>alert('xss')</script>
$name = Request::get('name');
// 输出: &lt;script&gt;alert(&#039;xss&#039;)&lt;/script&gt;

默认配置:

php
protected array $filter = [
    'htmlspecialchars' => [
        'flags' => ENT_QUOTES | ENT_SUBSTITUTE
    ]
];

禁用或修改过滤器

方法一:使用 param() 时传入空过滤器

php
// 不应用任何过滤器,获取原始值
$rawContent = Request::param('content', null, []);

方法二:自定义全局过滤器(需继承 Request 类)

php
<?php
declare(strict_types=1);

namespace App\Http;

use Viswoole\HttpServer\Request as BaseRequest;

class Request extends BaseRequest
{
    /**
     * 自定义过滤器配置
     */
    protected array $filter = [
        // 使用更严格的 HTML 实体编码
        'htmlspecialchars' => ['flags' => ENT_QUOTES | ENT_HTML5],
        // 额外去除首尾空白
        'trim' => null,
    ];
}

然后在配置文件中替换 Request 类的实现。

方法三:在特定场景使用额外过滤器

php
// 仅对该次取值应用 strip_tags
$html = Request::param('html_content', null, ['strip_tags']);

// 组合多个过滤器
$clean = Request::param('input', null, ['trim', 'strip_tags']);

访问底层 Swoole 对象

通过 getSwooleRequest() 获取底层对象

Facade 仅代理方法调用,无法直接访问属性。如需访问底层 Swoole 请求对象的属性,请通过 getSwooleRequest() 获取:

php
// 获取底层 Swoole 请求对象
$swooleRequest = Request::getSwooleRequest();

// 访问 Swoole 属性
$fd = $swooleRequest->fd;                    // 文件描述符
$headerSize = $swooleRequest->headerSize;    // 头部大小
$serverPort = $swooleRequest->server_port;   // 服务器端口

// 调用 Swoole 方法
$rawContent = $swooleRequest->rawContent();   // 原始请求内容
$isCompleted = $swooleRequest->isCompleted(); // 是否接收完成

// 设置属性
$swooleRequest->get = ['custom' => 'value']; // 修改 GET 数据

获取底层对象引用

php
/**
 * 获取底层的 Swoole\Http\Request 对象
 * @return \Swoole\Http\Request
 */
Request::getSwooleRequest(): \Swoole\Http\Request
php
$swooleRequest = Request::getSwooleRequest();
// 可直接操作 Swoole 原始对象的所有属性和方法

工厂方法

创建新 Request 实例

用于在非 onRequest 回调场景(如异步任务)中创建请求对象:

php
/**
 * @param array $options Swoole\Request::create 配置项
 * @return RequestInterface
 */
Request::create(array $options = []): RequestInterface

示例:

php
// 在异步任务中模拟请求
$request = Request::create([
    'parse_cookie' => true,
    'parse_body' => true,
    'parse_files' => true,
]);

// 手动设置请求数据
$request->get = ['action' => 'task'];
$request->post = ['data' => 'value'];

完整使用示例

php
<?php
declare(strict_types=1);

namespace App\Controller;

use Viswoole\HttpServer\Facade\Request;
use Viswoole\Router\Annotation\AutoController;
use Viswoole\Router\Annotation\RouteMapping;

#[AutoController]
class DebugController
{
    /**
     * 请求调试信息
     *
     * GET /debug/request-info
     */
    public static function requestInfo(): array
    {
        return [
            // === 基本信息 ===
            'method' => Request::getMethod(),
            'ip' => Request::ip(),
            'https' => Request::https(),
            'protocol' => Request::getProtocolVersion(),

            // === URL 信息 ===
            'uri' => (string)Request::getUri(),
            'path' => Request::getPath(),

            // === 请求头 ===
            'content_type' => Request::getHeader('content-type'),
            'accept' => Request::getHeader('accept'),
            'user_agent' => Request::getHeader('user-agent'),
            'authorization' => Request::getHeader('authorization') ? '***隐藏***' : null,

            // === 参数 ===
            'get_params' => Request::get(),
            'post_params' => Request::post(),
            'all_params' => Request::params(),

            // === Cookie ===
            'cookies' => Request::cookie(),

            // === 服务端信息 ===
            'server' => [
                'request_time' => Request::getServer('request_time_float'),
                'remote_addr' => Request::getServer('remote_addr'),
                'remote_port' => Request::getServer('remote_port'),
            ],

            // === 客户端期望 ===
            'expect_json' => Request::isJson(),
            'accept_type' => Request::getAcceptType(),
        ];
    }

    /**
     * API 接口示例
     *
     * POST /debug/api-test
     * Headers:
     *   X-API-Key: your-api-key
     *   X-Request-ID: unique-request-id
     * Body: { "query": "搜索关键词", "filters": ["type1", "type2"] }
     */
    #[RouteMapping(method: 'POST')]
    public static function apiTest(): array
    {
        // 获取自定义请求头
        $apiKey = Request::getHeader('X-API-Key');
        $requestId = Request::getHeader('X-Request-ID');

        // 获取请求体
        $query = Request::post('query');
        $filters = Request::post('filters', []);

        // 使用 params() 批量获取
        $params = Request::params(['query', 'filters'], true);

        // 处理业务逻辑...
        return [
            'request_id' => $requestId,
            'query' => $query,
            'filters' => $filters,
            'results' => [],
            'count' => 0
        ];
    }
}

最佳实践

1. 优先使用自动注入而非手动获取

php
// ✅ 推荐:使用注解自动注入
public static function search(#[InjectGet] string $keyword): array { ... }

// ❌ 避免:手动从 Request 获取(除非需要特殊处理)
public static function search(): array
{
    $keyword = Request::get('keyword');
    // ...
}

2. 对于复杂逻辑,直接使用 Request 对象

当需要条件判断、批量处理或多源数据时:

php
public static function advancedSearch(): array
{
    // 根据 Accept 头决定返回格式
    if (Request::isJson()) {
        $data = $this->searchLogic();
        return $response->json($data);
    }

    // HTML 请求返回视图
    $keyword = Request::get('keyword', '');
    return $this->render('search', compact('keyword'));
}

3. 注意 XSS 过滤的影响

对于富文本、Markdown 等需要保留 HTML 的场景:

php
// ✅ 正确:获取未过滤的内容
$content = Request::param('content', null, []);

// ❌ 错误:会被转义导致格式丢失
$content = Request::post('content');

4. 使用 params() 简化多参数获取

php
// ✅ 推荐:一行代码获取多个参数
extract(Request::params([
    'page' => 1,
    'size' => 20,
    'sort' => 'id',
    'order' => 'DESC'
]));

// ❌ 冗余:逐个获取
$page = Request::get('page', 1);
$size = Request::get('size', 20);
$sort = Request::get('sort', 'id');
$order = Request::get('order', 'DESC');