下班后……
晓清(前端工程师)和小平(后端工程师)那边聚集了好几个人。我处于好奇心,走过去看了看。看了一会,了解到晓清在给后端发送get请求的时候,页面一切正常;但是发送post请求的时候,会预先发送一个option请求。结果option请求被后台某框架拦截了,无法进行跨域,那么后台的post请求也就无法进行。这个问题导致他们进行了长时间的资料搜索和测试。直到现在还没有好的解决方案。(我也是刚刚才想通,有机会的话,明天再告诉他们了。
跨域跨域资源共享
跨域资源共享 俗称CORS,最好的资料在这里
ps:(搜索资料的时候,正好看到了阮一峰老师的博客——跨域资源共享 CORS 详解。我看了一下,和官方文档基本上一致,只是他的博客看起来很舒服,而且语言也变得简单一些。但其实里面是有错误的。具体哪里的话有问题,我就不指明了。所以之前团队里面有人说阮一峰是全才,我内心是不赞同的。他只是一个很普通的人。把一些入门的知识点整理的很好罢了。不过我内心也非常尊敬他,他翻译的书和一些博客文集写得很有意思。)
如何引起跨域的问题,我就不说了。现在要整理的是跨域之后,如何解决?
跨域之后
浏览器的同源策略,会导致跨域。跨域之后,浏览器还是会发送请求给服务器。(并不是之前某个年轻后端工程师说的浏览器直接拦截,当时很生气哇!!!后端明明不懂还不虚心听或者去查一下,硬要自我想象。)
这时候浏览器发送的请求会分为俩种:简单请求(simple request)和非简单请求(not-so-simple request)。
简单请求
1、某些请求不会触发 CORS 预检请求。本文称这样的请求为“简单请求”,
若请求满足所有下述条件,则该请求可视为“简单请求”:
1 | 使用下列方法之一: |
非简单请求
与前述简单请求不同,非简单请求要求必须首先使用 OPTIONS方法发起一个预检请求到服务器,以获知服务器是否允许该实际请求。”预检请求“的使用,可以避免跨域请求对服务器的用户数据产生未预期的影响。
当请求满足下述任一条件时,即应首先发送预检请求:
1 | * 使用了下面任一 HTTP 方法: |
俩种请求对比
简单请求
1 | GET /resources/public-data/ HTTP/1.1 |
第 1~10 行是请求首部。第10行 的请求首部字段 Origin 表明该请求来源于 http://foo.exmaple。
第 13~22 行是来自于 http://bar.other 的服务端响应。响应中携带了响应首部字段 Access-Control-Allow-Origin(第 16 行)。使用 Origin 和 Access-Control-Allow-Origin 就能完成最简单的访问控制。本例中,服务端返回的 Access-Control-Allow-Origin: * 表明,该资源可以被任意外域访问。如果服务端仅允许来自 http://foo.example 的访问,该首部字段的内容如下:
Access-Control-Allow-Origin: http://foo.example
现在,除了 http://foo.example,其它外域均不能访问该资源(该策略由请求首部中的 ORIGIN 字段定义,见第10行)。Access-Control-Allow-Origin 应当为 * 或者包含由 Origin 首部字段所指明的域名。
非简单请求
1 | OPTIONS /resources/post-here/ HTTP/1.1 |
浏览器检测到,从 JavaScript 中发起的请求需要被预检。从上面的报文中,我们看到,第 1~12 行发送了一个使用 OPTIONS 方法的“预检请求”。 OPTIONS 是 HTTP/1.1 协议中定义的方法,用以从服务器获取更多信息。该方法不会对服务器资源产生影响。 预检请求中同时携带了下面两个首部字段:
1 | Access-Control-Request-Method: POST |
首部字段 Access-Control-Request-Method 告知服务器,实际请求将使用 POST 方法。首部字段 Access-Control-Request-Headers 告知服务器,实际请求将携带两个自定义请求首部字段:X-PINGOTHER 与 Content-Type。服务器据此决定,该实际请求是否被允许。
第1426 行为预检请求的响应,表明服务器将接受后续的实际请求。重点看第 1720 行:
1 | Access-Control-Allow-Origin: http://foo.example |
首部字段 Access-Control-Allow-Methods 表明服务器允许客户端使用 POST, GET 和 OPTIONS 方法发起请求。该字段与 HTTP/1.1 Allow: response header 类似,但仅限于在需要访问控制的场景中使用。
首部字段 Access-Control-Allow-Headers 表明服务器允许请求中携带字段 X-PINGOTHER 与 Content-Type。与 Access-Control-Allow-Methods 一样,Access-Control-Allow-Headers 的值为逗号分割的列表。
最后,首部字段 Access-Control-Max-Age 表明该响应的有效时间为 86400 秒,也就是 24 小时。在有效时间内,浏览器无须为同一请求再次发起预检请求。请注意,浏览器自身维护了一个最大有效时间,如果该首部字段的值超过了最大有效时间,将不会生效。
发送完预检请求之后,通过服务器返回的字段,浏览器将进行下一步的POST请求。
当然如果服务器返回的字段不允许,页面就会报错,引起问题。
上面所说的就是后端服务器某框架截取了OPTION请求,返回的字段中不允许进行请求。所以导致报错。
修改方案
1、让前端和后端处于同一个域名,不引起跨域。(线上环境不允许)
2、通过查看框架设置,修改前端发送给后端的OPTION请求,是的前端允许跨域。
3、前端不要引起非简单请求。发送post的时候,发送简单请求就好了。