ajax跨域

出於安全方面的考慮,Web瀏覽器中JavaScript無法訪問其他服務器上的資源,這個限制僅在Web瀏覽器中有效。而跨域就是通過某些手段來繞過這個限制,實現不同服務器之間通信的效果。ajax跨域可以通過jsonp、cros或者服務端代理實現。

jsonp

jsonp利用<script>標籤不受限制訪問其他服務器資源的特點,通過<script>的src發送跨域訪問的url以及參數。jsonp需要對服務端返回的數據格式做修改,服務端返回的數據應該是一個可執行的JavaScript代碼。
如果json數據格式如下

{"sex": "男", "name": "小明", "age": 20}

那麼jsonp格式應該如下

callback({"sex": "男", "name": "小明", "age": 20});

其中callback是回調函數,一般由jsonp客戶端提供。jQuery提供了jsonp請求,下面用jsonp獲取京東的訂單列表數據。

$.ajax({
    url: 'http://order.jd.com/lazy/getOrderListCountJson.action', // jsonp路徑
    data: {},                 // 參數 
    //jsonp: 'callback',        // 服務端接收回調函數的參數,默認是callback
    //jsonpCallback: '',        // 回調函數名稱,如果不指定jQuery自動生成
    dataType: 'jsonp'         // 請求類型
}).done(function(json){
    // 請求成功處理
    console.dir(json);
}).fail(function(){
    // 請求失敗處理
});

如圖所示,jsonp請求屬於script請求。

jsonp

jsonp結果如下

jsonp

cors

CORS(Cross-Origin Resource Sharing)定義一種跨域訪問的機制,可以讓AJAX實現跨域訪問。實現此功能非常簡單,只需服務器添加響應標頭Access-Control-Allow-Origin。目前大部分的PC瀏覽器和幾乎所有的移動瀏覽器都支持CROS。

瀏覽器 CROS支持情況
IE 從IE8開始支持,其中IE8/IE9部分支持,IE10/IE11完全支持。
Firefox Firefox 3.5+ 開始支持
Chrome Chrome 3+ 開始支持
Opera Opera 12+ 開始支持
Safair Safair 4+ 開始支持

服務端代碼

import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import com.alibaba.fastjson.JSON;

@Controller
@RequestMapping("/test")
public class TestController {

    /**
     * 返回json
     * 
     * @param response
     */
    @RequestMapping("/json.do")
    public void json(HttpServletResponse response) {
        response.setContentType("text/plain");
        response.setCharacterEncoding("utf-8");

        // 添加響應頭,支持跨域ajax請求
        response.setHeader("Access-Control-Allow-Origin", "*");

        // 構造json
        Map<String, Object> json = new HashMap<String, Object>();
        json.put("date", new Date());
        json.put("name", "小明");
        json.put("age", 18);

        try {
            // 這裏用FastJSON生成JSON字符串
            response.getWriter().write(JSON.toJSONString(json));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

頁面代碼

$.ajax({
    url: 'http://localhost:8280/logweb/test/json.do',
    dataType: 'json',
    success: function(json){
        console.dir(json);
    },
    error: function(){
    }
});

如圖所示,從CSDN頁面發起ajax跨域請求。

cors


cors

如果服務端沒有設置響應頭Access-Control-Allow-Origin,瀏覽器會有如下錯誤提示。

cors error

服務端代理

服務端不受跨域請求的限制,可以在服務端代理ajax請求。

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;

/**
 * ajax請求代理
 * 用Apache HttpClient模擬發送請求
 * 所有請求都以POST方式提交
 */
@WebServlet("/ajax/proxy/servlet")
public class AjaxProxyServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    // ajax請求的實際url名稱
    private final String destUrlParamName = "destUrl";
    // 編碼
    private final String encoding = "utf-8";

    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 獲取請求參數
        List<NameValuePair> params = getParameters(request);

        // ajax請求的實際url
        String destUrl = request.getParameter(destUrlParamName);         
        HttpPost httppost = new HttpPost(destUrl);

        CloseableHttpClient httpclient = HttpClients.createDefault();
        UrlEncodedFormEntity formEntity;

        try{
            // 設置請求參數
            formEntity = new UrlEncodedFormEntity(params, encoding);
            httppost.setEntity(formEntity);

            CloseableHttpResponse httppostResponse = httpclient.execute(httppost);

            try{
                HttpEntity httpEntity = httppostResponse.getEntity();
                InputStream httpis = httpEntity.getContent();
                OutputStream os = response.getOutputStream();
                int len = 0;
                byte[] buffer = new byte[1024];

                while((len = httpis.read(buffer)) != -1){
                    os.write(buffer, 0, len);
                }

                os.flush();
            }finally{
                httppostResponse.close();
            }

        }catch(Exception e){
            e.printStackTrace();
        }

    }

    /**
     * 獲取ajax請求參數
     * @param request
     * @return 返回參數對數組
     */
    private List<NameValuePair> getParameters(HttpServletRequest request){
        Enumeration<String> paramNames = request.getParameterNames();
        List<NameValuePair> params = new ArrayList<NameValuePair>();

        while(paramNames.hasMoreElements()){
            String name = paramNames.nextElement();

            // 判斷,如果參數是ajax請求的目標路徑,則忽略
            if(!destUrlParamName.equals(name)){
                params.add(new BasicNameValuePair(name, request.getParameter(name)));
            }
        }

        return params;
    }

}

ajax跨域請求代碼

/**
* ajax代理請求
* @param  options  ajax配置參數,請參考$.ajax()的配置
*/
function ajaxProxy(options){
    // ajax代理的url
    var proxyUrl = 'http://localhost:8280/logweb/ajax/proxy/servlet',
        destUrl = options.url;

    // 把代理url和實際url替換
    options.url = proxyUrl;

    if(!options.data){
        options.data = {};
    }

    // 把實際url放到參數destUrl中,傳遞給服務端發送代理請求
    options.data.destUrl = destUrl; 
    options.type = 'post';
    return $.ajax(options);
}

下面用ajax代理請求博客園的分頁數據

ajaxProxy({
    url: 'http://www.cnblogs.com/mvc/AggSite/PostList.aspx', // 博客園分頁url
    dataType: 'html', // 數據格式是html
    data: {
        CategoryId: 808,
        CategoryType: "SiteHome",
        ItemListActionName: "PostList",
        PageIndex: 2,
        ParentCategoryId: 0
    },
    success: function(html){
        console.dir(html);

        //把html直接輸出到頁面
        $('body').html(html);
    },
    error: function(){
        console.dir(arguments);
    }
});

請求參數如圖所示

ajax代理

執行結果如圖所示

ajax代理

總結

  優點 缺點
jsonp 不存在兼容性問題,大部分js庫都支持jsonp。 通過script發送請求,url長度有限制,不能發送超過2048字節的請求。服務端需要提供專門的jsonp接口。
cros Web端和服務端基本上不用做代碼修改。 不能完全兼容所有瀏覽器。
ajax代理 不存在兼容性問題 相對於jsonp和cros,性能可能較差。服務端需要提供專門的代理。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章