我经过这篇文章把今天工做中遇到的HTTP跨域和OPTION请求的一个坑记录下来。javascript
场景是我须要在部署在域名a的Web应用里用JavaScript去消费一个部署在域名b的服务器上的服务。域名b上的服务也是我开发的,所以我将域名a加到了该服务的HTTP响应结构的头文件里,这样就容许了域名a上的JavaScript代码用AJAX访问域名b的服务。html
域名b上的服务是一个Servlet,容许域名a跨域访问的代码就一行:前端
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 作业务逻辑 response.setHeader("Access-Control-Allow-Origin", "域名a"); }
我在域名a的Web应用里用AJAX发起服务请求:java
执行后,发现并无显示200的弹出窗口。跨域
错误消息:Request header field Authorization is not allowed by Access-Control-Allow-Headers in preflight response.浏览器
观察Chrome开发者工具,发现其实域名b的服务已经成功执行了,确实返回了200的Status code,安全
并且我已经从Chrome开发者工具里观察到浏览器已经成功接到域名b发送回来的请求了。服务器
那这个错误是什么鬼呢?根据错误消息“Request header field Authorization is not allowed by Access-Control-Allow-Headers in preflight response” Google了一下,发现一些朋友遇到一样的问题:app
1. 如何解决出现AXIOS的Request header field Content-Type is not allowed by Access-Control-Allow-Headers in preflight response.cors
网页地址: http://www.javashuo.com/article/p-xcprhdot-gt.html
这位朋友的解决方案:
response.setHeader("Access-Control-Allow-Origin", "*"); response.setHeader("Access-Control-Allow-Credentials", "true"); response.setHeader("Access-Control-Allow-Methods", "*"); response.setHeader("Access-Control-Allow-Headers", "Content-Type,Access-Token"); response.setHeader("Access-Control-Expose-Headers", "*"); if (request.getMethod().equals("OPTIONS")) { HttpUtil.setResponse(response, HttpStatus.OK.value(), null); return; }
但我试过,在个人场景下仍是不工做,由于个人例子里,服务器已经针对OPTIONS请求返回HTTP 200的状态码了。
2. 这个Stackoverflow的帖子里,不少朋友都提供了本身的解决方案。
我一一试过,在个人场景里都不能工做。
因而我查询了Mozilla的一篇文档:HTTP访问控制(CORS)
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS
里面谈到了,在某些状况下,浏览器在发起“须要预检的请求”以前,必须首先发起一个“预检请求(Preflight)”到服务器,以探测服务器是否容许这个实际请求。"预检请求"机制的使用,是为了不跨域请求对服务器的用户数据产生未预期的影响。
那么哪些请求算做“须要预检的请求”呢?Mozilla的这篇文档定义得很清楚:
当请求知足下述任一条件时,即应首先发送预检请求:
我再检查个人代码,由于我在HTTP请求里用xhr.setRequestHeader("Authorization", "用户名:密码的base64编码" )添加了用于Basic Authentication的头部,所以迫使该请求成为了“须要预检的请求”,因此才有了OPTION请求的发送。
如今我将其注释掉:
此次遇到了401 Unauthorized错误了:
然而没有预检请求OPTION发出来了,请求类型变成了我指望的POST方式了。
可是如今就陷入了一个矛盾的境地:若是在请求头部加上Basic Authentication的信息,会遇到错误消息“Request header field Authorization is not allowed by Access-Control-Allow-Headers in preflight response.”。若是去掉,虽然避免了预检请求,可是又遇到401 Unauthorized错误了。
因而,我换了一种认证方式,终于成功实现了指望的跨域请求,在我域名a的前端应用里打印出了来自于域名b的服务的响应。
我使用了form认证方式,这种方式不会形成该请求成为一个”须要预检的请求“,因此最后跨域成功了。
var formData = new FormData(); formData.append('sap-client', "001"); formData.append('sap-user', "用户名"); formData.append('sap-password', "用户密码"); var request = new XMLHttpRequest(); request.open("POST", "域名b的url",false); request.send(formData); alert("response: " + request.responseText);
但愿个人这个踩坑经历对你们有点帮助。
要获取更多Jerry的原创技术文章,请关注公众号"汪子熙"或者扫描下面二维码: