Ajax的跨域请求数据的问题,一直是前端开发者经常讨论的话题。翻看了很多博客文章,发现很多人认为ajax跨域问题是Ajax本身的一些缺陷导致的,还有人认为这是服务器对Ajax请求的拦截,不过这些认识都是不全面的。其实禁止跨域请求是浏览器本身的一种安全策略——换句话说,其实禁止跨域不是什么ajax缺陷,是浏览器会对JavaScript的跨域请求有一些限制。
一、一些和跨域有关的概念
1.同源策略(same-origin policy)
2.跨域资源共享(Cross-Origin-Resource-Sharing)
二、允许跨域和禁止跨域的具体机制
我们可以写一个简单的例子来说明这个问题,用servlet写一个简单的接口:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { InputStream ipt = request.getInputStream(); try (InputStreamReader isr = new InputStreamReader(ipt); BufferedReader br = new BufferedReader(isr)) { String line = null; StringBuilder sb = new StringBuilder(); while ((line = br.readLine()) != null) { sb.append(line); } System.out.println(sb.toString()); } catch (IOException e) { e.printStackTrace(); } response.getWriter().write("{\"response\": \"response you\"}"); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); }之后把服务跑在127.0.0.1:8080下,之后用浏览器打开网上任意一个网页在控制台里发起对本地服务的ajax请求。很明显这是一个跨域的请求:
var xhr = new XMLHttpRequest(); xhr.open('POST', 'http://127.0.0.1:8080/cors-demo/Cors'); xhr.send('{"request": "hi"}'); xhr.onload = function(e) { var xhr = e.target; console.log(xhr.responseText); }
浏览器控制台很快的返回了这个跨域常见的报错:
XMLHttpRequest cannot load http://127.0.0.1:8080/cors-demo/Cors.
No 'Access-Control-Allow-Origin' header is present on the requested resource.
Origin 'http://write.blog.csdn.net' is therefore not allowed access其实,这个ajax请求是被服务端拿到了的,你是可以在后台的拿到前台send方法里面传入的{"request": "hi"}的,但是在Javascript里面却拿不到服务端的response。这应该怎么办呢?我们可以在服务端加上这句话:
response.addHeader("Access-Control-Allow-Origin", "http://write.blog.csdn.net");
这句话的意思是说,在response头里写入上述信息,允许源为“http://write.blog.csdn.net”的网页发起Ajax请求。之后便可以的发起跨域的ajax请求并正常的接受数据了。
综合前后端代码来看我们可以发现,其实这个request是发到服务端了,response也返回了客户端,但是浏览器会把当前网页的源与Access-Control-Allow-Origin里的url进行比较,如果发现有一样的url,就判定允许跨域访问,否则就无法在Javascript中拿到response信息,就会造成所谓的“禁止跨域访问”的现象。如果你想让一个web接口允许任何源的跨域访问,你就可以把头信息写成:Access-Control-Allow-Origin: * —— “*”表示任意的源。其实浏览在禁止跨域的访问里报的异常也能很好的帮助我们理解这个问题,上文的报错信息的大概意思就是:没有在response的头信息里找到Access-Control-Allow-Origin这个属性,所以源 “****” 没有权限访问 ”*****“ url。
三、为什么jsonp能解决跨域问题
var callback = 'callBkFunc'; this[callback] = function(result) { console.log(result); } var JSONP = document.createElement('script'); JSONP.type = 'text/javascript'; JSONP.src = "http://127.0.0.1:8080/cors-demo/Cors?callback=" + callback; document.getElementsByTagName("head")[0].appendChild(JSONP);大致流程就是声明一个script标签,并把资源路径设为我们想跨域访问的url。在声明一个方法名为“callBkFunc”的方法名,并把其作为一个参数传给后台。
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println(request.getParameter("callback")); String callbackName = request.getParameter("callback"); response.getWriter().write(callbackName + "({\"response\": \"response you\"});"); }在前端通过script标签向后台传了方法名之后,服务端代码用得到的这个方法名构造了一个动态的js脚本——也就是“callBkFunc(args)”。在args写的就是我们想传给前端的数据,之后前端就可以在callBkFunc定义的形参result里拿到后台传输给前端的数据了。