简而言之:GET 仍应是 Laravel 中搜索方法的默认选择,POST 仍是复杂过滤的实用回退,而 QUERY 只有在你足够在意 HTTP 语义并愿意承担基础设施成本时才值得使用。 这才是真正的权衡。

新的 HTTP QUERY 方法不再只是标准讨论中的一个想法。它已于 2026 年 6 月 作为 RFC 10008 成为正式规范。它的核心理念很简洁:让客户端发送一个安全、幂等的带请求体的请求。换句话说,保留检索的语义,同时避免将大型搜索载荷塞进查询字符串,或假装安全读取是 POST

这听起来非常适合搜索 API。Laravel 团队,尤其是那些构建管理面板、报表端点、AI 辅助过滤或多条件仪表盘的团队,会认为:终于有一个方法能匹配实际意图了。

问题是,协议正确性只是工作的一半。另一半是周围的一切:代理、客户端、缓存、API 网关、分析工具、团队熟悉度、浏览器行为和回退设计。QUERY 绝对可以让 API 更干净。它也可能成为那些技术上优雅、却悄然增加未来两年维护负担的选择之一。

QUERY 修复了 GETPOST 未解决的问题

当你的搜索端点已经超出简洁 URL 的范围时,QUERY 的价值最容易体现。

简单过滤应使用 GET

GET /orders?status=paid&sort=-created_at&page=2

进入全屏模式 退出全屏模式

当过滤条件较小、可链接且适合缓存时,这仍然是最佳选择。Laravel、浏览器、CDN、可观测性工具以及地球上每一个 API 客户端都能理解它。

问题始于搜索载荷变得更丰富时:

  • 嵌套过滤
  • 分组条件
  • 长 ID 列表
  • 带权重的全文规则
  • 日期范围和分面
  • 导出式报表查询
  • AI 生成的过滤载荷

此时,团队通常会转向 POST /orders/search 并附带 JSON。它能工作,但会模糊语义。你让一个只读操作看起来像一个会改变状态的写操作。RFC 10008 正是为了填补这个空白而存在。它将 QUERY 定义为安全且幂等,类似于 GET,同时仍允许请求体中的内容。RFC 明确将其描述为基于 URI 的 GET 查询与基于请求体的 POST 查询之间的桥梁。

这种语义清晰性并非学术问题。它会影响重试逻辑、缓存假设、API 可读性,以及未来维护者如何理解你的端点。一个 POST 搜索路由总是迫使你添加一个心理脚注:“是的,这实际上是一个读取操作。”而一个 QUERY 路由则不需要。

RFC 中还有一个不错的细节:可以通过标准的 Allow 头和新的 Accept-Query 响应头来发现支持情况,后者可以通告支持的查询体媒体类型。在规范层面,这比通常未记录的约定(“用 JSON POST 到这里,相信我们”)更干净。

因此,如果你纯粹从语义准确性的角度比较这些方法,排名很简单:

  • GET 在小型、URL 友好的搜索中胜出。
  • QUERY 在仍是安全读取的复杂、基于请求体的搜索中胜出。
  • POST 是兼容性工作马,而不是最干净的模型。

这是好的部分。更难的问题是,你的实际技术栈能否与之共存。

QUERY 在 Laravel 代码库中真正有帮助的地方

如果你要采用 QUERY,请为语义收益真实而非表面的端点采用它。

最佳场景:复杂的安全搜索

最佳场景是搜索或报表端点,其载荷对于查询字符串来说太大或太结构化,但仍没有副作用。

想象一个带有嵌套过滤组的内部分析屏幕:

{
  "filters": [
    {"field": "status", "operator": "in", "value": ["paid", "refunded"]},
    {"field": "country", "operator": "eq", "value": "IN"}
  ],
  "date_range": {
    "from": "2026-07-01", "to": "2026-07-27"
  },
  "sort": [
    {"field": "revenue", "direction": "desc"}
  ],
  "page": 1, "per_page": 50
}

进入全屏模式 退出全屏模式

这个载荷在 GET 中很尴尬,但它显然不是一个写操作。QUERYPOST 更匹配意图。

更适合共享搜索引擎

如果你的 Laravel 应用暴露了一个被多个客户端使用的搜索抽象,QUERY 也可以让契约更清晰。移动应用、内部工具、后端服务和 AI 代理都能理解一个正在被查询而非被修改的资源。

