后端 API 编写
API 结构
由于后端路由并不支持错误捕获,所以只能使用 try...catch
语句捕获错误。
ts
import type { NextRequest } from 'next/server'
export async function POST(request: NextRequest) {
try {
} catch (error: any) {
console.error('Error:', error)
}
}
返回结果
可以使用 NextResponse.json()
方法返回 JSON 数据。
获取请求内容
NextRequest
继承了 Request
类型的所有功能。因此可以使用 request
对象获取请求头、请求体等信息,也可以以流的方式读取请求体内的信息。
获取请求体
获取文本数据:
ts
export async function POST(request: NextRequest) {
const text = await request.text()
}
获取请求的 JSON 数据:
ts
export async function POST(request: NextRequest) {
const body = await request.json()
}
建议使用 zod
等库进行请求体的验证:
ts
import { z } from 'zod'
const schema = z.object({
name: z.string(),
age: z.number(),
})
export async function POST(request: NextRequest) {
try {
const body = await request.json()
const { name, age } = schema.parse(body)
} catch (error: any) {
console.error('Error:', error)
}
}
获取请求的表单数据:
ts
export async function POST(request: NextRequest) {
const formData = await request.formData()
const name = formData.get('name')
const email = formData.get('email')
}
表单验证推荐使用 zod-form-data
库:
ts
import { zfd } from 'zod-form-data'
const schema = zfd.formData({
name: zfd.text(),
age: zfd.numeric(z.number().min(25).max(50)),
likesPizza: zfd.checkbox(),
})
export async function POST(request: NextRequest) {
try {
const formData = await request.formData()
const { name, age, likesPizza } = await schema.parse(formData)
} catch (error: any) {
console.error('Error:', error)
}
}
获取请求的文件数据:
ts
export async function POST(request: NextRequest) {
const formData = await request.formData()
const file = formData.get('file')
const fileBuffer = await file.arrayBuffer()
}
以流的方式读取请求体:
ts
export async function POST(request: NextRequest) {
const reader = request.body?.getReader()
if (!reader) {
return new Response('No body found', { status: 400 })
}
let body = ''
while (true) {
const { done, value } = await reader.read()
if (done) {
break
}
body += new TextDecoder().decode(value)
}
}
动态路由参数
要接收路由参数,可以在函数参数中直接接收 params
参数,例如对于 api/users/[id]/route.ts
路由,通过 params.id
获取 ID 的值:
ts
export async function GET(
request: NextRequest,
{ params }: { params: { id: string } },
) {
const { id } = params
}
查询参数
使用 request.nextUrl.searchParams
获取查询参数:
ts
// /api/search?name=hello
export async function GET(request: NextRequest) {
const searchParams = request.nextUrl.searchParams
const name = searchParams.get('name')
}
Cookies
可以使用 cookies()
获取请求中的 Cookie:
ts
import { cookies } from 'next/headers'
export async function GET(request: NextRequest) {
const cookieStore = cookies()
const token = cookieStore.get('token')
// ...
}
也可以使用 request.cookies
获取请求中的 Cookie:
ts
const token = request.cookies.get('token')
要返回 Cookie,需要设置 Set-Cookie
头:
ts
export async function GET(request: NextRequest) {
const token = 'NewToken'
return new Response('Hello, Next.js!', {
status: 200,
headers: { 'Set-Cookie': `token=${token}` },
})
}
请求头
可以使用 headers()
获取请求头:
ts
import { headers } from 'next/headers'
export async function GET(request: NextRequest) {
const headersList = headers()
const referer = headersList.get('referer')
// ...
}
也可以使用 request.headers
获取请求头:
ts
const requestHeaders = new Headers(request.headers)
高级方法
流式传输
使用手动编写的异步迭代器生成数据,然后将流式数据返回:
ts
function iteratorToStream(iterator: any) {
return new ReadableStream({
async pull(controller) {
const { value, done } = await iterator.next()
if (done) {
controller.close()
} else {
controller.enqueue(value)
}
},
})
}
function sleep(time: number) {
return new Promise((resolve) => {
setTimeout(resolve, time)
})
}
const encoder = new TextEncoder()
async function* makeIterator() {
yield encoder.encode('<p>One</p>')
await sleep(200)
yield encoder.encode('<p>Two</p>')
await sleep(200)
yield encoder.encode('<p>Three</p>')
}
export async function GET() {
const iterator = makeIterator()
const stream = iteratorToStream(iterator)
return new Response(stream)
}
支持 GPT 的流式传输
Vercel 官方通过不同的库支持 OpenAI ChatGPT 等大模型的流式传输,如 ai
、@ai-sdk/openai
,详情见 AI SDK。
CORS 跨域支持
直接通过请求头即可配置跨域:
ts
export async function GET(request: NextRequest) {
return new Response('Hello, Next.js!', {
status: 200,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
},
})
}
建议使用路由中间件来统一处理跨域。
配置
通过导出不同名称的对象来配置路由处理程序:
ts
export const dynamic = 'auto'
export const dynamicParams = true
export const revalidate = false
export const fetchCache = 'auto'
export const runtime = 'nodejs'
export const preferredRegion = 'auto'