AJAX 跨域解决方案

69 阅读3分钟

简介

JSON是一种轻量级的数据传输格式,被广泛应用于当前Web应用中。JSON格式数据的编码和解析基本在所有主流语言中都被实现,所以现在大部分前后端分离的架构都以JSON格式进行数据的传输。

JavaScript的同源策略:只有协议+主机名+端口号 (如存在)相同,则允许相互访问。也就是说JavaScript只能访问和操作自己域下的资源,不能访问和操作其他域下的资源。比如www.aa.com下的一个页面,其中的ajax请求是不允许访问www.bb.com这样一个页面的。

跨域问题是针对JS和ajax的,html本身(比如a标签、script标签、甚至form标签(可以直接跨域发送数据并接收数据)等)没有跨域问题。

跨域示例:

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2xpYW5naGVjYWk1MjE3MTMxNA==,size_16,color_FFFFFF,t_70.png

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2xpYW5naGVjYWk1MjE3MTMxNA==,size_16,color_FFFFFF,t_70-16655086904703.png

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2xpYW5naGVjYWk1MjE3MTMxNA==,size_16,color_FFFFFF,t_70-16655087082996.png

解决方案一:使用JSONP

ajax请求受同源策略影响,不允许进行跨域请求,而script标签src属性中的链接却可以访问跨域的js脚本,利用这个特性,服务端不再返回JSON格式的数据,而是返回一段调用某个函数的js代码,在src中进行了调用,这样实现了跨域。

JSONP(JSON with Padding)是一个非官方的协议,它允许在服务器端集成Script tags返回至客户端,通过javascript callback的形式实现跨域访问(这仅仅是JSONP简单的实现形式)。JSONP是一种script tag的注入,将server返回的response添加到页面实现特定功能。

示例:

服务器端代码:

 @WebServlet(urlPatterns = "/jsonServlet")
 public class JsonServlet extends HttpServlet {
     @Override
     protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
         PrintWriter out = response.getWriter();
         String cb = request.getParameter("cb");
         out.print(cb+"({"name":"zhangsan","age":19});");
     }
 }

客户端实现一:JavaScript实现:

 <!DOCTYPE html>
 <html>
     <head>
         <meta charset="utf-8" />
         <title></title>
         <script type="text/javascript">  
             function fun(){
                 window.location.href="http://localhost:8080/jsonServlet?cb=jsonpCallback";
             }
             function jsonpCallback(json){   
                 alert("name:" + json.name + " age:" + json.age);
             }   
         </script>
         
         <!--script标签的src指向了另一个域http://localhost:8080/下的jsonServlet请求-->
         <script type="text/javascript" src="http://localhost:8080/qhdfl/jsonServlet?cb=jsonpCallback"></script>
     </head>
     <body>
         <button onclick="fun()">fun</button>
     </body>
 </html>

客户端实现二:JQuery的getJSON实现:

 <!DOCTYPE html>
 <html>
     <head>
         <meta charset="utf-8" />
         <title></title>
         <script src="js/jquery.js"></script>
         <script type="text/javascript">
             $(function() {
                 $.getJSON("http://localhost:8080/qhdfl/jsonServlet?cb=?", function(json) {
                     alert("name:" + json.name + " age:" + json.age);
                 });
             });
         </script>
     </head>
     <body>
     </body>
 </html>

客户端实现三:JQuery的jsonp实现:

 <!DOCTYPE html>
 <html>
     <head>
         <meta charset="utf-8" />
         <title></title>
         <script src="js/jquery.js"></script>
         <script type="text/javascript">  
             $(function(){
                 $.ajax({
                     type:"get",
                     url:"http://localhost:8080/qhdfl/jsonServlet",//跨域URL
                     dataType:"jsonp",  //数据格式设置为jsonp
                     jsonp:"cb",  //自定义参数的名称
                     jsonpCallback:"jsonpCallback",//自定义回调函数名称
                     success:function(res){//成功的回调函数
                         alert("name:" + res.name + " age:" + res.age);
                     }
                 });
             });
             function jsonpCallback(json){   
                 alert(json);
             }   
         </script>
     </head>
     <body>
     </body>
 </html>

jsonp用来自定义参数名称:

  • 不指定该参数时,JQuery会自动传递一个callback参数到后台,成功后,回调success函数。
  • 指定该参数自定义参数名称时,后台需要指定接受通过jsonp冒号传递的函数名称。执行成功后,先执行jsonpCallback定义的回调函数,后执行success函数。

解决方案二:服务器端添加响应头

添加响应头,允许跨域

 response.setHeader("Access-Control-Allow-Origin", "*");
 response.setHeader("Access-Control-Allow-Credentials", "true");
 response.setHeader("Access-Control-Allow-Methods", "POST, GET, PATCH, DELETE, PUT");
 response.setHeader("Access-Control-Max-Age", "3600");
 response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");

服务器端代码:

 @WebServlet(urlPatterns = "/jsonServlet")
 public class JsonServlet extends HttpServlet {
     @Override
     protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
         /**设置响应头允许ajax跨域访问**/
         response.setHeader("Access-Control-Allow-Origin","*");
         /*星号表示所有的异域请求都可以接受,*/
         response.setHeader("Access-Control-Allow-Methods","GET,POST");
         PrintWriter out = response.getWriter();
         out.print("{"name":"zhangsan","age":19}");
     }
 }

客户端代码:

 <!DOCTYPE html>
 <html>
     <head>
         <meta charset="utf-8" />
         <title></title>
         <script src="js/jquery.js"></script>
         <script type="text/javascript">  
             $(function(){
                 $.ajax({
                     type:"get",
                     url:"http://localhost:8080/qhdfl/jsonServlet",//跨域URL
                     dataType:"json",  //数据格式设置为json
                     success:function(res){//成功的回调函数
                         alert("name:" + res.name + " age:" + res.age);
                     }
                 });
             });
         </script>
     </head>
     <body>
     </body>
 </html>

解决方案三:使用代理方式

服务器A的test01.html页面想访问服务器B的后台action,返回“test”字符串,此时就出现跨域请求,浏览器控制台会出现报错提示,由于跨域是浏览器的同源策略造成的,对于服务器后台不存在该问题,可以在服务器A中添加一个代理action,在该action中完成对服务器B中action数据的请求,然后在返回到test01.html页面。