Ajax 跨域请求详解

来源:互联网 发布:touchslide.js 编辑:程序博客网 时间:2024/06/02 04:37

问题描述:
实际开发中,我们经常会看到这样的错误提示:
XMLHttpRequest cannot load http://…… No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘null’ is therefore not allowed access.
其实这就是ajax跨域请求失败的错误。这里小编整理几种解决方案供大家一起学习。

出现跨域请求错误的原因:
域英文叫DOMAIN——域(Domain)是Windows网络中独立运行的单位,域之间相互访问则需要建立信任关系(即Trust Relation)。信任关系是连接在域与域之间的桥梁。当一个域与其他域建立了信任关系后,2个域之间不但可以按需要相互进行管理,还可以跨网分配文件和打印机等设备资源,使不同的域之间实现网络资源的共享与管理,以及相互通信和数据传输。
域既是 Windows 网络操作系统的逻辑组织单元,也是Internet的逻辑组织单元,在 Windows 网络操作系统中,域是安全边界。域管理员只能管理域的内部,除非其他的域显式地赋予他管理权限,他才能够访问或者管理其他的域,每个域都有自己的安全策略,以及它与其他域的安全信任关系。
说到这里就不得不说同源策略:同源策略,它是由Netscape提出的一个著名的安全策略。现在所有支持JavaScript 的浏览器都会使用这个策略。所谓同源是指,域名,协议,端口相同。eg:当一个浏览器的两个tab页中分别打开百度和谷歌的页面时,百度页执行脚本的时候会检查这个脚本是属于哪个页面的,即检查是否同源,只有和百度同源的脚本才会被执行。

解决方案:
1. 通过 jsonp 解决请求跨域问题
2. 通过 ServletRequest 中 setHeader 方法解决函数请求跨域问题

具体实例:
首先,我们先写出一般情况下的 ajax 请求效果。

案例结构
这里写图片描述

html代码

<html><head>    <meta charset="UTF-8">    <title>Demo</title>    <script type="text/javascript" src="jquery-1.12.4.min.js"></script>    <script type="text/javascript">            $(function () {                $.ajax({                      type : "get",                      async:false,                      url : "http://localhost:8080/Ajax/Service",                                success : function(data){                          $(".showcontent").text("name:"+data.name+" | sex:"+data.sex);                      },                      error:function(){                          alert('fail');                      }                  })            })      </script></head><body>    <div class="showcontent" style="margin: auto;border: 1px solid red;width:300px;height:250px;padding:30px;"></div></body></html>

java代码

