服务器导入

`nuxt/server` 是服务器代码的导入接口,不依赖特定服务器运行时。

Geprüfte Übersetzung ·0 % geprüft

Nuxt 可以使用不同的服务器构建器:Nitro 2(nitropack,Nuxt 3 和 4 中的默认选项)、Nitro 3(nitro,从 Nuxt 5 开始),或直接使用 Vite。

nuxt/server 提供与特定服务器运行时无关的服务器代码通用工具。它是 Nuxt 的第二个运行时接口,与 nuxt/app(也可以通过 #app 访问)并列;后者用于应用中也会在浏览器运行的部分。

server/api/hello.ts
import { defineEventHandler, getQuery } from 'nuxt/server'

export default defineEventHandler((event) => {
  const { name } = getQuery<{ name?: string }>(event)
  return { message: `Hello, ${name ?? 'world'}!` }
})

这段代码可以在 @nuxt/nitro-server 和 @nuxt/vite-server 下运行;即使 h3 或 Nitro 升级主版本,也能继续运行,因为 Nuxt 会集中处理这些变化。

nuxt/server 从 Nuxt 4.6 开始提供,因此一个文件就能同时用于运行 nitropack v2 的 Nuxt 4.6、运行 Nitro v3 的 Nuxt 5,以及未来构建器带来的任何环境。

每个 Nuxt 应用都安装了 nuxt,因此 nuxt/server 无需别名或额外依赖即可解析。导入它的模块无需依赖 h3 或 Nitro 的 peer dependency。

类型和工具

导入用途
defineEventHandler定义请求处理程序。
createError、isNuxtError创建并识别 HTTP 错误。
getRequestURL、getRequestHost、getRequestProtocol获取请求的 URL、主机和协议。仅在你控制且会覆盖这些请求头的代理之后,才传入 { xForwardedHost: true, xForwardedProto: true } 来信任对应的转发标头。
getRequestHeader、getRequestHeaders读取请求标头。
getRouterParam、getRouterParams读取与请求匹配的动态路径段。
getRequestIP获取客户端 IP 地址,请求中会信任转发标头。
getQuery、getValidatedQuery读取查询字符串,可选择进行校验。
readBody、readValidatedBody读取并解析请求体,可选择进行校验。
getCookie、parseCookies、setCookie、deleteCookie读取单个或全部 Cookie,以及写入 Cookie。
setResponseStatus设置响应状态和原因短语。
handleCors应用 CORS 标头并响应预检请求。
sendRedirect重定向请求。
serverFetch在进程内获取应用的路由,路径相对于 app.baseURL,并转发 cookie 和 authorization 标头(除非设置 forwardHeaders)。在不支持进程内 fetch 的服务器构建器上会拒绝。
useSession、getSession、updateSession、clearSession读取和写入密封的会话 Cookie。
deriveSecret从 appSecret 派生用途专属的密钥。
getRouteRules、matchRouteRules获取请求匹配的路由规则,或获取其他路径匹配的规则。
useRuntimeConfig获取服务器的运行时配置。
useServerHooks获取服务器运行时钩子,包括 render:html 等渲染器钩子,以及 NuxtServerHooks 中声明的钩子。
useAppConfig获取应用配置;传入事件时,每个请求都会得到一份副本。

工具旁还导出了以下类型:RequestEvent、RequestEventContext、NuxtRequestEvent、EventHandler、AppRouteRules、ServerRoutes、CorsOptions、ValidateResult、NuxtError、NuxtErrorDetails、NuxtErrorJSON、NuxtErrorLike、Session、SessionConfig、SessionData、SessionEvent、SessionManager、SessionPassword 和 SessionUpdate。

这些名称不会从 nuxt/server 自动导入。在服务器代码中,同名的自动导入项是 h3 自身的辅助函数,它们接收 h3 event,且部分行为有所不同(例如 defineEventHandler、handleCors、getRouterParams 和 readValidatedBody),因此请显式从 nuxt/server 导入。其中也包括 defineEventHandler:nuxt/server 中的辅助函数会读取它传给处理程序的 event。

了解如何将现有服务器代码迁移到 nuxt/server,以及它的辅助函数与 h3 的辅助函数有何不同。
defineEventHandler 会保留处理程序的返回类型,而这正是路由的 $fetch 和 useFetch 调用以及对其调用 serverFetch 时的 response.json() 所需的类型。请为返回值添加类型注解,而不是为处理程序添加。

Event

RequestEvent 是 nuxt/server 工具操作所依据的 event,其中包含请求、请求 URL、待发送的响应以及请求上下文。

interface RequestEvent {
  readonly req: Request
  url: URL
  readonly res: { status?: number, statusText?: string, readonly headers: Headers }
  readonly context: RequestEventContext
}

要设置、读取、追加或移除响应标头,请直接使用 event.res.headers:它是标准的 Headers 对象。

Nitro 还提供了更多内容,例如 event.node 和 event.waitUntil();但这四个核心属性适用于所有运行时,无需使用辅助函数:

server/api/echo.post.ts
export default defineEventHandler(async (event) => {
  const { id } = await event.req.json()
  return { id, page: event.url.searchParams.get('page') }
})

NuxtRequestEvent 是配置的构建器所提供形态的同一个请求;在 @nuxt/nitro-server 下,它是 h3 v1 的 H3Event。

处理程序

处理程序文件必须默认导出 nuxt/server 中 defineEventHandler 的返回值。直接导出函数,或使用 h3 的 defineEventHandler 包装函数,收到的会是服务器运行时自己的 event。

处理程序可以返回:

  • 可 JSON 序列化的值,作为 JSON 发送,并使用设置在 event.res 上的状态和标头。
  • Response,使用其自身状态发送。设置在 event.res.headers 上的标头也会发送,并且会保留两处设置的 set-cookie 值。
  • ReadableStream,作为响应正文流式发送。

验证

readValidatedBody 和 getValidatedQuery 接受任意 Standard Schema(Zod、Valibot、ArkType 等),也接受返回已验证值的函数;返回 true 表示接受输入,返回 false 则表示拒绝输入。

server/api/users.post.ts
import { defineEventHandler, readValidatedBody } from 'nuxt/server'
import { z } from 'zod'

export default defineEventHandler(async (event) => {
  const user = await readValidatedBody(event, z.object({ name: z.string() }))
  return { created: user.name }
})

无效输入会被拒绝,并返回 400 错误;其 data.issues 会列出验证失败的内容。传入 onError 可以使用其他错误来拒绝输入。

客户端 IP

getRequestIP(event) 返回服务器运行时报告的连接地址;如果没有报告地址,则返回 undefined。默认情况下,不会信任任何转发标头,因为客户端可以在其中发送任意值。

在你控制的代理后面,可以传入 { xForwardedFor: true },改为读取 X-Forwarded-For 的第一个条目。仅当代理会覆盖该标头时才这样做:如果代理只是追加内容,客户端发送的值仍会排在最前面。

会话

useSession 会读取请求的会话;如果没有可读取的会话,就会将新会话密封到 Cookie 中。数据使用 iron 密封,因此数据存放在 Cookie 中,而非服务器存储中;Cookie 默认使用 httpOnly、secure、sameSite: 'lax' 和 path: '/'。

server/api/visits.ts
import { defineEventHandler, useSession } from 'nuxt/server'

export default defineEventHandler(async (event) => {
  const session = await useSession<{ visits: number }>(event)
  await session.update(data => ({ visits: (data.visits ?? 0) + 1 }))
  return { visits: session.data.visits }
})

如果没有设置 password,会话会使用从应用的 appSecret 派生的密钥进行密封,因此只需设置 NUXT_APP_SECRET。传入至少 32 个字符的 password,即可改用你自己的密钥进行密封。设置 maxAge(以秒为单位)可使 Cookie 和密封值同时过期;设置 name 可将 Cookie 名称从 nuxt-session 更改为其他名称;设置 cookie 可覆盖任意 Cookie 属性。

每个请求只会解封一次会话,因此你可以在任意多个位置调用 getSession。updateSession 和 clearSession 是不使用 manager 对象的同类操作。

派生密钥

deriveSecret(purpose) 会从 appSecret 派生特定用途的密钥:32 字节,以十六进制编码,只要 appSecret 未更改就保持稳定,并且每个用途的密钥都各不相同。会话辅助函数使用的就是它;模块也应使用它,而不是自行请求密钥。请将用途命名为其所属对象的命名空间。

import { deriveSecret } from 'nuxt/server'

const password = await deriveSecret('my-module:tokens')

如果未设置 appSecret,或其长度少于 32 个字符,就会抛出一个提及 NUXT_APP_SECRET 的 500 错误。

h3 提供了同名的会话辅助函数;在 @nuxt/nitro-server 下,自动导入的 useSession、getSession、updateSession 和 clearSession 会解析为这些函数,因此请显式从 nuxt/server 导入。它们是不同的实现,不能互换:h3 默认的 Cookie 名称是 h3 而非 nuxt-session,使用自己的配置,并且其中一个实现签发的会话无法由另一个实现解封。
会话 API 稳定,但密封 Cookie 的格式可能会在次要版本中更改:Nuxt 计划在 iron-webcrypto 发布基于 AES-GCM 的密封方式后迁移到该方式。届时,现有会话会以旧格式读取,并在下次写入时以新格式重新密封,因此用户不会被登出。密封值与 h3 的 useSession 生成的值不可互换。
会话存放在 Cookie 中,因此大小受浏览器可接受范围的限制。写入序列化后超过 4096 字节的会话会抛出错误;较大的数据应保存在服务器端,并通过 ID 在会话中引用。

超出接口范围

nuxt/server 不是 h3 的重新导出,因此如果你需要 h3 和 Nitro 提供的其他功能(例如服务器生命周期相关功能,如 defineNitroPlugin 和 defineNitroErrorHandler,或 Nitro 自己的运行时钩子),应直接导入。

如果处理程序需要 h3 自带的辅助函数(例如 readRawBody、readMultipartFormData、assertMethod、proxyRequest、fetchWithEvent 或 writeEarlyHints),或者会读取 event.node 或 event.waitUntil(),请从 h3 为该处理程序导入 defineEventHandler:

server/api/upload.post.ts
import { defineEventHandler, readMultipartFormData } from 'h3'

export default defineEventHandler(async (event) => {
  const parts = await readMultipartFormData(event)
  return { received: parts?.length ?? 0 }
})

Nitro 自己的 API 大多不需要传入 event:defineCachedEventHandler()、useStorage()、useDatabase() 和 tasks。请从 nitropack/runtime 导入它们。

不具备可移植性的内容

以下功能需要使用 nuxt/server 之外的内容,因为 Nuxt 无法为每个构建器都提供这些功能:

  • 存储。 useStorage() 来自 nitropack/runtime,其背后的 driver 配置也来自这里。
  • 缓存。 defineCachedEventHandler() 和 defineCachedFunction() 来自 nitropack/runtime。要使用能在任何环境运行的缓存,请直接依赖缓存库。
  • 服务器插件。 defineNitroPlugin() 来自 nitropack/runtime。在插件中,nuxt/server 的 useServerHooks() 可注册渲染器钩子(包括 render:html)以及 NuxtServerHooks 中声明的钩子;Nitro 自己的 request、beforeResponse 和 error 等钩子则来自 nitropack/runtime 中的 useNitroApp().hooks。
  • 惰性处理程序。 lazyEventHandler() 来自 h3。

仅限服务器代码

在 Vue 组件、插件或 shared/ 目录中导入 nuxt/server 会导致构建失败;错误会提示你改用 #app、#imports、$fetch 和 useFetch。这些上下文仍可解析其类型,因此 $fetch 能知道服务器路由会返回什么。

nuxt/server 也只能用于由服务器构建器打包的代码,因为构建器会提供其背后的运行时配置、应用配置、钩子和进程内 fetch。在 Nuxt 进程中运行的代码(例如 nuxt.config.ts、模块的 setup,或传给 addDevServerHandler 的函数)并不具备这些能力,因此不要在那里导入 nuxt/server:例如,useRuntimeConfig() 和 useAppConfig() 会返回空对象。请改为读取 nuxt.options,并参阅开发服务器处理程序了解如何在模块中编写处理程序。

模块

运行时代码仅从 nuxt/server 导入内容的模块,从 v4.6 起可在任何服务器构建器下运行,无需版本检查,也不依赖 h3 或 Nitro:

runtime/server/api/status.ts
import { defineEventHandler, useRuntimeConfig } from 'nuxt/server'

export default defineEventHandler(() => ({
  version: useRuntimeConfig().myModule.version,
}))
打包时请将 nuxt/server 保留为外部依赖;它会在 Nuxt 构建过程中解析为正确的服务器构建器工具。

支持 Nuxt <4.6 版本需要多做一步,因为这些项目无法解析 nuxt/server。你可以同时注册可移植文件和当前随包发布的文件,Nuxt 会选择应用能够运行的文件。

了解如何为每个服务器 API 注册一个处理程序。
了解服务器构建器如何提供 nuxt/server 背后的实现。