实现服务端逻辑

第一篇笔记我们已经介绍过,NextJS是一个全栈Web开发框架,前面我们介绍的内容大多是和前端开发相关的内容,这里我们继续学习如何用NextJS开发服务端功能。当然,NextJS的服务端在设计上相比一些成熟的服务端框架还是稍显逊色,实际开发中也经常将服务端功能交给其它语言或框架来实现,但总的来说用NextJS实现一些简单的逻辑也完全够用了,这里我们学习一下。

使用Route Handlers

在NextJS13的App Router下,“页面”和“路由处理器”是两个同等的概念。前者我们之前已经大量使用过,它由一组React组件构成,入口文件是page.jsx,它最终会渲染成页面交给浏览器;而后者则对应一个服务端接口,约定文件名为route.js

路由处理器和页面都是基于App Router文件路由系统的,它们遵循完全相同的路由构建规则,但要注意一条路由下不能同时存在page.jsxroute.js。一个最佳实践是让路由处理器的路由都以api开头,也就是所有路由处理器都放在app/api文件夹下。下面是一个简单的例子。

|_app
  |_api
    |_dashboard
      |_route.js

route.js

export const GET = async () => {
  return Response.json({
    code: 0,
    message: "success",
    data: null,
  });
};

通过目录结构我们可以得知,上面定义了一条响应/api/dashboard的路由,在route.js中,我们导出了一个名为GET的函数,函数名对应支持的HTTP方法,函数体内我们返回了一段JSON数据。这里我们仅编写了对应GET方法的处理函数,如果试图用其它HTTP方法请求该路由,NextJS会自动返回405(Method Not Allowed)错误。

另外要注意的一点是NextJS中GET请求的返回值默认是缓存的,但在以下条件下不会缓存数据:

  • 在GET函数中通过Request对象读取数据
  • 使用非GET请求
  • 使用cookies()headers()等函数读取了请求参数
  • 主动配置了Segment Config指定该路由不缓存数据

读取请求数据

读取请求参数

对于请求GET参数,我们可以直接用request.nextUrl.searchParams属性来读取,下面是一个例子。

export const GET = async (request) => {
  // 读取请求URL参数
  const searchParams = request.nextUrl.searchParams;

  return Response.json({
    code: 0,
    message: "success",
    data: searchParams,
  });
};

对于动态路由,我们可以使用处理函数的第二个参数接收,在params属性中访问对应的动态路由参数名即可。下面是一个例子,我这里定义的路由是/api/dashboard/[...slug]

export const GET = async (request, { params }) => {
  // 读取请求URL参数
  const slugs = params.slug;

  return Response.json({
    code: 0,
    message: "success",
    data: slugs,
  });
};

读取JSON请求

对于JSON请求,我们可以使用request.json()来读取,注意该操作是异步的,因此需要加上await关键字。

export const POST = async (request) => {
  // 读取请求JSON
  const reqJson = await request.json();

  return Response.json({
    code: 0,
    message: "success",
    data: reqJson,
  });
};

上传文件

文件上传通常要使用FormData格式的表单请求。下面例子实现了一个简单的文件上传功能,代码中我们将上传的文件一次性读入内存,然后写入文件,因此仅适用于小文件。

import path from "path";
import { writeFile } from "fs/promises";

export const POST = async (request) => {
  // 读取formData请求表单
  const formData = await request.formData();
  const file = formData.get("file");
  if (file) {
    // 读取文件全部数据
    const bytes = await file.arrayBuffer();
    // 转换成Buffer对象
    const buffer = Buffer.from(bytes);
    // 写入文件数据
    await writeFile(path.resolve("D:/test.txt"), buffer);
  }

  return Response.json({
    code: 0,
    message: "success",
    data: null,
  });
};

返回响应

返回JSON数据

返回JSON数据非常简单,我们直接调用Response.json()函数即可。

export const GET = async () => {
  return Response.json({
    code: 0,
    message: "success",
    data: null,
  });
};

返回文件流

返回文件流稍微有点复杂,这需要调用NodeJS相关的API来实现。下面例子代码我们返回了一个文件流。

import fs from "fs";
import path from "path";

export const GET = async (request) => {
  const filePath = path.resolve("D:/flag");
  const readStream = fs.createReadStream(filePath);
  return new Response(readStream, {
    headers: {
      "content-type": "application/octet-stream"
    },
  });
};

返回重定向

下面例子实现了在服务端返回重定向。

import { redirect } from "next/navigation";

export const GET = async () => {
  return redirect("/dashboard");
};

读写Cookie

读写Cookie需要使用cookies函数,下面是一个例子。

import { cookies } from "next/headers";

export const GET = async () => {
  const cookie = cookies();
  // 读取Cookie,返回结构类似 { name: 'user', value: 'test', path: '/' }
  const user = cookie.get("user");
  console.log(user);
  // 设置Cookie
  cookie.set("user", "test");
  return Response.json({
    code: 0,
    message: "success",
    data: null,
  });
};

读写Header

写入Header前面我们已经介绍了,Header需要在Response中写入,例子如下。

return new Response(readStream, {
  headers: {
    "content-type": "text/plain"
  },
});

读取Header可以使用headers()函数实现。

import { headers } from "next/headers";

export const GET = async () => {
  const header = headers();
  // 读取Header
  const ua = header.get("User-Agent");
  console.log(ua);
  return Response.json({
    code: 0,
    message: "success",
    data: null,
  });
};
作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。