From 3147393a6e8be172e89a1454be864b7be20c77a6 Mon Sep 17 00:00:00 2001 From: zhangxiang Date: Sun, 27 Apr 2025 11:24:32 +0800 Subject: [PATCH] feat: server config doc --- .../main-doc/docs/en/configure/app/usage.mdx | 2 +- .../en/guides/advanced-features/_meta.json | 1 + .../advanced-features/custom-server.mdx | 209 ++++++++++++++++++ .../guides/advanced-features/web-server.mdx | 175 ++++++++++++++- .../main-doc/docs/zh/configure/app/usage.mdx | 2 +- .../zh/guides/advanced-features/_meta.json | 1 + .../advanced-features/custom-server.mdx | 209 ++++++++++++++++++ .../guides/advanced-features/web-server.mdx | 175 ++++++++++++++- 8 files changed, 770 insertions(+), 4 deletions(-) create mode 100644 packages/document/main-doc/docs/en/guides/advanced-features/custom-server.mdx create mode 100644 packages/document/main-doc/docs/zh/guides/advanced-features/custom-server.mdx diff --git a/packages/document/main-doc/docs/en/configure/app/usage.mdx b/packages/document/main-doc/docs/en/configure/app/usage.mdx index 219a1370ce05..77b9d3fcc331 100644 --- a/packages/document/main-doc/docs/en/configure/app/usage.mdx +++ b/packages/document/main-doc/docs/en/configure/app/usage.mdx @@ -13,7 +13,7 @@ Modern.js does not support configuring the same configuration item in both `pack **Runtime configuration** can be configured in the `src/modern.runtime.(ts|js|mjs)` file. -**Server Runtime configuration** can be configured in the `modern.server-runtime.config.(ts|js|mjs)` file in the root path. +**Server Runtime configuration** can be configured in the `server/modern.server.(ts|js|mjs)` file. ## Compile Configuration diff --git a/packages/document/main-doc/docs/en/guides/advanced-features/_meta.json b/packages/document/main-doc/docs/en/guides/advanced-features/_meta.json index 59325b2c4600..3ce0c342a833 100644 --- a/packages/document/main-doc/docs/en/guides/advanced-features/_meta.json +++ b/packages/document/main-doc/docs/en/guides/advanced-features/_meta.json @@ -23,5 +23,6 @@ "label": "server-monitor", "collapsed": true }, + "custom-server", "web-server" ] diff --git a/packages/document/main-doc/docs/en/guides/advanced-features/custom-server.mdx b/packages/document/main-doc/docs/en/guides/advanced-features/custom-server.mdx new file mode 100644 index 000000000000..1aead40fd1f2 --- /dev/null +++ b/packages/document/main-doc/docs/en/guides/advanced-features/custom-server.mdx @@ -0,0 +1,209 @@ +--- +sidebar_position: 16 +--- + +# Custom Server + +Modern.js encapsulates most server-side capabilities required by projects, typically eliminating the need for server-side development. However, in certain scenarios such as user authentication, request preprocessing, or adding page skeletons, custom server-side logic may still be necessary. + +## Custom Server Capabilities + +Create the `server/modern.server.ts` file in the project directory, where you can configure **middleware**, **rendering middleware**, and **server plugins** to extend the Server. + +The execution order of middleware is: Middleware => PluginMiddleware => RenderMiddleware => PluginRenderMiddleware. + +### Basic Configuration + +```ts title="server/modern.server.ts" +import { defineServerConfig } from '@modern-js/server-runtime'; + +export default defineServerConfig({ + middlewares: [], + renderMiddlewares: [], + plugins: [], +}); +``` + + +### Type Definition + +`defineServerConfig` type definition is as follows: + +```ts +import type { MiddlewareHandler } from 'hono'; + +type MiddlewareOrder = 'pre' | 'post' | 'default'; +type MiddlewareObj = { + name: string; + path?: string; + method?: 'options' | 'get' | 'post' | 'put' | 'delete' | 'patch' | 'all'; + handler: MiddlewareHandler | MiddlewareHandler[]; + before?: Array; + order?: MiddlewareOrder; +}; +type ServerConfig = { + middlewares?: MiddlewareObj[]; + renderMiddlewares?: MiddlewareObj[]; + plugins?: (ServerPlugin | ServerPluginLegacy)[]; +} +``` + + +### Middleware + +Middleware supports executing custom logic before and after the **request handling** and **page routing** processes in Modern.js services. + +:::note +In the BFF scenario, BFF routing will only go through Middleware when the runtime framework is Hono. +::: + +#### Using Posture + +```ts title="server/modern.server.ts" +import { defineServerConfig, type MiddlewareHandler } from '@modern-js/server-runtime'; +import { getMonitors } from '@modern-js/runtime'; + +export const handler: MiddlewareHandler = async (c, next) => { + const monitors = getMonitors(); + const start = Date.now(); + + await next(); + + const end = Date.now(); + // Report Duration + monitors.timing('request_timing', end - start); +}; + +export default defineServerConfig({ + middlewares: [ + { + name: 'request-timing', + handler, + }, + ], +}); +``` + +:::warning +You must execute the `next` function to proceed with the subsequent Middleware. +::: + + +### RenderMiddleware + +Modern.js supports adding rendering middleware to the Server, allowing custom logic to be executed before and after handling page routes. + +#### Using Posture + +```ts title="server/modern.server.ts" +import { defineServerConfig, type MiddlewareHandler } from '@modern-js/server-runtime'; + +// Inject render performance metrics +const renderTiming: MiddlewareHandler = async (c, next) => { + const start = Date.now(); + + await next(); + + const end = Date.now(); + c.res.headers.set('server-timing', `render; dur=${end - start}`); +}; + +// Modify the Response Body +const modifyResBody: MiddlewareHandler = async (c, next) => { + await next(); + + const { res } = c; + const text = await res.text(); + const newText = text.replace('', '

bytedance

'); + + c.res = c.body(newText, { + status: res.status, + headers: res.headers, + }); +}; + +export default defineServerConfig({ + renderMiddlewares: [ + { + name: 'render-timing', + handler: renderTiming, + }, + { + name: 'modify-res-body', + handler: modifyResBody, + }, + ], +}); +``` + + +### Plugin + +Modern.js supports adding the aforementioned middleware and rendering middleware for the Server in custom plugins. + +#### Using Posture + + +```ts title="server/plugins/server.ts" +import type { ServerPluginLegacy } from '@modern-js/server-runtime'; + +export default (): ServerPluginLegacy => ({ + name: 'serverPlugin', + setup(api) { + return { + prepare(serverConfig) { + const { middlewares, renderMiddlewares } = api.useAppContext(); + + // Inject server-side data for page dataLoader consumption + middlewares?.push({ + name: 'server-plugin-middleware', + handler: async (c, next) => { + c.set('message', 'hi modern.js'); + await next(); + // ... + }, + }); + + // redirect + renderMiddlewares?.push({ + name: 'server-plugin-render-middleware', + handler: async (c, next) => { + const user = getUser(c.req); + if (!user) { + return c.redirect('/login'); + } + + await next(); + }, + }); + return serverConfig; + }, + }; + }, +}); +``` + + +```ts title="server/modern.server.ts" +import { defineServerConfig } from '@modern-js/server-runtime'; +import serverPlugin from './plugins/serverPlugin'; + +export default defineServerConfig({ + plugins: [serverPlugin()], +}); +``` + + +```ts title="src/routes/page.data.ts" +import { useHonoContext } from '@modern-js/server-runtime'; +import { defer } from '@modern-js/runtime/router'; + +export default () => { + const ctx = useHonoContext(); + // Consuming Data Injected by the Server-Side + const message = ctx.get('message'); + + // ... +}; + +``` diff --git a/packages/document/main-doc/docs/en/guides/advanced-features/web-server.mdx b/packages/document/main-doc/docs/en/guides/advanced-features/web-server.mdx index faf0e487c5de..78f49b150b2b 100644 --- a/packages/document/main-doc/docs/en/guides/advanced-features/web-server.mdx +++ b/packages/document/main-doc/docs/en/guides/advanced-features/web-server.mdx @@ -2,7 +2,11 @@ sidebar_position: 16 --- -# Custom Web Server +# Custom Web Server (Not Recommended) + +:::warning +Custom Web Server is compatible but no longer recommended. For extending Server capabilities, please refer to [Custom Server](/guides/advanced-features/custom-server.html). For migration guide, see [Migrate to the New Version of Custom Server](/guides/advanced-features/web-server.html#migrate-to-the-new-version-of-custom-server). +::: Modern.js encapsulates most server-side capabilities required by projects, typically eliminating the need for server-side development. However, in certain scenarios such as user authentication, request preprocessing, or adding page skeletons, custom server-side logic may still be necessary. @@ -96,3 +100,172 @@ Best practices when using Hooks: :::info For detailed API and more usage, see [Hook](/apis/app/runtime/web-server/hook). ::: + + +## Migrate to the New Version of Custom Server + +### Migration Background + +Modern.js Server is continuously evolving to provide more powerful features. We have optimized the definition and usage of middleware and Server plugins. +While the old custom Web Server approach is still compatible, we strongly recommend migrating according to this guide to fully leverage the advantages of the new version. + +### Migration Steps + +1. Upgrade Modern.js version to x.67.4 or above. +2. Configure middleware or plugins in `server/modern.server.ts` according to the new definition method. +3. Migrate the custom logic in `server/index.ts` to middleware or plugins, and update your code with reference to the differences between `Context` and `Next`. + +### Context Differences + +In the new version, the middleware handler type is Hono's `MiddlewareHandler`, meaning the `Context` type is `Hono Context`. The differences from the old custom Web Server's `Context` are as follows: + + +#### UnstableMiddleware + + +```ts +type Body = ReadableStream | ArrayBuffer | string | null; + +type UnstableMiddlewareContext< + V extends Record = Record, +> = { + request: Request; + response: Response; + get: Get; + set: Set; + // Current Matched Routing Information + route: string; + header: (name: string, value: string, options?: { append?: boolean }) => void; + status: (code: number) => void; + redirect: (location: string, status?: number) => Response; + body: (data: Body, init?: ResponseInit) => Response; + html: ( + data: string | Promise, + init?: ResponseInit, + ) => Response | Promise; +}; +``` + +Differences between UnstableMiddleware Context and Hono Context: + +| UnstableMiddleware | Hono | Description | +| :----------------------- | :---------------------------- | :------------------------------------------------------------------------ | +| `c.request` | `c.req.raw` | Refer to [HonoRequest raw](https://hono.dev/docs/api/request#raw) documentation | +| `c.response` | `c.res` | Refer to [Hono Context res](https://hono.dev/docs/api/context#res) documentation | +| `c.route` | `c.get('route')` | Get application context information. | +| `loaderContext.get` | `honoContext.get` | After injecting data using `c.set`, consume in dataLoader: the old version uses `loaderContext.get`, refer to the new version in [Plugin](/zh/guides/advanced-features/custom-server.html#使用姿势-2) example | + + +#### Middleware + +```ts +type MiddlewareContext = { + response: { + set: (key: string, value: string) => void; + status: (code: number) => void; + getStatus: () => number; + cookies: { + set: (key: string, value: string, options?: any) => void; + clear: () => void; + }; + raw: ( + body: string, + { status, headers }: { status: number; headers: Record }, + ) => void; + locals: Record; + }; + request: { + url: string; + host: string; + pathname: string; + query: Record; + cookie: string; + cookies: { + get: (key: string) => string; + }; + headers: IncomingHttpHeaders; + }; + source: { + req: IncomingMessage; + res: ServerResponse; + }; +}; + +``` + +Differences between Middleware `Context` and Hono `Context`: +| UnstableMiddleware | Hono | Description | +| :----------------------- | :---------------------------- | :--------------------------------------------------------------------------- | +| `c.request.cookie` | `c.req.cookie()` | Refer to [Hono Cookie Helper](https://hono.dev/docs/helpers/cookie) documentation | +| `c.request.pathname` | `c.req.path` | Refer to [HonoRequest path](https://hono.dev/docs/api/request#path) documentation | +| `c.request.url` | - | Hono `c.req.url` provides the full request URL, calculate manually from URL | +| `c.request.host` | `c.req.header('Host')` | Obtain host through header | +| `c.request.query` | `c.req.query()` | Refer to [HonoRequest query](https://hono.dev/docs/api/request#query) documentation | +| `c.request.headers` | `c.req.header()` | Refer to [HonoRequest header](https://hono.dev/docs/api/request#header) documentation | +| `c.response.set` | `c.res.headers.set` | Example: `c.res.headers.set('custom-header', '1')` | +| `c.response.status` | `c.status` | Example: `c.status(201)` | +| `c.response.cookies` | `c.header` | Example: `c.header('Set-Cookie', 'user_id=123')` | +| `c.response.raw` | `c.res` | Refer to [Hono Context res](https://hono.dev/docs/api/context#res) documentation | + +#### Hook + +```ts +type HookContext = { + response: { + set: (key: string, value: string) => void; + status: (code: number) => void; + getStatus: () => number; + cookies: { + set: (key: string, value: string, options?: any) => void; + clear: () => void; + }; + raw: ( + body: string, + { status, headers }: { status: number; headers: Record }, + ) => void; + }; + request: { + url: string; + host: string; + pathname: string; + query: Record; + cookie: string; + cookies: { + get: (key: string) => string; + }; + headers: IncomingHttpHeaders; + }; +}; + +type AfterMatchContext = HookContext & { + router: { + redirect: (url: string, status: number) => void; + rewrite: (entry: string) => void; + }; +}; + +type AfterRenderContext = { + template: { + get: () => string; + set: (html: string) => void; + prependHead: (fragment: string) => void; + appendHead: (fragment: string) => void; + prependBody: (fragment: string) => void; + appendBody: (fragment: string) => void; + }; +}; +``` + +Hook Context is mostly consistent with Middleware Context, so we need to pay extra attention to the additional parts of different Hooks. + +| UnstableMiddleware | Hono | Description | +| :----------------------- | :---------------------------- | :------------------------------------ | +| `router.redirect` | `c.redirect` | Refer to [Hono Context redirect](https://hono.dev/docs/api/context#redirect) documentation | +| `router.rewrite` | - | No corresponding capability provided at the moment | +| template API | `c.res` | Refer to [Hono Context res](https://hono.dev/docs/api/context#res) documentation | + + +### Differences in Next API + +In Middleware and Hooks, the render function executes even without invoking `next`. +In the new design, subsequent Middleware will only execute if the `next` function is invoked. diff --git a/packages/document/main-doc/docs/zh/configure/app/usage.mdx b/packages/document/main-doc/docs/zh/configure/app/usage.mdx index 4173bd8f6bcf..312cb00fb615 100644 --- a/packages/document/main-doc/docs/zh/configure/app/usage.mdx +++ b/packages/document/main-doc/docs/zh/configure/app/usage.mdx @@ -13,7 +13,7 @@ Modern.js 不支持同时在 `package.json` 中和 `modern.config.ts` 中配置 **运行时配置**可以在 `src/modern.runtime.(ts|js|mjs)` 文件中配置。 -**服务端运行时配置**可以在根路径下的 `modern.server-runtime.config.(ts|js|mjs)` 中进行配置。 +**服务端运行时配置**可以在 `server/modern.server.(ts|js|mjs)` 中进行配置。 ## 编译时配置 diff --git a/packages/document/main-doc/docs/zh/guides/advanced-features/_meta.json b/packages/document/main-doc/docs/zh/guides/advanced-features/_meta.json index 59325b2c4600..3ce0c342a833 100644 --- a/packages/document/main-doc/docs/zh/guides/advanced-features/_meta.json +++ b/packages/document/main-doc/docs/zh/guides/advanced-features/_meta.json @@ -23,5 +23,6 @@ "label": "server-monitor", "collapsed": true }, + "custom-server", "web-server" ] diff --git a/packages/document/main-doc/docs/zh/guides/advanced-features/custom-server.mdx b/packages/document/main-doc/docs/zh/guides/advanced-features/custom-server.mdx new file mode 100644 index 000000000000..d89aa360ba0f --- /dev/null +++ b/packages/document/main-doc/docs/zh/guides/advanced-features/custom-server.mdx @@ -0,0 +1,209 @@ +--- +sidebar_position: 16 +--- + +# 自定义 Server + +Modern.js 将大部分项目需要的服务端能力都进行了封装,通常项目无需进行服务端开发。但在有些开发场景下,例如用户鉴权、请求预处理、添加页面渲染骨架等,项目仍需要对服务端进行定制。 + +## 自定义 Server 能力 + +项目目录下创建 `server/modern.server.ts` 文件,可以在这个文件中配置**中间件**、**渲染中间件**与**服务端插件**来扩展 Server。 + +中间件的执行顺序是: Middleware => PluginMiddleware => RenderMiddleware => PluginRenderMiddleware。 + +### 基本配置 + +```ts title="server/modern.server.ts" +import { defineServerConfig } from '@modern-js/server-runtime'; + +export default defineServerConfig({ + middlewares: [], // 中间件 + renderMiddlewares: [], // 渲染中间件 + plugins: [], // 插件 +}); +``` + + +### 类型定义 + +`defineServerConfig` 类型定义如下: + +```ts +import type { MiddlewareHandler } from 'hono'; + +type MiddlewareOrder = 'pre' | 'post' | 'default'; +type MiddlewareObj = { + name: string; + path?: string; + method?: 'options' | 'get' | 'post' | 'put' | 'delete' | 'patch' | 'all'; + handler: MiddlewareHandler | MiddlewareHandler[]; + before?: Array; + order?: MiddlewareOrder; +}; +type ServerConfig = { + middlewares?: MiddlewareObj[]; + renderMiddlewares?: MiddlewareObj[]; + plugins?: (ServerPlugin | ServerPluginLegacy)[]; +} +``` + + +### Middleware + +Middleware 支持在 Modern.js 服务的**请求处理**与**页面路由**的流程前后,执行自定义逻辑。 + +:::note +BFF 场景只有运行时框架为 Hono 时,BFF 路由才会经过 Middleware。 +::: + +#### 使用姿势 + +```ts title="server/modern.server.ts" +import { defineServerConfig, type MiddlewareHandler } from '@modern-js/server-runtime'; +import { getMonitors } from '@modern-js/runtime'; + +export const handler: MiddlewareHandler = async (c, next) => { + const monitors = getMonitors(); + const start = Date.now(); + + await next(); + + const end = Date.now(); + // 上报耗时 + monitors.timing('request_timing', end - start); +}; + +export default defineServerConfig({ + middlewares: [ + { + name: 'request-timing', + handler, + }, + ], +}); +``` + +:::warning +必须执行 `next` 函数才会执行后续的 Middleware。 +::: + + +### RenderMiddleware + +Modern.js 支持为 Server 添加渲染中间件,支持在处理页面路由的前后执行自定义逻辑 + +#### 使用姿势 + +```ts title="server/modern.server.ts" +import { defineServerConfig, type MiddlewareHandler } from '@modern-js/server-runtime'; + +// 注入 render 性能指标 +const renderTiming: MiddlewareHandler = async (c, next) => { + const start = Date.now(); + + await next(); + + const end = Date.now(); + c.res.headers.set('server-timing', `render; dur=${end - start}`); +}; + +// 修改响应体 +const modifyResBody: MiddlewareHandler = async (c, next) => { + await next(); + + const { res } = c; + const text = await res.text(); + const newText = text.replace('', '

bytedance

'); + + c.res = c.body(newText, { + status: res.status, + headers: res.headers, + }); +}; + +export default defineServerConfig({ + renderMiddlewares: [ + { + name: 'render-timing', + handler: renderTiming, + }, + { + name: 'modify-res-body', + handler: modifyResBody, + }, + ], +}); +``` + + +### Plugin + +Modern.js 支持在自定义插件中为 Server 添加上述中间件及渲染中间件。 + +#### 使用姿势 + + +```ts title="server/plugins/server.ts" +import type { ServerPluginLegacy } from '@modern-js/server-runtime'; + +export default (): ServerPluginLegacy => ({ + name: 'serverPlugin', + setup(api) { + return { + prepare(serverConfig) { + const { middlewares, renderMiddlewares } = api.useAppContext(); + + // 注入服务端数据,供页面 dataLoader 消费 + middlewares?.push({ + name: 'server-plugin-middleware', + handler: async (c, next) => { + c.set('message', 'hi modern.js'); + await next(); + // ... + }, + }); + + // 重定向 + renderMiddlewares?.push({ + name: 'server-plugin-render-middleware', + handler: async (c, next) => { + const user = getUser(c.req); + if (!user) { + return c.redirect('/login'); + } + + await next(); + }, + }); + return serverConfig; + }, + }; + }, +}); +``` + + +```ts title="server/modern.server.ts" +import { defineServerConfig } from '@modern-js/server-runtime'; +import serverPlugin from './plugins/serverPlugin'; + +export default defineServerConfig({ + plugins: [serverPlugin()], +}); +``` + + +```ts title="src/routes/page.data.ts" +import { useHonoContext } from '@modern-js/server-runtime'; +import { defer } from '@modern-js/runtime/router'; + +export default () => { + const ctx = useHonoContext(); + // 消费服务端注入的数据 + const message = ctx.get('message'); + + // ... +}; + +``` diff --git a/packages/document/main-doc/docs/zh/guides/advanced-features/web-server.mdx b/packages/document/main-doc/docs/zh/guides/advanced-features/web-server.mdx index bedce303d66d..fb940486e4fe 100644 --- a/packages/document/main-doc/docs/zh/guides/advanced-features/web-server.mdx +++ b/packages/document/main-doc/docs/zh/guides/advanced-features/web-server.mdx @@ -2,7 +2,11 @@ sidebar_position: 16 --- -# 自定义 Web Server +# 自定义 Web Server(不推荐) + +:::warning +自定义 Web Server 兼容但不再推荐使用,扩展 Server 能力请移步 [自定义 Server](/zh/guides/advanced-features/custom-server.html),迁移指南参考 [迁移至新版自定义 Server](/zh/guides/advanced-features/web-server.html#%E8%BF%81%E7%A7%BB%E8%87%B3%E6%96%B0%E7%89%88%E8%87%AA%E5%AE%9A%E4%B9%89-server)。 +::: Modern.js 将大部分项目需要的服务端能力都进行了封装,通常项目无需进行服务端开发。但在有些开发场景下,例如用户鉴权、请求预处理、添加页面渲染骨架等,项目仍需要对服务端进行定制。 @@ -96,3 +100,172 @@ export const afterRender: AfterRenderHook = (ctx, next) => { :::info 详细 API 和更多用法可以查看 [Hook](/apis/app/runtime/web-server/hook)。 ::: + + +## 迁移至新版自定义 Server + +### 迁移背景 + +Modern.js Server 在不断演进,为了提供更强大的功能,我们对中间件和 Server 插件的定义和使用方式进行了优化。 +虽然旧版自定义 Web Server 写法仍被兼容,但我们强烈建议您按照本指南进行迁移,以充分利用新版的优势。 + +### 迁移步骤 + +1. 升级 Modern.js 版本至 x.67.4 及以上。 +2. 按照新版定义方式,在 `server/modern.server.ts` 中配置中间件或插件。 +3. 将 `server/index.ts` 自定义逻辑迁移到中间件或插件中,并参考 `Context` 和 `Next` 差异,更新您的代码。 + +### Context 差异 + +新版中间件 handler 类型为 Hono 的 `MiddlewareHandler`,即 `Context` 类型为 `Hono Context`。对比旧版自定义 Web Server 中 `Context` 差异如下: + +#### UnstableMiddleware + + +```ts +type Body = ReadableStream | ArrayBuffer | string | null; + +type UnstableMiddlewareContext< + V extends Record = Record, +> = { + request: Request; + response: Response; + get: Get; + set: Set; + // 当前匹配到的路由信息 + route: string; + header: (name: string, value: string, options?: { append?: boolean }) => void; + status: (code: number) => void; + redirect: (location: string, status?: number) => Response; + body: (data: Body, init?: ResponseInit) => Response; + html: ( + data: string | Promise, + init?: ResponseInit, + ) => Response | Promise; +}; +``` + +UnstableMiddleware `Context` 和 Hono `Context` 的具体差异: + +| UnstableMiddleware | Hono | 说明 | +| :----------------------- | :---------------------------- | :------------------------------------------------------------------------ | +| `c.request` | `c.req.raw` | 参考 [HonoRequest raw](https://hono.dev/docs/api/request#raw) 文档 | +| `c.response` | `c.res` | 参考 [Hono Context res](https://hono.dev/docs/api/context#res) 文档 | +| `c.route` | `c.get('route')` | 获取应用上下文信息。 | +| `loaderContext.get` | `honoContext.get` | 通过 `c.set` 注入数据后 dataLoader 中消费:旧版通过 `loaderContext.get` 获取,新版参考 [Plugin](/zh/guides/advanced-features/custom-server.html#使用姿势-2) 示例 | + +#### Middleware + +```ts +type MiddlewareContext = { + response: { + set: (key: string, value: string) => void; + status: (code: number) => void; + getStatus: () => number; + cookies: { + set: (key: string, value: string, options?: any) => void; + clear: () => void; + }; + raw: ( + body: string, + { status, headers }: { status: number; headers: Record }, + ) => void; + locals: Record; + }; + request: { + url: string; + host: string; + pathname: string; + query: Record; + cookie: string; + cookies: { + get: (key: string) => string; + }; + headers: IncomingHttpHeaders; + }; + source: { + req: IncomingMessage; + res: ServerResponse; + }; +}; + +``` + +Middleware `Context` 和 Hono `Context` 的具体差异: + +| UnstableMiddleware | Hono | 说明 | +| :----------------------- | :---------------------------- | :--------------------------------------------------------------------------- | +| `c.request.cookie` | `c.req.cookie()` | 参考 [Hono Cookie Helper](https://hono.dev/docs/helpers/cookie) 文档 | +| `c.request.pathname` | `c.req.path` | 参考 [HonoRequest path](https://hono.dev/docs/api/request#path) 文档 | +| `c.request.url` | - | Hono `c.req.url` 为完整请求路径,自行通过 url 计算 | +| `c.request.host` | `c.req.header('Host')` | 通过 header 获取 host | +| `c.request.query` | `c.req.query()` | 参考 [HonoRequest query](https://hono.dev/docs/api/request#query) 文档 | +| `c.request.headers` | `c.req.header()` | 参考 [HonoRequest header](https://hono.dev/docs/api/request#header) 文档 | +| `c.response.set` | `c.res.headers.set` | 例:`c.res.headers.set('custom-header', '1')` | +| `c.response.status` | `c.status` | 例:`c.status(201)` | +| `c.response.cookies` | `c.header` | 例:`c.header('Set-Cookie', 'user_id=123')` | +| `c.response.raw` | `c.res` | 参考 [Hono Context res](https://hono.dev/docs/api/context#res) 文档 | + + +#### Hook + +```ts +type HookContext = { + response: { + set: (key: string, value: string) => void; + status: (code: number) => void; + getStatus: () => number; + cookies: { + set: (key: string, value: string, options?: any) => void; + clear: () => void; + }; + raw: ( + body: string, + { status, headers }: { status: number; headers: Record }, + ) => void; + }; + request: { + url: string; + host: string; + pathname: string; + query: Record; + cookie: string; + cookies: { + get: (key: string) => string; + }; + headers: IncomingHttpHeaders; + }; +}; + +type AfterMatchContext = HookContext & { + router: { + redirect: (url: string, status: number) => void; + rewrite: (entry: string) => void; + }; +}; + +type AfterRenderContext = { + template: { + get: () => string; + set: (html: string) => void; + prependHead: (fragment: string) => void; + appendHead: (fragment: string) => void; + prependBody: (fragment: string) => void; + appendBody: (fragment: string) => void; + }; +}; +``` + +Hook Context 大部分和 Middleware Context 一致,因此我们要额外关注不同 Hook 多余的部分。 + +| UnstableMiddleware | Hono | 说明 | +| :----------------------- | :---------------------------- | :----------------------------- | +| `router.redirect` | `c.redirect` | 参考 [Hono Context redirect](https://hono.dev/docs/api/context#redirect) 文档 | +| `router.rewrite` | - | 暂时没有提供对应的能力 | +| template API | `c.res` | 参考 [Hono Context res](https://hono.dev/docs/api/context#res) 文档 | + + +### Next API 差异 + +在 Middleware 和 Hook 中,即使不执行 `next`,渲染函数也会执行。 +在新的设计中,必须执行 `next` 函数才会执行后续的 Middleware。