# HTTP QUERY 方法
> 接受在请求体中携带查询内容的安全、可缓存请求。
[HTTP `QUERY` 方法(RFC 10008)](https://www.rfc-editor.org/rfc/rfc10008)类似于 `GET` —— **安全、幂等且可缓存** —— 但会在请求 **体** 中通过 `Content-Type` 携带查询内容。它是对“我需要使用 GET,但我的查询内容对于 URL 来说过大或结构过于复杂”这一需求的标准解决方案。
H3 通过 [`app.query()`](/guide/basics/routing#http-query-method) 将 `QUERY` 作为一等方法提供,并额外提供两个辅助工具。
## 注册 `QUERY` 处理器
读取请求体的方式与处理 `POST` 一样:
```ts
import { readBody } from "h3";
app.query("/books", async (event) => {
const query = await readBody(event, { type: "text" });
return runSearch(query);
});
```
由于 `QUERY` 携带可由攻击者控制的请求体,[请求体大小限制](/utils/request#assertbodysizeevent-limit)与 `POST` 一样适用。
## 广告支持的格式
使用 [`appendAcceptQuery`](/utils/request#appendacceptqueryevent-mediatypes) 告知客户端资源理解哪些查询格式。它会设置 `Accept-Query` 响应标头(一个 [结构化字段](https://www.rfc-editor.org/rfc/rfc8941) 列表),也可以在普通的 `GET` 请求中设置,以便客户端在发送 `QUERY` 请求之前发现可用格式:
```ts
import { appendAcceptQuery } from "h3";
app.get("/books", (event) => {
appendAcceptQuery(event, ["application/sql", "application/jsonpath"]);
// Accept-Query: application/sql, application/jsonpath
return "Send a QUERY request with a SQL or JSONPath body.";
});
```
## 验证 `Content-Type`
使用 [`requireContentType`](/utils/request#requirecontenttypeevent-acceptedtypes) 强制执行 RFC 的错误语义。它返回匹配的媒体类型,或抛出 `400`(缺失)、`415`(不支持)或 `422`(格式错误):
```ts
import { requireContentType, readBody } from "h3";
app.query("/books", async (event) => {
const type = requireContentType(event, ["application/sql", "application/jsonpath"]);
const query = await readBody(event, { type: "text" });
return runQuery(type, query);
});
```
## 提供可缓存的 `GET` 替代方案
`QUERY` 响应无法通过 URL 寻址,因此浏览器和 CDN 无法缓存它。RFC 10008 建议通过 `Content-Location` 标头将客户端指向一个等效且可缓存的 `GET`。使用稳定的 ID 保存结果,并让客户端通过普通的、可由 HTTP 缓存的 `GET` 重复查询:
```ts
app.query("/books", async (event) => {
const result = runQuery(type, query);
const id = queryId(type, query); // 查询的稳定哈希值
cache.set(id, result);
event.res.headers.set("content-location", `/books/${id}`);
return result;
});
```
## 完整示例
一个自包含、可运行的演示——一个 `/books` 资源,接受类似 SQL 和 JSONPath 的查询,验证 `Content-Type`,并提供一个可缓存的 `GET` 替代方案。同时,它还会在 `/` 提供一个小型交互页面。
查看完整的 [`examples/query.mjs`](https://github.com/h3js/h3/tree/main/examples/query.mjs) 源代码,或使用 `node examples/query.mjs` 在本地运行。
与 `GET` 不同,`QUERY` **不在 CORS 安全列表中**,因此浏览器会发送预检请求。如果你向 [`handleCors`](/utils/security#handlecorsevent-options) 传入显式的 `methods` 允许列表,请加入 `"QUERY"`。