Next.js调用外部API创建PDF下载按钮遇CORS错误求助
解决Next.js中PDF下载的CORS错误问题
核心原因
你遇到的CORS错误是浏览器同源策略的限制导致:前端从http://localhost:3000发起的fetch请求,目标服务器https://example.com未返回允许该来源的Access-Control-Allow-Origin响应头,浏览器因此拦截了请求。
可行解决方案
方案1:用Next.js API路由做代理(推荐)
前端浏览器的跨域请求受约束,但Next.js的服务器端请求不受CORS限制。可以创建内部API路由作为中间代理,转发请求到外部服务器后再返回响应给前端。
- 创建代理路由
在项目中新建app/api/download/route.ts(Pages Router则建pages/api/download.ts):
// app/api/download/route.ts import { NextResponse } from 'next/server'; export async function GET(request: Request) { const { searchParams } = new URL(request.url); const slug = searchParams.get('slug'); if (!slug) { return NextResponse.json({ error: 'Slug is required' }, { status: 400 }); } try { const externalResponse = await fetch(`https://example.com/api/download/${slug}`); const blob = await externalResponse.blob(); return new Response(blob, { headers: { 'Content-Type': 'application/pdf', 'Content-Disposition': `attachment; filename="brochure.pdf"`, }, }); } catch (error) { return NextResponse.json({ error: 'Failed to fetch PDF' }, { status: 500 }); } }
- 修改前端组件代码
将原fetch目标改为内部代理路由:
// components/Brochure.js "use client"; import { useState } from "react"; import { Button } from "@/components/ui/button"; import { useParams } from "next/navigation"; interface BrochureProps { data: string; filename?: string; } const Brochure: React.FC<BrochureProps> = ({ data, filename = "brochure.pdf", }) => { const [isDownloading, setIsDownloading] = useState(false); const params = useParams(); const handleClick = async () => { setIsDownloading(true); try { // 请求内部代理路由 const response = await fetch(`/api/download?slug=${params.slug}`); const blob = await response.blob(); const downloadLink = window.URL.createObjectURL(blob); const aTag = document.createElement("a"); aTag.href = downloadLink; aTag.setAttribute("download", filename); document.body.appendChild(aTag); aTag.click(); aTag.remove(); window.URL.revokeObjectURL(downloadLink); // 释放内存 } catch (error) { console.error("Download failed:", error); } finally { setIsDownloading(false); } }; return ( <div className="bg-[#259FAF]"> <Button onClick={handleClick} className="text-center text-white w-full h-16 font-sans text-2xl"> {isDownloading ? "Downloading" : "DOWNLOAD BROCHURE"} </Button> </div> ); }; export default Brochure;
方案2:直接用a标签跳转(无需fetch)
如果外部API返回的是可直接访问的PDF文件,直接使用a标签原生下载功能,这种方式不会触发CORS检查:
// 修改handleClick函数 const handleClick = () => { setIsDownloading(true); const aTag = document.createElement("a"); aTag.href = `https://example.com/api/download/${params.slug}`; aTag.setAttribute("download", filename); document.body.appendChild(aTag); aTag.click(); aTag.remove(); // 模拟下载状态延迟(可选) setTimeout(() => setIsDownloading(false), 1000); };
注意:该方式生效的前提是外部服务器允许该链接作为下载资源(返回正确的Content-Disposition头)。
方案3:让外部服务器配置CORS(若有权限)
如果你能控制https://example.com的服务器配置,添加CORS响应头允许你的前端来源:
- 开发环境:添加
Access-Control-Allow-Origin: http://localhost:3000 - 生产环境:添加
Access-Control-Allow-Origin: 你的生产域名 - 同时可配置
Access-Control-Allow-Methods: GET等允许的请求方法。
内容的提问来源于stack exchange,提问作者Omkar Walavalkar
相关产品推荐
相关产品推荐