当你的 API 表面开始蔓延时,这一点很重要。将每个安全搜索路由命名为 .../search 并使用 POST 能工作,但它会慢慢训练你的代码库在过滤变得不方便时将读取视为写入。

更干净的服务边界

这里好的 Laravel 设计不是“把搜索逻辑放在路由中并庆祝现代 HTTP”。好的设计是将所有搜索输入规范化到一个查询对象中,并让传输成为边缘关注点。

例如:

final class OrderSearchData
{
    public function __construct(
        public array $filters = [],
        public array $sort = [],
        public ?string $from = null,
        public ?string $to = null,
        public int $page = 1,
        public int $perPage = 50,
    ) {}

    public static function fromRequest(Request $request): self
    {
        $payload = $request->isMethod('QUERY')
            ? $request->json()->all()
            : $request->all();

        return new self(
            filters: $payload['filters'] ?? [],
            sort: $payload['sort'] ?? [],
            from: data_get($payload, 'date_range.from'),
            to: data_get($payload, 'date_range.to'),
            page: (int) ($payload['page'] ?? 1),
            perPage: min((int) ($payload['per_page'] ?? 50), 200),
        );
    }
}

进入全屏模式 退出全屏模式

这种模式很重要,因为如果你采用 QUERY,你几乎肯定也应该保留 GETPOST 的兼容路径。应用核心不应该关心搜索载荷是通过哪种传输方式传入的。

为什么 QUERY 仍可能成为维护陷阱

这是人们在对标准感到兴奋时常常跳过的一部分。

QUERY 在基于请求体的读取方面比 POST 语义更合适。这并不意味着它对 2026 年的每个 Laravel 团队来说都是更好的运营选择。

Laravel 很友好,其余技术栈可能不友好

Laravel 的请求对象很灵活。文档明确显示 Request::method() 返回传入的动词,isMethod() 可以测试它,这意味着框架可以检查到达它的任何方法。Laravel 路由文档也显示了一流辅助方法仅针对常见动词以及 match()any()。这个差距很说明问题。

QUERY 还不是 Laravel 的一流快乐路径。你正在离开铺好的道路。

这意味着你必须考虑框架代码之外的事情:

  • 你的 Web 服务器会原封不动地传递该方法吗?
  • 你的负载均衡器或 API 网关会允许它吗?
  • 你的 WAF 规则会默认将其视为可疑吗?
  • 你的请求日志、仪表盘和 APM 工具会正确地对其进行分组吗?
  • 你的 SDK、测试工具和生成的客户端会保留它吗?

一个方法可以是有效的 HTTP,但在真实基础设施中仍会尴尬多年。

浏览器和客户端的人机工程学仍不均衡

这是许多优雅 API 想法失去动力的地方。你的服务器可能接受 QUERY,但你的消费者不仅仅是服务器。它们是浏览器、前端应用、移动客户端、Postman 集合、CLI 工具、生成的 SDK 和内部脚本。

即使客户端在技术上可以发送自定义方法,也并不意味着周围的工具会将其视为正常。团队最终会发现软失败而非硬失败:中间件假设、CORS 摩擦、分析盲点、自动生成文档中方法渲染错误,或测试工具需要手动覆盖。

一个干净的规范搭配笨拙的工具采用,正是维护陷阱诞生的方式。

缓存理论上更好,但在商品化基础设施中并非如此

RFC 10008 指出 QUERY 响应是可缓存的。这是相对于通常含糊的 POST 搜索模式而言的真正语义优势。但同一 RFC 也指出,缓存 QUERY 本质上比 GET 更复杂,因为缓存键必须考虑请求体。

这是关键的实际点。商品化缓存和 CDN 是围绕 GET 的肌肉记忆构建的。一旦你的缓存键依赖于请求内容,你就需要对中间件的行为有更多的信心。

所以,是的,QUERY 在语义上比 POST 更可缓存。不,这并不意味着你的技术栈会突然提供 effortless 的查询体缓存。

可观测性在变好之前会稍微变差

GET 请求很容易检查,因为过滤条件在 URL 中。那很嘈杂,但操作上很方便。POST 请求很无聊但很熟悉。QUERY 处于一个尴尬的中间状态:像 GET 一样安全,像 POST 一样基于请求体,而且不常见,以至于一些工具默认不知道如何处理它。

如果你的团队严重依赖请求日志、指标标记或运维仪表盘,不常见的动词会产生额外工作。不是不可能的工作。只是你在称设计“更干净”之前需要预算的工作。

