当遇到跨域问题时,不要立即选择复制并尝试。处理前请详细阅读本文。我相信它可以帮助你。
分析前准备:
首先,确保服务器不处理跨域问题。其次,先测试服务器接口是否正常。
图片
网站8080访问服务器接口时,出现跨域问题。那么如何解决呢?接下来我将跨域遇到的各种情况全部列出来,并通过nginx代理来解决(后台也是如此,只要了解原理即可)。
跨域主要涉及4个响应头:
网上很多文章告诉你,直接将这些响应头添加到Nginx中可以解决跨域问题。当然大部分情况都可以解决,但是相信还是有很多情况即使配置正确,仍然会报跨域问题。 。
什么是预检请求? :当发生跨域情况时,浏览器首先询问服务器当前网页的域名是否在服务器的权限列表中,以及可以使用哪些HTTP动词和头信息字段。只有收到肯定回复后,浏览器才会发出正式请求,否则会报错。如下图
图片
开始动手模拟:
Nginx代理端口:22222,配置如下
server { listen 22222; server_name localhost; location / { proxy_pass http://localhost:59200; } }
测试代理是否成功,通过Nginx代理2222端口再次访问接口,可以看到通过代理后接口可以正常访问,如下图。
图片
接下来使用网站8080访问Nginx代理后面的接口地址。错误信息如下↓↓↓
案例一:
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.
图片
通过错误信息(注意红色部分)可以清楚定位错误,表明是预请求。首先会进行CORS机制跨域(一次请求),请求成功后才会发送真正的请求。这样做的目的是确保服务器了解 CORS 标准,以保护不支持 CORS 的旧服务器
从错误信息中我们可以得知预检请求的请求响应头缺失--Allow-。如果有错误,我们只需更改它即可。修改Nginx配置信息如下(红色部分为添加的部分),缺少什么就填什么,非常简单明了
server { listen 22222; server_name localhost; location / { add_header Access-Control-Allow-Origin 'http://localhost:8080'; proxy_pass http://localhost:59200; } }
哈哈,正当我满心欢喜以为自己可以解决的时候,却发现还是报了同样的问题。
图片
不过我们的配置没有任何问题。问题出在 Nginx 上。
图片
该指令用于添加返回标头字段,当且仅当状态代码是图中列出的代码时才有效。如果希望每条响应报文都携带头域信息,则需要在最后添加(经过我的测试,只需要添加--Allow-头信息,其他头信息如果不添加都会被带回) ),然后我们尝试添加它。
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.
图片
从错误信息中可以知道,作为跨域浏览器默认行为的预请求()并没有收到ok状态码。此时配置文件就被修改了。当请求为请求时,会返回一个状态码给浏览器(一般是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.
图片
意思是前置请求响应头中缺少该头信息 --Allow-(每种情况都会不同,发生跨域后,添加的自定义头信息是不允许的,需要添加到请求响应头中--允许-,这样浏览器就知道这个头信息的携带被服务器识别为合法的。知道问题后,再修改配置文件,添加相应的部分,再试一次
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($=''),在进行预检请求时,外部配置就会失效。为什么? ↓↓。
官方文档是这样说的:
可能有。当且仅当该级别上没有这些时,这些才来自该级别。
这意味着当当前级别没有指令时,将继承上一级的指令。相反,如果当前级别存在,则不应该能够继承上一个级别。
图片
配置修改如下:
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版本可能会更新,不知道这条规则是否会被修改。考虑到这样的写法可能会携带两个--Allow-,这种情况是不允许的。 ,下面会提到。所以配置应该修改如下:
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请求,并且--Allow-请求响应头默认只支持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方法。我们需要更改--Allow-的配置(是不是少了什么?我这里只添加了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类型后,--Allow-响应头会自动验证-type请求头,和情况3一样,缺什么就补什么。如果不加-type,会报如下错误。
如果想简单一点,--Allow-和--Allow-可以设置为*,表示都匹配。但不建议将--Allow-设置为*。出于安全考虑,有必要对域名进行限制。
图片
全部添加后,问题解决。这里报的405是我服务器上的接口只打开GET,没有打开PUT。此时我使用PUT方法来请求这个接口,所以接口会返回这个状态码。
图片
案例5:
最后说一下另一种情况,就是后端处理跨域,不需要自己处理(这里投诉一下,有的后端工程师改了服务端代码来解决跨域)域,但他们不明白原理,所以在网上粘贴代码片段可能会导致响应信息处理不完整,例如,如果没有全部添加或添加到正确的点,则。您使用的可能不包含实际使用的项目中可能没有添加请求返回状态码等,导致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.
图片
图片
这意味着此时返回多个--Allow-请求响应头,但只允许一个。这种情况当然要修改配置,去掉--Allow-配置。不过这种情况下,建议配置 Nginx 和 只选择其中之一,自行解决跨域问题。
这里注意,如果按照我上面写的方式,那个--Allow- in if $ = '' 是不能删除的。把!=''里面的删掉就可以了,因为这里如果是预检请求的话,就直接做,该请求不会再转发到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; } }
最后,这是一篇关于解决跨域问题过程的文章。如果您仔细阅读,相信您可以轻松理解并在实际使用中自行解决问题。希望对大家有所帮助。以上内容是我根据自己的测试代码得出的自己的理解。如果我理解有什么不对的地方,请指正。
鱼云提供全球范围的云服务器和物理服务器租赁服务,具备强大的DDoS防御功能,确保您业务安全稳定运行,同时提供灵活定制和专业支持以满足多样化需求。