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

NextJS中如何无需封装API调用即可记录API请求耗时?

解决Next.js中间件统计API请求总耗时的方案

你当前的代码无法统计API真实耗时,是因为NextResponse.next()会立即返回,不会等待后端API处理完成,所以计算的只是中间件内部的处理时间,不是从请求进入到API响应返回的总耗时。

下面提供两种无需逐个封装API的解决方案:

方案一:代理API请求(推荐)

通过中间件拦截所有API路由请求,手动发起fetch调用实际API端点,等待响应返回后计算耗时。这种方式能准确获取完整的请求-响应周期时间,且无需修改任何API内部代码。

import { NextResponse } from 'next/server';

export default async function middleware(request) {
  // 仅处理/api开头的路由,可根据实际路由规则调整
  if (request.nextUrl.pathname.startsWith('/api/')) {
    const startTime = Date.now();
    
    // 原样转发请求到实际API,确保请求头、方法、请求体等参数一致
    const apiResponse = await fetch(request.url, {
      headers: request.headers,
      method: request.method,
      body: request.body,
      credentials: 'same-origin',
    });
    
    const endTime = Date.now();
    const duration = endTime - startTime;
    console.log(`API ${request.nextUrl.pathname} 请求耗时: ${duration}ms`);
    
    // 返回克隆的响应对象,避免原响应流被消耗后无法使用
    return new NextResponse(apiResponse.body, {
      status: apiResponse.status,
      statusText: apiResponse.statusText,
      headers: apiResponse.headers,
    });
  }

  // 非API路由直接放行
  return NextResponse.next();
}

// 配置中间件仅匹配API路由,减少不必要的拦截
export const config = {
  matcher: '/api/:path*',
};

方案二:监听响应流完成

利用响应体的可读流特性,通过克隆并读取整个响应流,等待流读取完成后计算耗时。这种方式不需要重新发起请求,但需要处理流的异步读取逻辑。

import { NextResponse } from 'next/server';

export default async function middleware(request) {
  if (request.nextUrl.pathname.startsWith('/api/')) {
    const startTime = Date.now();
    const response = NextResponse.next();
    
    // 仅当响应存在body时处理
    if (response.body) {
      // 克隆响应体,避免原响应的流被消耗导致前端无法获取数据
      const clonedBody = response.body.clone();
      // 读取整个流直到结束
      await new Promise(resolve => {
        const reader = clonedBody.getReader();
        const readChunk = () => {
          reader.read().then(({ done }) => {
            done ? resolve() : readChunk();
          });
        };
        readChunk();
      });
      
      const endTime = Date.now();
      const duration = endTime - startTime;
      console.log(`API ${request.nextUrl.pathname} 请求耗时: ${duration}ms`);
    }
    
    return response;
  }

  return NextResponse.next();
}

export const config = {
  matcher: '/api/:path*',
};

两种方案对比

  • 方案一:逻辑直观,耗时统计准确,适用于所有API场景;但需要手动转发请求参数,要注意处理特殊请求(如文件上传、自定义请求头)。
  • 方案二:无需重新发起请求,性能开销更低;但仅适用于有响应体的API请求,对分块传输的大响应可能会有延迟统计的情况。

内容的提问来源于stack exchange,提问作者No Idea For Name

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 22:57:25