有意义的 Laravel 实现模式

如果你想试验 QUERY,请采用双路径设计,而不是纯粹主义。

错误的推出方式是:

  • 发明一个 QUERY 端点
  • 立即移除 POST 搜索
  • 假设基础设施会配合
  • 一次一个代理或一个客户端地发现故障

更好的推出方式是集中搜索逻辑,并有意地暴露多个传输方式。

推荐的路由形状

将你的规范搜索行为保持在一个操作或服务中,然后暴露:

  • GET 用于简单过滤
  • POST 作为广泛的兼容性路由
  • QUERY 作为有能力的客户端的语义正确路由

它可以看起来像这样:

use App\Http\Controllers\OrderSearchController;
use Illuminate\Support\Facades\Route;

Route::get('/orders', [OrderSearchController::class, 'index']);
Route::post('/orders/search', [OrderSearchController::class, 'search']);
Route::any('/orders/query', [OrderSearchController::class, 'query']);

进入全屏模式 退出全屏模式

而在控制器中:

final class OrderSearchController
{
    public function index(Request $request, OrderSearch $search)
    {
        $data = OrderSearchData::fromRequest($request);

        return response()->json($search->run($data));
    }

    public function search(Request $request, OrderSearch $search)
    {
        $data = OrderSearchData::fromRequest($request);

        return response()->json($search->run($data));
    }

    public function query(Request $request, OrderSearch $search)
    {
        abort_unless($request->isMethod('QUERY'), 405);

        $data = OrderSearchData::fromRequest($request);

        return response()
            ->json($search->run($data))
            ->header('Accept-Query', 'application/json');
    }
}

进入全屏模式 退出全屏模式

这在框架纯粹主义者看来并不漂亮,但它是诚实的。它承认 Laravel 还没有给你一个精致的 Route::query() 抽象,并且兼容性仍然很重要。

验证和幂等性仍然重要

因为 QUERY 是安全且幂等的,你的实现需要表现得像它一样。这听起来很明显,但很容易意外违反。

不要让带有 QUERY 的搜索端点:

  • 将“上次搜索”状态作为副作用持久化
  • 在每次请求时写入审计行,除非你愿意将它们视为良性副作用
  • 自动触发导出、通知或队列任务
  • 以像写入 API 一样的方式预热昂贵的物化视图

如果你将端点标记为 QUERY,请保持它在操作上是只读的。否则你会得到最糟糕的两个世界:不熟悉的方法加上破坏的语义。

如果你认真对待,请使用 Accept-Query

如果你采用 QUERY,不要将其作为部落知识隐藏起来。RFC 10008 添加 Accept-Query 响应头是有原因的。如果你的端点支持 JSON 查询体,请通告它。

这会让功能可被发现,并让你的 API 契约比随机的内部 wiki 页面更诚实一些。

那么 Laravel 团队应该使用 QUERY 吗?

通常,不作为唯一的搜索方法

这是我在真实代码审查中会捍卫的建议。

如果你的受众广泛、公开、以浏览器为主或集成密集,GETPOST 仍然是最安全的组合。GET 覆盖简单和 URL 友好的情况。POST 覆盖复杂的基于请求体的搜索,而无需迫使你的其余技术栈学习一个仍不常见的动词。

如果你的环境受控、客户端已知、基础设施团队可以验证方法传递,并且你足够在意协议语义以正确维护边缘,那么 QUERY 就值得考虑。在那种更狭窄的设置中,它确实比假装每个复杂读取都是 POST 更干净。

所以真正的比较看起来是这样的:

  • 当搜索自然地可以用 URL 表示并受益于通用工具支持时,使用 GET
  • 当兼容性比语义纯粹性更重要时,使用 POST
  • 当操作确实是安全读取、载荷属于请求体,且你的技术栈足够成熟以支持一个不那么常见的方法而不会出现意外时,使用 QUERY

最后一个条件就是整个故事。QUERY 不是一个噱头,也不是自动的陷阱。它是一个更锋利的工具,只有当系统的其余部分准备好时才会有回报。

如果你的团队仍在为基本的 API 一致性而战,不要从这里开始。如果你的团队已经关心传输语义、缓存行为和多客户端搜索设计,那么 QUERY 终于成为一个真正的选项。只是要像基础设施决策一样采用它,而不是语法升级。


在 QCode 上阅读完整文章:https://qcode.in/http-query-laravel-cleaner-search-api-future-maintenance-trap/