Next.js 14客户端组件如何无需配置跨域调用外部API?
我正在使用Next.js 14 App Router,了解可在Server Component中通过Server Actions获取数据,但因某些原因,特定路由的page.tsx必须为客户端组件。尝试在客户端组件中使用useEffect()结合fetch()调用外部API端点时,尽管目标服务器已允许所有源请求,仍在控制台出现CORS错误:
Access to fetch at 'http://localhost:9000/endpoint' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
同时Next.js错误面板显示:
Unhandled Runtime Error TypeError: Failed to fetch
但通过Server Actions使用相同fetch()调用同一端点却能成功。目前可通过在next.config.js中配置rewrites临时解决:
/** @type {import('next').NextConfig} */ const nextConfig = { async rewrites() { return [ { source: "/endpoint", destination: "https://exampleapi.com/endpoint", }, ]; }, }; module.exports = nextConfig;
该方法可解决CORS及“Failed to fetch”运行时错误,但需为每个端点单独配置。请问是否存在无需修改next.config.js,在客户端组件的useEffect中使用fetch()获取数据的其他方法?
方法1:在客户端组件中调用Server Action(推荐)
既然Server Action能绕过CORS成功调用API,直接在客户端组件中复用这个逻辑即可,无需自行编写客户端fetch请求。
首先定义Server Action(放在单独的文件中,需添加'use server'指令):
'use server'; export async function fetchExternalData() { const res = await fetch('http://localhost:9000/endpoint', { method: 'GET', // 匹配目标API的请求方法 // 保留你之前Server Action中成功的配置(如请求头、认证信息等) }); if (!res.ok) { throw new Error('获取数据失败'); } return res.json(); }
然后在客户端组件中调用该Server Action:
'use client'; import { fetchExternalData } from '@/app/actions'; import { useEffect, useState } from 'react'; export default function ClientPage() { const [data, setData] = useState(null); const [error, setError] = useState(null); useEffect(() => { const getData = async () => { try { const result = await fetchExternalData(); setData(result); } catch (err) { setError(err.message); } }; getData(); }, []); if (error) return <div>错误:{error}</div>; if (!data) return <div>加载中...</div>; return <div>数据:{JSON.stringify(data)}</div>; }
此方案通过服务器端发起请求,完全规避浏览器的CORS限制,且无需修改配置文件。
方法2:使用Next.js API Route作为通用代理
创建一个通用的代理API端点,可处理多个外部API请求,无需逐个配置rewrites。
在app/api/proxy/route.ts中创建代理逻辑:
export async function GET(request: Request) { const urlParams = new URL(request.url).searchParams; const targetUrl = urlParams.get('url'); if (!targetUrl) { return new Response('缺少目标URL参数', { status: 400 }); } try { const res = await fetch(targetUrl, { // 根据需求传递请求头、认证信息等 headers: { 'Content-Type': 'application/json', }, cache: 'no-store', // 避免缓存,按需调整 }); const responseData = await res.json(); return new Response(JSON.stringify(responseData), { status: res.status, headers: { 'Content-Type': 'application/json', }, }); } catch (err) { return new Response('代理请求失败', { status: 500 }); } }
然后在客户端组件的useEffect中调用这个代理端点:
'use client'; import { useEffect, useState } from 'react'; export default function ClientPage() { const [data, setData] = useState(null); const [error, setError] = useState(null); useEffect(() => { const getData = async () => { try { const targetApi = 'http://localhost:9000/endpoint'; const res = await fetch(`/api/proxy?url=${encodeURIComponent(targetApi)}`); if (!res.ok) { throw new Error('代理请求失败'); } const result = await res.json(); setData(result); } catch (err) { setError(err.message); } }; getData(); }, []); // 渲染逻辑同前 }
该方案只需创建一个代理端点,即可支持任意外部API请求,无需修改next.config.js。
方法3:排查目标服务器CORS配置(若有权限修改)
若你有权限调整目标服务器配置,可检查以下几点:
- 确认服务器返回
Access-Control-Allow-Origin: *(或明确包含http://localhost:3000) - 针对带自定义请求头或非GET/POST的请求,服务器需正确处理OPTIONS预请求,返回
Access-Control-Allow-Methods、Access-Control-Allow-Headers等响应头 - 重启服务器,确保新的CORS配置生效
内容的提问来源于stack exchange,提问作者Darshan Hande

