中间件与代理
cormi dev、cormi start 运行一个 HTTP 服务(@cordisjs/plugin-server)。一个请求依次经过:
- 文件与转发:开发服务器提供的模块(dev)、构建产物中的静态资源(start)、mock、代理。它们直接回答,不经过下面的中间件;
- 应用的中间件(
ctx.middleware),按order从外到内; - 路由:接口(
server/api)、loader端点、插件注册的路由; - 页面:GET 请求的回退,开启 SSR 时在服务端渲染。
所以中间件包裹的是应用自己的请求——接口、loader 与页面——会话、鉴权、限流只作用于它们,而不会为每个脚本、图片读一次会话。
中间件
中间件基于 fetch:收到 Request,返回 Response。next() 是之后的一切——内层的中间件、路由与页面;中间件可以修改它的响应,也可以不调用它而直接返回:
import type { Context } from '@cormijs/cormi'
export const inject = ['middleware']
export function apply(ctx: Context) {
ctx.middleware.use(async (request, { state }, next) => {
const start = performance.now()
const response = await next()
response.headers.set('server-timing', `app;dur=${(performance.now() - start).toFixed(1)}`)
return response
}, { path: '/api' })
}| 选项 | 默认值 | 说明 |
|---|---|---|
order | 0 | 小的在外层:先看到请求、后看到响应 |
path | / | 只处理这个路径之下的请求 |
ctx.middleware.use() 是调用方插件的 effect:插件卸载(或热重载)时,中间件随之移除。next() 返回的响应总可以修改响应头,即使它来自 fetch() 或 Response.redirect()。前端应用同样可以使用中间件,例如统一添加响应头。
官方的中间件插件有各自的位置,应用自己的中间件(默认 0)在它们之内,能读到会话与登录用户:
order | 插件 |
|---|---|
| -400 | @cormijs/plugin-security:安全响应头 |
| -300 | @cormijs/plugin-cors:跨域 |
| -200 | @cormijs/plugin-rate-limit:限流 |
| -100 | @cormijs/plugin-session:state.session |
| -90 | @cormijs/plugin-auth:state.user |
见 会话、鉴权与常用中间件。
请求的上下文
中间件的第二个参数,与接口的 ApiContext、loader 的 context 一样,包含:
| 成员 | 说明 |
|---|---|
state | 请求级的状态,见下文 |
ip | 客户端的地址;应用在代理之后时是代理的地址,客户端在 x-forwarded-for 中 |
读写 cookie 用 @cormijs/cormi 的 getCookies(request) 与 setCookie(response, name, value, options);一个响应可以设置多个 cookie。
请求状态
每个请求有一个 state 对象,中间件、接口与页面的 loader 读写的是同一个对象。会话、登录用户这类信息由中间件写入,之后的处理直接读取。state 的类型由声明合并扩展:
declare module '@cormijs/cormi' {
interface State {
requestId: string
}
}
ctx.middleware.use(async (request, { state }, next) => {
state.requestId = request.headers.get('x-request-id') ?? crypto.randomUUID()
return next()
})// server/api/health.ts
export function GET(request: Request, { state }: ApiContext) {
return { requestId: state.requestId }
}代理
preset 的 proxy 把请求转发到其他服务器,常用于前端应用对接单独部署的后端:
- name: '@cormijs/preset'
config:
proxy:
/api:
target: http://localhost:3000
changeOrigin: true
pathRewrite:
^/api: ''
headers:
x-from: cormi| 选项 | 说明 |
|---|---|
target | 转发的目标服务器 |
changeOrigin | 发送目标服务器的 host;原来的 host 保留在 x-forwarded-host |
pathRewrite | 改写路径,键是正则 |
headers | 附加到转发请求的请求头 |
代理在 dev 与 start 下生效,排在 mock 之后。请求通过 fetch() 转发并以流的形式返回,不支持 WebSocket。
ctx.server
需要更底层的控制时,插件可以直接使用 ctx.server 注册路由与 connect 风格的中间件,见 Node 端服务。