You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js 13 App Router API路由读取JSON文件,求更优实现方案

Next.js 13 App Router 读取语言文件的优化方案

你当前的API路由能正常运行,但确实有可以优化的空间,下面是几种更高效或符合Next.js官方推荐的实现方式:

现有代码回顾

// app/api/file/[lang]/write/route.ts

import { APIResponseType } from '@/models/api/response';
import { NextRequest, NextResponse } from 'next/server';

type Params = {
  params: {
    lang: string;
  };
};

export async function GET(req: NextRequest, { params: { lang } }: Params) {
  const data = await import(`../../../../../languages/${lang}.json`);
  return NextResponse.json<APIResponseType>({
    message: 'Get language successfully',
    data: data
  });
}

优化方案

1. 使用Node.js fs 模块读取文件(服务器端推荐)

动态import JSON文件在开发环境没问题,但生产环境下Next.js会把这些文件打包到客户端bundle里,可能存在缓存不一致的情况。直接用Node.js的fs/promises模块读取项目目录下的文件更可控,适合服务器端操作:

// app/api/file/[lang]/write/route.ts
import { APIResponseType } from '@/models/api/response';
import { NextRequest, NextResponse } from 'next/server';
import { readFile } from 'fs/promises';
import { join } from 'path';
import { cwd } from 'process';

type Params = {
  params: {
    lang: string;
  };
};

export async function GET(req: NextRequest, { params: { lang } }: Params) {
  // 验证lang参数,防止路径遍历攻击
  if (!/^[a-z-]+$/.test(lang)) {
    return NextResponse.json<APIResponseType>({
      message: 'Invalid language code',
      data: null
    }, { status: 400 });
  }

  try {
    const filePath = join(cwd(), 'languages', `${lang}.json`);
    const fileContent = await readFile(filePath, 'utf-8');
    const data = JSON.parse(fileContent);
    
    return NextResponse.json<APIResponseType>({
      message: 'Get language successfully',
      data: data
    });
  } catch (err) {
    return NextResponse.json<APIResponseType>({
      message: 'Language file not found',
      data: null
    }, { status: 404 });
  }
}

2. 预渲染语言数据(静态生成/增量静态再生)

如果语言文件不频繁更新,完全可以在构建时预渲染这些数据,避免每次请求都读取文件。比如在专门的工具函数里提前获取:

// app/i18n/getLangData.ts
import { readFile } from 'fs/promises';
import { join } from 'path';
import { cwd } from 'process';

export async function getLangData(lang: string) {
  if (!/^[a-z-]+$/.test(lang)) throw new Error('Invalid language code');
  
  const filePath = join(cwd(), 'languages', `${lang}.json`);
  const fileContent = await readFile(filePath, 'utf-8');
  return JSON.parse(fileContent);
}

然后在API路由里结合generateStaticParams预生成所有可能的lang路由:

// app/api/file/[lang]/write/route.ts
import { APIResponseType } from '@/models/api/response';
import { NextResponse } from 'next/server';
import { getLangData } from '@/app/i18n/getLangData';

type Params = {
  params: {
    lang: string;
  };
};

// 预生成所有支持的语言路由
export async function generateStaticParams() {
  return [
    { lang: 'en' },
    { lang: 'zh-CN' },
    // 其他支持的语言
  ];
}

export async function GET(req: Request, { params: { lang } }: Params) {
  try {
    const data = await getLangData(lang);
    return NextResponse.json<APIResponseType>({
      message: 'Get language successfully',
      data: data
    });
  } catch (err) {
    return NextResponse.json<APIResponseType>({
      message: 'Language file not found',
      data: null
    }, { status: 404 });
  }
}

3. 缓存读取结果

如果语言文件更新不频繁,可以在服务器端缓存读取结果,避免重复IO操作:

// app/api/file/[lang]/write/route.ts
import { APIResponseType } from '@/models/api/response';
import { NextRequest, NextResponse } from 'next/server';
import { readFile } from 'fs/promises';
import { join } from 'path';
import { cwd } from 'process';

type Params = {
  params: {
    lang: string;
  };
};

// 内存缓存,生产环境可以用Redis等更持久的缓存方案
const langCache = new Map<string, any>();

export async function GET(req: NextRequest, { params: { lang } }: Params) {
  if (!/^[a-z-]+$/.test(lang)) {
    return NextResponse.json<APIResponseType>({
      message: 'Invalid language code',
      data: null
    }, { status: 400 });
  }

  // 先从缓存取数据
  if (langCache.has(lang)) {
    return NextResponse.json<APIResponseType>({
      message: 'Get language successfully',
      data: langCache.get(lang)
    });
  }

  try {
    const filePath = join(cwd(), 'languages', `${lang}.json`);
    const fileContent = await readFile(filePath, 'utf-8');
    const data = JSON.parse(fileContent);
    
    langCache.set(lang, data);
    return NextResponse.json<APIResponseType>({
      message: 'Get language successfully',
      data: data
    });
  } catch (err) {
    return NextResponse.json<APIResponseType>({
      message: 'Language file not found',
      data: null
    }, { status: 404 });
  }
}

关键注意点

  • 安全验证:一定要对lang参数做校验,防止路径遍历攻击(比如传入../secret-file读取非语言目录的文件)。
  • 错误处理:添加try/catch捕获文件不存在、读取失败等异常,返回合适的HTTP状态码。
  • 环境适配:fs模块只能在服务器端使用,Next.js App Router的API路由默认是服务器端组件,所以可以直接使用;如果是客户端组件,不能直接用fs,需要通过API路由获取数据。

内容的提问来源于stack exchange,提问作者Em Ha Tuan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 09:57:33