This is the full developer documentation for Hono. # Start of Hono documentation # Hono Hono - 在日语中是火焰🔥的意思 - 是一个基于 Web 标准构建的小型、简单且超快的 Web 框架。它适用于任何 JavaScript 运行时:Cloudflare Workers、Fastly Compute、Deno、Bun、Vercel、Netlify、AWS Lambda、Lambda@Edge 和 Node.js。 ¥Hono - ***means flame🔥 in Japanese*** - is a small, simple, and ultrafast web framework built on Web Standards. It works on any JavaScript runtime: Cloudflare Workers, Fastly Compute, Deno, Bun, Vercel, Netlify, AWS Lambda, Lambda@Edge, and Node.js. 快速,但不仅仅是快速。 ¥Fast, but not only fast. ```ts twoslash import { Hono } from 'hono' const app = new Hono() app.get('/', (c) => c.text('Hono!')) export default app ``` ## 快速入门 ¥Quick Start 只需运行这个: ¥Just run this: ::: code-group ```sh [npm] npm create hono@latest ``` ```sh [yarn] yarn create hono ``` ```sh [pnpm] pnpm create hono@latest ``` ```sh [bun] bun create hono@latest ``` ```sh [deno] deno init --npm hono@latest ``` ::: ## 功能 ¥Features * 超快 🚀 - 路由 `RegExpRouter` 非常快。不使用线性循环。快速。 ¥**Ultrafast** 🚀 - The router `RegExpRouter` is really fast. Not using linear loops. Fast. * 轻量级 🪶 - `hono/tiny` 预设小于 14kB。Hono 没有任何依赖,仅使用 Web 标准。 ¥**Lightweight** 🪶 - The `hono/tiny` preset is under 14kB. Hono has zero dependencies and uses only the Web Standards. * 多运行时 🌍 - 适用于 Cloudflare Workers、Fastly Compute、Deno、Bun、AWS Lambda 或 Node.js。相同的代码在所有平台上运行。 ¥**Multi-runtime** 🌍 - Works on Cloudflare Workers, Fastly Compute, Deno, Bun, AWS Lambda, or Node.js. The same code runs on all platforms. * 包含适配 🔋 - Hono 具有内置中间件、自定义中间件、第三方中间件和助手。包含适配。 ¥**Batteries Included** 🔋 - Hono has built-in middleware, custom middleware, third-party middleware, and helpers. Batteries included. * Delightful DX 😃 - 超级干净的 API。一流的 TypeScript 支持。现在,我们得到了 "类型"。 ¥**Delightful DX** 😃 - Super clean APIs. First-class TypeScript support. Now, we've got "Types". ## 用例 ¥Use-cases Hono 是一个类似于 Express 的简单 Web 应用框架,没有前端。但它在 CDN Edges 上运行,并允许你与中间件结合使用时构建更大的应用。以下是一些用例示例。 ¥Hono is a simple web application framework similar to Express, without a frontend. But it runs on CDN Edges and allows you to construct larger applications when combined with middleware. Here are some examples of use-cases. * 构建 Web API ¥Building Web APIs * 后端服务器的代理 ¥Proxy of backend servers * CDN 前端 ¥Front of CDN * Edge 应用 ¥Edge application * 库的基本服务器 ¥Base server for a library * 全栈应用 ¥Full-stack application ## 谁在使用 Hono? ¥Who is using Hono? | 项目 | 平台 | 用于什么? | | ---------------------------------------------------------------------------------- | ------------------ | -------------------------------------------------------- | | [cdnjs](https://cdnjs.com) | Cloudflare Workers | 免费开源 CDN 服务。Hono 用于 API 服务器。 | | [Cloudflare D1](https://www.cloudflare.com/developer-platform/d1/) | Cloudflare Workers | 无服务器 SQL 数据库。Hono 用于内部 API 服务器。 | | [Cloudflare Workers KV](https://www.cloudflare.com/developer-platform/workers-kv/) | Cloudflare Workers | 无服务器键值数据库。Hono 用于内部 API 服务器。 | | [BaseAI](https://baseai.dev) | 本地 AI 服务器 | 带内存的无服务器 AI 代理管道。一个用于 Web 的开源代理 AI 框架。带有 Hono 的 API 服务器。 | | [Unkey](https://unkey.dev) | Cloudflare Workers | 一个开源 API 身份验证和授权。Hono 用于 API 服务器。 | | [OpenStatus](https://openstatus.dev) | Bun | 一个开源网站和 API 监控平台。Hono 用于 API 服务器。 | | [Deno 基准测试](https://deno.com/benchmarks) | Deno | 基于 V8 构建的安全 TypeScript 运行时。Hono 用于基准测试。 | 以及以下内容。 ¥And the following. * [Drivly](https://driv.ly/) - Cloudflare Workers * [repeat.dev](https://repeat.dev/) - Cloudflare Workers 你想看更多吗?参见 [谁在生产中使用 Hono?](https://github.com/orgs/honojs/discussions/1510)。 ¥Do you want to see more? See [Who is using Hono in production?](https://github.com/orgs/honojs/discussions/1510). ## 1 分钟内掌握 Hono ¥Hono in 1 minute 使用 Hono 为 Cloudflare Workers 创建应用的演示。 ¥A demonstration to create an application for Cloudflare Workers with Hono. ![Demo](/images/sc.gif) ## 超快 ¥Ultrafast 与 Cloudflare Workers 的其他路由相比,Hono 是最快的。 ¥**Hono is the fastest**, compared to other routers for Cloudflare Workers. ``` Hono x 402,820 ops/sec ±4.78% (80 runs sampled) itty-router x 212,598 ops/sec ±3.11% (87 runs sampled) sunder x 297,036 ops/sec ±4.76% (77 runs sampled) worktop x 197,345 ops/sec ±2.40% (88 runs sampled) Fastest is Hono ✨ Done in 28.06s. ``` 参见 [更多基准](/docs/concepts/benchmarks)。 ¥See [more benchmarks](/docs/concepts/benchmarks). ## 轻量级 ¥Lightweight Hono 很小。使用 `hono/tiny` 预设,其大小在最小化时低于 14KB。有许多中间件和适配器,但它们仅在使用时打包在一起。有关上下文,Express 的大小为 572KB。 ¥**Hono is so small**. With the `hono/tiny` preset, its size is **under 14KB** when minified. There are many middleware and adapters, but they are bundled only when used. For context, the size of Express is 572KB. ``` $ npx wrangler dev --minify ./src/index.ts ⛅️ wrangler 2.20.0 -------------------- ⬣ Listening at http://0.0.0.0:8787 - http://127.0.0.1:8787 - http://192.168.128.165:8787 Total Upload: 11.47 KiB / gzip: 4.34 KiB ``` ## 多个路由 ¥Multiple routers Hono 有多个路由。 ¥**Hono has multiple routers**. RegExpRouter 是 JavaScript 世界中最快的路由。它使用在调度之前创建的单个大型正则表达式来匹配路由。使用 SmartRouter,它支持所有路由模式。 ¥**RegExpRouter** is the fastest router in the JavaScript world. It matches the route using a single large Regex created before dispatch. With **SmartRouter**, it supports all route patterns. LinearRouter 可以非常快速地注册路由,因此它适用于每次初始化应用的环境。PatternRouter 只是添加和匹配模式,使其变小。 ¥**LinearRouter** registers the routes very quickly, so it's suitable for an environment that initializes applications every time. **PatternRouter** simply adds and matches the pattern, making it small. 参见 [有关路由的更多信息](/docs/concepts/routers)。 ¥See [more information about routes](/docs/concepts/routers). ## Web 标准 ¥Web Standards 由于使用了 Web 标准,Hono 可以在很多平台上运行。 ¥Thanks to the use of the **Web Standards**, Hono works on a lot of platforms. * Cloudflare Workers * Cloudflare 页面 ¥Cloudflare Pages * Fastly 计算 ¥Fastly Compute * Deno * Bun * Vercel * AWS Lambda * Lambda@Edge * 其他 ¥Others 通过使用 [Node.js 适配器](https://github.com/honojs/node-server),Hono 可以在 Node.js 上运行。 ¥And by using [a Node.js adapter](https://github.com/honojs/node-server), Hono works on Node.js. 参见 [有关 Web 标准的更多信息](/docs/concepts/web-standard)。 ¥See [more information about Web Standards](/docs/concepts/web-standard). ## 中间件和助手 ¥Middleware & Helpers Hono 有许多中间件和助手。这使 "写得少,做得多" 成为现实。 ¥**Hono has many middleware and helpers**. This makes "Write Less, do more" a reality. 开箱即用,Hono 提供中间件和助手: ¥Out of the box, Hono provides middleware and helpers for: * [基本身份验证](/docs/middleware/builtin/basic-auth) ¥[Basic Authentication](/docs/middleware/builtin/basic-auth) * [承载身份验证](/docs/middleware/builtin/bearer-auth) ¥[Bearer Authentication](/docs/middleware/builtin/bearer-auth) * [主体限制](/docs/middleware/builtin/body-limit) ¥[Body Limit](/docs/middleware/builtin/body-limit) * [缓存](/docs/middleware/builtin/cache) ¥[Cache](/docs/middleware/builtin/cache) * [压缩](/docs/middleware/builtin/compress) ¥[Compress](/docs/middleware/builtin/compress) * [上下文存储](/docs/middleware/builtin/context-storage) ¥[Context Storage](/docs/middleware/builtin/context-storage) * [Cookie](/docs/helpers/cookie) * [CORS](/docs/middleware/builtin/cors) * [ETag](/docs/middleware/builtin/etag) * [html](/docs/helpers/html) * [JSX](/docs/guides/jsx) * [JWT 身份验证](/docs/middleware/builtin/jwt) ¥[JWT Authentication](/docs/middleware/builtin/jwt) * [日志器](/docs/middleware/builtin/logger) ¥[Logger](/docs/middleware/builtin/logger) * [语言](/docs/middleware/builtin/language) ¥[Language](/docs/middleware/builtin/language) * [漂亮的 JSON](/docs/middleware/builtin/pretty-json) ¥[Pretty JSON](/docs/middleware/builtin/pretty-json) * [安全标头](/docs/middleware/builtin/secure-headers) ¥[Secure Headers](/docs/middleware/builtin/secure-headers) * [SSG](/docs/helpers/ssg) * [流式传输](/docs/helpers/streaming) ¥[Streaming](/docs/helpers/streaming) * [GraphQL 服务器](https://github.com/honojs/middleware/tree/main/packages/graphql-server) ¥[GraphQL Server](https://github.com/honojs/middleware/tree/main/packages/graphql-server) * [Firebase 身份验证](https://github.com/honojs/middleware/tree/main/packages/firebase-auth) ¥[Firebase Authentication](https://github.com/honojs/middleware/tree/main/packages/firebase-auth) * [Sentry](https://github.com/honojs/middleware/tree/main/packages/sentry) * 其他! ¥Others! 例如,使用 Hono 添加 ETag 和请求日志记录只需要几行代码: ¥For example, adding ETag and request logging only takes a few lines of code with Hono: ```ts import { Hono } from 'hono' import { etag } from 'hono/etag' import { logger } from 'hono/logger' const app = new Hono() app.use(etag(), logger()) ``` 参见 [有关中间件的更多信息](/docs/concepts/middleware)。 ¥See [more information about Middleware](/docs/concepts/middleware). ## 开发者体验 ¥Developer Experience Hono 提供了令人愉悦的 "开发者体验"。 ¥Hono provides a delightful "**Developer Experience**". 借助 `Context` 对象,可以轻松访问请求/响应。此外,Hono 是用 TypeScript 编写的。Hono 有 "类型"。 ¥Easy access to Request/Response thanks to the `Context` object. Moreover, Hono is written in TypeScript. Hono has "**Types**". 例如,路径参数将是字面量类型。 ¥For example, the path parameters will be literal types. ![SS](/images/ss.png) Validator 和 Hono Client `hc` 启用了 RPC 模式。在 RPC 模式下,你可以使用自己喜欢的验证器(例如 Zod),并轻松地与客户端共享服务器端 API 规范并构建类型安全的应用。 ¥And, the Validator and Hono Client `hc` enable the RPC mode. In RPC mode, you can use your favorite validator such as Zod and easily share server-side API specs with the client and build type-safe applications. 参见 [Hono Stacks](/docs/concepts/stacks)。 ¥See [Hono Stacks](/docs/concepts/stacks). # 第三方中间件 ¥Third-party Middleware 第三方中间件是指未打包在 Hono 包中的中间件。大多数中间件都利用了外部库。 ¥Third-party middleware refers to middleware not bundled within the Hono package. Most of this middleware leverages external libraries. ### 身份验证 ¥Authentication * [Auth.js(Next Auth)](https://github.com/honojs/middleware/tree/main/packages/auth-js) * [Clerk Auth](https://github.com/honojs/middleware/tree/main/packages/clerk-auth) * [OAuth 提供程序](https://github.com/honojs/middleware/tree/main/packages/oauth-providers) ¥[OAuth Providers](https://github.com/honojs/middleware/tree/main/packages/oauth-providers) * [OIDC 身份验证](https://github.com/honojs/middleware/tree/main/packages/oidc-auth) ¥[OIDC Auth](https://github.com/honojs/middleware/tree/main/packages/oidc-auth) * [Firebase Auth](https://github.com/honojs/middleware/tree/main/packages/firebase-auth) * [验证 RSA JWT (JWKS)](https://github.com/wataruoguchi/verify-rsa-jwt-cloudflare-worker) ¥[Verify RSA JWT (JWKS)](https://github.com/wataruoguchi/verify-rsa-jwt-cloudflare-worker) ### 验证器 ¥Validators * [ArkType 验证器](https://github.com/honojs/middleware/tree/main/packages/arktype-validator) ¥[ArkType validator](https://github.com/honojs/middleware/tree/main/packages/arktype-validator) * [效果模式验证器](https://github.com/honojs/middleware/tree/main/packages/effect-validator) ¥[Effect Schema Validator](https://github.com/honojs/middleware/tree/main/packages/effect-validator) * [标准架构验证器](https://github.com/honojs/middleware/tree/main/packages/standard-validator) ¥[Standard Schema Validator](https://github.com/honojs/middleware/tree/main/packages/standard-validator) * [TypeBox 验证器](https://github.com/honojs/middleware/tree/main/packages/typebox-validator) ¥[TypeBox Validator](https://github.com/honojs/middleware/tree/main/packages/typebox-validator) * [Typia 验证器](https://github.com/honojs/middleware/tree/main/packages/typia-validator) ¥[Typia Validator](https://github.com/honojs/middleware/tree/main/packages/typia-validator) * [unknownutil Validator](https://github.com/ryoppippi/hono-unknownutil-validator) * [Valibot 验证器](https://github.com/honojs/middleware/tree/main/packages/valibot-validator) ¥[Valibot Validator](https://github.com/honojs/middleware/tree/main/packages/valibot-validator) * [Zod Validator](https://github.com/honojs/middleware/tree/main/packages/zod-validator) ### OpenAPI * [Zod OpenAPI](https://github.com/honojs/middleware/tree/main/packages/zod-openapi) * [标量 API 参考](https://github.com/scalar/scalar/tree/main/integrations/hono) ¥[Scalar API Reference](https://github.com/scalar/scalar/tree/main/integrations/hono) * [Swagger UI](https://github.com/honojs/middleware/tree/main/packages/swagger-ui) * [Hono OpenAPI](https://github.com/rhinobase/hono-openapi) ### 其他 ¥Others * [Bun Transpiler](https://github.com/honojs/middleware/tree/main/packages/bun-transpiler) * [esbuild Transpiler](https://github.com/honojs/middleware/tree/main/packages/esbuild-transpiler) * [事件触发器](https://github.com/honojs/middleware/tree/main/packages/event-emitter) ¥[Event Emitter](https://github.com/honojs/middleware/tree/main/packages/event-emitter) * [GraphQL 服务器](https://github.com/honojs/middleware/tree/main/packages/graphql-server) ¥[GraphQL Server](https://github.com/honojs/middleware/tree/main/packages/graphql-server) * [Hono 速率限制器](https://github.com/rhinobase/hono-rate-limiter) ¥[Hono Rate Limiter](https://github.com/rhinobase/hono-rate-limiter) * [Node WebSocket 助手](https://github.com/honojs/middleware/tree/main/packages/node-ws) ¥[Node WebSocket Helper](https://github.com/honojs/middleware/tree/main/packages/node-ws) * [Prometheus 指标](https://github.com/honojs/middleware/tree/main/packages/prometheus) ¥[Prometheus Metrics](https://github.com/honojs/middleware/tree/main/packages/prometheus) * [Qwik City](https://github.com/honojs/middleware/tree/main/packages/qwik-city) * [React 兼容性](https://github.com/honojs/middleware/tree/main/packages/react-compat) ¥[React Compatibility](https://github.com/honojs/middleware/tree/main/packages/react-compat) * [React Renderer](https://github.com/honojs/middleware/tree/main/packages/react-renderer) * [RONIN(数据库)](https://github.com/ronin-co/hono-client) ¥[RONIN (Database)](https://github.com/ronin-co/hono-client) * [Sentry](https://github.com/honojs/middleware/tree/main/packages/sentry) * [tRPC 服务器](https://github.com/honojs/middleware/tree/main/packages/trpc-server) ¥[tRPC Server](https://github.com/honojs/middleware/tree/main/packages/trpc-server) * [Geo](https://github.com/ktkongtong/hono-geo-middleware/tree/main/packages/middleware) * [Hono 简单 DI](https://github.com/maou-shonen/hono-simple-DI) ¥[Hono Simple DI](https://github.com/maou-shonen/hono-simple-DI) * [Highlight.io](https://www.highlight.io/docs/getting-started/backend-sdk/js/hono) * [Apitally(API 监控和分析)](https://docs.apitally.io/frameworks/hono) ¥[Apitally (API monitoring & analytics)](https://docs.apitally.io/frameworks/hono) # 基本身份验证中间件 ¥Basic Auth Middleware 此中间件可以将基本身份验证应用于指定路径。使用 Cloudflare Workers 或其他平台实现基本身份验证比看起来要复杂,但有了这个中间件,一切就变得轻而易举了。 ¥This middleware can apply Basic authentication to a specified path. Implementing Basic authentication with Cloudflare Workers or other platforms is more complicated than it seems, but with this middleware, it's a breeze. 有关基本身份验证方案在后台如何工作的更多信息,请参阅 [MDN 文档](https://web.nodejs.cn/en-US/docs/Web/HTTP/Authentication#basic_authentication_scheme)。 ¥For more information about how the Basic auth scheme works under the hood, see the [MDN docs](https://web.nodejs.cn/en-US/docs/Web/HTTP/Authentication#basic_authentication_scheme). ## 导入 ¥Import ```ts import { Hono } from 'hono' import { basicAuth } from 'hono/basic-auth' ``` ## 用法 ¥Usage ```ts const app = new Hono() app.use( '/auth/*', basicAuth({ username: 'hono', password: 'acoolproject', }) ) app.get('/auth/page', (c) => { return c.text('You are authorized') }) ``` 要限制到特定路由 + 方法: ¥To restrict to a specific route + method: ```ts const app = new Hono() app.get('/auth/page', (c) => { return c.text('Viewing page') }) app.delete( '/auth/page', basicAuth({ username: 'hono', password: 'acoolproject' }), (c) => { return c.text('Page deleted') } ) ``` 如果你想自己验证用户,请指定 `verifyUser` 选项;返回 `true` 表示它被接受。 ¥If you want to verify the user by yourself, specify the `verifyUser` option; returning `true` means it is accepted. ```ts const app = new Hono() app.use( basicAuth({ verifyUser: (username, password, c) => { return ( username === 'dynamic-user' && password === 'hono-password' ) }, }) ) ``` ## 选项 ¥Options ### <徽章类型="danger" text="required" /> username:`string` ¥ username: `string` 正在进行身份验证的用户的用户名。 ¥The username of the user who is authenticating. ### password:`string` 提供的用户名的密码值,用于进行身份验证。 ¥The password value for the provided username to authenticate against. ### <徽章类型="info" 文本="optional" /> 字段:`string` ¥ realm: `string` 字段的域名,作为返回的 WWW-Authenticate 质询标头的一部分。默认为 `"Secure Area"`。查看更多:[https://web.nodejs.cn/en-US/docs/Web/HTTP/Headers/WWW-Authenticate#directives](https://web.nodejs.cn/en-US/docs/Web/HTTP/Headers/WWW-Authenticate#directives) ¥The domain name of the realm, as part of the returned WWW-Authenticate challenge header. The default is `"Secure Area"`.\ See more: [https://web.nodejs.cn/en-US/docs/Web/HTTP/Headers/WWW-Authenticate#directives](https://web.nodejs.cn/en-US/docs/Web/HTTP/Headers/WWW-Authenticate#directives) ### hashFunction:`Function` 用于处理哈希函数以安全地比较密码的函数。 ¥A function to handle hashing for safe comparison of passwords. ### verifyUser:`(username: string, password: string, c: Context) => boolean | Promise` 验证用户的函数。 ¥The function to verify the user. ### invalidUserMessage:`string | object | MessageFunction` `MessageFunction` 是 `(c: Context) => string | object | Promise`。如果用户无效,则显示自定义消息。 ¥`MessageFunction` is `(c: Context) => string | object | Promise`. The custom message if the user is invalid. ## 更多选项 ¥More Options ### ...用户:`{ username: string, password: string }[]` ¥ ...users: `{ username: string, password: string }[]` ## 秘诀 ¥Recipes ### 定义多个用户 ¥Defining Multiple Users 此中间件还允许你传递包含定义更多 `username` 和 `password` 对的对象的任意参数。 ¥This middleware also allows you to pass arbitrary parameters containing objects defining more `username` and `password` pairs. ```ts app.use( '/auth/*', basicAuth( { username: 'hono', password: 'acoolproject', // Define other params in the first object realm: 'www.example.com', }, { username: 'hono-admin', password: 'super-secure', // Cannot redefine other params here }, { username: 'hono-user-1', password: 'a-secret', // Or here } ) ) ``` 或更少的硬编码: ¥Or less hardcoded: ```ts import { users } from '../config/users' app.use( '/auth/*', basicAuth( { realm: 'www.example.com', ...users[0], }, ...users.slice(1) ) ) ``` # Bearer Auth 中间件 ¥Bearer Auth Middleware Bearer Auth Middleware 通过验证 Request 标头中的 API 令牌来提供身份验证。访问端点的 HTTP 客户端将添加 `Authorization` 标头,并以 `Bearer {token}` 作为标头值。 ¥The Bearer Auth Middleware provides authentication by verifying an API token in the Request header. The HTTP clients accessing the endpoint will add the `Authorization` header with `Bearer {token}` as the header value. 从终端使用 `curl`,它看起来像这样: ¥Using `curl` from the terminal, it would look like this: ```sh curl -H 'Authorization: Bearer honoiscool' http://localhost:8787/auth/page ``` ## 导入 ¥Import ```ts import { Hono } from 'hono' import { bearerAuth } from 'hono/bearer-auth' ``` ## 用法 ¥Usage > [!NOTE] 你的 `token` 必须与正则表达式 `/[A-Za-z0-9._~+/-]+=*/` 匹配,否则将返回 400 错误。值得注意的是,此正则表达式同时适用于 URL 安全的 Base64 和标准 Base64 编码的 JWT。此中间件不需要承载令牌是 JWT,只需要它与上述正则表达式匹配。 > > ¥[!NOTE] > Your `token` must match the regex `/[A-Za-z0-9._~+/-]+=*/`, otherwise a 400 error will be returned. Notably, this regex acommodates both URL-safe Base64- and standard Base64-encoded JWTs. This middleware does not require the bearer token to be a JWT, just that it matches the above regex. ```ts const app = new Hono() const token = 'honoiscool' app.use('/api/*', bearerAuth({ token })) app.get('/api/page', (c) => { return c.json({ message: 'You are authorized' }) }) ``` 要限制到特定路由 + 方法: ¥To restrict to a specific route + method: ```ts const app = new Hono() const token = 'honoiscool' app.get('/api/page', (c) => { return c.json({ message: 'Read posts' }) }) app.post('/api/page', bearerAuth({ token }), (c) => { return c.json({ message: 'Created post!' }, 201) }) ``` 要实现多个令牌(例如,任何有效令牌都可以读取,但创建/更新/删除仅限于特权令牌): ¥To implement multiple tokens (E.g., any valid token can read but create/update/delete are restricted to a privileged token): ```ts const app = new Hono() const readToken = 'read' const privilegedToken = 'read+write' const privilegedMethods = ['POST', 'PUT', 'PATCH', 'DELETE'] app.on('GET', '/api/page/*', async (c, next) => { // List of valid tokens const bearer = bearerAuth({ token: [readToken, privilegedToken] }) return bearer(c, next) }) app.on(privilegedMethods, '/api/page/*', async (c, next) => { // Single valid privileged token const bearer = bearerAuth({ token: privilegedToken }) return bearer(c, next) }) // Define handlers for GET, POST, etc. ``` 如果你想自己验证 token 的值,请指定 `verifyToken` 选项;返回 `true` 表示它被接受。 ¥If you want to verify the value of the token yourself, specify the `verifyToken` option; returning `true` means it is accepted. ```ts const app = new Hono() app.use( '/auth-verify-token/*', bearerAuth({ verifyToken: async (token, c) => { return token === 'dynamic-token' }, }) ) ``` ## 选项 ¥Options ### <徽章类型="danger" 文本="required" /> 令牌:`string` | `string[]` ¥ token: `string` | `string[]` 用于验证传入 bearer 令牌的字符串。 ¥The string to validate the incoming bearer token against. ### <徽章类型="info" 文本="optional" /> 字段:`string` ¥ realm: `string` 字段的域名,作为返回的 WWW-Authenticate 质询标头的一部分。默认为 `""`。查看更多:[https://web.nodejs.cn/en-US/docs/Web/HTTP/Headers/WWW-Authenticate#directives](https://web.nodejs.cn/en-US/docs/Web/HTTP/Headers/WWW-Authenticate#directives) ¥The domain name of the realm, as part of the returned WWW-Authenticate challenge header. The default is `""`. See more: [https://web.nodejs.cn/en-US/docs/Web/HTTP/Headers/WWW-Authenticate#directives](https://web.nodejs.cn/en-US/docs/Web/HTTP/Headers/WWW-Authenticate#directives) ### prefix:`string` 授权标头值的前缀(或称为 `schema`)。默认为 `"Bearer"`。 ¥The prefix (or known as `schema`) for the Authorization header value. The default is `"Bearer"`. ### 标头名称:`string` ¥ headerName: `string` 标头名称。默认值为 `Authorization`。 ¥The header name. The default value is `Authorization`. ### hashFunction:`Function` 用于处理哈希函数以安全地比较身份验证令牌的函数。 ¥A function to handle hashing for safe comparison of authentication tokens. ### verifyToken:`(token: string, c: Context) => boolean | Promise` 验证令牌的函数。 ¥The function to verify the token. ### noAuthenticationHeaderMessage:`string | object | MessageFunction` `MessageFunction` 是 `(c: Context) => string | object | Promise`。如果没有身份验证标头,则显示自定义消息。 ¥`MessageFunction` is `(c: Context) => string | object | Promise`. The custom message if it does not have an authentication header. ### invalidAuthenticationHeaderMessage:`string | object | MessageFunction` 如果身份验证标头无效,则显示自定义消息。 ¥The custom message if the authentication header is invalid. ### invalidTokenMessage:`string | object | MessageFunction` 如果令牌无效,则显示自定义消息。 ¥The custom message if the token is invalid. # 主体限制中间件 ¥Body Limit Middleware Body Limit Middleware 可以限制请求正文的文件大小。 ¥The Body Limit Middleware can limit the file size of the request body. 此中间件首先使用请求中的 `Content-Length` 标头的值(如果存在)。如果未设置,它将读取流中的主体,并在其大于指定的文件大小时执行错误处理程序。 ¥This middleware first uses the value of the `Content-Length` header in the request, if present. If it is not set, it reads the body in the stream and executes an error handler if it is larger than the specified file size. ## 导入 ¥Import ```ts import { Hono } from 'hono' import { bodyLimit } from 'hono/body-limit' ``` ## 用法 ¥Usage ```ts const app = new Hono() app.post( '/upload', bodyLimit({ maxSize: 50 * 1024, // 50kb onError: (c) => { return c.text('overflow :(', 413) }, }), async (c) => { const body = await c.req.parseBody() if (body['file'] instanceof File) { console.log(`Got file sized: ${body['file'].size}`) } return c.text('pass :)') } ) ``` ## 选项 ¥Options ### maxSize:`number` 你想要限制的文件的最大文件大小。默认为 `100 * 1024` - `100kb`。 ¥The maximum file size of the file you want to limit. The default is `100 * 1024` - `100kb`. ### onError:`OnError` 如果超出指定的文件大小,则调用错误处理程序。 ¥The error handler to be invoked if the specified file size is exceeded. ## 与 Bun 一起使用以处理大量请求 ¥Usage with Bun for large requests 如果明确使用 Body Limit Middleware 来允许请求主体大于默认值,则可能需要相应地更改 `Bun.serve` 配置。[撰写本文时](https://github.com/oven-sh/bun/blob/f2cfa15e4ef9d730fc6842ad8b79fb7ab4c71cb9/packages/bun-types/bun.d.ts#L2191)、`Bun.serve` 的默认请求正文限制为 128MiB。如果你将 Hono 的 Body Limit Middleware 设置为大于该值,你的请求仍将失败,此外,中间件中指定的 `onError` 处理程序将不会被调用。这是因为 `Bun.serve()` 会在将请求传递给 Hono 之前将状态代码设置为 `413` 并终止连接。 ¥If the Body Limit Middleware is used explicitly to allow a request body larger than the default, it might be necessary to make changes to your `Bun.serve` configuration accordingly. [At the time of writing](https://github.com/oven-sh/bun/blob/f2cfa15e4ef9d730fc6842ad8b79fb7ab4c71cb9/packages/bun-types/bun.d.ts#L2191), `Bun.serve`'s default request body limit is 128MiB. If you set Hono's Body Limit Middleware to a value bigger than that, your requests will still fail and, additionally, the `onError` handler specified in the middleware will not be called. This is because `Bun.serve()` will set the status code to `413` and terminate the connection before passing the request to Hono. 如果要使用 Hono 和 Bun 接受大于 128MiB 的请求,则还需要为 Bun 设置限制: ¥If you want to accept requests larger than 128MiB with Hono and Bun, you need to set the limit for Bun as well: ```ts export default { port: process.env['PORT'] || 3000, fetch: app.fetch, maxRequestBodySize: 1024 * 1024 * 200, // your value here } ``` 或者,取决于你的设置: ¥or, depending on your setup: ```ts Bun.serve({ fetch(req, server) { return app.fetch(req, { ip: server.requestIP(req) }) }, maxRequestBodySize: 1024 * 1024 * 200, // your value here }) ``` # 缓存中间件 ¥Cache Middleware Cache 中间件使用 Web 标准的 [缓存 API](https://web.nodejs.cn/en-US/docs/Web/API/Cache)。 ¥The Cache middleware uses the Web Standards' [Cache API](https://web.nodejs.cn/en-US/docs/Web/API/Cache). Cache 中间件目前支持使用自定义域的 Cloudflare Workers 项目和使用 [Deno 1.26+](https://github.com/denoland/deno/releases/tag/v1.26.0) 的 Deno 项目。也可用于 Deno Deploy。 ¥The Cache middleware currently supports Cloudflare Workers projects using custom domains and Deno projects using [Deno 1.26+](https://github.com/denoland/deno/releases/tag/v1.26.0). Also available with Deno Deploy. Cloudflare Workers 尊重 `Cache-Control` 标头并返回缓存的响应。有关详细信息,请参阅 [Cloudflare 文档上的缓存](https://developers.cloudflare.com/workers/runtime-apis/cache/)。Deno 不尊重标头,因此如果你需要更新缓存,则需要实现自己的机制。 ¥Cloudflare Workers respects the `Cache-Control` header and return cached responses. For details, refer to [Cache on Cloudflare Docs](https://developers.cloudflare.com/workers/runtime-apis/cache/). Deno does not respect headers, so if you need to update the cache, you will need to implement your own mechanism. 有关每个平台的说明,请参阅下面的 [用法](#usage)。 ¥See [Usage](#usage) below for instructions on each platform. ## 导入 ¥Import ```ts import { Hono } from 'hono' import { cache } from 'hono/cache' ``` ## 用法 ¥Usage ::: code-group ```ts [Cloudflare Workers] app.get( '*', cache({ cacheName: 'my-app', cacheControl: 'max-age=3600', }) ) ``` ```ts [Deno] // Must use `wait: true` for the Deno runtime app.get( '*', cache({ cacheName: 'my-app', cacheControl: 'max-age=3600', wait: true, }) ) ``` ::: ## 选项 ¥Options ### cacheName:`string` | `(c: Context) => string` | `Promise` 缓存的名称。可用于存储具有不同标识符的多个缓存。 ¥The name of the cache. Can be used to store multiple caches with different identifiers. ### wait:`boolean` 一个布尔值,表示 Hono 是否应等待 `cache.put` 函数的 Promise 解析后再继续请求。对于 Deno 环境,必须为真。默认为 `false`。 ¥A boolean indicating if Hono should wait for the Promise of the `cache.put` function to resolve before continuing with the request. *Required to be true for the Deno environment*. The default is `false`. ### cacheControl:`string` `Cache-Control` 标头的指令字符串。有关更多信息,请参阅 [MDN 文档](https://web.nodejs.cn/en-US/docs/Web/HTTP/Headers/Cache-Control)。未提供此选项时,不会将 `Cache-Control` 标头添加到请求中。 ¥A string of directives for the `Cache-Control` header. See the [MDN docs](https://web.nodejs.cn/en-US/docs/Web/HTTP/Headers/Cache-Control) for more information. When this option is not provided, no `Cache-Control` header is added to requests. ### evolve:`string` | `string[]` 在响应中设置 `Vary` 标头。如果原始响应标头已包含 `Vary` 标头,则合并值,删除任何重复项。将其设置为 `*` 将导致错误。有关 Vary 标头及其对缓存策略的影响的更多详细信息,请参阅 [MDN 文档](https://web.nodejs.cn/en-US/docs/Web/HTTP/Headers/Vary)。 ¥Sets the `Vary` header in the response. If the original response header already contains a `Vary` header, the values are merged, removing any duplicates. Setting this to `*` will result in an error. For more details on the Vary header and its implications for caching strategies, refer to the [MDN docs](https://web.nodejs.cn/en-US/docs/Web/HTTP/Headers/Vary). ### keyGenerator:`(c: Context) => string | Promise` 为 `cacheName` 存储中的每个请求生成密钥。这可用于根据请求参数或上下文参数缓存数据。默认为 `c.req.url`。 ¥Generates keys for every request in the `cacheName` store. This can be used to cache data based on request parameters or context parameters. The default is `c.req.url`. # 合并中间件 ¥Combine Middleware Combine Middleware 将多个中间件函数组合成一个中间件。它提供了三个功能: ¥Combine Middleware combines multiple middleware functions into a single middleware. It provides three functions: * `some` - 仅运行给定的中间件之一。 ¥`some` - Runs only one of the given middleware. * `every` - 运行所有给定的中间件。 ¥`every` - Runs all given middleware. * `except` - 仅在条件不满足时运行所有给定的中间件。 ¥`except` - Runs all given middleware only if a condition is not met. ## 导入 ¥Import ```ts import { Hono } from 'hono' import { some, every, except } from 'hono/combine' ``` ## 用法 ¥Usage 以下是使用 Combine Middleware 的复杂访问控制规则的示例。 ¥Here's an example of complex access control rules using Combine Middleware. ```ts import { Hono } from 'hono' import { bearerAuth } from 'hono/bearer-auth' import { getConnInfo } from 'hono/cloudflare-workers' import { every, some } from 'hono/combine' import { ipRestriction } from 'hono/ip-restriction' import { rateLimit } from '@/my-rate-limit' const app = new Hono() app.use( '*', some( every( ipRestriction(getConnInfo, { allowList: ['192.168.0.2'] }), bearerAuth({ token }) ), // If both conditions are met, rateLimit will not execute. rateLimit() ) ) app.get('/', (c) => c.text('Hello Hono!')) ``` ### some 运行第一个返回 true 的中间件。中间件按顺序应用,如果任何一个中间件成功退出,后续的中间件将不会运行。 ¥Runs the first middleware that returns true. Middleware is applied in order, and if any middleware exits successfully, subsequent middleware will not run. ```ts import { some } from 'hono/combine' import { bearerAuth } from 'hono/bearer-auth' import { myRateLimit } from '@/rate-limit' // If client has a valid token, skip rate limiting. // Otherwise, apply rate limiting. app.use( '/api/*', some(bearerAuth({ token }), myRateLimit({ limit: 100 })) ) ``` ### every 运行所有中间件,如果任何一个失败则停止。中间件按顺序应用,如果任何一个中间件抛出错误,后续的中间件将不会运行。 ¥Runs all middleware and stops if any of them fail. Middleware is applied in order, and if any middleware throws an error, subsequent middleware will not run. ```ts import { some, every } from 'hono/combine' import { bearerAuth } from 'hono/bearer-auth' import { myCheckLocalNetwork } from '@/check-local-network' import { myRateLimit } from '@/rate-limit' // If client is in local network, skip authentication and rate limiting. // Otherwise, apply authentication and rate limiting. app.use( '/api/*', some( myCheckLocalNetwork(), every(bearerAuth({ token }), myRateLimit({ limit: 100 })) ) ) ``` ### except 运行所有中间件,除非满足条件。你可以传递字符串或函数作为条件。如果需要匹配多个目标,请将它们作为数组传递。 ¥Runs all middleware except when the condition is met. You can pass a string or function as the condition. If multiple targets need to be matched, pass them as an array. ```ts import { except } from 'hono/combine' import { bearerAuth } from 'hono/bearer-auth' // If client is accessing public API, skip authentication. // Otherwise, require a valid token. app.use('/api/*', except('/api/public/*', bearerAuth({ token }))) ``` # 压缩中间件 ¥Compress Middleware 此中间件根据 `Accept-Encoding` 请求标头压缩响应主体。 ¥This middleware compresses the response body, according to `Accept-Encoding` request header. ::: info 信息 注意:在 Cloudflare Workers 和 Deno Deploy 上,响应主体将自动压缩,因此无需使用此中间件。 ¥**Note**: On Cloudflare Workers and Deno Deploy, the response body will be compressed automatically, so there is no need to use this middleware. Bun:此中间件使用 bun 中尚不支持的 `CompressionStream`。 ¥**Bun**: This middleware uses `CompressionStream` which is not yet supported in bun. ::: ## 导入 ¥Import ```ts import { Hono } from 'hono' import { compress } from 'hono/compress' ``` ## 用法 ¥Usage ```ts const app = new Hono() app.use(compress()) ``` ## 选项 ¥Options ### 编码:`'gzip'` | `'deflate'` ¥ encoding: `'gzip'` | `'deflate'` 允许响应压缩的压缩方案。`gzip` 或 `deflate`。如果未定义,则两者都允许,并将基于 `Accept-Encoding` 标头使用。如果未提供此选项且客户端在 `Accept-Encoding` 标头中同时提供两者,则优先考虑 `gzip`。 ¥The compression scheme to allow for response compression. Either `gzip` or `deflate`. If not defined, both are allowed and will be used based on the `Accept-Encoding` header. `gzip` is prioritized if this option is not provided and the client provides both in the `Accept-Encoding` header. ### <徽章类型="info" 文本="optional" /> 阈值:`number` ¥ threshold: `number` 要压缩的最小大小(以字节为单位)。默认为 1024 字节。 ¥The minimum size in bytes to compress. Defaults to 1024 bytes. # 上下文存储中间件 ¥Context Storage Middleware Context Storage Middleware 将 Hono `Context` 存储在 `AsyncLocalStorage` 中,使其可在全球范围内访问。 ¥The Context Storage Middleware stores the Hono `Context` in the `AsyncLocalStorage`, to make it globally accessible. ::: info 信息 注意此中间件使用 `AsyncLocalStorage`。运行时应该支持它。 ¥**Note** This middleware uses `AsyncLocalStorage`. The runtime should support it. Cloudflare Workers:要启用 `AsyncLocalStorage`,请将 [`nodejs_compat` 或 `nodejs_als` 标志](https://developers.cloudflare.com/workers/configuration/compatibility-dates/#nodejs-compatibility-flag) 添加到你的 `wrangler.toml` 文件中。 ¥**Cloudflare Workers**: To enable `AsyncLocalStorage`, add the [`nodejs_compat` or `nodejs_als` flag](https://developers.cloudflare.com/workers/configuration/compatibility-dates/#nodejs-compatibility-flag) to your `wrangler.toml` file. ::: ## 导入 ¥Import ```ts import { Hono } from 'hono' import { contextStorage, getContext } from 'hono/context-storage' ``` ## 用法 ¥Usage 如果将 `contextStorage()` 作为中间件应用,`getContext()` 将返回当前 Context 对象。 ¥The `getContext()` will return the current Context object if the `contextStorage()` is applied as a middleware. ```ts type Env = { Variables: { message: string } } const app = new Hono() app.use(contextStorage()) app.use(async (c, next) => { c.set('message', 'Hello!') await next() }) // You can access the variable outside the handler. const getMessage = () => { return getContext().var.message } app.get('/', (c) => { return c.text(getMessage()) }) ``` 在 Cloudflare Workers 上,你可以访问处理程序外部的绑定。 ¥On Cloudflare Workers, you can access the bindings outside the handler. ```ts type Env = { Bindings: { KV: KVNamespace } } const app = new Hono() app.use(contextStorage()) const setKV = (value: string) => { return getContext().env.KV.put('key', value) } ``` # CORS 中间件 ¥CORS Middleware Cloudflare Workers 作为 Web API 有很多用例,可以从外部前端应用调用它们。对于它们,我们必须实现 CORS,让我们也使用中间件来做到这一点。 ¥There are many use cases of Cloudflare Workers as Web APIs and calling them from external front-end application. For them we have to implement CORS, let's do this with middleware as well. ## 导入 ¥Import ```ts import { Hono } from 'hono' import { cors } from 'hono/cors' ``` ## 用法 ¥Usage ```ts const app = new Hono() // CORS should be called before the route app.use('/api/*', cors()) app.use( '/api2/*', cors({ origin: 'http://example.com', allowHeaders: ['X-Custom-Header', 'Upgrade-Insecure-Requests'], allowMethods: ['POST', 'GET', 'OPTIONS'], exposeHeaders: ['Content-Length', 'X-Kuma-Revision'], maxAge: 600, credentials: true, }) ) app.all('/api/abc', (c) => { return c.json({ success: true }) }) app.all('/api2/abc', (c) => { return c.json({ success: true }) }) ``` 多个来源: ¥Multiple origins: ```ts app.use( '/api3/*', cors({ origin: ['https://example.com', 'https://example.org'], }) ) // Or you can use "function" app.use( '/api4/*', cors({ // `c` is a `Context` object origin: (origin, c) => { return origin.endsWith('.example.com') ? origin : 'http://example.com' }, }) ) ``` ## 选项 ¥Options ### origin:`string` | `string[]` | `(origin:string, c:Context) => string` "Access-Control-Allow-Origin" CORS 标头的值。你还可以传递像 `origin: (origin) => (origin.endsWith('.example.com') ? origin : 'http://example.com')` 这样的回调函数。默认为 `*`。 ¥The value of "*Access-Control-Allow-Origin*" CORS header. You can also pass the callback function like `origin: (origin) => (origin.endsWith('.example.com') ? origin : 'http://example.com')`. The default is `*`. ### allowMethods:`string[]` "Access-Control-Allow-Methods" CORS 标头的值。默认为 `['GET', 'HEAD', 'PUT', 'POST', 'DELETE', 'PATCH']`。 ¥The value of "*Access-Control-Allow-Methods*" CORS header. The default is `['GET', 'HEAD', 'PUT', 'POST', 'DELETE', 'PATCH']`. ### allowHeaders:`string[]` "Access-Control-Allow-Headers" CORS 标头的值。默认为 `[]`。 ¥The value of "*Access-Control-Allow-Headers*" CORS header. The default is `[]`. ### maxAge:`number` "Access-Control-Max-Age" CORS 标头的值。 ¥The value of "*Access-Control-Max-Age*" CORS header. ### 凭证:`boolean` ¥ credentials: `boolean` "Access-Control-Allow-Credentials" CORS 标头的值。 ¥The value of "*Access-Control-Allow-Credentials*" CORS header. ### 暴露标头:`string[]` ¥ exposeHeaders: `string[]` "Access-Control-Expose-Headers" CORS 标头的值。默认为 `[]`。 ¥The value of "*Access-Control-Expose-Headers*" CORS header. The default is `[]`. ## 依赖于环境的 CORS 配置 ¥Environment-dependent CORS configuration 如果要根据执行环境(例如开发或生产)调整 CORS 配置,则从环境变量注入值很方便,因为它无需应用了解其自己的执行环境。有关说明,请参阅下面的示例。 ¥If you want to adjust CORS configuration according to the execution environment, such as development or production, injecting values from environment variables is convenient as it eliminates the need for the application to be aware of its own execution environment. See the example below for clarification. ```ts app.use('*', async (c, next) => { const corsMiddlewareHandler = cors({ origin: c.env.CORS_ORIGIN, }) return corsMiddlewareHandler(c, next) }) ``` # CSRF 保护 ¥CSRF Protection CSRF Protection 中间件通过检查请求标头来防止 CSRF 攻击。 ¥CSRF Protection Middleware prevents CSRF attacks by checking request headers. 此中间件通过将 `Origin` 标头的值与请求的 URL 进行比较来防止 CSRF 攻击(例如使用表单元素提交)。 ¥This middleware protects against CSRF attacks such as submitting with a form element by comparing the value of the `Origin` header with the requested URL. 不发送 `Origin` 标头的旧浏览器或使用反向代理删除 `Origin` 标头的环境可能无法正常工作。在这样的环境中,使用其他 CSRF 令牌方法。 ¥Old browsers that do not send `Origin` headers, or environments that use reverse proxies to remove `Origin` headers, may not work well. In such environments, use the other CSRF token methods. ## 导入 ¥Import ```ts import { Hono } from 'hono' import { csrf } from 'hono/csrf' ``` ## 用法 ¥Usage ```ts const app = new Hono() app.use(csrf()) // Specifying origins with using `origin` option // string app.use(csrf({ origin: 'myapp.example.com' })) // string[] app.use( csrf({ origin: ['myapp.example.com', 'development.myapp.example.com'], }) ) // Function // It is strongly recommended that the protocol be verified to ensure a match to `$`. // You should *never* do a forward match. app.use( '*', csrf({ origin: (origin) => /https:\/\/(\w+\.)?myapp\.example\.com$/.test(origin), }) ) ``` ## 选项 ¥Options ### origin:`string` | `string[]` | `Function` 指定来源。 ¥Specify origins. # ETag 中间件 ¥ETag Middleware 使用此中间件,你可以轻松添加 ETag 标头。 ¥Using this middleware, you can add ETag headers easily. ## 导入 ¥Import ```ts import { Hono } from 'hono' import { etag } from 'hono/etag' ``` ## 用法 ¥Usage ```ts const app = new Hono() app.use('/etag/*', etag()) app.get('/etag/abc', (c) => { return c.text('Hono is cool') }) ``` ## 保留的标头 ¥The retained headers 304 响应必须包含在等效 200 OK 响应中发送的标头。默认标头为 Cache-Control、Content-Location、Date、ETag、Expires 和 Vary。 ¥The 304 Response must include the headers that would have been sent in an equivalent 200 OK response. The default headers are Cache-Control, Content-Location, Date, ETag, Expires, and Vary. 如果要添加发送的标头,可以使用 `retainedHeaders` 选项和包含默认标头的 `RETAINED_304_HEADERS` 字符串数组变量: ¥If you want to add the header that is sent, you can use `retainedHeaders` option and `RETAINED_304_HEADERS` strings array variable that includes the default headers: ```ts import { etag, RETAINED_304_HEADERS } from 'hono/etag' // ... app.use( '/etag/*', etag({ retainedHeaders: ['x-message', ...RETAINED_304_HEADERS], }) ) ``` ## 选项 ¥Options ### strong:`boolean` 定义使用或不使用 [弱验证](https://web.nodejs.cn/en-US/docs/Web/HTTP/Conditional_requests#weak_validation)。如果设置了 `true`,则将 `w/` 添加到值的前缀。默认为 `false`。 ¥Define using or not using a [weak validation](https://web.nodejs.cn/en-US/docs/Web/HTTP/Conditional_requests#weak_validation). If `true` is set, then `w/` is added to the prefix of the value. The default is `false`. ### <徽章类型="info" 文本="optional" /> 保留标头:`string[]` ¥ retainedHeaders: `string[]` 你想要在 304 响应中保留的标头。 ¥The headers that you want to retain in the 304 Response. ### 生成摘要:`(body: Uint8Array) => ArrayBuffer | Promise` ¥ generateDigest: `(body: Uint8Array) => ArrayBuffer | Promise` 自定义摘要生成函数。默认情况下,它使用 `SHA-1`。此函数以响应主体作为 `Uint8Array` 调用,并应返回哈希作为 `ArrayBuffer` 或 Promise 1。 ¥A custom digest generation function. By default, it uses `SHA-1`. This function is called with the response body as a `Uint8Array` and should return a hash as an `ArrayBuffer` or a Promise of one. # IP 限制中间件 ¥IP Restriction Middleware IP 限制中间件是根据用户的 IP 地址限制对资源的访问的中间件。 ¥IP Restriction Middleware is middleware that limits access to resources based on the IP address of the user. ## 导入 ¥Import ```ts import { Hono } from 'hono' import { ipRestriction } from 'hono/ip-restriction' ``` ## 用法 ¥Usage 对于在 Bun 上运行的应用,如果你只想允许从本地访问,可以按如下方式编写。指定你想要在 `denyList` 中拒绝的规则和你想要在 `allowList` 中允许的规则。 ¥For your application running on Bun, if you want to allow access only from local, you can write it as follows. Specify the rules you want to deny in the `denyList` and the rules you want to allow in the `allowList`. ```ts import { Hono } from 'hono' import { getConnInfo } from 'hono/bun' import { ipRestriction } from 'hono/ip-restriction' const app = new Hono() app.use( '*', ipRestriction(getConnInfo, { denyList: [], allowList: ['127.0.0.1', '::1'], }) ) app.get('/', (c) => c.text('Hello Hono!')) ``` 将适合你环境的 [ConnInfo 助手](/docs/helpers/conninfo) 中的 `getConninfo` 作为 `ipRestriction` 的第一个参数传递。例如,对于 Deno,它看起来像这样: ¥Pass the `getConninfo` from the [ConnInfo helper](/docs/helpers/conninfo) appropriate for your environment as the first argument of `ipRestriction`. For example, for Deno, it would look like this: ```ts import { getConnInfo } from 'hono/deno' import { ipRestriction } from 'hono/ip-restriction' //... app.use( '*', ipRestriction(getConnInfo, { // ... }) ) ``` ## 规则 ¥Rules 按照以下说明编写规则。 ¥Follow the instructions below for writing rules. ### IPv4 * `192.168.2.0` - 静态 IP 地址 ¥`192.168.2.0` - Static IP Address * `192.168.2.0/24` - CIDR 表示法 ¥`192.168.2.0/24` - CIDR Notation * `*` - 所有地址 ¥`*` - ALL Addresses ### IPv6 * `::1` - 静态 IP 地址 ¥`::1` - Static IP Address * `::1/10` - CIDR 表示法 ¥`::1/10` - CIDR Notation * `*` - 所有地址 ¥`*` - ALL Addresses ## 错误处理 ¥Error handling 要自定义错误,请在第三个参数中返回 `Response`。 ¥To customize the error, return a `Response` in the third argument. ```ts app.use( '*', ipRestriction( getConnInfo, { denyList: ['192.168.2.0/24'], }, async (remote, c) => { return c.text(`Blocking access from ${remote.addr}`, 403) } ) ) ``` # JSX 渲染器中间件 ¥JSX Renderer Middleware JSX Renderer Middleware 允许你在使用 `c.render()` 函数渲染 JSX 时设置布局,而无需使用 `c.setRenderer()`。此外,它通过使用 `useRequestContext()` 允许访问组件内的 Context 实例。 ¥JSX Renderer Middleware allows you to set up the layout when rendering JSX with the `c.render()` function, without the need for using `c.setRenderer()`. Additionally, it enables access to instances of Context within components through the use of `useRequestContext()`. ## 导入 ¥Import ```ts import { Hono } from 'hono' import { jsxRenderer, useRequestContext } from 'hono/jsx-renderer' ``` ## 用法 ¥Usage ```jsx const app = new Hono() app.get( '/page/*', jsxRenderer(({ children }) => { return (
Menu
{children}
) }) ) app.get('/page/about', (c) => { return c.render(

About me!

) }) ``` ## 选项 ¥Options ### docType:`boolean` | `string` 如果你不想在 HTML 开头添加 DOCTYPE,请将 `docType` 选项设置为 `false`。 ¥If you do not want to add a DOCTYPE at the beginning of the HTML, set the `docType` option to `false`. ```tsx app.use( '*', jsxRenderer( ({ children }) => { return ( {children} ) }, { docType: false } ) ) ``` 你可以指定 DOCTYPE。 ¥And you can specify the DOCTYPE. ```tsx app.use( '*', jsxRenderer( ({ children }) => { return ( {children} ) }, { docType: '', } ) ) ``` ### <徽章类型="info" 文本="optional" /> 流:`boolean` | `Record` ¥ stream: `boolean` | `Record` 如果你将其设置为 `true` 或提供 Record 值,它将被渲染为流式响应。 ¥If you set it to `true` or provide a Record value, it will be rendered as a streaming response. ```tsx const AsyncComponent = async () => { await new Promise((r) => setTimeout(r, 1000)) // sleep 1s return
Hi!
} app.get( '*', jsxRenderer( ({ children }) => { return (

SSR Streaming

{children} ) }, { stream: true } ) ) app.get('/', (c) => { return c.render( loading...}> ) }) ``` 如果设置了 `true`,则添加以下标头: ¥If `true` is set, the following headers are added: ```ts { 'Transfer-Encoding': 'chunked', 'Content-Type': 'text/html; charset=UTF-8', 'Content-Encoding': 'Identity' } ``` 你可以通过指定记录值来自定义标头值。 ¥You can customize the header values by specifying the Record values. ## 嵌套布局 ¥Nested Layouts `Layout` 组件允许嵌套布局。 ¥The `Layout` component enables nesting the layouts. ```tsx app.use( jsxRenderer(({ children }) => { return ( {children} ) }) ) const blog = new Hono() blog.use( jsxRenderer(({ children, Layout }) => { return (
{children}
) }) ) app.route('/blog', blog) ``` ## `useRequestContext()` `useRequestContext()` 返回 Context 的实例。 ¥`useRequestContext()` returns an instance of Context. ```tsx import { useRequestContext, jsxRenderer } from 'hono/jsx-renderer' const app = new Hono() app.use(jsxRenderer()) const RequestUrlBadge: FC = () => { const c = useRequestContext() return {c.req.url} } app.get('/page/info', (c) => { return c.render(
You are accessing:
) }) ``` ::: warning 警告 你不能将 `useRequestContext()` 与 Deno 的 `precompile` JSX 选项一起使用。使用 `react-jsx`: ¥You can't use `useRequestContext()` with the Deno's `precompile` JSX option. Use the `react-jsx`: ```json "compilerOptions": { "jsx": "precompile", // [!code --] "jsx": "react-jsx", // [!code ++] "jsxImportSource": "hono/jsx" } } ``` ::: ## 扩展 `ContextRenderer` ¥Extending `ContextRenderer` 通过定义 `ContextRenderer`(如下所示),你可以将其他内容传递给渲染器。例如,当你想要根据页面更改 head 标签的内容时,这很方便。 ¥By defining `ContextRenderer` as shown below, you can pass additional content to the renderer. This is handy, for instance, when you want to change the contents of the head tag depending on the page. ```tsx declare module 'hono' { interface ContextRenderer { ( content: string | Promise, props: { title: string } ): Response } } const app = new Hono() app.get( '/page/*', jsxRenderer(({ children, title }) => { return ( {title}
Menu
{children}
) }) ) app.get('/page/favorites', (c) => { return c.render(
  • Eating sushi
  • Watching baseball games
, { title: 'My favorites', } ) }) ``` # JWK Auth 中间件 ¥JWK Auth Middleware JWK Auth 中间件通过使用 JWK(JSON Web Key)验证令牌来验证请求。如果指定,它会检查 `Authorization` 标头和其他配置的源,例如 cookie。具体来说,它使用提供的 `keys` 验证令牌,如果指定,则从 `jwks_uri` 检索密钥,如果设置了 `cookie` 选项,则支持从 cookie 中提取令牌。 ¥The JWK Auth Middleware authenticates requests by verifying tokens using JWK (JSON Web Key). It checks for an `Authorization` header and other configured sources, such as cookies, if specified. Specifically, it validates tokens using the provided `keys`, retrieves keys from `jwks_uri` if specified, and supports token extraction from cookies if the `cookie` option is set. :::info 信息 从客户端发送的 Authorization 标头必须具有指定的方案。 ¥The Authorization header sent from the client must have a specified scheme. 示例:`Bearer my.token.value` 或 `Basic my.token.value` ¥Example: `Bearer my.token.value` or `Basic my.token.value` ::: ## 导入 ¥Import ```ts import { Hono } from 'hono' import { jwk } from 'hono/jwk' ``` ## 用法 ¥Usage ```ts const app = new Hono() app.use( '/auth/*', jwk({ jwks_uri: `https://${backendServer}/.well-known/jwks.json`, }) ) app.get('/auth/page', (c) => { return c.text('You are authorized') }) ``` 获取有效负载: ¥Get payload: ```ts const app = new Hono() app.use( '/auth/*', jwk({ jwks_uri: `https://${backendServer}/.well-known/jwks.json`, }) ) app.get('/auth/page', (c) => { const payload = c.get('jwtPayload') return c.json(payload) // eg: { "sub": "1234567890", "name": "John Doe", "iat": 1516239022 } }) ``` ## 选项 ¥Options ### 键:`HonoJsonWebKey[] | (() => Promise)` ¥ keys: `HonoJsonWebKey[] | (() => Promise)` 你的公钥的值,或返回它们的函数。 ¥The values of your public keys, or a function that returns them. ### jwks\_uri:`string` 如果设置了此值,则尝试从此 URI 获取 JWK,期望 JSON 响应带有 `keys`,这些响应将添加到提供的 `keys` 选项中。 ¥If this value is set, attempt to fetch JWKs from this URI, expecting a JSON response with `keys`, which are added to the provided `keys` option. ### cookie:`string` 如果设置了此值,则使用该值作为密钥从 cookie 标头中检索该值,然后将其验证为令牌。 ¥If this value is set, then the value is retrieved from the cookie header using that value as a key, which is then validated as a token. # JWT Auth 中间件 ¥JWT Auth Middleware JWT Auth 中间件通过使用 JWT 验证令牌来提供身份验证。如果未设置 `cookie` 选项,中间件将检查 `Authorization` 标头。 ¥The JWT Auth Middleware provides authentication by verifying the token with JWT. The middleware will check for an `Authorization` header if the `cookie` option is not set. :::info 信息 从客户端发送的 Authorization 标头必须具有指定的方案。 ¥The Authorization header sent from the client must have a specified scheme. 示例:`Bearer my.token.value` 或 `Basic my.token.value` ¥Example: `Bearer my.token.value` or `Basic my.token.value` ::: ## 导入 ¥Import ```ts import { Hono } from 'hono' import { jwt } from 'hono/jwt' import type { JwtVariables } from 'hono/jwt' ``` ## 用法 ¥Usage ```ts // Specify the variable types to infer the `c.get('jwtPayload')`: type Variables = JwtVariables const app = new Hono<{ Variables: Variables }>() app.use( '/auth/*', jwt({ secret: 'it-is-very-secret', }) ) app.get('/auth/page', (c) => { return c.text('You are authorized') }) ``` 获取有效负载: ¥Get payload: ```ts const app = new Hono() app.use( '/auth/*', jwt({ secret: 'it-is-very-secret', }) ) app.get('/auth/page', (c) => { const payload = c.get('jwtPayload') return c.json(payload) // eg: { "sub": "1234567890", "name": "John Doe", "iat": 1516239022 } }) ``` ::: tip 提示 `jwt()` 只是一个中间件函数。如果要使用环境变量(例如:`c.env.JWT_SECRET`),可以按如下方式使用: ¥`jwt()` is just a middleware function. If you want to use an environment variable (eg: `c.env.JWT_SECRET`), you can use it as follows: ```js app.use('/auth/*', (c, next) => { const jwtMiddleware = jwt({ secret: c.env.JWT_SECRET, }) return jwtMiddleware(c, next) }) ``` ::: ## 选项 ¥Options ### <徽章类型="danger" 文本="required" /> 秘密:`string` ¥ secret: `string` 你的密钥值。 ¥A value of your secret key. ### cookie:`string` 如果设置了此值,则使用该值作为密钥从 cookie 标头中检索该值,然后将其验证为令牌。 ¥If this value is set, then the value is retrieved from the cookie header using that value as a key, which is then validated as a token. ### alg:`string` 用于验证的算法类型。默认为 `HS256`。 ¥An algorithm type that is used for verifying.\ The default is `HS256`. 可用的类型有 `HS256` | `HS384` | `HS512` | `RS256` | `RS384` | `RS512` | `PS256` | `PS384` | `PS512` | `ES256` | `ES384` | `ES512` | `EdDSA`。 ¥Available types are `HS256` | `HS384` | `HS512` | `RS256` | `RS384` | `RS512` | `PS256` | `PS384` | `PS512` | `ES256` | `ES384` | `ES512` | `EdDSA`. # 语言中间件 ¥Language Middleware 语言检测器中间件会自动从各种来源确定用户的首选语言(语言环境),并通过 `c.get('language')` 提供该语言。检测策略包括查询参数、cookie、标头和 URL 路径段。非常适合国际化 (i18n) 和特定于语言环境的内容。 ¥The Language Detector middleware automatically determines a user's preferred language (locale) from various sources and makes it available via `c.get('language')`. Detection strategies include query parameters, cookies, headers, and URL path segments. Perfect for internationalization (i18n) and locale-specific content. ## 导入 ¥Import ```ts import { Hono } from 'hono' import { languageDetector } from 'hono/language' ``` ## 基本用法 ¥Basic Usage 从查询字符串、cookie 和标头(默认顺序)检测语言,并回退到英语: ¥Detect language from query string, cookie, and header (default order), with fallback to English: ```ts const app = new Hono() app.use( languageDetector({ supportedLanguages: ['en', 'ar', 'ja'], // Must include fallback fallbackLanguage: 'en', // Required }) ) app.get('/', (c) => { const lang = c.get('language') return c.text(`Hello! Your language is ${lang}`) }) ``` ### 客户端示例 ¥Client Examples ```sh # Via path curl http://localhost:8787/ar/home # Via query parameter curl http://localhost:8787/?lang=ar # Via cookie curl -H 'Cookie: language=ja' http://localhost:8787/ # Via header curl -H 'Accept-Language: ar,en;q=0.9' http://localhost:8787/ ``` ## 默认配置 ¥Default Configuration ```ts export const DEFAULT_OPTIONS: DetectorOptions = { order: ['querystring', 'cookie', 'header'], lookupQueryString: 'lang', lookupCookie: 'language', lookupFromHeaderKey: 'accept-language', lookupFromPathIndex: 0, caches: ['cookie'], ignoreCase: true, fallbackLanguage: 'en', supportedLanguages: ['en'], cookieOptions: { sameSite: 'Strict', secure: true, maxAge: 365 * 24 * 60 * 60, httpOnly: true, }, debug: false, } ``` ## 关键行为 ¥Key Behaviors ### 检测工作流程 ¥Detection Workflow 1. 顺序:默认按此顺序检查源: ¥**Order**: Checks sources in this sequence by default: * 查询参数 (?lang=ar) ¥Query parameter (?lang=ar) * Cookie(语言=ar) ¥Cookie (language=ar) * Accept-Language 标头 ¥Accept-Language header 2. 缓存:将检测到的语言存储在 cookie 中(默认为 1 年) ¥**Caching**: Stores detected language in a cookie (1 year by default) 3. 后备:如果没有有效检测则使用 `fallbackLanguage`(必须在 `supportedLanguages` 中) ¥**Fallback**: Uses `fallbackLanguage` if no valid detection (must be in `supportedLanguages`) ## 高级配置 ¥Advanced Configuration ### 自定义检测顺序 ¥Custom Detection Order 优先考虑 URL 路径检测(例如,/en/about): ¥Prioritize URL path detection (e.g., /en/about): ```ts app.use( languageDetector({ order: ['path', 'cookie', 'querystring', 'header'], lookupFromPathIndex: 0, // /en/profile → index 0 = 'en' supportedLanguages: ['en', 'ar'], fallbackLanguage: 'en', }) ) ``` ### 语言代码转换 ¥Language Code Transformation 规范化复杂代码(例如,en-US → en): ¥Normalize complex codes (e.g., en-US → en): ```ts app.use( languageDetector({ convertDetectedLanguage: (lang) => lang.split('-')[0], supportedLanguages: ['en', 'ja'], fallbackLanguage: 'en', }) ) ``` ### Cookie 配置 ¥Cookie Configuration ```ts app.use( languageDetector({ lookupCookie: 'app_lang', caches: ['cookie'], cookieOptions: { path: '/', // Cookie path sameSite: 'Lax', // Cookie same-site policy secure: true, // Only send over HTTPS maxAge: 86400 * 365, // 1 year expiration httpOnly: true, // Not accessible via JavaScript domain: '.example.com', // Optional: specific domain }, }) ) ``` 要禁用 cookie 缓存: ¥To disable cookie caching: ```ts languageDetector({ caches: false, }) ``` ### 调试 ¥Debugging 日志检测步骤: ¥Log detection steps: ```ts languageDetector({ debug: true, // Shows: "Detected from querystring: ar" }) ``` ## 选项参考 ¥Options Reference ### 基本选项 ¥Basic Options | 选项 | 类型 | 默认 | 必需 | 描述 | | :------------------- | :--------------- | :------------------------------------ | :- | :------ | | `supportedLanguages` | `string[]` | `['en']` | 是 | 允许的语言代码 | | `fallbackLanguage` | `string` | `'en'` | 是 | 默认语言 | | `order` | `DetectorType[]` | `['querystring', 'cookie', 'header']` | 否 | 检测序列 | | `debug` | `boolean` | `false` | 否 | 启用日志记录 | ### 检测选项 ¥Detection Options | 选项 | 类型 | 默认 | 描述 | | :-------------------- | :------- | :------------------ | :-------- | | `lookupQueryString` | `string` | `'lang'` | 查询参数名称 | | `lookupCookie` | `string` | `'language'` | Cookie 名称 | | `lookupFromHeaderKey` | `string` | `'accept-language'` | 标头名称 | | `lookupFromPathIndex` | `number` | `0` | 路径段索引 | ### Cookie 选项 ¥Cookie Options | 选项 | 类型 | 默认 | 描述 | | :----------------------- | :---------------------------- | :----------- | :---------- | | `caches` | `CacheType[] \| false` | `['cookie']` | 缓存设置 | | `cookieOptions.path` | `string` | `'/'` | Cookie 路径 | | `cookieOptions.sameSite` | `'Strict' \| 'Lax' \| 'None'` | `'Strict'` | SameSite 策略 | | `cookieOptions.secure` | `boolean` | `true` | 仅 HTTPS | | `cookieOptions.maxAge` | `number` | `31536000` | 到期时间(秒) | | `cookieOptions.httpOnly` | `boolean` | `true` | JS 可访问性 | | `cookieOptions.domain` | `string` | `undefined` | Cookie 域 | ### 高级选项 ¥Advanced Options | 选项 | 类型 | 默认 | 描述 | | :------------------------ | :------------------------- | :---------- | :-------- | | `ignoreCase` | `boolean` | `true` | 不区分大小写的匹配 | | `convertDetectedLanguage` | `(lang: string) => string` | `undefined` | 语言代码转换器 | ## 验证和错误处理 ¥Validation & Error Handling * `fallbackLanguage` 必须在 `supportedLanguages` 中(设置期间会抛出错误) ¥`fallbackLanguage` must be in `supportedLanguages` (throws error during setup) * `lookupFromPathIndex` 必须≥0 ¥`lookupFromPathIndex` must be ≥ 0 * 无效配置在中间件初始化期间抛出错误 ¥Invalid configurations throw errors during middleware initialization * 失败的检测会默默使用 `fallbackLanguage` ¥Failed detections silently use `fallbackLanguage` ## 常用秘诀 ¥Common Recipes ### 基于路径的路由 ¥Path-Based Routing ```ts app.get('/:lang/home', (c) => { const lang = c.get('language') // 'en', 'ar', etc. return c.json({ message: getLocalizedContent(lang) }) }) ``` ### 支持多种语言 ¥Multiple Supported Languages ```ts languageDetector({ supportedLanguages: ['en', 'en-GB', 'ar', 'ar-EG'], convertDetectedLanguage: (lang) => lang.replace('_', '-'), // Normalize }) ``` # 日志器中间件 ¥Logger Middleware 这是一个简单的日志器。 ¥It's a simple logger. ## 导入 ¥Import ```ts import { Hono } from 'hono' import { logger } from 'hono/logger' ``` ## 用法 ¥Usage ```ts const app = new Hono() app.use(logger()) app.get('/', (c) => c.text('Hello Hono!')) ``` ## 日志详细信息 ¥Logging Details Logger 中间件记录每个请求的以下详细信息: ¥The Logger Middleware logs the following details for each request: * 传入请求:记录 HTTP 方法、请求路径和传入请求。 ¥**Incoming Request**: Logs the HTTP method, request path, and incoming request. * 传出响应:记录 HTTP 方法、请求路径、响应状态代码和请求/响应时间。 ¥**Outgoing Response**: Logs the HTTP method, request path, response status code, and request/response times. * 状态代码着色:响应状态代码采用颜色编码,以便更好地查看和快速识别状态类别。不同的状态代码类别由不同的颜色表示。 ¥**Status Code Coloring**: Response status codes are color-coded for better visibility and quick identification of status categories. Different status code categories are represented by different colors. * 已用时间:请求/响应周期所花费的时间以人性化的形式记录,以毫秒 (ms) 或秒 (s) 为单位。 ¥**Elapsed Time**: The time taken for the request/response cycle is logged in a human-readable format, either in milliseconds (ms) or seconds (s). 通过使用 Logger 中间件,你可以轻松监控 Hono 应用中的请求和响应流,并快速识别任何问题或性能瓶颈。 ¥By using the Logger Middleware, you can easily monitor the flow of requests and responses in your Hono application and quickly identify any issues or performance bottlenecks. 你还可以通过提供自己的 `PrintFunc` 函数来进一步扩展中间件,以实现定制的日志记录行为。 ¥You can also extend the middleware further by providing your own `PrintFunc` function for tailored logging behavior. ## PrintFunc Logger 中间件接受可选的 `PrintFunc` 函数作为参数。此函数允许你自定义日志器并添加其他日志。 ¥The Logger Middleware accepts an optional `PrintFunc` function as a parameter. This function allows you to customize the logger and add additional logs. ## 选项 ¥Options ### fn:`PrintFunc(str: string, ...rest: string[])` * `str`:由日志器传递。 ¥`str`: Passed by the logger. * `...rest`:要打印到控制台的其他字符串属性。 ¥`...rest`: Additional string props to be printed to console. ### 示例 ¥Example 为 Logger 中间件设置自定义 `PrintFunc` 函数: ¥Setting up a custom `PrintFunc` function to the Logger Middleware: ```ts export const customLogger = (message: string, ...rest: string[]) => { console.log(message, ...rest) } app.use(logger(customLogger)) ``` 在路由中设置自定义日志器: ¥Setting up the custom logger in a route: ```ts app.post('/blog', (c) => { // Routing logic customLogger('Blog saved:', `Path: ${blog.url},`, `ID: ${blog.id}`) // Output // <-- POST /blog // Blog saved: Path: /blog/example, ID: 1 // --> POST /blog 201 93ms // Return Context }) ``` # 方法覆盖中间件 ¥Method Override Middleware 此中间件根据表单、标头或查询的值执行指定方法的处理程序(该方法与请求的实际方法不同),并返回其响应。 ¥This middleware executes the handler of the specified method, which is different from the actual method of the request, depending on the value of the form, header, or query, and returns its response. ## 导入 ¥Import ```ts import { Hono } from 'hono' import { methodOverride } from 'hono/method-override' ``` ## 用法 ¥Usage ```ts const app = new Hono() // If no options are specified, the value of `_method` in the form, // e.g. DELETE, is used as the method. app.use('/posts', methodOverride({ app })) app.delete('/posts', (c) => { // .... }) ``` ## 例如 ¥For example 由于 HTML 表单无法发送 DELETE 方法,你可以将值 `DELETE` 放在名为 `_method` 的属性中并发送它。`app.delete()` 的处理程序将被执行。 ¥Since HTML forms cannot send a DELETE method, you can put the value `DELETE` in the property named `_method` and send it. And the handler for `app.delete()` will be executed. HTML 表单: ¥The HTML form: ```html
``` 应用: ¥The application: ```ts import { methodOverride } from 'hono/method-override' const app = new Hono() app.use('/posts', methodOverride({ app })) app.delete('/posts', () => { // ... }) ``` 你可以更改默认值或使用标头值和查询值: ¥You can change the default values or use the header value and query value: ```ts app.use('/posts', methodOverride({ app, form: '_custom_name' })) app.use( '/posts', methodOverride({ app, header: 'X-METHOD-OVERRIDE' }) ) app.use('/posts', methodOverride({ app, query: '_method' })) ``` ## 选项 ¥Options ### app:`Hono` 你的应用中使用了 `Hono` 的实例。 ¥The instance of `Hono` is used in your application. ### 表格:`string` ¥ form: `string` 表单键,其值包含方法名称。默认为 `_method`。 ¥Form key with a value containing the method name. The default is `_method`. ### 标头:`boolean` ¥ header: `boolean` 标头名称,其值包含方法名称。 ¥Header name with a value containing the method name. ### 查询:`boolean` ¥ query: `boolean` 查询参数键,其值包含方法名称。 ¥Query parameter key with a value containing the method name. # 漂亮的 JSON 中间件 ¥Pretty JSON Middleware Pretty JSON 中间件为 JSON 响应主体启用 "JSON 漂亮打印"。将 `?pretty` 添加到 url 查询参数,JSON 字符串将被美化。 ¥Pretty JSON middleware enables "*JSON pretty print*" for JSON response body. Adding `?pretty` to url query param, the JSON strings are prettified. ```js // GET / {"project":{"name":"Hono","repository":"https://github.com/honojs/hono"}} ``` 将是: ¥will be: ```js // GET /?pretty { "project": { "name": "Hono", "repository": "https://github.com/honojs/hono" } } ``` ## 导入 ¥Import ```ts import { Hono } from 'hono' import { prettyJSON } from 'hono/pretty-json' ``` ## 用法 ¥Usage ```ts const app = new Hono() app.use(prettyJSON()) // With options: prettyJSON({ space: 4 }) app.get('/', (c) => { return c.json({ message: 'Hono!' }) }) ``` ## 选项 ¥Options ### <徽章类型="info" 文本="optional" /> 空间:`number` ¥ space: `number` 缩进的空格数。默认为 `2`。 ¥Number of spaces for indentation. The default is `2`. ### 查询:`string` ¥ query: `string` 用于应用的查询字符串的名称。默认为 `pretty`。 ¥The name of the query string for applying. The default is `pretty`. # 请求 ID 中间件 ¥Request ID Middleware 请求 ID 中间件为每个请求生成一个唯一的 ID,你可以在处理程序中使用该 ID。 ¥Request ID Middleware generates a unique ID for each request, which you can use in your handlers. ## 导入 ¥Import ```ts import { Hono } from 'hono' import { requestId } from 'hono/request-id' ``` ## 用法 ¥Usage 你可以通过应用请求 ID 中间件的处理程序和中间件中的 `requestId` 变量访问请求 ID。 ¥You can access the Request ID through the `requestId` variable in the handlers and middleware to which the Request ID Middleware is applied. ```ts const app = new Hono() app.use('*', requestId()) app.get('/', (c) => { return c.text(`Your request id is ${c.get('requestId')}`) }) ``` 如果你想要明确指定类型,请导入 `RequestIdVariables` 并将其传入 `new Hono()` 的泛型中。 ¥If you want to explicitly specify the type, import `RequestIdVariables` and pass it in the generics of `new Hono()`. ```ts import type { RequestIdVariables } from 'hono/request-id' const app = new Hono<{ Variables: RequestIdVariables }>() ``` ## 选项 ¥Options ### limitLength:`number` 请求 ID 的最大长度。默认为 `255`。 ¥The maximum length of the request ID. The default is `255`. ### 标头名称:`string` ¥ headerName: `string` 用于请求 ID 的标头名称。默认为 `X-Request-Id`。 ¥The header name used for the request ID. The default is `X-Request-Id`. ### 生成器:`(c: Context) => string` ¥ generator: `(c: Context) => string` 请求 ID 生成函数。默认情况下,它使用 `crypto.randomUUID()`。 ¥The request ID generation function. By default, it uses `crypto.randomUUID()`. # 安全标头中间件 ¥Secure Headers Middleware 安全标头中间件简化了安全标头的设置。部分受 Helmet 功能的启发,它允许你控制特定安全标头的激活和停用。 ¥Secure Headers Middleware simplifies the setup of security headers. Inspired in part by the capabilities of Helmet, it allows you to control the activation and deactivation of specific security headers. ## 导入 ¥Import ```ts import { Hono } from 'hono' import { secureHeaders } from 'hono/secure-headers' ``` ## 用法 ¥Usage 你可以默认使用最佳设置。 ¥You can use the optimal settings by default. ```ts const app = new Hono() app.use(secureHeaders()) ``` 你可以通过将不必要的标头设置为 false 来抑制它们。 ¥You can suppress unnecessary headers by setting them to false. ```ts const app = new Hono() app.use( '*', secureHeaders({ xFrameOptions: false, xXssProtection: false, }) ) ``` 你可以使用字符串覆盖默认标头值。 ¥You can override default header values using a string. ```ts const app = new Hono() app.use( '*', secureHeaders({ strictTransportSecurity: 'max-age=63072000; includeSubDomains; preload', xFrameOptions: 'DENY', xXssProtection: '1', }) ) ``` ## 支持的选项 ¥Supported Options 每个选项对应以下 Header 键值对。 ¥Each option corresponds to the following Header Key-Value pairs. | 选项 | 标头 | 值 | 默认 | | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------- | ----- | | * | X-Powered-By | (删除标头) | True | | contentSecurityPolicy | [Content-Security-Policy](https://web.nodejs.cn/en-US/docs/Web/HTTP/CSP) | 用法:[设置内容安全策略](#setting-content-security-policy) | 无设置 | | contentSecurityPolicyReportOnly | [Content-Security-Policy-Report-Only](https://web.nodejs.cn/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only) | 用法:[设置内容安全策略](#setting-content-security-policy) | 无设置 | | crossOriginEmbedderPolicy | [Cross-Origin-Embedder-Policy](https://web.nodejs.cn/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy) | require-corp | **假** | | crossOriginResourcePolicy | [Cross-Origin-Resource-Policy](https://web.nodejs.cn/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy) | same-origin | True | | crossOriginOpenerPolicy | [Cross-Origin-Opener-Policy](https://web.nodejs.cn/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy) | same-origin | True | | originAgentCluster | [Origin-Agent-Cluster](https://web.nodejs.cn/en-US/docs/Web/HTTP/Headers/Origin-Agent-Cluster) | ?1 | True | | referrerPolicy | [Referrer-Policy](https://web.nodejs.cn/en-US/docs/Web/HTTP/Headers/Referrer-Policy) | no-referrer | True | | reportingEndpoints | [Reporting-Endpoints](https://www.w3.org/TR/reporting-1/#header) | 用法:[设置内容安全策略](#setting-content-security-policy) | 无设置 | | reportTo | [Report-To](https://web.nodejs.cn/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/report-to) | 用法:[设置内容安全策略](#setting-content-security-policy) | 无设置 | | strictTransportSecurity | [Strict-Transport-Security](https://web.nodejs.cn/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security) | max-age=15552000;includeSubDomains | True | | xContentTypeOptions | [X-Content-Type-Options](https://web.nodejs.cn/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options) | nosniff | True | | xDnsPrefetchControl | [X-DNS-Prefetch-Control](https://web.nodejs.cn/en-US/docs/Web/HTTP/Headers/X-DNS-Prefetch-Control) | off | True | | xDownloadOptions | [X-Download-Options](https://web.nodejs.cn/en-US/docs/Web/HTTP/Headers/X-Download-Options) | noopen | True | | xFrameOptions | [X-Frame-Options](https://web.nodejs.cn/en-US/docs/Web/HTTP/Headers/X-Frame-Options) | SAMEORIGIN | True | | xPermittedCrossDomainPolicies | [X-Permitted-Cross-Domain-Policies](https://web.nodejs.cn/en-US/docs/Web/HTTP/Headers/X-Permitted-Cross-Domain-Policies) | none | True | | xXssProtection | [X-XSS-Protection](https://web.nodejs.cn/en-US/docs/Web/HTTP/Headers/X-XSS-Protection) | 0 | True | | permissionPolicy | [权限策略](https://web.nodejs.cn/en-US/docs/Web/HTTP/Headers/Permissions-Policy) | 用法:[设置权限策略](#setting-permission-policy) | 无设置 | ## 中间件冲突 ¥Middleware Conflict 处理操纵相同标头的中间件时,请谨慎规范的顺序。 ¥Please be cautious about the order of specification when dealing with middleware that manipulates the same header. 在这种情况下,Secure-headers 运行并删除 `x-powered-by`: ¥In this case, Secure-headers operates and the `x-powered-by` is removed: ```ts const app = new Hono() app.use(secureHeaders()) app.use(poweredBy()) ``` 在这种情况下,Powered-By 运行并添加 `x-powered-by`: ¥In this case, Powered-By operates and the `x-powered-by` is added: ```ts const app = new Hono() app.use(poweredBy()) app.use(secureHeaders()) ``` ## 设置内容安全策略 ¥Setting Content-Security-Policy ```ts const app = new Hono() app.use( '/test', secureHeaders({ reportingEndpoints: [ { name: 'endpoint-1', url: 'https://example.com/reports', }, ], // -- or alternatively // reportTo: [ // { // group: 'endpoint-1', // max_age: 10886400, // endpoints: [{ url: 'https://example.com/reports' }], // }, // ], contentSecurityPolicy: { defaultSrc: ["'self'"], baseUri: ["'self'"], childSrc: ["'self'"], connectSrc: ["'self'"], fontSrc: ["'self'", 'https:', 'data:'], formAction: ["'self'"], frameAncestors: ["'self'"], frameSrc: ["'self'"], imgSrc: ["'self'", 'data:'], manifestSrc: ["'self'"], mediaSrc: ["'self'"], objectSrc: ["'none'"], reportTo: 'endpoint-1', sandbox: ['allow-same-origin', 'allow-scripts'], scriptSrc: ["'self'"], scriptSrcAttr: ["'none'"], scriptSrcElem: ["'self'"], styleSrc: ["'self'", 'https:', "'unsafe-inline'"], styleSrcAttr: ['none'], styleSrcElem: ["'self'", 'https:', "'unsafe-inline'"], upgradeInsecureRequests: [], workerSrc: ["'self'"], }, }) ) ``` ### `nonce` 属性 ¥`nonce` attribute 你可以通过将从 `hono/secure-headers` 导入的 `NONCE` 添加到 `scriptSrc` 或 `styleSrc` 来将 [`nonce` 属性](https://web.nodejs.cn/en-US/docs/Web/HTML/Global_attributes/nonce) 添加到 `script` 或 `style` 元素: ¥You can add a [`nonce` attribute](https://web.nodejs.cn/en-US/docs/Web/HTML/Global_attributes/nonce) to a `script` or `style` element by adding the `NONCE` imported from `hono/secure-headers` to a `scriptSrc` or `styleSrc`: ```tsx import { secureHeaders, NONCE } from 'hono/secure-headers' import type { SecureHeadersVariables } from 'hono/secure-headers' // Specify the variable types to infer the `c.get('secureHeadersNonce')`: type Variables = SecureHeadersVariables const app = new Hono<{ Variables: Variables }>() // Set the pre-defined nonce value to `scriptSrc`: app.get( '*', secureHeaders({ contentSecurityPolicy: { scriptSrc: [NONCE, 'https://allowed1.example.com'], }, }) ) // Get the value from `c.get('secureHeadersNonce')`: app.get('/', (c) => { return c.html( {/** contents */} `} Hello! ) }) ``` ### 充当功能组件 ¥Act as functional component 由于 `html` 返回 HtmlEscapedString,因此它可以充当功能齐全的组件而无需使用 JSX。 ¥Since `html` returns an HtmlEscapedString, it can act as a fully functional component without using JSX. #### 使用 `html` 代替 `memo` 来加速进程 ¥Use `html` to speed up the process instead of `memo` ```typescript const Footer = () => html`
My Address...
` ``` ### 接收 props 并嵌入值 ¥Receives props and embeds values ```typescript interface SiteData { title: string description: string image: string children?: any } const Layout = (props: SiteData) => html` ${props.title} ${props.children} ` const Content = (props: { siteData: SiteData; name: string }) => (

Hello {props.name}

) app.get('/', (c) => { const props = { name: 'World', siteData: { title: 'Hello <> World', description: 'This is a description', image: 'https://example.com/image.png', }, } return c.html() }) ``` ## `raw()` ```ts app.get('/', (c) => { const name = 'John "Johnny" Smith' return c.html(html`

I'm ${raw(name)}.

`) }) ``` ## 提示 ¥Tips 得益于这些库,Visual Studio Code 和 vim 还将模板字面量解释为 HTML,从而允许应用语法高亮和格式化。 ¥Thanks to these libraries, Visual Studio Code and vim also interprets template literals as HTML, allowing syntax highlighting and formatting to be applied. * [https://marketplace.visualstudio.com/items?itemName=bierner.lit-html](https://marketplace.visualstudio.com/items?itemName=bierner.lit-html) * [https://github.com/MaxMEllon/vim-jsx-pretty](https://github.com/MaxMEllon/vim-jsx-pretty) # JWT 身份验证助手 ¥JWT Authentication Helper 此辅助程序提供用于编码、解码、签名和验证 JSON Web Tokens (JWT) 的函数。JWT 通常用于 Web 应用中的身份验证和授权目的。此辅助程序提供强大的 JWT 功能,支持各种加密算法。 ¥This helper provides functions for encoding, decoding, signing, and verifying JSON Web Tokens (JWTs). JWTs are commonly used for authentication and authorization purposes in web applications. This helper offers robust JWT functionality with support for various cryptographic algorithms. ## 导入 ¥Import 要使用此辅助程序,你可以按如下方式导入它: ¥To use this helper, you can import it as follows: ```ts import { decode, sign, verify } from 'hono/jwt' ``` ::: info 信息 [JWT 中间件](/docs/middleware/builtin/jwt) 还从 `hono/jwt` 导入 `jwt` 函数。 ¥[JWT Middleware](/docs/middleware/builtin/jwt) also import the `jwt` function from the `hono/jwt`. ::: ## `sign()` 此函数通过编码有效负载并使用指定的算法和密钥对其进行签名来生成 JWT 令牌。 ¥This function generates a JWT token by encoding a payload and signing it using the specified algorithm and secret. ```ts sign( payload: unknown, secret: string, alg?: 'HS256'; ): Promise; ``` ### 示例 ¥Example ```ts import { sign } from 'hono/jwt' const payload = { sub: 'user123', role: 'admin', exp: Math.floor(Date.now() / 1000) + 60 * 5, // Token expires in 5 minutes } const secret = 'mySecretKey' const token = await sign(payload, secret) ``` ### 选项 ¥Options
#### payload:`unknown` 要签名的 JWT 有效负载。你可以像在 [有效负载验证](#payload-validation) 中一样包含其他声明。 ¥The JWT payload to be signed. You can include other claims like in [Payload Validation](#payload-validation). #### <徽章类型="danger" 文本="required" /> 秘密:`string` ¥ secret: `string` 用于 JWT 验证或签名的密钥。 ¥The secret key used for JWT verification or signing. #### alg:[AlgorithmTypes](#supported-algorithmtypes) 用于 JWT 签名或验证的算法。默认为 HS256。 ¥The algorithm used for JWT signing or verification. The default is HS256. ## `verify()` 此函数检查 JWT 令牌是否真实且仍然有效。它确保令牌未被更改,并且仅在你添加 [有效负载验证](#payload-validation) 时才检查有效性。 ¥This function checks if a JWT token is genuine and still valid. It ensures the token hasn't been altered and checks validity only if you added [Payload Validation](#payload-validation). ```ts verify( token: string, secret: string, alg?: 'HS256'; ): Promise; ``` ### 示例 ¥Example ```ts import { verify } from 'hono/jwt' const tokenToVerify = 'token' const secretKey = 'mySecretKey' const decodedPayload = await verify(tokenToVerify, secretKey) console.log(decodedPayload) ``` ### 选项 ¥Options
#### <徽章类型="danger" 文本="required" /> 令牌:`string` ¥ token: `string` 要验证的 JWT 令牌。 ¥The JWT token to be verified. #### <徽章类型="danger" 文本="required" /> 秘密:`string` ¥ secret: `string` 用于 JWT 验证或签名的密钥。 ¥The secret key used for JWT verification or signing. #### alg:[AlgorithmTypes](#supported-algorithmtypes) 用于 JWT 签名或验证的算法。默认为 HS256。 ¥The algorithm used for JWT signing or verification. The default is HS256. ## `decode()` 此函数解码 JWT 令牌而不执行签名验证。它从令牌中提取并返回标头和有效负载。 ¥This function decodes a JWT token without performing signature verification. It extracts and returns the header and payload from the token. ```ts decode(token: string): { header: any; payload: any }; ``` ### 示例 ¥Example ```ts import { decode } from 'hono/jwt' // Decode the JWT token const tokenToDecode = 'eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJzdWIiOiAidXNlcjEyMyIsICJyb2xlIjogImFkbWluIn0.JxUwx6Ua1B0D1B0FtCrj72ok5cm1Pkmr_hL82sd7ELA' const { header, payload } = decode(tokenToDecode) console.log('Decoded Header:', header) console.log('Decoded Payload:', payload) ``` ### 选项 ¥Options
#### <徽章类型="danger" 文本="required" /> 令牌:`string` ¥ token: `string` 要解码的 JWT 令牌。 ¥The JWT token to be decoded. > `decode` 函数允许你检查 JWT 令牌的标头和有效负载而无需执行验证。这对于调试或从 JWT 令牌中提取信息很有用。 > > ¥The `decode` function allows you to inspect the header and payload of a JWT token ***without*** performing verification. This can be useful for debugging or extracting information from JWT tokens. ## 有效负载验证 ¥Payload Validation 验证 JWT 令牌时,将执行以下有效负载验证: ¥When verifying a JWT token, the following payload validations are performed: * `exp`:检查令牌以确保其未过期。 ¥`exp`: The token is checked to ensure it has not expired. * `nbf`:检查令牌以确保其未在指定时间之前被使用。 ¥`nbf`: The token is checked to ensure it is not being used before a specified time. * `iat`:检查令牌以确保其不会在将来发出。 ¥`iat`: The token is checked to ensure it is not issued in the future. 如果你打算在验证期间执行这些检查,请确保你的 JWT 有效负载包含这些字段作为对象。 ¥Please ensure that your JWT payload includes these fields, as an object, if you intend to perform these checks during verification. ## 自定义错误类型 ¥Custom Error Types 该模块还定义了自定义错误类型来处理与 JWT 相关的错误。 ¥The module also defines custom error types to handle JWT-related errors. * `JwtAlgorithmNotImplemented`:表示请求的 JWT 算法未实现。 ¥`JwtAlgorithmNotImplemented`: Indicates that the requested JWT algorithm is not implemented. * `JwtTokenInvalid`:表示 JWT 令牌无效。 ¥`JwtTokenInvalid`: Indicates that the JWT token is invalid. * `JwtTokenNotBefore`:表示令牌在有效期之前使用。 ¥`JwtTokenNotBefore`: Indicates that the token is being used before its valid date. * `JwtTokenExpired`:表示令牌已过期。 ¥`JwtTokenExpired`: Indicates that the token has expired. * `JwtTokenIssuedAt`:表示令牌中的 "iat" 声明不正确。 ¥`JwtTokenIssuedAt`: Indicates that the "iat" claim in the token is incorrect. * `JwtTokenSignatureMismatched`:表示令牌中的签名不匹配。 ¥`JwtTokenSignatureMismatched`: Indicates a signature mismatch in the token. ## 支持的算法类型 ¥Supported AlgorithmTypes 该模块支持以下 JWT 加密算法: ¥The module supports the following JWT cryptographic algorithms: * `HS256`:使用 SHA-256 的 HMAC ¥`HS256`: HMAC using SHA-256 * `HS384`:使用 SHA-384 的 HMAC ¥`HS384`: HMAC using SHA-384 * `HS512`:使用 SHA-512 的 HMAC ¥`HS512`: HMAC using SHA-512 * `RS256`:使用 SHA-256 的 RSASSA-PKCS1-v1_5 ¥`RS256`: RSASSA-PKCS1-v1_5 using SHA-256 * `RS384`:使用 SHA-384 的 RSASSA-PKCS1-v1_5 ¥`RS384`: RSASSA-PKCS1-v1_5 using SHA-384 * `RS512`:使用 SHA-512 的 RSASSA-PKCS1-v1_5 ¥`RS512`: RSASSA-PKCS1-v1_5 using SHA-512 * `PS256`:RSASSA-PSS 使用 SHA-256 和 MGF1 使用 SHA-256 ¥`PS256`: RSASSA-PSS using SHA-256 and MGF1 with SHA-256 * `PS384`:RSASSA-PSS 使用 SHA-386 和 MGF1 使用 SHA-386 ¥`PS384`: RSASSA-PSS using SHA-386 and MGF1 with SHA-386 * `PS512`:RSASSA-PSS 使用 SHA-512 和 MGF1 使用 SHA-512 ¥`PS512`: RSASSA-PSS using SHA-512 and MGF1 with SHA-512 * `ES256`:使用 P-256 和 SHA-256 的 ECDSA ¥`ES256`: ECDSA using P-256 and SHA-256 * `ES384`:使用 P-384 和 SHA-384 的 ECDSA ¥`ES384`: ECDSA using P-384 and SHA-384 * `ES512`:使用 P-521 和 SHA-512 的 ECDSA ¥`ES512`: ECDSA using P-521 and SHA-512 * `EdDSA`:使用 Ed25519 的 EdDSA ¥`EdDSA`: EdDSA using Ed25519 # 代理助手 ¥Proxy Helper 当使用 Hono 应用作为(反向)代理时,Proxy Helper 提供了有用的功能。 ¥Proxy Helper provides useful functions when using Hono application as a (reverse) proxy. ## 导入 ¥Import ```ts import { Hono } from 'hono' import { proxy } from 'hono/proxy' ``` ## `proxy()` `proxy()` 是用于代理的 `fetch()` API 封装器。参数和返回值与 `fetch()` 相同(代理特定选项除外)。 ¥`proxy()` is a `fetch()` API wrapper for proxy. The parameters and return value are the same as for `fetch()` (except for the proxy-specific options). `Accept-Encoding` 标头被替换为当前运行时可以处理的编码。删除不必要的响应标头,并返回一个 `Response` 对象,你可以将其作为处理程序的响应返回。 ¥The `Accept-Encoding` header is replaced with an encoding that the current runtime can handle. Unnecessary response headers are deleted, and a `Response` object is returned that you can return as a response from the handler. ### 示例 ¥Examples 简单用法: ¥Simple usage: ```ts app.get('/proxy/:path', (c) => { return proxy(`http://${originServer}/${c.req.param('path')}`) }) ``` 复杂用法: ¥Complicated usage: ```ts app.get('/proxy/:path', async (c) => { const res = await proxy( `http://${originServer}/${c.req.param('path')}`, { headers: { ...c.req.header(), // optional, specify only when forwarding all the request data (including credentials) is necessary. 'X-Forwarded-For': '127.0.0.1', 'X-Forwarded-Host': c.req.header('host'), Authorization: undefined, // do not propagate request headers contained in c.req.header('Authorization') }, } ) res.headers.delete('Set-Cookie') return res }) ``` 或者你可以将 `c.req` 作为参数传递。 ¥Or you can pass the `c.req` as a parameter. ```ts app.all('/proxy/:path', (c) => { return proxy(`http://${originServer}/${c.req.param('path')}`, { ...c.req, // optional, specify only when forwarding all the request data (including credentials) is necessary. headers: { ...c.req.header(), 'X-Forwarded-For': '127.0.0.1', 'X-Forwarded-Host': c.req.header('host'), Authorization: undefined, // do not propagate request headers contained in c.req.header('Authorization') }, }) }) ``` ### `ProxyFetch` `proxy()` 的类型定义为 `ProxyFetch`,如下所示 ¥The type of `proxy()` is defined as `ProxyFetch` and is as follows ```ts interface ProxyRequestInit extends Omit { raw?: Request headers?: | HeadersInit | [string, string][] | Record | Record } interface ProxyFetch { ( input: string | URL | Request, init?: ProxyRequestInit ): Promise } ``` # SSG 助手 ¥SSG Helper SSG Helper 从你的 Hono 应用生成静态站点。它将检索已注册路由的内容并将其保存为静态文件。 ¥SSG Helper generates a static site from your Hono application. It will retrieve the contents of registered routes and save them as static files. ## 用法 ¥Usage ### 手动 ¥Manual 如果你有一个简单的 Hono 应用,如下所示: ¥If you have a simple Hono application like the following: ```tsx // index.tsx const app = new Hono() app.get('/', (c) => c.html('Hello, World!')) app.use('/about', async (c, next) => { c.setRenderer((content, head) => { return c.html( {head.title ?? ''}

{content}

) }) await next() }) app.get('/about', (c) => { return c.render('Hello!', { title: 'Hono SSG Page' }) }) export default app ``` 对于 Node.js,创建如下构建脚本: ¥For Node.js, create a build script like this: ```ts // build.ts import app from './index' import { toSSG } from 'hono/ssg' import fs from 'fs/promises' toSSG(app, fs) ``` 通过执行脚本,文件将输出如下: ¥By executing the script, the files will be output as follows: ```bash ls ./static about.html index.html ``` ### Vite 插件 ¥Vite Plugin 使用 `@hono/vite-ssg` Vite 插件,你可以轻松处理该过程。 ¥Using the `@hono/vite-ssg` Vite Plugin, you can easily handle the process. 有关更多详细信息,请参见此处: ¥For more details, see here: [https://github.com/honojs/vite-plugins/tree/main/packages/ssg](https://github.com/honojs/vite-plugins/tree/main/packages/ssg) ## toSSG `toSSG` 是生成静态站点的主要函数,以应用和文件系统模块作为参数。它基于以下内容: ¥`toSSG` is the main function for generating static sites, taking an application and a filesystem module as arguments. It is based on the following: ### 输入 ¥Input toSSG 的参数在 ToSSGInterface 中指定。 ¥The arguments for toSSG are specified in ToSSGInterface. ```ts export interface ToSSGInterface { ( app: Hono, fsModule: FileSystemModule, options?: ToSSGOptions ): Promise } ``` * `app` 指定已注册路由的 `new Hono()`。 ¥`app` specifies `new Hono()` with registered routes. * `fs` 指定以下对象,假设 `node:fs/promise`。 ¥`fs` specifies the following object, assuming `node:fs/promise`. ```ts export interface FileSystemModule { writeFile(path: string, data: string | Uint8Array): Promise mkdir( path: string, options: { recursive: boolean } ): Promise } ``` ### 使用 Deno 和 Bun 的适配器 ¥Using adapters for Deno and Bun 如果要在 Deno 或 Bun 上使用 SSG,则每个文件系统都提供了一个 `toSSG` 函数。 ¥If you want to use SSG on Deno or Bun, a `toSSG` function is provided for each file system. 对于 Deno: ¥For Deno: ```ts import { toSSG } from 'hono/deno' toSSG(app) // The second argument is an option typed `ToSSGOptions`. ``` 对于 Bun: ¥For Bun: ```ts import { toSSG } from 'hono/bun' toSSG(app) // The second argument is an option typed `ToSSGOptions`. ``` ### 选项 ¥Options 选项在 ToSSGOptions 接口中指定。 ¥Options are specified in the ToSSGOptions interface. ```ts export interface ToSSGOptions { dir?: string concurrency?: number beforeRequestHook?: BeforeRequestHook afterResponseHook?: AfterResponseHook afterGenerateHook?: AfterGenerateHook extensionMap?: Record } ``` * `dir` 是静态文件的输出目标。默认值为 `./static`。 ¥`dir` is the output destination for Static files. The default value is `./static`. * `concurrency` 是同时生成的文件数。默认值为 `2`。 ¥`concurrency` is the concurrent number of files to be generated at the same time. The default value is `2`. * `extensionMap` 是一个映射,其中包含 `Content-Type` 作为键和扩展字符串作为值。这用于确定输出文件的文件扩展名。 ¥`extensionMap` is a map containing the `Content-Type` as a key and the string of the extension as a value. This is used to determine the file extension of the output file. 稍后将描述每个 Hook。 ¥Each Hook will be described later. ### 输出 ¥Output `toSSG` 以以下 Result 类型返回结果。 ¥`toSSG` returns the result in the following Result type. ```ts export interface ToSSGResult { success: boolean files: string[] error?: Error } ``` ## Hook 你可以通过在选项中指定以下自定义钩子来自定义 `toSSG` 的流程。 ¥You can customize the process of `toSSG` by specifying the following custom hooks in options. ```ts export type BeforeRequestHook = (req: Request) => Request | false export type AfterResponseHook = (res: Response) => Response | false export type AfterGenerateHook = ( result: ToSSGResult ) => void | Promise ``` ### BeforeRequestHook/AfterResponseHook `toSSG` 针对应用中注册的所有路由,但如果你想要排除某些路由,则可以通过指定 Hook 来过滤它们。 ¥`toSSG` targets all routes registered in app, but if there are routes you want to exclude, you can filter them by specifying a Hook. 例如,如果你只想输出 GET 请求,请在 `beforeRequestHook` 中过滤 `req.method`。 ¥For example, if you want to output only GET requests, filter `req.method` in `beforeRequestHook`. ```ts toSSG(app, fs, { beforeRequestHook: (req) => { if (req.method === 'GET') { return req } return false }, }) ``` 例如,如果你只想在 StatusCode 为 200 或 500 时输出,请在 `afterResponseHook` 中过滤 `res.status`。 ¥For example, if you want to output only when StatusCode is 200 or 500, filter `res.status` in `afterResponseHook`. ```ts toSSG(app, fs, { afterResponseHook: (res) => { if (res.status === 200 || res.status === 500) { return res } return false }, }) ``` ### AfterGenerateHook 如果你想钩子 `toSSG` 的结果,请使用 `afterGenerateHook`。 ¥Use `afterGenerateHook` if you want to hook the result of `toSSG`. ```ts toSSG(app, fs, { afterGenerateHook: (result) => { if (result.files) { result.files.forEach((file) => console.log(file)) } }) }) ``` ## 生成文件 ¥Generate File ### 路由和文件名 ¥Route and Filename 以下规则适用于注册的路由信息​​和生成的文件名。默认 `./static` 的行为如下: ¥The following rules apply to the registered route information and the generated file name. The default `./static` behaves as follows: * `/` -> `./static/index.html` * `/path` -> `./static/path.html` * `/path/` -> `./static/path/index.html` ### 文件扩展名 ¥File Extension 文件扩展名取决于每个路由返回的 `Content-Type`。例如,来自 `c.html` 的响应保存为 `.html`。 ¥The file extension depends on the `Content-Type` returned by each route. For example, responses from `c.html` are saved as `.html`. 如果你想要自定义文件扩展名,请设置 `extensionMap` 选项。 ¥If you want to customize the file extensions, set the `extensionMap` option. ```ts import { toSSG, defaultExtensionMap } from 'hono/ssg' // Save `application/x-html` content with `.html` toSSG(app, fs, { extensionMap: { 'application/x-html': 'html', ...defaultExtensionMap, }, }) ``` 请注意,无论扩展名是什么,以斜杠结尾的路径都会保存为 index.ext。 ¥Note that paths ending with a slash are saved as index.ext regardless of the extension. ```ts // save to ./static/html/index.html app.get('/html/', (c) => c.html('html')) // save to ./static/text/index.txt app.get('/text/', (c) => c.text('text')) ``` ## 中间件 ¥Middleware 引入支持 SSG 的内置中间件。 ¥Introducing built-in middleware that supports SSG. ### ssgParams 你可以使用 Next.js 的 `generateStaticParams` 之类的 API。 ¥You can use an API like `generateStaticParams` of Next.js. 示例: ¥Example: ```ts app.get( '/shops/:id', ssgParams(async () => { const shops = await getShops() return shops.map((shop) => ({ id: shop.id })) }), async (c) => { const shop = await getShop(c.req.param('id')) if (!shop) { return c.notFound() } return c.render(

