Next.js第十五课 - 路由处理器

4 阅读3分钟

前面我们学习了 Metadata,本节来聊聊路由处理器(Route Handlers)。路由处理器让你能在 Next.js 中创建 API 端点,这对于构建全栈应用非常有用。

Route Handlers 概述

Route Handlers 允许你在 Next.js 中创建 API 端点:

  1. 使用 Web 标准 Request 和 Response API
  2. 支持 Edge Runtime
  3. 可以返回 JSON、文本、图片等
  4. 支持动态路由

Route Handlers 是构建 API 的推荐方式。

基本 Route Handler

创建 API 端点

// app/api/hello/route.ts
import { NextResponse } from 'next/server'

export async function GET() {
  return NextResponse.json({
    message: 'Hello, World!',
  })
}

访问 /api/hello 会返回 JSON 响应。

使用不同的 HTTP 方法

// app/api/users/route.ts
import { NextResponse } from 'next/server'

export async function GET() {
  const users = await db.user.findMany()
  return NextResponse.json(users)
}

export async function POST(request: Request) {
  const body = await request.json()
  const user = await db.user.create({ data: body })
  return NextResponse.json(user, { status: 201 })
}

动态路由

基本动态路由

// app/api/posts/[id]/route.ts
import { NextResponse } from 'next/server'

export async function GET(
  request: Request,
  { params }: { params: { id: string } }
) {
  const post = await db.post.findUnique({
    where: { id: params.id },
  })

  if (!post) {
    return NextResponse.json(
      { error: '文章不存在' },
      { status: 404 }
    )
  }

  return NextResponse.json(post)
}

处理不同的 HTTP 方法

export async function PATCH(
  request: Request,
  { params }: { params: { id: string } }
) {
  const body = await request.json()
  const post = await db.post.update({
    where: { id: params.id },
    data: body,
  })

  return NextResponse.json(post)
}

export async function DELETE(
  request: Request,
  { params }: { params: { id: string } }
) {
  await db.post.delete({
    where: { id: params.id },
  })

  return new NextResponse(null, { status: 204 })
}

请求处理

获取查询参数

export async function GET(request: Request) {
  const { searchParams } = new URL(request.url)
  const page = searchParams.get('page') || '1'
  const limit = searchParams.get('limit') || '10'

  const posts = await db.post.findMany({
    skip: (Number(page) - 1) * Number(limit),
    take: Number(limit),
  })

  return NextResponse.json(posts)
}

获取请求体

export async function POST(request: Request) {
  const body = await request.json()

  // 验证数据
  if (!body.title || !body.content) {
    return NextResponse.json(
      { error: '标题和内容不能为空' },
      { status: 400 }
    )
  }

  const post = await db.post.create({
    data: body,
  })

  return NextResponse.json(post, { status: 201 })
}

获取请求头

import { headers } from 'next/header'

export async function GET(request: Request) {
  const headersList = headers()
  const authorization = headersList.get('authorization')

  if (!authorization) {
    return NextResponse.json(
      { error: '未授权' },
      { status: 401 }
    )
  }

  // 处理请求...
}

响应处理

返回 JSON

export async function GET() {
  return NextResponse.json({
    message: '成功',
    data: { /* ... */ },
  })
}

返回文本

export async function GET() {
  return new NextResponse('Hello, World!', {
    status: 200,
    headers: {
      'Content-Type': 'text/plain',
    },
  })
}

返回图片

export async function GET() {
  const image = await fetch('https://example.com/image.jpg')
  const imageBuffer = Buffer.from(await image.arrayBuffer())

  return new NextResponse(imageBuffer, {
    headers: {
      'Content-Type': 'image/jpeg',
    },
  })
}

设置响应头

export async function GET() {
  return NextResponse.json(
    { data: '...' },
    {
      headers: {
        'Cache-Control': 'max-age=3600',
        'X-Custom-Header': 'value',
      },
    }
  )
}

错误处理

返回错误响应

export async function POST(request: Request) {
  try {
    const body = await request.json()
    const result = await someOperation(body)

    return NextResponse.json(result)
  } catch (error) {
    return NextResponse.json(
      { error: '服务器错误' },
      { status: 500 }
    )
  }
}

数据验证

import { z } from 'zod'

const schema = z.object({
  title: z.string().min(1),
  content: z.string().min(10),
})

export async function POST(request: Request) {
  const body = await request.json()

  const result = schema.safeParse(body)

  if (!result.success) {
    return NextResponse.json(
      { errors: result.error.errors },
      { status: 400 }
    )
  }

  // 处理验证后的数据...
}

配置选项

指定运行时

export const runtime = 'edge' // 或 'nodejs'

export async function GET() {
  // Edge Runtime 环境
}

动态渲染

export const dynamic = 'force-dynamic'

export async function GET() {
  // 每次请求都重新执行
}

CORS 配置

export async function OPTIONS(request: Request) {
  return new NextResponse(null, {
    status: 200,
    headers: {
      'Access-Control-Allow-Origin': '*',
      'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
      'Access-Control-Allow-Headers': 'Content-Type, Authorization',
    },
  })
}

export async function GET(request: Request) {
  return NextResponse.json(
    { data: '...' },
    {
      headers: {
        'Access-Control-Allow-Origin': '*',
      },
    }
  )
}

实用建议

这里分享几个在编写路由处理器时特别有用的技巧。

使用适当的 HTTP 方法

实际开发中,使用正确的 HTTP 方法能让你的 API 更加规范和易用:

// 推荐这样做 - 使用正确的 HTTP 方法
export async function GET() { /* 获取资源 */ }
export async function POST() { /* 创建资源 */ }
export async function PATCH() { /* 更新资源 */ }
export async function DELETE() { /* 删除资源 */ }

// 虽然可以用 POST 处理所有操作,但不推荐
export async function POST() { /* 所有操作 */ }

验证输入数据

这个技巧特别重要——永远不要信任用户输入,始终验证:

export async function POST(request: Request) {
  const body = await request.json()

  // 验证数据
  if (!body.email || !body.password) {
    return NextResponse.json(
      { error: '邮箱和密码不能为空' },
      { status: 400 }
    )
  }

  // 然后处理数据...
}

处理错误

这里有个小建议:始终处理可能的错误,这样能让你的 API 更加健壮:

export async function GET() {
  try {
    const data = await fetchData()
    return NextResponse.json(data)
  } catch (error) {
    console.error(error)
    return NextResponse.json(
      { error: '服务器错误' },
      { status: 500 }
    )
  }
}

总结

本节我们学习了 Next.js 的 Route Handlers,包括基本用法、动态路由、请求处理、响应处理等。Route Handlers 是构建 API 的推荐方式,使用标准的 Web API,简单而强大。

如果你对本节内容有任何疑问,欢迎在评论区提出来,我们一起学习讨论。

blog.uuhb.cn/archives/ne…