Next封装统一请求

分类: 编码创建于: 6/1/2025

在 Next.js 框架中,封装统一的请求方法并区分服务端和客户端是一个常见需求,因为 Next.js 既支持服务端渲染 (SSR) 也支持客户端渲染 (CSR)。服务端和客户端的环境不同(例如,服务端没有 window 对象,客户端可能需要处理用户认证 token 等),因此需要分别处理两者的请求逻辑。

以下是一个封装统一请求方法的示例,基于 axios 库,区分服务端和客户端的请求逻辑:

1. 创建请求工具文件

我们可以创建一个 utils/request.js 文件,用于封装请求方法,并根据环境(服务端或客户端)进行不同的处理。

1import axios from 'axios';
2import { getSession } from 'next-auth/react'; // 假设使用 next-auth 处理认证
3
4// 创建 axios 实例
5const createAxiosInstance = (baseURL, headers = {}) => {
6  return axios.create({
7    baseURL: baseURL || process.env.NEXT_PUBLIC_API_BASE_URL, // API 基础 URL
8    timeout: 10000, // 请求超时时间
9    headers: {
10      'Content-Type': 'application/json',
11      ...headers,
12    },
13  });
14};
15
16// 拦截器 - 请求前处理
17const setupInterceptors = (instance, isServer) => {
18  instance.interceptors.request.use(
19    async (config) => {
20      // 客户端环境:从 session 获取 token
21      if (!isServer) {
22        const session = await getSession();
23        if (session?.accessToken) {
24          config.headers.Authorization = `Bearer ${session.accessToken}`;
25        }
26      }
27      // 服务端环境:可能从其他地方获取 token 或不设置
28      return config;
29    },
30    (error) => {
31      return Promise.reject(error);
32    }
33  );
34
35  // 响应拦截器
36  instance.interceptors.response.use(
37    (response) => {
38      return response.data; // 返回响应数据
39    },
40    (error) => {
41      // 统一处理错误
42      const errorMessage = error.response?.data?.message || 'Request failed';
43      return Promise.reject(new Error(errorMessage));
44    }
45  );
46
47  return instance;
48};
49
50// 服务端请求方法
51export const serverRequest = (headers = {}) => {
52  const instance = createAxiosInstance(process.env.NEXT_PUBLIC_API_BASE_URL, headers);
53  return setupInterceptors(instance, true);
54};
55
56// 客户端请求方法
57export const clientRequest = (headers = {}) => {
58  const instance = createAxiosInstance(process.env.NEXT_PUBLIC_API_BASE_URL, headers);
59  return setupInterceptors(instance, false);
60};
61
62// 统一请求方法(根据环境选择服务端或客户端)
63export const request = (headers = {}) => {
64  const isServer = typeof window === 'undefined';
65  return isServer ? serverRequest(headers) : clientRequest(headers);
66};
67
68// 封装常用请求方法
69export const api = {
70  get: (url, params = {}, headers = {}) =>
71    request(headers).get(url, { params }),
72  post: (url, data = {}, headers = {}) =>
73    request(headers).post(url, data),
74  put: (url, data = {}, headers = {}) =>
75    request(headers).put(url, data),
76  delete: (url, headers = {}) =>
77    request(headers).delete(url),
78};

2. 使用说明

  • 服务端请求:在 getServerSidePropsgetStaticProps 中,可以直接使用 serverRequest 或通过 request 自动判断环境。
  • 客户端请求:在 React 组件中(例如 useEffect 或事件处理中),可以使用 clientRequest 或通过 request 自动判断环境。

服务端使用示例(在 getServerSideProps 中)

1export async function getServerSideProps(context) {
2  try {
3    // 服务端请求,传递额外的 headers(如果需要)
4    const response = await api.get('/api/data', {}, { 'Custom-Header': 'value' });
5    return {
6      props: {
7        data: response,
8      },
9    };
10  } catch (error) {
11    console.error('Server request error:', error);
12    return {
13      props: {
14        data: null,
15      },
16    };
17  }
18}

客户端使用示例(在 React 组件中)

1import { useEffect, useState } from 'react';
2import { api } from '../utils/request';
3
4export default function Home() {
5  const [data, setData] = useState(null);
6
7  useEffect(() => {
8    const fetchData = async () => {
9      try {
10        const response = await api.get('/api/data');
11        setData(response);
12      } catch (error) {
13        console.error('Client request error:', error);
14      }
15    };
16    fetchData();
17  }, []);
18
19  return (
20    <div>
21      {data ? <pre>{JSON.stringify(data, null, 2)}</pre> : 'Loading...'}
22    </div>
23  );
24}

3. 关键点说明

  • 环境判断:通过 typeof window === 'undefined' 判断当前是否为服务端环境。
  • 请求拦截器:在请求前可以添加认证 token 或其他逻辑,客户端可以从 next-auth 或本地存储中获取 token,服务端可能需要从上下文或其他方式获取。
  • 错误处理:统一处理请求错误,返回友好的错误信息。
  • 环境变量:API 基础 URL 可以通过环境变量 NEXT_PUBLIC_API_BASE_URL 配置,确保在 .env 文件中正确设置。

4. 扩展功能

  • 认证处理:如果项目使用其他认证方式(如 JWT 存储在 cookie 中),可以在服务端通过 context.req.cookies 获取 cookie,在客户端通过 document.cookie 或其他方式获取。
  • 自定义配置:可以根据需求扩展 api 对象,支持更多请求方法(如 patch)或添加自定义参数。
  • 重试机制:可以在拦截器中添加请求重试逻辑,处理网络不稳定等问题。

通过以上方式,你可以在 Next.js 中实现一个统一且灵活的请求封装,适用于服务端和客户端的不同场景。