共计 7623 个字符,预计需要花费 20 分钟才能阅读完成。
当你遇到跨域问题,不要立即就抉择复制去尝试。请具体看完这篇文章再解决,我置信它能帮到你。
筹备
- 前端网站地址:http://localhost:8080
- 服务端网址:http://localhost:59200
首先保障服务端是没有解决跨域的,其次,先用 postman 测试服务端接口是失常的
当网站 8080 去拜访服务端接口时,就产生了跨域问题,那么如何解决?接下来我把跨域遇到的各种状况都列举进去并通过 nginx 代理的形式解决(后盾也是一样的,只有你了解的原理)。
跨域次要波及 4 个响应头
- Access-Control-Allow-Origin 用于设置容许跨域申请源地址(预检申请和正式申请在跨域时候都会验证)
- Access-Control-Allow-Headers 跨域容许携带的非凡头信息字段(只在预检申请验证)
- Access-Control-Allow-Methods 跨域容许的申请办法或者说 HTTP 动词(只在预检申请验证)
- Access-Control-Allow-Credentials 是否容许跨域应用 cookies,如果要跨域应用 cookies,能够增加上此申请响应头,值设为 true(设置或者不设置,都不会影响申请发送,只会影响在跨域时候是否要携带 cookies,然而如果设置,预检申请和正式申请都须要设置)。不过不倡议跨域应用(我的项目中用到过,不过不稳固,有些浏览器带不过来),除非必要,因为有很多计划能够代替。
网上很多文章都是通知你间接 Nginx 增加这几个响应头信息就能解决跨域,当然大部分状况是能解决,然而我置信还是有很多状况,明明配置上了,也同样会报跨域问题。
什么是预检申请?
当产生跨域条件时候,览器先询问服务器,以后网页所在的域名是否在服务器的许可名单之中,以及能够应用哪些 HTTP 动词和头信息字段。只有失去必定回答,浏览器才会收回正式的 XMLHttpRequest 申请,否则就报错。如下图
模仿
Nginx 代理端口:22222 , 配置如下
server {
listen 22222;
server_name localhost;
location / {proxy_pass http://localhost:59200;}
}
测试代理是否胜利,通过 Nginx 代理端口 2222 再次拜访接口,能够看到如下图通过代理后接口也是能失常拜访
接下来开始用网站 8080 拜访 Nginx 代理后的接口地址,报错状况如下↓↓↓
状况 1
Access to XMLHttpRequest at 'http://localhost:22222/api/Login/TestGet' from origin 'http://localhost:8080' 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.
通过错误信息能够很清晰的定位到谬误(留神看标红局部)priflight 阐明是个预申请,CORS 机制跨域会首先进行 preflight(一个 OPTIONS 申请),该申请胜利后才会发送真正的申请。这一设计旨在确保服务器对 CORS 规范知情,以爱护不反对 CORS 的旧服务器
通过错误信息,咱们能够失去是预检申请的申请响应头短少了 Access-Control-Allow-Origin,错哪里,咱们改哪里就好了。批改 Nginx 配置信息如下(红色局部为增加局部),缺什么就补什么,很简单明了
server {
listen 22222;
server_name localhost;
location / {
add_header Access-Control-Allow-Origin 'http://localhost:8080';
proxy_pass http://localhost:59200;
}
}
哈哈,当满怀欢喜的认为能解决后,发现还是报了同样的问题
不过咱们的配置没什么问题, 问题在 Nginx, 下图链接 http://nginx.org/en/docs/http…
add_header
指令用于增加返回头字段,当且仅当状态码为图中列出的那些时无效。如果想要每次响应信息都携带头字段信息,须要在最初增加 always(经我测试,只有 Access-Control-Allow-Origin 这个头信息须要加 always,其余的不加 always 也会携带回来),那咱们加上试试
server {
listen 22222;
server_name localhost;
location / {
add_header Access-Control-Allow-Origin 'http://localhost:8080' always;
proxy_pass http://localhost:59200;
}
}
批改了配置后,发现失效了,当然不是跨域就解决了,是下面这个问题曾经解决了,因为报错内容曾经变了
状况 2
Access to XMLHttpRequest at 'http://localhost:22222/api/Login/TestGet' from origin 'http://localhost:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
通过报错信息提醒能够得悉,是跨域浏览器默认行为的预申请(option 申请)没有收到 ok 状态码,此时再批改配置文件,当申请为 option 申请时候,给浏览器返回一个状态码(个别是 204)
server {
listen 22222;
server_name localhost;
location / {
add_header Access-Control-Allow-Origin 'http://localhost:8080' always;
if ($request_method = 'OPTIONS') {return 204;}
proxy_pass http://localhost:59200;
}
}
当配置完后,发现报错信息变了
状况 3
Access to XMLHttpRequest at 'http://localhost:22222/api/Login/TestGet' from origin 'http://localhost:8080' has been blocked by CORS policy: Request header field authorization is not allowed by Access-Control-Allow-Headers in preflight response.
意思就是预申请响应头 Access-Control-Allow-Headers 中短少头信息 authorization(各种状况会不一样,在产生跨域后,在自定义增加的头信息是不容许的,须要增加到申请响应头 Access-Control-Allow-Headers 中,以便浏览器晓得此头信息的携带是服务器抵赖非法的,我这里携带的是 authorization,其余的可能是 token 之类的,缺什么加什么),晓得了问题所在,而后批改配置文件,增加对应短少的局部,再试试
server {
listen 22222;
server_name localhost;
location / {
add_header Access-Control-Allow-Origin 'http://localhost:8080' always;
if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Headers 'authorization'; #为什么写在 if 外面而不是接着 Access-Control-Allow-Origin 往下写?因为这里只有预检申请才会查看
return 204;
}
proxy_pass http://localhost:59200;
}
}
此时发现报错问题又回到了状况 1
经测试验证,只有if ($request_method = 'OPTIONS')
外面写了 add_header
,当为预检申请时内部配置的都会生效,为什么?↓↓。
官网文档是这样说的:
There could be several add_header directives. These directives are inherited from the previous level if and only if there are no add_header directives defined on the current level.
意思就是以后层级无 add_header 指令时,则继承上一层级的 add_header。相同的若以后层级有了 add_header,就应该无奈继承上一层的 add_header。
配置批改如下:
server {
listen 22222;
server_name localhost;
location / {
add_header Access-Control-Allow-Origin 'http://localhost:8080' always;
if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin 'http://localhost:8080';
add_header Access-Control-Allow-Headers 'authorization';
return 204;
}
proxy_pass http://localhost:59200;
}
}
此时改完发现跨域问题曾经解决了,
不过以上尽管解决了跨域问题,然而思考前期可能 Nginx 版本更新, 不晓得这个规定会不会被批改,思考到这样的写法可能会携带上两个 Access-Control-Allow-Origin,这种状况也是不容许的,上面会说到。所以配置适当批改如下:
server {
listen 22222;
server_name localhost;
location / {if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin 'http://localhost:8080';
add_header Access-Control-Allow-Headers 'authorization';
return 204;
}
if ($request_method != 'OPTIONS') {add_header Access-Control-Allow-Origin 'http://localhost:8080' always;}
proxy_pass http://localhost:59200;
}
}
还没完,持续聊 ↓↓
状况 4:
比拟晚期的 API 可能只用到了 POST 和 GET 申请,而 Access-Control-Allow-Methods 这个申请响应头跨域默认只反对 POST 和 GET,当呈现其余申请类型时候,同样会呈现跨域异样。
比方,我这里将申请的 API 接口申请形式从原来的 GET 改成 PUT,在发动一次试试。在管制台上会抛出谬误:
Access to XMLHttpRequest at 'http://localhost:22222/api/Login/TestGet' from origin 'http://localhost:8080' has been blocked by CORS policy: Method PUT is not allowed by Access-Control-Allow-Methods in preflight response.
报错内容也讲的很分明,在这个预申请中,PUT 办法是不容许在跨域中应用的,咱们须要改下 Access-Control-Allow-Methods 的配置(缺什么加上么,这里我只加了 PUT,能够本人加全一点),让浏览器晓得服务端是容许的
server {
listen 22222;
server_name localhost;
location / {if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin 'http://localhost:8080';
add_header Access-Control-Allow-Headers 'content-type,authorization';
add_header Access-Control-Allow-Methods 'PUT';# 为这么只加在这个 if 中,不再上面的 if 也加上?因为这里只有预检申请会校验,当然你加上也没事。return 204;
}
if ($request_method != 'OPTIONS') {add_header Access-Control-Allow-Origin 'http://localhost:8080' always;}
proxy_pass http://localhost:59200;
}
}
这里留神一下,改成 PUT 类型后,Access-Control-Allow-Headers 申请响应头又会主动校验 content-type 这个申请头,和状况 3 是一样的,缺啥补啥就行了。如果不加上 content-type,则会报如下谬误。(想简略的话,Access-Control-Allow-Headers 和 Access-Control-Allow-Methods 能够设置为 * , 示意全都匹配。然而 Access-Control-Allow-Origin 就不倡议设置成 * 了,为了平安思考,限度域名是很有必要的。)
都加上后,问题就解决了,这里报 405 是我服务端这个接口只凋谢了 GET,没有凋谢 PUT,而此刻我将此接口用 PUT 办法去申请,所以接口会返回这个状态码。
状况 5:
最初再说一种状况,就是后端解决了跨域,就不须要本人在解决了(这里吐槽下,某些后端工程师本人改服务端代码解决跨域,然而又不了解其中原理,网上轻易找段代码黏贴,导致响应信息可能解决不齐全,如 method 没增加全,headers 没加到点上,本人用的那个可能复制过去的并不蕴含理论我的项目所用到的,没有增加 options 申请返回状态码等,导致 Nginx 再用通用的配置就会可能报以下异样)
Access to XMLHttpRequest at 'http://localhost:22222/api/Login/TestGet' from origin 'http://localhost:8080' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header contains multiple values '*, http://localhost:8080', but only one is allowed.
意思就是此刻 Access-Control-Allow-Origin 申请响应头返回了多个,而只容许有一个,这种状况当然批改配置去掉 Access-Control-Allow-Origin 这个配置就能够了,不过遇到这种状况,倡议 Nginx 配置和服务端本人解决跨域只选其一。(这里留神如果按我下面的写法,if $request_method = ‘OPTIONS’ 这个外面的 Access-Control-Allow-Origin 可不能删除,删除!=’OPTIONS’ 外面的就好了,因为这里如果是预检申请间接就 ruturn 了,申请不会再转发到 59200 服务,如果也删除了,就会报和状况 1 一样的谬误。所以为什么说要不服务端代码层面解决跨域,要不就 Nginx 代理解决,不要混着搞,不然不明确原理的人,网上找一段代码贴就很可能解决不了问题)
再贴一份残缺配置(* 号依据本人‘爱好’填写):
server {
listen 22222;
server_name localhost;
location / {if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin 'http://localhost:8080';
add_header Access-Control-Allow-Headers '*';
add_header Access-Control-Allow-Methods '*';
add_header Access-Control-Allow-Credentials 'true';
return 204;
}
if ($request_method != 'OPTIONS') {
add_header Access-Control-Allow-Origin 'http://localhost:8080' always;
add_header Access-Control-Allow-Credentials 'true';
}
proxy_pass http://localhost:59200;
}
}
或者:
server {
listen 22222;
server_name localhost;
location / {
add_header Access-Control-Allow-Origin 'http://localhost:8080' always;
add_header Access-Control-Allow-Headers '*';
add_header Access-Control-Allow-Methods '*';
add_header Access-Control-Allow-Credentials 'true';
if ($request_method = 'OPTIONS') {return 204;}
proxy_pass http://localhost:59200;
}
}
最初,这是一篇解决跨域遇到问题解决问题的过程,如果认真看完了,我置信应该都能很容易的了解,并且在理论应用中本人解决该问题,心愿能帮忙到大家,以上内容都是本人了解本人测试码进去的,如有了解不对的中央,望大家斧正。
起源:cnblogs.com/fnz0/p/15803011.html
如果本文对你有帮忙的话,欢送 点赞 & 转发,这对我持续分享 & 创作优质文章十分重要。感激🙏🏻