You need to enable JavaScript to run this app. Axios与Express:如何通过单请求实现后端分步骤返回处理响应
优惠活动
大模型
产品
解决方案
定价
更多

Axios与Express:如何通过单请求实现后端分步骤返回处理响应

完全可以实现,单请求流式传输是最匹配你需求的方案

你之前担心的多次请求重复走公共校验逻辑的问题,完全可以靠HTTP分块流式响应彻底解决,全程只需要向后端发1次请求,后端每完成一个处理节点就实时把进度推给前端,前端收到一段就更新一段通知,不需要等全部处理完成才拿到结果。

后端Node.js 侧实现

Node 生态下不管是原生http模块、Express、Koa还是NestJS,都原生支持分块传输的响应能力,不需要引入额外的消息队列、WebSocket这类重型组件:

  • 首先配置响应头,关闭各级缓冲,确保数据写完立刻推送到前端:
    • 把响应类型设为application/x-ndjson(换行分隔的JSON格式,方便前后端逐行解析,比纯文本流更容易处理结构化的进度消息)
    • 设置Transfer-Encoding: chunked,不要设置固定长度的Content-Length,避免浏览器等待全量内容才触发接收
    • 如果场景走Nginx反向代理,记得加X-Accel-Buffering: no头,关闭Nginx的响应缓冲
  • 业务处理流到哪个节点,就直接往响应对象里写对应的进度消息,等全流程结束再调用end收尾。

举个Express的极简可运行示例:

// 后端示例代码
app.get('/api/process-with-progress', async (req, res) => {
  // 配置流式响应头
  res.setHeader('Content-Type', 'application/x-ndjson');
  res.setHeader('Transfer-Encoding', 'chunked');
  res.setHeader('X-Accel-Buffering', 'no');

  // 节点1:公共校验逻辑(只做一次)
  const authCheck = await validateRequestToken(req);
  if (!authCheck.pass) {
    res.write(JSON.stringify({ step: 1, message: '身份校验失败,请重新登录' }) + '\n');
    return res.end();
  }
  res.write(JSON.stringify({ step: 1, message: '身份校验、参数校验完成' }) + '\n');

  // 节点2:核心业务逻辑处理
  res.write(JSON.stringify({ step: 2, message: '开始读取业务数据,预计耗时3s' }) + '\n');
  const businessData = await fetchBusinessData(req.query.id);

  // 节点3:结果加工
  res.write(JSON.stringify({ step: 3, message: '数据读取完成,正在加工最终结果' }) + '\n');
  const finalResult = await processData(businessData);

  // 节点4:返回最终结果,收尾
  res.write(JSON.stringify({ step: 4, message: '全流程处理完成', data: finalResult }) + '\n');
  res.end();
});

前端React + Axios 侧实时接收逻辑

很多人误以为Axios只能接收全部响应内容后才返回结果,实际上它内置的下载进度回调完全可以拿到传输过程中的分块内容,不需要换用Fetch API或者其他库:

  • 发起请求时把responseType设为stream,同时监听onDownloadProgress进度事件
  • 用一个变量缓存已经解析过的内容,每次拿到新的累计传输内容时,截取出新增的未解析片段,按换行符拆成单条JSON,逐行更新到页面的通知列表里

组件内的实现示例:

// React组件内的请求逻辑示例
import axios from 'axios';
import { useState } from 'react';

export default function ProgressNotify() {
  const [notifyList, setNotifyList] = useState([]);

  const triggerProcess = async () => {
    setNotifyList([]);
    let parsedContent = ''; // 缓存已经解析过的内容,避免重复处理

    await axios.get('/api/process-with-progress', {
      responseType: 'stream',
      // 监听下载进度,实时拿到分块内容
      onDownloadProgress: (progressEvent) => {
        const fullReceived = progressEvent.event.target.response;
        // 提取本次进度更新里新增的未解析内容
        const newFragment = fullReceived.replace(parsedContent, '');
        parsedContent = fullReceived;

        // 逐行解析NDJSON格式的消息,更新通知
        newFragment.split('\n').forEach(line => {
          const validLine = line.trim();
          if (!validLine) return;
          try {
            const notify = JSON.parse(validLine);
            setNotifyList(prev => [...prev, notify.message]);
          } catch (err) {
            // 忽略传输过程中没接收完整的截断行
          }
        });
      }
    });
  };

  return (
    <div>
      <button onClick={triggerProcess}>启动任务</button>
      <div className="notify-container">
        {notifyList.map((msg, index) => <p key={index}>{msg}</p>)}
      </div>
    </div>
  );
}

方案适配说明

  • 相比你之前考虑的多请求逐次等待方案,这个流式方案全程只做一次身份校验、一次参数解析,完全避免了重复公共逻辑带来的性能损耗,网络往返次数从N次降到1次,整体性能开销最低
  • 如果后续需要实现服务端主动推送通知(不需要前端主动发请求),可以在这套逻辑基础上平滑迁移到SSE服务端推送,实现逻辑和当前的流方案高度重合,不需要做大幅改造

内容的提问来源于stack exchange,提问作者ImanGM

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.11.12 23:45:01