實踐解決跨域問題的三種方式剖析

最近在作我星際schub網站的時候,遇到了跨域問題,我先把後端node部署在了服務器上,而後在本地localhost測試,出現了問題:
這裏寫圖片描述html

瀏覽器都提示咱們使用這個header頭:前端

解決辦法:vue

1. CORS

服務器設置響應頭:node

response.setHeader("Access-Control-Allow-Origin", "*")

(這樣可能引發CSRF攻擊,通常設置成對應的域名就行,
response.setHeader("Access-Control-Allow-Origin", "http:localhost:8080/") )
fetch的option加上 mode:"cors"。jquery

(須要注意的是,若是要發送Cookie,Access-Control-Allow-Origin就不能設爲星號,必須指定明確的、與請求網頁一致的域名。同時,Cookie依然遵循同源政策,只有用服務器域名設置的Cookie纔會上傳,其餘域名的Cookie並不會上傳,且(跨源)原網頁代碼中的document.cookie也沒法讀取服務器域名下的Cookie。)
關於CORS的講解,能夠看阮一峯的這篇文章:連接ajax

2.jsonp

jsonp的方式,注意這種方式只能發送get請求,就算指定爲post,最後發送的仍是get,且跟上面方式同樣,也須要服務器的配合支持。以前在作vue音樂播放器項目的時候,調用的是qq音樂的接口,qq音樂的接口返回的就是jsonp格式的
這裏寫圖片描述npm

一、 jsonp發送的並非ajax請求;
二、 利用動態建立一個script標籤,由於script標籤是沒有同源策略限制的,是能夠跨域的;
三、 把這個script標籤的src指向咱們請求的服務端地址,這個地址會攜帶一個參數:callback ,一個回調函數,服務端會把數據經過這個回調函數返回給客戶端,可是客戶端沒有這個函數怎麼接收呢?因此在發送請求以前,要在全局(window)註冊這樣一個方法,利用這個方法,來得到數據;
四、 這個回調函數名須要跟服務端約定好,是一致的。json

若是讓咱們用原生js實現一個jsonp,那就是這樣:後端

<script>
         //指定的回調函數
         function showData (result) {
             var data = JSON.stringify(result); //json對象轉成字符串
             $("#text").val(data);
         }
 
         $(document).ready(function () {
 
             $("#btn").click(function () {
                 //向頭部輸入一個腳本,該腳本發起一個跨域請求
                 $("head").append("<script src='http://localhost:9090/student?callback=showData'><\/script>");
             });
 
         });
     </script>

後端一是獲取到前端傳過來的回調函數名稱,二是獲取到數據,最後把數據用回調函數包圍住,執行這個前端的這個回調函數。咱們就能夠利用前端這個事先全局註冊的回調函數來顯示操做數據了。跨域

用Java寫的後臺demo就是這樣:

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  response.setCharacterEncoding("UTF-8");
  response.setContentType("text/html;charset=UTF-8");

  //數據
  List<Student> studentList = getStudentList();


  JSONArray jsonArray = JSONArray.fromObject(studentList);
 String result = jsonArray.toString();

 //前端傳過來的回調函數名稱
 String callback = request.getParameter("callback");
 //用回調函數名稱包裹返回數據,這樣,返回數據就做爲回調函數的參數傳回去了
 result = callback + "(" + result + ")";

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

這裏再分析一下jQuery關於jsonp的實現。

1.最簡單的方式:

$.ajax({
            type:"GET",
            url:"http://www.xxx.com/getMySeat", //訪問的連接
            dataType:"jsonp",  //數據格式設置爲jsonp
            success:function(data){  //成功的回調函數
                alert(data);
            },
            error: function (e) {
                alert("error");
            }
        });

服務端代碼不變,最簡單的方式,只需配置一個dataType:'jsonp',就能夠發起一個跨域請求。jsonp指定服務器返回的數據類型爲jsonp格式,能夠看發起的請求路徑,自動帶了一個callback=xxx,callback是本身不手動設置參數名字的默認值,xxx是jquery隨機生成的一個回調函數名稱。
這裏的success就跟上面的showData同樣,若是有success函數則默認success()做爲回調函數。

2.回調函數你能夠寫到script下(默認屬於window對象),或者指明寫到window對象裏,看jquery源碼,能夠看到jsonp調用回調函數時,是調用的window.callback。
而後看調用結果,發現,請求時帶的參數是:callback=showData;調用回調函數的時候,先調用了指定的showData,而後再調用了success。因此,success是返回成功後一定會調用的函數,就看你怎麼寫了。
指定回調函數名稱,加一個option字段便可:
這裏寫圖片描述
最後仍是會調用success:
這裏寫圖片描述

  1. 如何改變callback這個默認值(回調函數名稱的key值),在jq裏:
    這裏寫圖片描述
    這樣一改以後,響應的後端代碼也要改動了,獲取的字段key值就是theFunction了,
    這裏寫圖片描述
    對應的請求路徑也就變了:
    這裏寫圖片描述

除了jq對jsonp的實現,在上面提到的vue音樂播放器項目中,我使用了jsonp npm包,用這個包,就不須要本身在window上註冊回調函數,qq音樂接口中jsonpCallback就是服務端返回的回調函數(a方法) 咱們本地在window上註冊的a方法是在調用的jsonp庫中自動被實現。咱們本身寫成promise的方法後直接then便可。

可是後來我又遇到一個問題,schub星際網站項目中我想調用WCS職業聯賽積分賽排名的接口的時候,用fetch固然仍是提示跨域問題了,這時候後端我又不能修改,因此CORS方式確定不行,jsonp的方式也須要後端配合支持,上面兩種方法都不適用了。下面就是最終解決問題的第三種方法。

  1. 代理服務器,跨域問題是瀏覽器的同源策略限制,服務器之間是沒有的,那咱們先把請求給咱們的代理服務器,再讓咱們的代理服務器去調用這個接口,在發送給前端就能夠了。

前端:
這裏寫圖片描述

代理服務器(node):
這裏寫圖片描述

注意服務器返回的數據在response.data裏,而後發送給前端的響應res。
(後來我先後端設置的端口不一樣,代理服務器也要設置Access-Control-Allow-Origin的頭爲 *)

4.第四點我想談下不一樣跨域方式中關於cookie的。

在後面的博客將會談到cookie再跨域中的問題。

相關文章
相關標籤/搜索