React服务端组件中fetch接收204响应抛出TypeError的原因与处理
在Next.js App Router场景下,当API路由返回HTTP 204(No Content)状态码时,服务端组件(RSC)中的fetch请求会抛出错误:
TypeError: Response constructor: Invalid response status code 204
但将状态码改为202(Accepted)则无此问题,且客户端组件中执行相同的fetch请求完全正常。以下是相关代码示例:
API路由代码
export default function handler(req, res) { res.status(204).end(); }
服务端组件代码
export default async function Home() { let res; try { res = await fetch("https://rpvequ-3000.csb.app/api/hello"); console.log("res.status", res.status); console.log("res", res); } catch (error) { console.log("res in error", res); console.log("error", error); } return ( <main> <p>HELLO WORLD</p> </main> ); }
客户端组件对比代码
"use client"; import React, { useEffect } from "react"; const ClientComp = ({ children }) => { useEffect(() => { let res; (async () => { try { res = await fetch("http://localhost:3000/api/hello"); console.log("res.status", res.status); console.log("res", res); } catch (error) { console.log("res in error", res); console.log("error", error); } })(); }, []); return <div>{children}</div>; }; export default ClientComp;
原因分析
Next.js服务端组件中使用的fetch并非浏览器原生实现,而是经过框架封装的版本,需遵循React服务端组件的序列化规则。
HTTP 204状态码的核心要求是响应不能包含任何内容体,但Next.js封装的fetch在处理响应时,会尝试将结果序列化为可传递给客户端的格式,这个过程中可能因204响应无内容体的特性与内部Response构造逻辑冲突,最终抛出错误。
而客户端组件使用的是浏览器原生fetch,其处理逻辑完全符合HTTP规范,能正常识别并返回204响应的Response对象,不会触发错误。
解决办法:在RSC中处理204响应
1. 使用原生Node.js HTTP模块替代fetch
绕开Next.js封装的fetch,直接用Node.js的http/https模块发送请求,完全控制响应处理流程:
import https from 'https'; export default async function Home() { let statusCode; try { statusCode = await new Promise((resolve, reject) => { https.get('https://rpvequ-3000.csb.app/api/hello', (res) => { resolve(res.statusCode); res.resume(); // 消耗响应流,避免内存泄漏 }).on('error', reject); }); console.log('statusCode', statusCode); if (statusCode === 204) { // 自定义204响应的处理逻辑 console.log('Received 204 No Content'); } } catch (error) { console.log('error', error); } return ( <main> <p>HELLO WORLD</p> </main> ); }
2. 捕获错误并针对性处理
通过try/catch捕获错误,结合错误信息判断是否为204响应相关问题:
export default async function Home() { let res; try { res = await fetch("https://rpvequ-3000.csb.app/api/hello"); console.log("res.status", res.status); // 正常响应的处理逻辑 } catch (error) { if (error.message.includes('Invalid response status code 204')) { // 处理204响应的业务逻辑 console.log('Handled 204 No Content response'); } else { console.log('Other error', error); } } return ( <main> <p>HELLO WORLD</p> </main> ); }
3. 修改API返回状态码(临时方案)
如果业务允许,可将204替换为202(Accepted)或200(OK,返回空对象/空字符串作为响应体),规避Next.js的序列化错误。
测试注意事项
每次重启开发服务器时需删除.next文件夹,否则fetch会直接使用缓存内容,导致测试结果不准确。
内容的提问来源于stack exchange,提问作者Magnus

