閱讀700 返回首頁    go 阿裏雲 go 技術社區[雲棲]


跨域訪問-ajax

ajax跨域訪問方案分為jsonp和cors,本文將會對兩種方案進行介紹。

jsonp

jsonp的原理是利用html文檔script標簽可以跨域訪問的特點,通過在HTML文檔中動態添加script標簽,從其它域獲取數據並在原HTML文檔中回調處理數據。下麵利用jquery封裝好的jsonp,跨域獲取數據。

清單:www.a.com的html文檔

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>AJAX Tester</title>
    <script type="text/javascript" src="js/jquery.js"></script>


    <script type="text/javascript">
        $(function () {
            // jsonp形式的請求
            $.ajax({
                url: "https://www.b.com/user_info?callback=?",
                data: {id: 1},
                dataType: "jsonp"
            }).done(showResult);
        });

        function showResult(ret) {
            console.log(ret);
            $("#container").html(ret.name);
        }
    </script>

</head>
<body>
My name is:
<span  ></span>

<script type="text/javascript">

</script>

</body>
</html>

代碼中,采用ajax jsonp的方式請求www.b.com域名獲取數據,並顯示在頁麵中。 JSONP默認采取get方式請求數據。

清單:www.b.com的API

@WebServlet("/user_info")
public class GetUserInfoServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request,
                         HttpServletResponse response) throws ServletException, IOException {
        // 要正確設置響應類型,避免IE出現下載
        response.setContentType("application/javascript");

        String userInfo = "{\"id\":1, \"name\":\"zhangsan\"}";

        String ret;

        // 通過參數傳遞回調函數名,一定程度降低了前後端代碼的耦合度
        String callback = request.getParameter("callback");
        if (callback != null && !callback.isEmpty()) {
            ret = callback + "(" + userInfo + ")"; //拚接jsonp字符串
        } else {
            ret = userInfo; //正常返回json數據
        }

        response.getWriter().write(ret);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }

}

JSONP的返回數據的格式是 callback_method(json_data) ,這裏的callback_method對應於原html文檔中存在的一個函數的函數名。

缺點: jsonp采用的是get方法請求數據,當數據量很大的時候,不能解決跨域請求數據的問題。

CORS

CORS,全稱跨域資源共享(Cross-Origin Resource Sharing)。CORS 是一種網絡瀏覽器的技術規範,它為Web服務器定義了一種方式,允許網頁從不同的域訪問其資源。

既然CORS是一種規範,那麼必須需要瀏覽器實現這種規範,這就不可避免出現瀏覽器兼容的問題。我們看一下各瀏覽器對CORS的支持


image
image

CORS請求又分為簡單請求和非簡單請求。簡單請求是指通過GET方式獲取資源;非簡單請求是指 如POST、PUT、DELETE類型請求,或者在請求中有其它自定義的HTTP Header。下麵詳細介紹一下對於不同的請求的實現方法。

簡單請求

response中需要添加Access-Control-Allow-Origin頭,告訴瀏覽器哪些域名的網站可以獲取返回的數據

response.addHeader("Access-Control-Allow-Origin", "*"); //可以跨域訪問的域名

清單:www.a.com的html文檔獲取www.b.com的數據

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>AJAX Tester</title>
    <script type="text/javascript" src="js/jquery.js"></script>


    <script type="text/javascript">
        $(function () {
            $.ajax({
                url: "https://www.b.com/user_info_2",
                method: "GET"
            }).done(showResult);

        });

        function showResult(ret) {
            $("#container").html(ret.name);
        }
    </script>

</head>
<body>
My name is:
<span  ></span>

</body>
</html>

清單:www.b.com通過cors方式返回數據給www.a.com

@WebServlet("/user_info_2")
public class GetUserInfoServlet2 extends HttpServlet {
    protected void doGet(HttpServletRequest request,
                         HttpServletResponse response) throws ServletException, IOException {
        // 簡單請求,直接設置Access-Control-Allow-Origin就可以了
        response.addHeader("Access-Control-Allow-Origin", "*"); //可以跨域訪問的域名

        // 要正確設置響應類型,避免IE出現下載
        response.setContentType("application/json");

        response.getWriter().write("{\"id\":1, \"name\":\"zhangsan\"}");
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }
}

非簡單請求

對於非簡單請求,會執行一次preflight操作,瀏覽器會發起一次OPTIONS類型請求,要對OPTIONS請求給予正確應答,應答內容是允許誰訪問,允許什麼方法,允許什麼自定義頭的request。下麵看一下一次request請求的CORS流程。


image


打開調試模式後,可以看到一次request請求被分成了兩次:
第一個請求是用OPTIONS方法。 //這裏所做是的是預檢查,從服務器確認是否可以繼續
第二個請求才是你發起的請求方法。

清單:www.a.com的html文檔,對www.b.com發起ajax請求數據,並攜帶了自定義 HEADER和采用POST方法

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>AJAX Tester</title>
    <script type="text/javascript" src="js/jquery.js"></script>

    <script type="text/javascript">
            $.ajax({
                url: "https://www.b.com/user_info_2",
                method: "POST",
                beforeSend: function (xhr) {
                    //添加自定義頭
                    console.log("before send");
                    xhr.setRequestHeader("myheader", "abc")
                }
            }).done(showResult);

        });

        function showResult(ret) {
            $("#container").html(ret.name);
        }
    </script>

</head>
<body>
My name is:
<span  ></span>

</body>
</html>

清單:www.b.com的API在option的處理中,對response添加了一些頭。

@WebServlet("/user_info_2")
public class GetUserInfoServlet2 extends HttpServlet {
    @Override
    protected void doOptions(HttpServletRequest request,
                             HttpServletResponse response) throws ServletException, IOException {
        System.out.println("OPTIONS");
        response.addHeader("Access-Control-Allow-Origin", "*");

        response.addHeader("Access-Control-Allow-Methods",
                "GET, POST, OPTIONS, DELETE");
        response.addHeader("Access-Control-Allow-Headers", "myheader, myheader2");
    }

    protected void doGet(HttpServletRequest request,
                         HttpServletResponse response) throws ServletException, IOException {

        // 要正確設置響應類型,避免IE出現下載
        response.setContentType("application/json");

        response.getWriter().write("{\"id\":1, \"name\":\"zhangsan\"}");
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }

}

大家可以看到利用CORS方案處理ajax跨域請求的問題,但是比較麻煩的是需要修改java代碼,侵入性很強。為了解決這個問題,我們可以在nginx中配置,讓response返回的時候自動添加返回頭。

niginx的配置如下:

  if ($request_method = 'OPTIONS') {
                add_header 'Access-Control-Allow-Origin' '*';
                add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';

                add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';

                add_header 'Access-Control-Max-Age' 1728000;
                add_header 'Content-Type' 'text/plain charset=UTF-8';
                add_header 'Content-Length' 0;
                return 204;
             }
             if ($request_method = 'POST') {
                add_header 'Access-Control-Allow-Origin' '*';
                add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
                add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
             }
             if ($request_method = 'GET') {
                add_header 'Access-Control-Allow-Origin' '*';
                add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
                add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
             }

原文鏈接

最後更新:2017-06-30 15:02:10

  上一篇:go  跨域訪問 - cookies
  下一篇:go  996月入三萬,他卻要放棄