什么是跨域请求_JavaScript中如何解决跨域问题

跨域请求被浏览器同源策略拦截,服务端实际已接收并响应;CORS预检失败因后端未正确配置Access-Control-Allow-Origin等响应头;开发可配vite/webpack代理临时解决,JSONP已淘汰。

跨域请求不是“不安全所以被禁”,而是浏览器出于同源策略(Same-Origin Policy)主动拦截了 JavaScript 发起的跨域 XMLHttpRequestfetch 请求——服务端其实完全收到了请求,也正常返回了响应,但浏览器拒绝把响应内容交给 JS 脚本。

为什么 fetch 会报 “No 'Access-Control-Allow-Origin' header” 错误

这是典型的 CORS(Cross-Origin Resource Sharing)预检失败提示。浏览器在发送某些“非简单请求”(如带 Content-Type: application/json、自定义 header、PUT/DELETE 方法)前,会先发一个 OPTIONS 预检请求。如果后端没正确响应这个 OPTIONS,或者没返回必需的 CORS 头,fetch 就会直接报错,连真正的请求都不会发出去。

  • 必须由后端设置响应头:Access-Control-Allow-Origin(不能是通配符 * + 凭据)
  • 若前端带 credentials: 'include',后端还必须加:Access-Control-Allow-Credentials: true
  • 预检请求要求后端对 OPTIONS 返回 200,并带上 Access-Control-Allow-MethodsAccess-Control-Allow-Headers

前端临时绕过:开发环境用 webpack/vite 代理

代理只在开发服务器生效,不解决生产跨域,但能立刻让接口调通,避免反复改后端配置。

  • vite:在 vite.config.ts 中配 server.proxy,例如将 /api 代理到 http://localhost:3000
  • webpack-dev-server:在 devServer.proxy 中写对象或数组规则,注意路径重写(rewritepathRewrite 已废弃,改用 configure 函数)
  • 关键点:代理目标地址不能带结尾斜杠(target: 'http://localhost:3000' ✅,'http://localhost:3000/' ❌),否则路径拼接错

JSONP 已淘汰,不要在新项目中使用

JSONP 利用 标签不受同源限制的特性,通过动态插入 script 加载远程 JS 执行回调。但它只支持 GET,没有错误捕获机制,无法设 timeout,且服务端必须配合返回函数调用格式(如 callback({...}))。现代浏览器已弃用 document.write,主流框架也不再内置 JSONP 支持。

  • 如果你看到老代码里还有 $.getJSON(url + '?callback=?') 或手动创建 script 标签,说明它没走 CORS,而是降级到了 JSONP
  • Chrome 98+ 对 document.write 在非初始页面加载时直接静默忽略,JSONP 回调可能永远不执行
  • 真正需要兼容 IE10 及以下?优先考虑后端加 CORS,而不是复活 JSONP
fetch('/api/user', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  credentials: 'include',
  body: JSON.stringify({ id: 123 })
})
  .then(r => r.json())
  .catch(err => console.error('网络或 CORS 失败:', err))

CORS 的核心从来不在前端“怎么发”,而在于后端是否明确声明“允许谁、用什么方法、带什么头来访问我”。很多前端开发者卡在“为什么本地能通上线就跨域”,往往是因为 nginx 或云函数网关没透传后端返回的 CORS 响应头,或者反向代理覆盖了它们。