路由
Nitro 支持文件系统路由,可自动将文件映射到路由。路由会在构建时进行编译和代码分割,因此无需运行时路由器。
#路由处理器
路由处理器是一个接收 event 对象(H3Event)并返回响应的函数。
import type { H3Event } from "nitro";
export default (event: H3Event) => {
return "world";
}#文件系统路由
文件会自动映射到 h3 路由。定义路由非常简单,只需在 serverDir 的 api/ 或 routes/ 目录中创建文件。
Note
文件系统路由要求在配置中设置 serverDir(或 scanDirs);默认情况下不会扫描任何目录。
每个文件定义一个处理器,你可以在文件名后追加 HTTP 方法,以匹配特定的请求方法。
routes/
api/
test.ts <-- /api/test
hello.get.ts <-- /hello (仅 GET)
hello.post.ts <-- /hello (仅 POST)
vite.config.ts你可以通过创建子目录来嵌套路由。
routes/
api/
[org]/
[repo]/
index.ts <-- /api/:org/:repo
issues.ts <-- /api/:org/:repo/issues
index.ts <-- /api/:org
package.json#路由组
要组织相关的路由文件而不影响最终生成的 URL,可以将它们放在用括号 ( 和 ) 包裹的文件夹中。
例如:
routes/
api/
(admin)/
users.ts <-- /api/users
reports.ts <-- /api/reports
(public)/
index.ts <-- /api
package.json[!NOTE] 路由组不会成为路由路径的一部分,仅用于组织文件。
#静态路由
在 routes/ 或 routes/api/ 目录中创建文件。文件路径会成为路由路径。然后导出一个路由处理器:
import { defineHandler } from "nitro";
export default defineHandler(() => {
return { hello: "API" };
});#动态路由
#单参数
要定义带参数的路由,请使用 [<param>] 语法,其中 <param> 是参数名称。参数可以通过 event.context.params 对象访问。
import { defineHandler } from "nitro";
export default defineHandler((event) => {
const { name } = event.context.params;
return `Hello ${name}!`;
});调用 /hello/nitro 会返回:
Hello nitro!#多参数
你可以使用 [<param1>]/[<param2>] 语法在路由中定义多个参数,其中每个参数都是一个文件夹。不能在单个文件名或文件夹中定义多个参数。
import { defineHandler } from "nitro";
export default defineHandler((event) => {
const { name, age } = event.context.params;
return `Hello ${name}! You are ${age} years old.`;
});#捕获所有参数
你可以使用 [...<param>] 语法捕获 URL 的所有剩余部分。这将把 / 包含在参数中。
import { defineHandler } from "nitro";
export default defineHandler((event) => {
const { name } = event.context.params;
return `Hello ${name}!`;
});调用 /hello/nitro/is/hot 会返回:
Hello nitro/is/hot!#特定请求方法
你可以在文件名后追加 HTTP 方法来强制路由仅匹配特定的 HTTP 请求方法,例如 hello.get.ts 将只匹配 GET 请求。你可以使用任何你想要的 HTTP 方法。
支持的方法:get、post、put、delete、patch、head、options、query、connect、trace。
Note
query 匹配 QUERY 方法,这是一种携带请求体的安全且幂等的 GET 替代方法。在代理、CDN 和部署平台中的支持仍不一致,因此对于公共 API,请保留 GET 或 POST 回退方案。
// routes/users/[id].get.ts
import { defineHandler } from "nitro";
export default defineHandler(async (event) => {
const { id } = event.context.params;
// 对 id 进行一些操作
return `User profile!`;
});#捕获所有路由
你可以创建一个特殊路由,用于匹配未被其他路由处理的所有请求,适合作为默认路由或回退路由。
要创建捕获所有路由,请创建一个名为 [...].ts 的文件。
import { defineHandler } from "nitro";
export default defineHandler((event) => {
return `Hello ${event.url.pathname}!`;
});Note
对于未命名的捕获所有路由([...].ts),匹配到的通配符片段可以通过 event.context.params._ 访问。
服务器入口和渲染器也会作为捕获所有处理器,并在基于文件的捕获所有路由之后串联执行。
#特定环境的处理器
你可以在文件名中添加 .dev、.prod 或 .prerender 后缀,使路由仅包含在特定构建中,例如:routes/test.get.dev.ts 或 routes/test.get.prod.ts。
该后缀放在方法后缀之后(如果有的话):
routes/
env/
index.dev.ts <-- /env (仅开发环境)
index.get.prod.ts <-- /env (GET, 仅生产环境)Tip
要针对多个环境,或使用预设名称作为环境,请通过 routes 配置以编程方式注册路由。
#忽略文件
你可以使用 ignore 配置选项来排除文件不被路由扫描。它接受相对于服务器目录的 glob 模式数组。
import { defineConfig } from "nitro";
export default defineConfig({
ignore: [
"routes/api/**/_*", // 忽略 api/ 中以 _ 开头的文件
"middleware/_*.ts", // 忽略以 _ 开头的中间件
"routes/_*.ts", // 忽略根路由中以 _ 开头的文件
],
});#路由元数据
你可以在路由处理器文件中使用 defineRouteMeta 宏,在构建时定义路由元数据。
Important
此功能目前处于实验阶段。
import { defineRouteMeta } from "nitro";
import { defineHandler } from "nitro";
defineRouteMeta({
openAPI: {
tags: ["test"],
description: "Test route description",
parameters: [{ in: "query", name: "test", required: true }],
},
});
export default defineHandler(() => "OK");#程序化路由处理器
除了文件系统路由外,你还可以使用 routes 配置选项以程序化方式注册路由处理器。
#routes 配置
routes 选项允许你将路由模式映射到处理器:
import { defineConfig } from "nitro";
export default defineConfig({
routes: {
"/api/hello": "./server/routes/api/hello.ts",
"/api/custom": {
handler: "./server/routes/api/hello.ts",
method: "POST",
lazy: true,
},
"/virtual": {
handler: "#virtual-route",
},
},
});每个路由条目可以是一个简单的字符串(处理器路径)或一个具有以下选项的对象:
| 选项 | 类型 | 描述 |
|---|---|---|
handler | string | 事件处理器文件或虚拟模块 ID 的路径 |
method | string | 要匹配的 HTTP 方法(get、post 等) |
lazy | boolean | 使用懒加载导入处理器 |
format | "web" | "node" | 处理器类型。"node" 处理器会被转换为兼容 web 的格式 |
env | string | string[] | 包含此处理器的环境("dev"、"prod"、"prerender" 或预设名称) |
#handlers 配置
handlers 数组适用于注册具有路由匹配控制权的中间件:
import { defineConfig } from "nitro";
export default defineConfig({
handlers: [
{
route: "/api/**",
handler: "./server/utils/api-auth.ts",
middleware: true,
},
],
});每个处理器条目支持以下选项:
| 选项 | 类型 | 描述 |
|---|---|---|
route | string | HTTP 路径名模式(例如 /test、/api/:id、/blog/**) |
handler | string | 事件处理器文件或虚拟模块 ID 的路径 |
method | string | 要匹配的 HTTP 方法(get、post 等) |
middleware | boolean | 在路由处理器之前作为中间件运行处理器 |
lazy | boolean | 使用懒加载导入处理器 |
format | "web" | "node" | 处理器类型。"node" 处理器会被转换为兼容 web 的格式 |
env | string | string[] | 包含此处理器的环境("dev"、"prod"、"prerender" 或预设名称) |
#中间件
中间件在路由处理器之前运行,让你可以检查或扩展传入的请求。有关中间件在请求管道中的位置,请参阅生命周期。
Tip
中间件可以在请求被处理之前修改请求,但不能在请求处理之后修改。
中间件会从 middleware/ 目录自动注册。
middleware/
auth.ts
logger.ts
...
routes/
hello.ts#简单中间件
中间件的定义方式与路由处理器完全相同,但有一个例外:它们不应返回任何内容。
import { defineHandler } from "nitro";
export default defineHandler((event) => {
// Extend or modify the event
event.context.user = { name: "Nitro" };
});middleware/ 目录中的中间件会自动为所有路由注册。要为特定路由模式注册中间件,请参阅路由作用域中间件。
Warning
从中间件返回值会关闭请求:该值会成为响应,并且不会继续运行后续处理器。请避免从中间件返回内容;应改用路由处理器。
#执行顺序
中间件按目录列表顺序执行。
middleware/
auth.ts <-- 第一个
logger.ts <-- 第二个
... <-- 第三个在中间件文件名前添加数字以控制它们的执行顺序。
middleware/
1.logger.ts <-- 第一个
2.auth.ts <-- 第二个
3.... <-- 第三个Note
文件名会按字符串排序,因此 10.filename.ts 会紧跟在 1.filename.ts 之后,并排在 2.filename.ts 之前。如果同一目录中有超过 10 个中间件,请在 1-9 前加上 0(例如 01.filename.ts)。
#请求过滤
全局中间件会在每个请求上运行,但你可以添加自定义条件来限定其作用范围。例如,检查 URL,以便将中间件应用于特定路由:
import { defineHandler } from "nitro";
export default defineHandler((event) => {
// 仅对 /auth 路由执行
if (event.url.pathname.startsWith('/auth')) {
event.context.user = { name: "Nitro" };
}
});#路由作用域中间件
你可以使用 handlers 配置并设置 middleware 选项和特定的 route 来为特定路由模式注册中间件:
import { defineConfig } from "nitro";
export default defineConfig({
handlers: [
{
route: "/api/**",
handler: "./server/utils/api-auth.ts",
middleware: true,
},
],
});与全局中间件(从 middleware/ 目录自动注册,并匹配 /**)不同,路由作用域中间件只会对匹配指定模式的请求运行。
请将路由作用域处理器文件放在 middleware/ 目录之外(如上例所示),这样目录扫描不会同时将它们注册为全局中间件。
#错误处理
你可以使用 H3 中提供的工具来处理路由处理器和中间件中的错误。
错误发送回客户端的方式取决于环境。在开发环境中,Accept 标头为 text/html 的请求(例如浏览器请求)会收到 HTML 错误页面。在生产环境中,错误始终以 JSON 形式发送。此行为可能会受到请求属性的影响,例如 Accept 或 User-Agent 标头。
要使用 error hook 全局捕获错误,请参阅生命周期。
#代码分割
Nitro 为每个路由处理程序创建单独的代码块。代码块在首次请求时按需加载,因此 /api/users 不会加载 /api/posts 的代码。
查看 inlineDynamicImports 以将所有内容打包到单个文件中。
#路由规则
路由规则允许你直接从配置中将行为(重定向、代理、缓存、身份验证、自定义标头)附加到路由模式,而无需编写处理器代码。
每条规则都会将路由模式(使用 rou3 语法)映射到一组选项,这些选项基于 h3 路由规则。设置 cache 选项后,匹配的处理器会自动使用 defineCachedHandler 包装。请参阅缓存指南了解更多信息。
使用 routeRules 配置选项设置路由规则:
import { defineConfig } from "nitro";
export default defineConfig({
routeRules: {
'/blog/**': { swr: true },
'/blog2/**': { swr: 600 },
'/blog3/**': { static: true },
'/blog4/**': { cache: { /* 缓存选项 */ } },
'/assets/**': { headers: { 'cache-control': 's-maxage=0' } },
'/api/v1/**': { cors: true, headers: { 'access-control-allow-methods': 'GET' } },
'/old-page': { redirect: '/new-page' },
'/old-page/**': { redirect: '/new-page/**' },
'/proxy/example': { proxy: 'https://example.com' },
'/proxy/**': { proxy: '/api/**' },
}
});#规则合并与覆盖
路由规则按照从最不具体到最具体的顺序匹配。当多个规则匹配一个请求时,它们的选项会合并,更具体的规则优先。
你可以使用 false 来禁用由更通用模式设置的规则:
import { defineConfig } from "nitro";
export default defineConfig({
routeRules: {
'/api/cached/**': { swr: true },
'/api/cached/no-cache': { cache: false, swr: false },
'/api/**': { cors: true },
'/api/internal/**': { cors: false },
}
});#按方法限定的规则
在规则键前加上大写的 HTTP 方法(后跟一个空格),即可将规则限定为使用该方法的请求。不带方法前缀的键适用于所有方法。按方法限定的规则会合并到匹配相同路径的、与方法无关的规则之上,因此你可以在共享默认配置的基础上叠加特定于方法的行为:
import { defineConfig } from "nitro";
export default defineConfig({
routeRules: {
// 应用于所有方法
'/api/**': { headers: { 'x-api': 'true' } },
// Only POST requests to /api/** get this header
'POST /api/**': { headers: { 'x-write': 'true' } },
// Cache GET requests to /feed only
'GET /feed': { swr: 600 },
}
});Note
方法匹配由服务器运行时针对每个请求进行解析。平台原生的静态生成(例如 Netlify/Cloudflare 的 _headers 和 _redirects、Vercel 的 config.json)不会按方法进行拆分,因此对于希望平台写入其静态配置的 headers/redirect/proxy 规则,建议使用与方法无关的键。
#Headers
为匹配的路由设置自定义响应头:
import { defineConfig } from "nitro";
export default defineConfig({
routeRules: {
'/api/**': { headers: { 'cache-control': 's-maxage=60' } },
'**': { headers: { 'x-powered-by': 'Nitro' } },
}
});#跨域资源共享(CORS)
在运行时使用 cors 规则处理 CORS。cors: true 会应用宽松的默认设置(来源、方法和允许的请求头均为 *):简单请求会获得 access-control-allow-origin: * 和 access-control-expose-headers: *,而 OPTIONS 预检请求会被直接响应(状态码为 204),并返回匹配的 access-control-allow-* 请求头。
Note
CORS 由正在运行的服务器(h3 的 handleCors)应用,而不是由静态/CDN 配置应用。
在可以直接从边缘节点提供预渲染/静态资源的平台上,只有当请求到达服务器处理程序时,才会添加 CORS 请求头。
传入对象可以进行更精细的控制:来源允许列表、credentials、maxAge 等(h3 CorsOptions)。将 credentials: true 与通配符来源结合使用是无效的,并会在构建时抛出错误:
import { defineConfig } from "nitro";
export default defineConfig({
routeRules: {
// 宽松的默认设置
'/api/public/**': { cors: true },
// 限制为特定来源并启用凭据
'/api/v1/**': {
cors: { origin: ['https://app.example.com'], credentials: true },
},
}
});#重定向
将匹配的路由重定向到另一个 URL。使用字符串进行简单重定向(默认为 307 状态码),或使用对象进行更精细的控制:
import { defineConfig } from "nitro";
export default defineConfig({
routeRules: {
// 简单重定向(307 状态码)
'/old-page': { redirect: '/new-page' },
// 自定义状态码的重定向
'/legacy': { redirect: { to: 'https://example.com/', status: 308 } },
// Wildcard redirect, preserves the path after the pattern
'/old-blog/**': { redirect: 'https://blog.example.com/**' },
}
});#代理
将请求代理到另一个 URL。支持内部和外部目标:
import { defineConfig } from "nitro";
export default defineConfig({
routeRules: {
// 代理到确切 URL
'/api/proxy/example': { proxy: 'https://example.com' },
// 代理到内部路由
'/api/proxy/**': { proxy: '/api/echo' },
// Wildcard proxy, preserves the path after the pattern
'/cdn/**': { proxy: 'https://cdn.jsdelivr.net/**' },
// 带选项的代理
'/external/**': {
proxy: {
to: 'https://api.example.com/**',
// 其他 H3 代理选项...
},
},
}
});#身份验证
没有 auth 路由规则:身份验证需要可执行逻辑,因此应放在中间件中。对于单个路由,请使用 h3 的 basicAuth 中间件:
import { defineHandler } from "nitro";
import { basicAuth } from "nitro/h3";
export default defineHandler({
middleware: [basicAuth({ username: 'admin', password: 'supersecret', realm: 'Admin Area' })],
handler: (event) => `Hello, ${event.context.basicAuth?.username}!`,
});对于整个子树,可以将其注册为路由作用域中间件,或注册为 middleware/ 目录中的全局中间件。
#缓存(SWR / 静态)
使用 cache、swr 或 static 选项控制缓存行为:
import { defineConfig } from "nitro";
export default defineConfig({
routeRules: {
// 启用 stale-while-revalidate 缓存
'/blog/**': { swr: true },
// 带 maxAge(秒)的 SWR
'/blog/posts/**': { swr: 600 },
// 完整缓存选项
'/api/data/**': {
cache: {
maxAge: 60,
swr: true,
// ...其他缓存选项
},
},
// 禁用缓存
'/api/realtime/**': { cache: false },
}
});Tip
swr: true 是 cache: { swr: true } 的简写,swr: <number> 是 cache: { swr: true, maxAge: <number> } 的简写。
#预渲染
标记路由以在构建时进行预渲染:
import { defineConfig } from "nitro";
export default defineConfig({
routeRules: {
'/about': { prerender: true },
'/dynamic/**': { prerender: false },
}
});预渲染路由会在构建时生成一次,并作为静态文件提供。它们不会在运行时重新验证。
Warning
不要在同一路由上同时使用 prerender 和 isr。预渲染具有更高优先级,因此 isr 会被忽略,页面也不会在部署后重新生成。在支持的平台上需要增量再生时,请单独使用 isr。
#ISR(Vercel)
为 Vercel 部署配置增量静态再生:
import { defineConfig } from "nitro";
export default defineConfig({
routeRules: {
'/isr/**': { isr: true },
'/isr-ttl/**': { isr: 60 },
'/isr-custom/**': {
isr: {
expiration: 60,
allowQuery: ['q'],
group: 1,
},
},
}
});Note
ISR 会按需生成页面,并根据你设置的过期时间重新验证页面。它是构建时 prerender 的替代方案,而不是可以在同一路由上与其结合使用的功能。
#路由规则参考
| 选项 | 类型 | 描述 |
|---|---|---|
headers | Record<string, string> | 自定义响应标头 |
redirect | string | { to: string, status?: number } | 重定向到另一个 URL(默认状态码:307) |
proxy | string | { to: string, ...proxyOptions } | 将请求代理到另一个 URL |
cors | boolean | CorsOptions | 通过 h3 的 handleCors 处理 CORS(true = 宽松设置) |
cache | object | false | 缓存选项(请参阅缓存指南) |
swr | boolean | number | cache: { swr: true, maxAge: number } 的简写(false 会重置继承的缓存规则) |
static | boolean | number | 静态缓存的简写 |
prerender | boolean | 启用/禁用预渲染 |
isr | boolean | number | object | 增量静态再生(Vercel) |
#运行时路由规则
路由规则可以通过 runtimeConfig 提供,允许通过环境变量覆盖而无需重新构建:
import { defineConfig } from "nitro";
export default defineConfig({
runtimeConfig: {
nitro: {
routeRules: {
'/api/**': { headers: { 'x-env': 'production' } },
},
},
},
});#配置参考
这些配置选项控制路由行为:
| 选项 | 类型 | 默认值 | 描述 |
|---|---|---|---|
baseURL | string | "/" | 所有路由的基础 URL |
apiBaseURL | string | "/api" | api/ 目录中路由的基础 URL |
apiDir | string | "api" | API 路由的目录名称 |
routesDir | string | "routes" | 基于文件的路由的目录名称 |
serverDir | string | false | false | 用于扫描路由、中间件、插件等的服务器目录 |
scanDirs | string[] | [] | 扫描路由的额外目录 |
routes | Record<string, string | handler> | {} | 路由到处理程序的映射 |
handlers | NitroEventHandler[] | [] | 程序化处理程序注册(主要用于中间件) |
routeRules | Record<string, NitroRouteConfig> | {} | 匹配模式的路由规则 |
ignore | string[] | [] | 文件扫描期间忽略的 glob 模式 |