跨域请求产生错误的原因及处理方法
// 每日前端夜话 第428篇
// 正文共:1400 字
// 预计阅读时间:6 分钟
如果你在开发网站时曾经尝试通过框架或是浏览器的 fetch
、XHR
请求过外部 API 的话,那么一定遇到过跨域请求,还有那个触目惊心的 CORS 错误信息;今天咱们来讨论跨域问题的原因以及解决方法。
跨域请求
如果你没有没有遇过,可以试着在浏览器的 console
页输入下面的代码:
const xhr = new XMLHttpRequest()
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
console.log(xhr.status === 200 ? xhr.responseText : 'error')
}
}
xhr.open('GET', 'https://google.com')
xhr.send()
这段代码通过调用浏览器的 XMLHttpRequest
对 Google 发出请求,而得到的结果如图所示:
这就是跨域请求问题,当通过 JavaScript 对不同的来源发送请求时,这个请求的响应就会被浏览器拦截,不交给 JavaScript 处理。这里的“不同来源”指的是目标资源与当前网页的域(domain)、通讯协议(protocol)或网络端口(port)只要有任一项不同,就算是不同来源。例如下面这几个例子:
假设当前用户在:https://example.com :
[✅] https://example.com/test -> 同域
[❌] https://m.example.com -> 不同域
[❌] https://example.com:3000 -> 端口不同
[❌] http://example.com -> 通讯协议不同
理解什么是跨域了,那为什么浏览器要把跨域请求资源拦截掉呢?
其实这是考虑到用户的信息安全。
假设小黑是一个恶意开发者,他编写的网站会尝试通过 XHR 打向百度、微博等目标网站;如果使用者原先就有目标网站的登录状态,小黑便能窥探他的隐私,得到不该取得的数据。再想想看,如果目标网站换成 Email、银行、电商,如果没有浏览器限制跨域请求的保护,恶意开发者便能为所欲为。
❝注意:跨域请求虽然会被浏览器拦截下来,但拦截的是响应(Response)而不是请求(Request)。
❞
解决方案
关于跨域请求的解决方案有很多,例如 JSONP,也就是通过 HTML 中没有跨域限制的标签如 img
、script
等,再通过指定回调函数,将响应的内容介接回 JavaScript 中;或是通过 iframe
,绕过跨域保护获取目标资源等。下面仅说明两种常见也相对正规的解决方式。
CORS
最标准、正确的解决方法是通过 W3C 规范 的“ 跨域资源共享(Cross-Origin Resource Sharing ,CORS)”,通过服务器在 HTTP 头中的设置,可以使浏览器能够获取不同来源的资源。
CORS 规范中,清楚定义了跨域存取控制的运作方式。
首先服务器端需要在响应头中加上如 Access-Control-Allow-Origin
、Access-Control-Request-Method
、Access-Control-Request-Headers
等设定,来限制服务器所能接受的来源、请求的方法、可携带的头等等。
当浏览器发送资源请求时,如果是简单请求便会直接送出请求;若不符合前述条件,则会通过预检(Preflighted)请求先敲敲门,确认是否可以通过服务器的限制,然后才会发送正式的请求。
❝CORS 除了上述內容外,也有关于 Cookies 的传送方式,如何允许跨域写入 Cookies 等内容。
❞
代理服务器
由于 CORS 的头设置是在服务器端,如果服务器是自己的,那么可以轻易的调整服务器设置,让前端能取得必要的资源;但如果你请求的是外部 API,总不能每次遇到 CORS 错误,就要求别人去修改头设置吧。
简单暴力的方法就是通过代理服务器帮我们获取资源;由于跨域保护的限制是浏览器的规范,只要不通过浏览器发送请求,自然也就不会有限制。
常见的作法是通过 nginx
做简单的反向代理;例如在自己的开发环境,前后端分离的架构,前后端服务分别启动在 3000 和 5000 端口,则可以用这样的配置:
server{
listen 3000;
server_name localhost;
location ^~ /api {
proxy_pass http://localhost:5000;
}
}
当前端需要发送 API 请求时,可以直接请求 localhost:3000/api/...
,这个请求会被 nginx
拦截,并转发给后端所在的 localhost:5000
,这样就能简单的绕过跨域保护了。
总结
跨域是前端常见的需求,CORS 的错误信息也是我们很容易被卡住的地方;其实只要清楚 CORS 规范中的 HTTP 头设置,并在服务器端做对应的调整,就可以顺利的完成跨域请求。
点分享
点收藏
点点赞
点在看
本文分享自微信公众号 - 前端先锋(jingchengyideng)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Sentinel断路器与熔断降级
前言 Sentinel的熔断降级通过断路器实现,本文通过介绍熔断器的定义、如何构建熔断器、断路器校验逻辑、断路器状态转换、异常/慢调用熔断流量是如何统计等方面梳理断路器的工作原理和实现方式。 一、断路器定义 Sentinel中的熔断降级使用断路器实现,先看下断路器概念,来自维基百科的定义: 断路器有分简单与较进阶的版本,简单的断路器只需要知道服务是否可用。而较进阶的版本比起前者更有效率。进阶的断路器带有至少三个状态:关闭:断路器在预设的情形下是呈现关闭的状态,而断路器本身“带有”计数功能,每当错误发生一次,计数器也就会进行“累加”的动作,到了一定的错误发生次数断路器就会被“开启”,这个时候亦会在内部启用一个计时器,一旦时间到了就会切换成半开启的状态。开启:在开启的状态下任何请求都会“直接”被拒绝并且抛出异常讯息。半开启:在此状态下断路器会允许部分的请求,如果这些请求都能成功通过,那么就意味着错误已经不存在,则会被“切换回”关闭状态并“重置”计数。倘若请求中有“任一”的错误发生,则会回复到“开启”状态,并且重新计时,给予系统一段休息时间 说明:从概念中可以看出断路器的关键点在于统计流量...
- 下一篇
一篇搞定Java集合底层实现原理
Java集合类实现原理 1.Iterable接口 定义了迭代集合的迭代方法 iterator() forEach() 对1.8的Lambda表达式提供了支持 2. Collection接口 定义了集合添加的通用方法 int size(); boolean isEmpty(); boolean contains(); boolean add() boolean addAll() boolean remove() removeAll() Object[] toArray() 3.List接口 元素被添加到集合中以后,取出的时候是按照放入顺序。 List可以重复。 存在下标,可以直接依靠下标取值 E get() E set() E indexOf() int lastIndexOf() ListIterator listIterator() 3.1 ArrayList类 底层是一个Object数组。 transient Object[] elementData; // non-private to simplify nested class access 初始容量为10 private st...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- Linux系统CentOS6、CentOS7手动修改IP地址
- Windows10,CentOS7,CentOS8安装Nodejs环境
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- CentOS7安装Docker,走上虚拟化容器引擎之路
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- CentOS8编译安装MySQL8.0.19
- 2048小游戏-低调大师作品
- CentOS7设置SWAP分区,小内存服务器的救世主
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库