{shop.name}

) } ) ``` ### disableSSG 具有 `disableSSG` 中间件集的路由被 `toSSG` 排除在静态文件生成之外。 ¥Routes with the `disableSSG` middleware set are excluded from static file generation by `toSSG`. ```ts app.get('/api', disableSSG(), (c) => c.text('an-api')) ``` ### onlySSG 具有 `onlySSG` 中间件集的路由将在 `toSSG` 执行后被 `c.notFound()` 覆盖。 ¥Routes with the `onlySSG` middleware set will be overridden by `c.notFound()` after `toSSG` execution. ```ts app.get('/static-page', onlySSG(), (c) => c.html(

Welcome to my site

)) ``` # 流式助手 ¥Streaming Helper Streaming Helper 提供用于流式响应的方法。 ¥The Streaming Helper provides methods for streaming responses. ## 导入 ¥Import ```ts import { Hono } from 'hono' import { stream, streamText, streamSSE } from 'hono/streaming' ``` ## `stream()` 它返回一个简单的流响应作为 `Response` 对象。 ¥It returns a simple streaming response as `Response` object. ```ts app.get('/stream', (c) => { return stream(c, async (stream) => { // Write a process to be executed when aborted. stream.onAbort(() => { console.log('Aborted!') }) // Write a Uint8Array. await stream.write(new Uint8Array([0x48, 0x65, 0x6c, 0x6c, 0x6f])) // Pipe a readable stream. await stream.pipe(anotherReadableStream) }) }) ``` ## `streamText()` 它返回带有 `Content-Type:text/plain`、`Transfer-Encoding:chunked` 和 `X-Content-Type-Options:nosniff` 标头的流响应。 ¥It returns a streaming response with `Content-Type:text/plain`, `Transfer-Encoding:chunked`, and `X-Content-Type-Options:nosniff` headers. ```ts app.get('/streamText', (c) => { return streamText(c, async (stream) => { // Write a text with a new line ('\n'). await stream.writeln('Hello') // Wait 1 second. await stream.sleep(1000) // Write a text without a new line. await stream.write(`Hono!`) }) }) ``` ::: warning 警告 如果你正在为 Cloudflare Workers 开发应用,流媒体可能无法在 Wrangler 上正常工作。如果是这样,请为 `Content-Encoding` 标头添加 `Identity`。 ¥If you are developing an application for Cloudflare Workers, a streaming may not work well on Wrangler. If so, add `Identity` for `Content-Encoding` header. ```ts app.get('/streamText', (c) => { c.header('Content-Encoding', 'Identity') return streamText(c, async (stream) => { // ... }) }) ``` ::: ## `streamSSE()` 它允许你无缝地流式传输服务器发送事件 (SSE)。 ¥It allows you to stream Server-Sent Events (SSE) seamlessly. ```ts const app = new Hono() let id = 0 app.get('/sse', async (c) => { return streamSSE(c, async (stream) => { while (true) { const message = `It is ${new Date().toISOString()}` await stream.writeSSE({ data: message, event: 'time-update', id: String(id++), }) await stream.sleep(1000) } }) }) ``` ## 错误处理 ¥Error Handling 流助手的第三个参数是错误处理程序。此参数是可选的,如果不指定,错误将作为控制台错误输出。 ¥The third argument of the streaming helper is an error handler. This argument is optional, if you don't specify it, the error will be output as a console error. ```ts app.get('/stream', (c) => { return stream( c, async (stream) => { // Write a process to be executed when aborted. stream.onAbort(() => { console.log('Aborted!') }) // Write a Uint8Array. await stream.write( new Uint8Array([0x48, 0x65, 0x6c, 0x6c, 0x6f]) ) // Pipe a readable stream. await stream.pipe(anotherReadableStream) }, (err, stream) => { stream.writeln('An error occurred!') console.error(err) } ) }) ``` 执行回调后,流将自动关闭。 ¥The stream will be automatically closed after the callbacks are executed. ::: warning 警告 如果流式助手的回调函数抛出错误,则不会触发 Hono 的 `onError` 事件。 ¥If the callback function of the streaming helper throws an error, the `onError` event of Hono will not be triggered. `onError` 是一个钩子,用于在发送响应之前处理错误并覆盖响应。但是,当执行回调函数时,流已经开始,因此无法覆盖它。 ¥`onError` is a hook to handle errors before the response is sent and overwrite the response. However, when the callback function is executed, the stream has already started, so it cannot be overwritten. ::: # 测试助手 ¥Testing Helper Testing Helper 提供使 Hono 应用测试更容易的功能。 ¥The Testing Helper provides functions to make testing of Hono applications easier. ## 导入 ¥Import ```ts import { Hono } from 'hono' import { testClient } from 'hono/testing' ``` ## `testClient()` `testClient()` 将 Hono 的实例作为其第一个参数并返回 [Hono 客户端](/docs/guides/rpc#client) 的对象。通过使用它,你可以使用编辑器完成定义你的请求。 ¥The `testClient()` takes an instance of Hono as its first argument and returns an object of the [Hono Client](/docs/guides/rpc#client). By using this, you can define your request with the editor completion. ```ts import { testClient } from 'hono/testing' it('test', async () => { const app = new Hono().get('/search', (c) => c.json({ hello: 'world' }) ) const res = await testClient(app).search.$get() expect(await res.json()).toEqual({ hello: 'world' }) }) ``` # WebSocket 助手 ¥WebSocket Helper WebSocket Helper 是 Hono 应用中服务器端 WebSockets 的助手。目前 Cloudflare Workers / Pages、Deno 和 Bun 适配器可用。 ¥WebSocket Helper is a helper for server-side WebSockets in Hono applications. Currently Cloudflare Workers / Pages, Deno, and Bun adapters are available. ## 导入 ¥Import ::: code-group ```ts [Cloudflare Workers] import { Hono } from 'hono' import { upgradeWebSocket } from 'hono/cloudflare-workers' ``` ```ts [Deno] import { Hono } from 'hono' import { upgradeWebSocket } from 'hono/deno' ``` ```ts [Bun] import { Hono } from 'hono' import { createBunWebSocket } from 'hono/bun' import type { ServerWebSocket } from 'bun' const { upgradeWebSocket, websocket } = createBunWebSocket() // ... export default { fetch: app.fetch, websocket, } ``` ::: 如果你使用 Node.js,则可以使用 [@hono/node-ws](https://github.com/honojs/middleware/tree/main/packages/node-ws)。 ¥If you use Node.js, you can use [@hono/node-ws](https://github.com/honojs/middleware/tree/main/packages/node-ws). ## `upgradeWebSocket()` `upgradeWebSocket()` 返回用于处理 WebSocket 的处理程序。 ¥`upgradeWebSocket()` returns a handler for handling WebSocket. ```ts const app = new Hono() app.get( '/ws', upgradeWebSocket((c) => { return { onMessage(event, ws) { console.log(`Message from client: ${event.data}`) ws.send('Hello from server!') }, onClose: () => { console.log('Connection closed') }, } }) ) ``` 可用事件: ¥Available events: * `onOpen` - 目前,Cloudflare Workers 不支持它。 ¥`onOpen` - Currently, Cloudflare Workers does not support it. * `onMessage` * `onClose` * `onError` ::: warning 警告 如果你在使用 WebSocket Helper 的路由上使用修改标头的中间件(例如,应用 CORS),则可能会遇到错误,提示你无法修改不可变标头。这是因为 `upgradeWebSocket()` 也在内部更改标头。 ¥If you use middleware that modifies headers (e.g., applying CORS) on a route that uses WebSocket Helper, you may encounter an error saying you can't modify immutable headers. This is because `upgradeWebSocket()` also changes headers internally. 因此,如果你同时使用 WebSocket Helper 和中间件,请谨慎使用。 ¥Therefore, please be cautious if you are using WebSocket Helper and middleware at the same time. ::: ## RPC 模式 ¥RPC-mode 使用 WebSocket Helper 定义的处理程序支持 RPC 模式。 ¥Handlers defined with WebSocket Helper support RPC mode. ```ts // server.ts const wsApp = app.get( '/ws', upgradeWebSocket((c) => { //... }) ) export type WebSocketApp = typeof wsApp // client.ts const client = hc('http://localhost:8787') const socket = client.ws.$ws() // A WebSocket object for a client ``` ## 示例 ¥Examples 查看使用 WebSocket Helper 的示例。 ¥See the examples using WebSocket Helper. ### 服务器和客户端 ¥Server and Client ```ts // server.ts import { Hono } from 'hono' import { upgradeWebSocket } from 'hono/cloudflare-workers' const app = new Hono().get( '/ws', upgradeWebSocket(() => { return { onMessage: (event) => { console.log(event.data) }, } }) ) export default app ``` ```ts // client.ts import { hc } from 'hono/client' import type app from './server' const client = hc('http://localhost:8787') const ws = client.ws.$ws(0) ws.addEventListener('open', () => { setInterval(() => { ws.send(new Date().toString()) }, 1000) }) ``` ### 带有 JSX 的 Bun ¥Bun with JSX ```tsx import { Hono } from 'hono' import { createBunWebSocket } from 'hono/bun' const { upgradeWebSocket, websocket } = createBunWebSocket() const app = new Hono() app.get('/', (c) => { return c.html(
) }) const ws = app.get( '/ws', upgradeWebSocket((c) => { let intervalId return { onOpen(_event, ws) { intervalId = setInterval(() => { ws.send(new Date().toString()) }, 200) }, onClose() { clearInterval(intervalId) }, } }) ) export default { fetch: app.fetch, websocket, } ``` # 最佳实践 ¥Best Practices Hono 非常灵活。你可以根据需要编写应用。但是,有更好的最佳实践可以遵循。 ¥Hono is very flexible. You can write your app as you like. However, there are best practices that are better to follow. ## 尽可能不要制作 "控制器" ¥Don't make "Controllers" when possible 如果可能,你不应创建 "类似 Ruby on Rails 的控制器"。 ¥When possible, you should not create "Ruby on Rails-like Controllers". ```ts // 🙁 // A RoR-like Controller const booksList = (c: Context) => { return c.json('list books') } app.get('/books', booksList) ``` 问题与类型有关。例如,如果不编写复杂的泛型,则无法在 Controller 中推断路径参数。 ¥The issue is related to types. For example, the path parameter cannot be inferred in the Controller without writing complex generics. ```ts // 🙁 // A RoR-like Controller const bookPermalink = (c: Context) => { const id = c.req.param('id') // Can't infer the path param return c.json(`get ${id}`) } ``` 因此,你不需要创建类似 RoR 的控制器,而应该在路径定义后直接编写处理程序。 ¥Therefore, you don't need to create RoR-like controllers and should write handlers directly after path definitions. ```ts // 😃 app.get('/books/:id', (c) => { const id = c.req.param('id') // Can infer the path param return c.json(`get ${id}`) }) ``` ## `hono/factory` 中的 `factory.createHandlers()` ¥`factory.createHandlers()` in `hono/factory` 如果你仍想创建类似 RoR 的控制器,请在 [`hono/factory`](/docs/helpers/factory) 中使用 `factory.createHandlers()`。如果你使用这种方法,类型推断将正常工作。 ¥If you still want to create a RoR-like Controller, use `factory.createHandlers()` in [`hono/factory`](/docs/helpers/factory). If you use this, type inference will work correctly. ```ts import { createFactory } from 'hono/factory' import { logger } from 'hono/logger' // ... // 😃 const factory = createFactory() const middleware = factory.createMiddleware(async (c, next) => { c.set('foo', 'bar') await next() }) const handlers = factory.createHandlers(logger(), middleware, (c) => { return c.json(c.var.foo) }) app.get('/api', ...handlers) ``` ## 构建更大的应用 ¥Building a larger application 使用 `app.route()` 构建更大的应用而无需创建 "类似 Ruby on Rails 的控制器"。 ¥Use `app.route()` to build a larger application without creating "Ruby on Rails-like Controllers". 如果你的应用有 `/authors` 和 `/books` 端点,并且你希望将文件与 `index.ts` 分开,请创建 `authors.ts` 和 `books.ts`。 ¥If your application has `/authors` and `/books` endpoints and you wish to separate files from `index.ts`, create `authors.ts` and `books.ts`. ```ts // authors.ts import { Hono } from 'hono' const app = new Hono() app.get('/', (c) => c.json('list authors')) app.post('/', (c) => c.json('create an author', 201)) app.get('/:id', (c) => c.json(`get ${c.req.param('id')}`)) export default app ``` ```ts // books.ts import { Hono } from 'hono' const app = new Hono() app.get('/', (c) => c.json('list books')) app.post('/', (c) => c.json('create a book', 201)) app.get('/:id', (c) => c.json(`get ${c.req.param('id')}`)) export default app ``` 然后,导入它们并将其与 `app.route()` 一起挂载在路径 `/authors` 和 `/books` 上。 ¥Then, import them and mount on the paths `/authors` and `/books` with `app.route()`. ```ts // index.ts import { Hono } from 'hono' import authors from './authors' import books from './books' const app = new Hono() // 😃 app.route('/authors', authors) app.route('/books', books) export default app ``` ### 如果要使用 RPC 功能 ¥If you want to use RPC features 上面的代码对于正常用例来说效果很好。但是,如果你想使用 `RPC` 功能,可以通过如下链接获取正确的类型。 ¥The code above works well for normal use cases. However, if you want to use the `RPC` feature, you can get the correct type by chaining as follows. ```ts // authors.ts import { Hono } from 'hono' const app = new Hono() .get('/', (c) => c.json('list authors')) .post('/', (c) => c.json('create an author', 201)) .get('/:id', (c) => c.json(`get ${c.req.param('id')}`)) export default app ``` 如果你将 `app` 的类型传递给 `hc`,它将获得正确的类型。 ¥If you pass the type of the `app` to `hc`, it will get the correct type. ```ts import app from './authors' import { hc } from 'hono/client' // 😃 const client = hc('http://localhost') // Typed correctly ``` 有关更多详细信息,请参阅 [RPC 页面](/docs/guides/rpc#using-rpc-with-larger-applications)。 ¥For more detailed information, please see [the RPC page](/docs/guides/rpc#using-rpc-with-larger-applications). # 示例 ¥Examples 查看 [示例部分](/examples/)。 ¥See the [Examples section](/examples/). # 常见问题 ¥Frequently Asked Questions 本指南收集了有关 Hono 的常见问题 (FAQ) 及其解决方法。 ¥This guide is a collection of frequently asked questions (FAQ) about Hono and how to resolve them. ## Hono 有官方的 Renovate 配置吗? ¥Is there an official Renovate config for Hono? Hono 团队目前不维护 [Renovate](https://github.com/renovatebot/renovate) 配置。因此,请按如下方式使用第三方 renovate-config。 ¥The Hono teams does not currently maintain [Renovate](https://github.com/renovatebot/renovate) Configuration. Therefore, please use third-party renovate-config as follows. 在你的 `renovate.json` 中: ¥In your `renovate.json` : ```json // renovate.json { "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": [ "github>shinGangan/renovate-config-hono" // [!code ++] ] } ``` 有关更多详细信息,请参阅 [renovate-config-hono](https://github.com/shinGangan/renovate-config-hono) 存储库。 ¥see [renovate-config-hono](https://github.com/shinGangan/renovate-config-hono) repository for more details. # 助手工具 ¥Helpers 辅助程序可用于协助开发你的应用。与中间件不同,它们不充当处理程序,而是提供有用的功能。 ¥Helpers are available to assist in developing your application. Unlike middleware, they don't act as handlers, but rather provide useful functions. 例如,以下是如何使用 [Cookie 助手](/docs/helpers/cookie): ¥For instance, here's how to use the [Cookie helper](/docs/helpers/cookie): ```ts import { getCookie, setCookie } from 'hono/cookie' const app = new Hono() app.get('/cookie', (c) => { const yummyCookie = getCookie(c, 'yummy_cookie') // ... setCookie(c, 'delicious_cookie', 'macha') // }) ``` ## 可用助手 ¥Available Helpers * [接受](/docs/helpers/accepts) ¥[Accepts](/docs/helpers/accepts) * [适配器](/docs/helpers/adapter) ¥[Adapter](/docs/helpers/adapter) * [Cookie](/docs/helpers/cookie) * [css](/docs/helpers/css) * [开发](/docs/helpers/dev) ¥[Dev](/docs/helpers/dev) * [工厂](/docs/helpers/factory) ¥[Factory](/docs/helpers/factory) * [html](/docs/helpers/html) * [JWT](/docs/helpers/jwt) * [SSG](/docs/helpers/ssg) * [流式传输](/docs/helpers/streaming) ¥[Streaming](/docs/helpers/streaming) * [测试](/docs/helpers/testing) ¥[Testing](/docs/helpers/testing) * [WebSocket](/docs/helpers/websocket) # 客户端组件 ¥Client Components `hono/jsx` 不仅支持服务器端,还支持客户端。这意味着可以创建一个在浏览器中运行的交互式 UI。我们称之为客户端组件或 `hono/jsx/dom`。 ¥`hono/jsx` supports not only server side but also client side. This means that it is possible to create an interactive UI that runs in the browser. We call it Client Components or `hono/jsx/dom`. 它速度快而且非常小。`hono/jsx/dom` 中的计数器程序使用 Brotli 压缩后只有 2.8KB。但是,React 占用 47.8KB。 ¥It is fast and very small. The counter program in `hono/jsx/dom` is only 2.8KB with Brotli compression. But, 47.8KB for React. 本节介绍客户端组件特有的功能。 ¥This section introduces Client Components-specific features. ## 反例 ¥Counter example 以下是简单计数器的示例,相同的代码与 React 中的工作方式相同。 ¥Here is an example of a simple counter, the same code works as in React. ```tsx import { useState } from 'hono/jsx' import { render } from 'hono/jsx/dom' function Counter() { const [count, setCount] = useState(0) return (

Count: {count}

) } function App() { return ( ) } const root = document.getElementById('root') render(, root) ``` ## `render()` 你可以使用 `render()` 在指定的 HTML 元素内插入 JSX 组件。 ¥You can use `render()` to insert JSX components within a specified HTML element. ```tsx render(, container) ``` ## 与 React 兼容的 Hooks ¥Hooks compatible with React hono/jsx/dom 具有与 React 兼容或部分兼容的 Hooks。你可以通过查看 [React 文档](https://react.nodejs.cn/reference/react/hooks) 来了解这些 API。 ¥hono/jsx/dom has Hooks that are compatible or partially compatible with React. You can learn about these APIs by looking at [the React documentation](https://react.nodejs.cn/reference/react/hooks). * `useState()` * `useEffect()` * `useRef()` * `useCallback()` * `use()` * `startTransition()` * `useTransition()` * `useDeferredValue()` * `useMemo()` * `useLayoutEffect()` * `useReducer()` * `useDebugValue()` * `createElement()` * `memo()` * `isValidElement()` * `useId()` * `createRef()` * `forwardRef()` * `useImperativeHandle()` * `useSyncExternalStore()` * `useInsertionEffect()` * `useFormStatus()` * `useActionState()` * `useOptimistic()` ## `startViewTransition()` 系列 ¥`startViewTransition()` family `startViewTransition()` 系列包含原始钩子和函数,可轻松处理 [查看转换 API](https://web.nodejs.cn/en-US/docs/Web/API/View_Transitions_API)。以下是如何使用它们的示例。 ¥The `startViewTransition()` family contains original hooks and functions to handle [View Transitions API](https://web.nodejs.cn/en-US/docs/Web/API/View_Transitions_API) easily. The followings are examples of how to use them. ### 1. 最简单的例子 ¥ An easiest example 你可以使用 `document.startViewTransition` 或 `startViewTransition()` 语法编写过渡。 ¥You can write a transition using the `document.startViewTransition` shortly with the `startViewTransition()`. ```tsx import { useState, startViewTransition } from 'hono/jsx' import { css, Style } from 'hono/css' export default function App() { const [showLargeImage, setShowLargeImage] = useState(false) return ( <>