• 解决AJAX跨域:1、利用JSONP;2、JS设置Header


    一、利用JSONP:

      首先来看看在页面中如何使用jQuery的ajax解决跨域问题的简单版:

    $(document).ready(function(){
        var url='http://localhost:8080/WorkGroupManagment/open/getGroupById"
           +"?id=1&callback=?';
        $.ajax({
            url:url,
            dataType:'jsonp',
            processData: false, 
            type:'get',
            success:function(data){
                alert(data.name);
            },
            error:function(XMLHttpRequest, textStatus, errorThrown) {
                alert(XMLHttpRequest.status);
                alert(XMLHttpRequest.readyState);
                alert(textStatus);
            }
        });
    });

      这样写是完全没有问题的,起先error的处理函数中仅仅是alert(“error”),为了进一步弄清楚是什么原因造成了错误,故将处理函数变为上面的实现方式。最后一行alert反馈parsererror问题。百思不得其解,原因是jsonp的格式与json格式有着细微的差别,所以在server端的代码上稍稍有所不同。

      比较一下json与jsonp格式的区别:

      JSON格式:

    {
        "message":"获取成功",
        "state":"1",
        "result":{"name":"工作组1","id":1,"description":"11"}
    }

      JSONP格式:

    callback({
        "message":"获取成功",
        "state":"1",
        "result":{"name":"工作组1","id":1,"description":"11"}
    })

      看出来区别了吧,在url中callback传到后台的参数是什么callback就是什么,jsonp比json外面有多了一层callback()。这样就知道怎么处理它了,于是修改后台代码。

      后台java代码最终如下:

    @RequestMapping(value = "/getGroupById")
    public String getGroupById(@RequestParam("id") Long id, HttpServletRequest request, HttpServletResponse response) throws IOException {
        String callback = request.getParameter("callback");
        ReturnObject result = null;
        Group group = null;
        try {
          group = groupService.getGroupById(id);
          result = new ReturnObject(group, "获取成功", Constants.RESULT_SUCCESS);
        } catch (BusinessException e) {
          e.printStackTrace();
          result = new ReturnObject(group, "获取失败", Constants.RESULT_FAILED);
        }
        String json = JsonConverter.bean2Json(result);
        response.setContentType("text/html");
        response.setCharacterEncoding("utf-8");
        PrintWriter out = response.getWriter();
        out.print(callback + "(" + json + ")");
        return null;
    }

      注意:这里需要先将查询结果转换为json格式,然后用参数callback在json外面再套一层,就变成了jsonp。指定数据类型为jsonp的ajax就可以做进一步处理了。

      虽然这样解决了跨域问题,还是回顾下造成parsererror的原因。原因在于盲目的把json格式的数据当做jsonp格式的数据让ajax处理,造成了这个错误,此时server端代码是这样的:

    @RequestMapping(value = "/getGroupById")
    @ResponseBody
    public ReturnObject getGroupById(@RequestParam("id") Long id,HttpServletRequest request, HttpServletResponse response){
        String callback = request.getParameter("callback");
        ReturnObject result = null;
        Group group = null;
        try{
            group = groupService.getGroupById(id);
            result = new ReturnObject(group, "获取成功", Constants.RESULT_SUCCESS);
        } catch (BusinessException e) {
            e.printStackTrace();
            result = new ReturnObject(group, "获取失败", Constants.RESULT_FAILED);
        }
        return result;
    }
    //错误处理方式

    二、JS设置Header,实现跨域访问:

      受浏览器的同源策略限制,JavaSript只能请求本域内的资源。跨域资源共享(Cross-Origin Resource Sharing, CORS)是为解决Ajax技术难实现跨域问题而提出的一个规范,这个规范试着从根本上解决安全的跨域资源共享问题。在此之前,解决此类问题的途径往往是服务器代理、JSONP等,治标不治本。目前基本所有浏览器都已经支持该规范。

      一个域是由schema、host、port三者共同组成,与路径无关。所谓跨域,是指在http://example-foo.com/域上通过XMLHttpRequest对象调用http://example-bar.com/域上的资源。CORS约定服务器端和浏览器在HTTP协议之上,通过一些额外HTTP头部信息,进行跨域资源共享的协商。服务器端和浏览器都必需遵循规范中的要求。

      CORS把HTTP请求分成两类,不同类别按不同的策略进行跨域资源共享协商。

    1. 简单跨域请求。
      当HTTP请求出现以下两种情况时,浏览器认为是简单跨域请求:

    1). 请求方法是GET、HEAD或者POST,并且当请求方法是POST时,Content-Type必须是application/x-www-form-urlencoded, multipart/form-data或着text/plain中的一个值。
    2). 请求中没有自定义HTTP头部。

      对于简单跨域请求,浏览器要做的就是在HTTP请求中添加Origin Header,将JavaScript脚本所在域填充进去,向其他域的服务器请求资源。服务器端收到一个简单跨域请求后,根据资源权限配置,在响应头中添加Access-Control-Allow-Origin Header。浏览器收到响应后,查看Access-Control-Allow-Origin Header,如果当前域已经得到授权,则将结果返回给JavaScript,否则浏览器忽略此次响应。

    2. 带预检(Preflighted)的跨域请求。
      当HTTP请求出现以下两种情况时,浏览器认为是带预检(Preflighted)的跨域请求:

    1). 除GET、HEAD和POST(only with application/x-www-form-urlencoded, multipart/form-data, text/plain Content-Type)以外的其他HTTP方法。
    2). 请求中出现自定义HTTP头部。

      带预检(Preflighted)的跨域请求需要浏览器在发送真实HTTP请求之前先发送一个OPTIONS的预检请求,检测服务器端是否支持真实请求进行跨域资源访问,真实请求的信息在OPTIONS请求中通过Access-Control-Request-Method Header和Access-Control-Request-Headers Header描述,此外与简单跨域请求一样,浏览器也会添加Origin Header。服务器端接到预检请求后,根据资源权限配置,在响应头中放入Access-Control-Allow-Origin Header、Access-Control-Allow-Methods和Access-Control-Allow-Headers Header,分别表示允许跨域资源请求的域、请求方法和请求头。此外,服务器端还可以加入Access-Control-Max-Age Header,允许浏览器在指定时间内,无需再发送预检请求进行协商,直接用本次协商结果即可。浏览器根据OPTIONS请求返回的结果来决定是否继续发送真实的请求进行跨域资源访问。这个过程对真实请求的调用者来说是透明的。

      XMLHttpRequest支持通过withCredentials属性实现在跨域请求携带身份信息(Credential,例如Cookie或者HTTP认证信息)。浏览器将携带Cookie Header的请求发送到服务器端后,如果服务器没有响应Access-Control-Allow-Credentials Header,那么浏览器会忽略掉这次响应。

      这里讨论的HTTP请求是指由Ajax XMLHttpRequest对象发起的,所有的CORS HTTP请求头都可由浏览器填充,无需在XMLHttpRequest对象中设置。以下是CORS协议规定的HTTP头,用来进行浏览器发起跨域资源请求时进行协商:
    1. Origin。HTTP请求头,任何涉及CORS的请求都必需携带。
    2. Access-Control-Request-Method。HTTP请求头,在带预检(Preflighted)的跨域请求中用来表示真实请求的方法。
    3. Access-Control-Request-Headers。HTTP请求头,在带预检(Preflighted)的跨域请求中用来表示真实请求的自定义Header列表。
    4. Access-Control-Allow-Origin。HTTP响应头,指定服务器端允许进行跨域资源访问的来源域。可以用通配符*表示允许任何域的JavaScript访问资源,但是在响应一个携带身份信息(Credential)的HTTP请求时,Access-Control-Allow-Origin必需指定具体的域,不能用通配符。
    5. Access-Control-Allow-Methods。HTTP响应头,指定服务器允许进行跨域资源访问的请求方法列表,一般用在响应预检请求上。
    6. Access-Control-Allow-Headers。HTTP响应头,指定服务器允许进行跨域资源访问的请求头列表,一般用在响应预检请求上。
    7. Access-Control-Max-Age。HTTP响应头,用在响应预检请求上,表示本次预检响应的有效时间。在此时间内,浏览器都可以根据此次协商结果决定是否有必要直接发送真实请求,而无需再次发送预检请求。

    8. Access-Control-Allow-Credentials。HTTP响应头,凡是浏览器请求中携带了身份信息,而响应头中没有返回Access-Control-Allow-Credentials: true的,浏览器都会忽略此次响应。

      总结:只要是带自定义header的跨域请求,在发送真实请求前都会先发送OPTIONS请求,浏览器根据OPTIONS请求返回的结果来决定是否继续发送真实的请求进行跨域资源访问。所以复杂请求肯定会两次请求服务端。

      js 端的ajax请求:

    $.ajax({  
       url: "http://test.com",  
       dataType: 'json',  
       type: 'GET',  
       beforeSend: function (xhr) {  
           xhr.setRequestHeader("Test", "testheadervalue");  
       },  
       async: false,  
       cache: false,  
       //contentType: 'application/x-www-form-urlencoded',  
       success: function (sResponse) {  
       }  
    });  

      服务端的action:

    //允许跨域访问  
    HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*");  
    HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS,DELETE,PUT");  
    HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "Test");

    总结:

    1、JSONP格式:(1)url加:&callback=?;type变为"jsonp";(2)服务器端返回数据要为jsonp格式,即:out.print(callback + "(" + json + ")");要套一层callback()

    2、服务器Action层增加Header头信息

  • 相关阅读:
    k3 cloud中获取自己开发的单据
    k3 cloud列表中出现很多空白
    k3 cloud支付申请单下推付款单时候提示未将对象引用设置到对象的实例
    k3 cloud出现应收单下推收款单,把收款单是结算方式修改成银行承兑汇票之后保存提示:收款单明细中结算方式为票据业务的实收金额之和不等于票据的当前占用金额之和,请检查数据!
    k3 cloud中出现合计和汇总以后没有显示出来,合价要新增一行以后才出现值
    共享打印机
    k3 cloud总账凭证点击保存的时候提示未将对象引用到对应的实例
    k3 cloud成本调整单引入单据后,再做出库成本核算。成本调整单列表已审核的单据消失,非已审核的单据还在,这是出库成本核算设置参数的问题吗?
    K3 cloud选单时候必须把必录的数据录完以后才可以选单
    k3 cloud成本调整单提示期末余额不存在调整单分录的维度,请先出库核算确认是否存在核算维度的数据
  • 原文地址:https://www.cnblogs.com/goloving/p/7650248.html
Copyright © 2020-2023  润新知