package com.idol.controller;import java.io.IOException;import java.io.PrintWriter;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class Service extends HttpServlet {    @Override    protected void doGet(HttpServletRequest req, HttpServletResponse resp)            throws ServletException, IOException {        doPost(req, resp);    }    @Override    protected void doPost(HttpServletRequest req, HttpServletResponse resp)            throws ServletException, IOException {        req.setCharacterEncoding("UTF-8");        resp.setContentType("application/json;charset=UTF-8"); //定义响应类型,防止ie下载json文件        String json = "{\"name\":\"idol\",\"sex\":\"male\"}"; //当自定义 json 对象时需要注意:除 boolean 和 int 类型的值外,其他参数类型必须均为字符串类型,故需用 \" 符号包裹。        PrintWriter out = resp.getWriter();        try {            out.print(json);            out.flush();            out.close();        } catch (Exception e) {            e.printStackTrace();        }           }   }

web.xml

<?xml version="1.0" encoding="UTF-8"?><web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">  <display-name>Ajax</display-name>  <servlet>    <servlet-name>Service</servlet-name>    <servlet-class>com.idol.controller.Service</servlet-class>  </servlet>  <servlet-mapping>    <servlet-name>Service</servlet-name>    <url-pattern>/Service</url-pattern>  </servlet-mapping></web-app>

页面显示如下内容:

name:idol | sex:male

通常情况下我们通过请求拿到的 json 数据是这样的:

{"name":"idol","sex":"male"}

而 jsonp 格式的数据是这样的:

callback({"name":"idol","sex":"male"})

类似于被一个回调函数包裹着一样。
A . jsonp 解决方案:
首先,通过 jsonp 去处理 ajax 请求跨域问题的时候,无论是通过 jQuery 还是原生的 JS 我们都需要对 java 代码中的 PrintWriter 输出的参数进行设置。它的参数不再是以前的 json 格式的数据,取而代之的是一个回调函数。

I . 使用 javascript 完成跨域请求

java代码

package com.idol.controller;import java.io.IOException;import java.io.PrintWriter;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class Service extends HttpServlet {    @Override    protected void doGet(HttpServletRequest req, HttpServletResponse resp)            throws ServletException, IOException {        doPost(req, resp);    }    @Override    protected void doPost(HttpServletRequest req, HttpServletResponse resp)            throws ServletException, IOException {        req.setCharacterEncoding("UTF-8");        resp.setContentType("application/json;charset=UTF-8");//定义响应类型,防止ie下载json文件        String json = "{\"name\":\"idol\",\"sex\":\"male\"}";         /*当自定义 json 对象时需要注意:除 boolean 和 int 类型的值外,        其他参数类型必须均为字符串类型,故需用 \" 符号包裹。*/        String back = req.getParameter("callback");          /*注意,以此 URL 为例:        http://localhost:8081/Ajax/Service?back=callback        在原生 js 中,getParameter() 的参数必须与等号前的参数相同,        等号后的参数必须与 js 中定义的 callback 函数同名;        */        PrintWriter out = resp.getWriter();        try {            out.print(back+"("+json+")");            out.flush();            out.close();        } catch (Exception e) {            e.printStackTrace();        }           }   }

html代码

<html><head>    <meta charset="UTF-8">    <title>Demo</title>    <script type="text/javascript" src="jquery-1.12.4.min.js"></script>     </head><body>    <pre class="showcontent"></pre>    <script type="text/javascript">        function callback (data) {            $(".showcontent").text("name:"+data.name+" | sex:"+data.sex)          }    </script>    <script type="text/javascript" src="http://localhost:8081/Ajax/Service?back=callback"></script></body></html>

PS:
1. 此时的 html 文件是在桌面新建的,并没有与 servlet 在同一项目中

这里写图片描述
-

<script type="text/javascript" src="http://localhost:8081/Ajax/Service?back=callback">

jsonp 的引入必须在 <script type="text/javascript">……</script>下。
- <script type="text/javascript">……</script>中 callback 函数不可包裹在其他函数内,包括window.onload=function () {}函数中。

II . 使用 JQuery 完成跨域请求

java 代码同 JavaScript 部分

html 代码

<html><head>    <meta charset="UTF-8">    <title>Demo</title>    <script type="text/javascript" src="jquery-1.12.4.min.js"></script>    <script type="text/javascript">        $(function () {            $.ajax({                  type : "get",                  async:false,                  url : "http://localhost:8081/Ajax/Service",                  dataType : "jsonp",//数据类型为jsonp                  jsonp: "callback",//服务端用于接收callback调用的function名的参数                  success : function(data){                      $(".showcontent").text("name:"+data.name+" | sex:"+data.sex);                  },                  error:function(){                      alert('fail');                  }              });         })          </script></head><body>    <div class="showcontent"></div></body></html>

当然 jQuery 中除了 ajax 函数还可以通过 getJSON 函数进行请求。具体代码如下:

<html><head>    <meta charset="UTF-8">    <title>Demo</title>    <script type="text/javascript" src="jquery-1.12.4.min.js"></script>    <script type="text/javascript">        $(function () {            $.getJSON("http://localhost:8081/Ajax/Service?callback=?",function(data){                $(".showcontent").text("name:"+data.name+" | sex:"+data.sex);            });                 })          </script></head><body>    <div class="showcontent"></div></body></html>

B . java servlet 中的解决方案:
I . 在 servlet 中改

java 代码

package com.idol.controller;import java.io.IOException;import java.io.PrintWriter;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class Service extends HttpServlet {    @Override    protected void doGet(HttpServletRequest req, HttpServletResponse resp)            throws ServletException, IOException {        doPost(req, resp);    }    @Override    protected void doPost(HttpServletRequest req, HttpServletResponse resp)            throws ServletException, IOException {        req.setCharacterEncoding("UTF-8");        resp.setHeader("Access-Control-Allow-Origin", "*");  //设置可以访问的 URL        resp.setHeader("Access-Control-Allow-Methods","GET,POST");  //设置访问的方式        resp.setContentType("text/html;charset=UTF-8"); //定义响应类型,防止ie下载json文件        String json = "{\"name\":\"idol\",\"sex\":\"male\"}"; //当自定义 json 对象时需要注意:除 boolean 和 int 类型的值外,其他参数类型必须均为字符串类型,故需用 \" 符号包裹。        PrintWriter out = resp.getWriter();        try {            out.print(json);            out.flush();            out.close();        } catch (Exception e) {            e.printStackTrace();        }       }   }

html 代码

<html><head>    <meta charset="UTF-8">    <title>Demo</title>    <script type="text/javascript" src="jquery-1.12.4.min.js"></script>    <script type="text/javascript">        $(function () {            $.ajax({                  type : "get",                  async:false,                  url : "http://localhost:8081/Ajax/Service",                        success : function(data){                      var json = eval("("+data+")");                    $(".showcontent").text("name:"+json.name+" | sex:"+json.sex);                  },                  error:function(){                      alert('fail');                  }              });         })    </script></head><body>    <div class="showcontent"></div></body></html>

II . 设置过滤器
Java代码

package com.ev_image.servlet;import java.io.IOException;import javax.servlet.Filter;import javax.servlet.FilterChain;import javax.servlet.FilterConfig;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.http.HttpServletResponse;public class CorsFilter implements Filter {    @Override    public void destroy() {        // TODO Auto-generated method stub    }    @Override    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,      ServletException {      // TODO Auto-generated method stub          HttpServletResponse resp = (HttpServletResponse) response;          resp.setContentType("text/html;charset=UTF-8");          resp.setHeader("Access-Control-Allow-Origin", "*");          resp.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");          resp.setHeader("Access-Control-Max-Age", "0");          resp.setHeader("Access-Control-Allow-Headers", "Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With,userId,token");          resp.setHeader("Access-Control-Allow-Credentials", "true");          resp.setHeader("XDomainRequestAllowed","1");         chain.doFilter(request, response);      }      @Override    public void init(FilterConfig arg0) throws ServletException {        // TODO Auto-generated method stub    }}

web.xml

  <filter>    <filter-name>corsfilter</filter-name>    <filter-class>com.ev_image.servlet.CorsFilter</filter-class>  </filter>  <filter-mapping>    <filter-name>corsfilter</filter-name>    <url-pattern>/*</url-pattern>  </filter-mapping>

html 代码同上。

0 0