server
Nuxt会自动扫描这些目录中的文件,以便在应用程序中注册具有HMR支持的API和服务器处理程序。
-| server/
---| api/
-----| hello.ts # /api/hello
---| routes/
-----| bonjour.ts # /bonjour
---| middleware/
-----| log.ts # 记录所有请求
每个文件应该导出一个使用defineEventHandler()
或eventHandler()
(别名)定义的默认函数。
处理程序可以直接返回JSON数据、Promise
,或使用event.node.res.end()
发送响应。
export default defineEventHandler((event) => {
return {
hello: 'world'
}
})
现在你可以在你的页面和组件中普遍调用这个API:
<script setup lang="ts">
const { data } = await useFetch('/api/hello')
</script>
<template>
<pre>{{ data }}</pre>
</template>
服务器路由
~/server/api
目录中的文件会自动在其路由中添加/api
前缀。
要添加没有/api
前缀的服务器路由,请将它们放入~/server/routes
目录中。
示例:
export default defineEventHandler(() => 'Hello World!')
在上面的示例中,/hello
路由可以通过http://localhost:3000/hello访问。
服务器中间件
Nuxt会自动读取~/server/middleware
目录中的任何文件,以创建项目的服务器中间件。
中间件处理程序将在任何其他服务器路由之前在每个请求上运行,以添加或检查标头、记录请求或扩展事件的请求对象。
示例:
export default defineEventHandler((event) => {
console.log('New request: ' + getRequestURL(event))
})
export default defineEventHandler((event) => {
event.context.auth = { user: 123 }
})
服务器插件
Nuxt会自动读取~/server/plugins
目录中的任何文件,并将它们注册为Nitro插件。这允许扩展Nitro的运行时行为并钩入生命周期事件。
示例:
export default defineNitroPlugin((nitroApp) => {
console.log('Nitro plugin', nitroApp)
})
服务器工具
服务器路由由unjs/h3提供支持,并附带一组方便的辅助函数。
你可以在~/server/utils
目录中自己添加更多的辅助函数。
例如,你可以定义一个自定义处理程序工具,它包装了原始处理程序并在返回最终响应之前执行其他操作。
示例:
import type { EventHandler, EventHandlerRequest } from 'h3'
export const defineWrappedResponseHandler = <T extends EventHandlerRequest, D> (
handler: EventHandler<T, D>
): EventHandler<T, D> =>
defineEventHandler<T>(async event => {
try {
// 在路由处理程序之前执行某些操作
const response = await handler(event)
// 在路由处理程序之后执行某些操作
return { response }
} catch (err) {
// 错误处理
return { err }
}
})
服务器类型
为了提高IDE中对来自'nitro'和'vue'的自动导入的清晰度,你可以添加一个~/server/tsconfig.json
文件,内容如下:
{
"extends": "../.nuxt/tsconfig.server.json"
}
尽管在目前的版本中这些值不会在类型检查时得到尊重(nuxi typecheck
),但你应该在IDE中获得更好的类型提示。
示例
路由参数
服务器路由可以在文件名中使用方括号内的动态参数,如/api/hello/[name].ts
,并通过event.context.params
访问。
export default defineEventHandler((event) => {
const name = getRouterParam(event, 'name')
return `Hello, ${name}!`
})
现在你可以在/api/hello/nuxt
上普遍调用此API,并获得Hello, nuxt!
。
匹配HTTP方法
处理文件名可以使用.get
、.post
、.put
、.delete
等后缀来匹配请求的HTTP方法。
export default defineEventHandler(() => 'Test get handler')
export default defineEventHandler(() => 'Test post handler')
在上面的示例中,使用以下方式获取/test
:
- GET方法:返回
Test get handler
- POST方法:返回
Test post handler
- 任何其他方法:返回405错误
你还可以在目录中使用index.[method].ts
来以不同的方式组织代码,这对于创建API命名空间非常有用。
export default defineEventHandler((event) => {
// 处理`api/foo`端点的GET请求
})
捕获所有路由
捕获所有路由对于后备路由处理非常有用。
例如,创建一个名为~/server/api/foo/[...].ts
的文件将为所有不匹配任何路由处理程序的请求注册一个捕获所有路由,如/api/foo/bar/baz
。
export default defineEventHandler((event) => {
// event.context.path 获取路由路径:'/api/foo/bar/baz'
// event.context.params._ 获取路由段:'bar/baz'
return `Default foo handler`
})
你可以通过使用~/server/api/foo/[...slug].ts
来为捕获所有路由设置一个名称,并通过event.context.params.slug
访问它。
export default defineEventHandler((event) => {
// event.context.params.slug 获取路由段:'bar/baz'
return `Default foo handler`
})
处理请求体
export default defineEventHandler(async (event) => {
const body = await readBody(event)
return { body }
})
现在你可以使用以下方式普遍调用此API:
<script setup>
async function submit() {
const { body } = await $fetch('/api/submit', {
method: 'post',
body: { test: 123 }
})
}
</script>
submit.post.ts
来匹配可以接受请求体的POST
方法的请求。在GET请求中使用readBody
时,readBody
将抛出405 Method Not Allowed
HTTP错误。查询参数
示例查询/api/query?foo=bar&baz=qux
export default defineEventHandler((event) => {
const query = getQuery(event)
return { a: query.foo, b: query.baz }
})
错误处理
如果没有抛出错误,将返回状态码200 OK
。
任何未捕获的错误都将返回500 Internal Server Error
HTTP错误。
要返回其他错误代码,请使用createError
抛出异常:
export default defineEventHandler((event) => {
const id = parseInt(event.context.params.id) as number
if (!Number.isInteger(id)) {
throw createError({
statusCode: 400,
statusMessage: 'ID should be an integer',
})
}
return 'All good'
})
状态码
要返回其他状态码,请使用setResponseStatus
实用程序。
例如,返回202 Accepted
:
export default defineEventHandler((event) => {
setResponseStatus(event, 202)
})
运行时配置
export default defineEventHandler(async (event) => {
const config = useRuntimeConfig(event)
const repo = await $fetch('https://api.github.com/repos/nuxt/nuxt', {
headers: {
Authorization: `token ${config.githubToken}`
}
})
return repo
})
请求Cookie
export default defineEventHandler((event) => {
const cookies = parseCookies(event)
return { cookies }
})
高级用法
Nitro配置
你可以在nuxt.config
中使用nitro
键直接设置Nitro配置。
export default defineNuxtConfig({
// https://nitro.unjs.io/config
nitro: {}
})
嵌套路由
import { createRouter, defineEventHandler, useBase } from 'h3'
const router = createRouter()
router.get('/test', defineEventHandler(() => 'Hello World'))
export default useBase('/api/hello', router.handler)
发送流
import fs from 'node:fs'
import { sendStream } from 'h3'
export default defineEventHandler((event) => {
return sendStream(event, fs.createReadStream('/path/to/file'))
})
发送重定向
export default defineEventHandler(async (event) => {
await sendRedirect(event, '/path/redirect/to', 302)
})
旧版处理程序或中间件
export default fromNodeMiddleware((req, res) => {
res.end('Legacy handler')
})
export default fromNodeMiddleware((req, res, next) => {
console.log('Legacy middleware')
next()
})
next()
回调的旧版中间件与async
的中间件或返回Promise
的中间件组合使用。服务器存储
Nitro提供了一个跨平台的存储层。为了配置额外的存储挂载点,你可以使用nitro.storage
或服务器插件。
添加Redis存储的示例:
使用nitro.storage
:
export default defineNuxtConfig({
nitro: {
storage: {
redis: {
driver: 'redis',
/* redis连接器选项 */
port: 6379, // Redis端口
host: "127.0.0.1", // Redis主机
username: "", // 需要Redis >= 6
password: "",
db: 0, // 默认为0
tls: {} // tls/ssl
}
}
}
})
然后在你的API处理程序中:
export default defineEventHandler(async (event) => {
// 使用以下方式列出所有键
const keys = await useStorage('redis').getKeys()
// 使用以下方式设置键
await useStorage('redis').setItem('foo', 'bar')
// 使用以下方式删除键
await useStorage('redis').removeItem('foo')
return {}
})
或者,你可以使用服务器插件和运行时配置创建存储挂载点:
import redisDriver from 'unstorage/drivers/redis'
export default defineNitroPlugin(() => {
const storage = useStorage()
// 动态传递来自运行时配置或其他来源的凭据
const driver = redisDriver({
base: 'redis',
host: useRuntimeConfig().redis.host,
port: useRuntimeConfig().redis.port,
/* 其他redis连接器选项 */
})
// 挂载驱动程序
storage.mount('redis', driver)
})