服务路由
server/routes/ 目录与 server/api/ 类似,但不带 /api 前缀,适合提供非 JSON API 的服务端功能。
与 API 路由的区别
| 特性 | server/api/ | server/routes/ |
|---|---|---|
| URL 前缀 | /api/ | / |
| 典型用途 | REST API(数据接口) | 站点地图、RSS、健康检查 |
| 返回格式 | JSON(默认) | 任意格式(XML、文本、图片等) |
| 路由参数 | ✅ 支持 | ✅ 支持 |
| HTTP 方法 | ✅ 支持 | ✅ 支持 |
什么时候用 server/routes/?
当你需要提供非 API 的服务端功能时:
- SEO 相关文件:
sitemap.xml、robots.txt - 订阅源:RSS / Atom Feed
- 运维:健康检查、版本信息
- 动态图片:OG 图片生成
简单记忆
返回 JSON 数据 → server/api/;返回其他格式 → server/routes/。
基本用法
ts
// server/routes/sitemap.ts → /sitemap
export default defineEventHandler(() => {
const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url><loc>https://example.com/</loc></url>
</urlset>`
setResponseHeader(event, 'content-type', 'text/xml')
return sitemap
})路由文件的 URL 映射规则
server/routes/sitemap.ts→/sitemapserver/routes/sitemap.xml.ts→/sitemap.xmlserver/routes/feed.xml.ts→/feed.xmlserver/routes/blog/[slug].ts→/blog/:slug
文件名中包含扩展名
(如 sitemap.xml.ts)可以生成带扩展名的 URL(如 /sitemap.xml)。
常见用途
sitemap.xml
ts
// server/routes/sitemap.xml.ts → /sitemap.xml
export default defineEventHandler(async (event) => {
// 动态获取文章列表
const posts = await $fetch('/api/posts')
setResponseHeader(event, 'content-type', 'text/xml')
return `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://example.com/</loc>
<changefreq>daily</changefreq>
<priority>1.0</priority>
</url>
${posts.map((post: any) => `
<url>
<loc>https://example.com/blog/${post.slug}</loc>
<lastmod>${post.updatedAt}</lastmod>
<priority>0.8</priority>
</url>
`).join('')}
</urlset>`
})SEO 提示
sitemap.xml 帮助搜索引擎发现和索引你的页面。推荐动态生成,确保搜索引擎始终能发现新内容。
robots.txt
ts
// server/routes/robots.txt.ts → /robots.txt
export default defineEventHandler((event) => {
const config = useRuntimeConfig()
setResponseHeader(event, 'content-type', 'text/plain')
return `User-agent: *
Allow: /
Disallow: /admin/
Disallow: /api/
Sitemap: ${config.public.siteUrl}/sitemap.xml`
})动态 robots.txt 的好处
可以根据环境变量或配置动态生成,如开发环境禁止所有爬虫。
RSS Feed
ts
// server/routes/feed.xml.ts → /feed.xml
export default defineEventHandler(async (event) => {
const posts = await fetchRecentPosts()
setResponseHeader(event, 'content-type', 'application/xml')
return `<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>My Blog</title>
<link>https://example.com</link>
<description>最新文章</description>
<atom:link href="https://example.com/feed.xml" rel="self" type="application/rss+xml"/>
${posts.map(post => `
<item>
<title>${escapeXml(post.title)}</title>
<link>https://example.com/blog/${post.slug}</link>
<guid isPermaLink="true">https://example.com/blog/${post.slug}</guid>
<pubDate>${new Date(post.publishedAt).toUTCString()}</pubDate>
<description>${escapeXml(post.excerpt)}</description>
</item>
`).join('')}
</channel>
</rss>`
})INFO
️ XML 中的特殊字符:RSS 内容中的 <、>、& 等字符需要转义 否则 XML 解析会失败。使用 escapeXml() 函数处理
健康检查
ts
// server/routes/health.ts → /health
export default defineEventHandler(() => {
return {
status: 'ok',
timestamp: new Date().toISOString(),
uptime: process.uptime(),
memory: process.memoryUsage(),
version: useRuntimeConfig().public.version,
}
})健康检查的作用
负载均衡器(如 Nginx、AWS ALB)和监控系统通过访问 /health 来检测服务是否正常运行。如果返回 200,说明服务健康。
OG 图片生成
ts
// server/routes/og.png.ts → /og.png
export default defineEventHandler(async (event) => {
const title = getQuery(event).title as string
// 使用 Satori 生成 OG 图片
const svg = await generateOGImage({ title })
setResponseHeader(event, 'content-type', 'image/png')
// 将 SVG 转为 PNG
return await sharp(Buffer.from(svg)).png().toBuffer()
})OG 图片
社交媒体(微信、Twitter、Facebook)分享链接时显示的预览图片。推荐使用 Satori 将 JSX/HTML 转为 SVG,再转为 PNG。
动态服务路由
与 API 路由一样支持动态参数:
ts
// server/routes/blog/[slug].ts → /blog/my-post
export default defineEventHandler((event) => {
const slug = getRouterParam(event, 'slug')
return { slug }
})
// server/routes/users/[id].ts → /users/123
export default defineEventHandler((event) => {
const id = getRouterParam(event, 'id')
return { id }
})
// 匹配所有子路由
// server/routes/docs/[...slug].ts → /docs/a/b/c
export default defineEventHandler((event) => {
const slug = getRouterParam(event, 'slug')
// slug = 'a/b/c'
return { slug }
})HTTP 方法路由
与 API 路由一样支持按 HTTP 方法定义:
ts
// server/routes/contact.ts
export default defineEventHandler((event) => {
// 根据方法分发
if (event.method === 'GET') {
return { message: 'Contact form' }
}
if (event.method === 'POST') {
// 处理表单提交
return { success: true }
}
})服务路由通常只处理 GET 请求
(提供静态文件内容)。如果需要处理 POST 等方法,推荐使用 server/api/。
知识脉络
text
API 路由 → 你在这里:服务路由
│
├─→ 下一步:服务中间件
│
└─→ 相关:SEO 与元数据 → 链接预取(sitemap / robots)