1. 程式人生 > >解決AJAX跨域:1、利用JSONP;2、JS設置Header

解決AJAX跨域:1、利用JSONP;2、JS設置Header

javascrip char 響應 ajax請求 cep foo ace 類型 stat

一、利用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");

解決AJAX跨域:1、利用JSONP;2、JS設置Header