1. 程式人生 > >跨域請求:Fetch實現跨域請求與POST方式引數提交

跨域請求:Fetch實現跨域請求與POST方式引數提交

一、要實現跨域,首先需要理解什麼叫做跨域。

跨域是指,不同域名之間相互訪問。
例如 :我的電腦上有2個伺服器 192.168.0.11  192.168.0.12  
如果第一個伺服器上的頁面要訪問第二個伺服器,就叫做跨域  
或者http://www.baidu.com 要訪問http://www.xxx.com ,也是不同域名,也是跨域  
明白什麼叫做跨域之後就可以想怎麼解決這個問題了。要實現跨域就需要明白跨域資源共享協議:CORS。
要詳細瞭解CORS,請參照一下部落格地址,這裡就不再贅述。

參考部落格:

跨域資源共享 CORS 詳解(http://www.ruanyifeng.com/blog/2016/04/cors.html)

二、解決跨域

CORS需要瀏覽器和伺服器同時支援。目前,所有瀏覽器都支援該功能,IE瀏覽器不能低於IE10。

整個CORS通訊過程,都是瀏覽器自動完成,不需要使用者參與。對於開發者來說,CORS通訊與同源的AJAX通訊沒有差別,程式碼完全一樣。瀏覽器一旦發現AJAX請求跨源,就會自動新增一些附加的頭資訊,有時還會多出一次附加的請求,但使用者不會有感覺。

因此,實現CORS通訊的關鍵是伺服器。只要伺服器實現了CORS介面,就可以跨源通訊。

以下以SPRING+React+ Fetch進行示例。

上面也說了,CORS需要瀏覽器和伺服器同時支援,那麼伺服器如何支援跨域呢。

如果伺服器是apache

(1)修改http服務的配置檔案:C:\wamp\bin\apache\Apache2.4.4\conf\httpd.conf

LoadModule headers_module modules/mod_headers.so前面的註釋刪除.

(2)新增Header set Access-Control-Allow-Origin *

<Directory />

    AllowOverride none

    Require all granted

         Header set Access-Control-Allow-Origin *

</Directory>

(3)重啟http服務

如果是tomcat,比如spring MVC專案

首先要建立一個Filter攔截器進行攔截
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;

public class CorsFilter implements Filter{

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) res;  
        response.setHeader("Access-Control-Allow-Origin", "*");  
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");  
        response.setHeader("Access-Control-Allow-Headers", "token,Access-Control-Allow-Origin,Access-Control-Allow-   Methods,Access-Control-Max-Age,authorization");
        chain.doFilter(req, res);
        
    }

    @Override
    public void destroy() {
    }

}

關鍵程式碼:response.setHeader("Access-Control-Allow-Origin", "*");

修改web.xml,增加:

<filter>  
      <filter-name>corsFilter</filter-name>  
      <filter-class>=**.**.CorsFilter</filter-class>  
    </filter>  
    <filter-mapping>  
      <filter-name>corsFilter</filter-name>  
      <url-pattern>*.json</url-pattern>  
    </filter-mapping>

**.**需要用自己的類路徑代替

上面伺服器支援跨域了,下面開始前端頁面進行跨域請求

import xFetch from './xFetch';

export default class UserService {
  constructor(options) {
  }


  getUserList(callback) {
    let formData = new FormData();
    formData.append("groupId", 1);

    var init = {
        method: 'POST',
        mode:'cors',
        cache: 'default',
        headers: {
          'Accept': 'application/x-www-form-urlencoded',
          'Access-Control-Allow-Origin':'*',
          'Access-Control-Allow-Methods': 'POST, GET, OPTIONS, PUT, DELETE',
          'token': '**********'
        },
        body:formData
      };
    xFetch('http://127.0.0.1:8080/**/**/**.json',init
    ).then(
      ({jsonResult}) => {
        /*
        jsonResult.map(item => {
          alert(item.title);
        })*/
        callback(jsonResult, {});
      }
    );
  }

  getUserHoldRoleList(callback) {
    xFetch('/userHoldRoleList').then(
      ({jsonResult}) => {
        /*
        jsonResult.map(item => {
          alert(item.title);
        })*/
        callback(jsonResult, {});
      }
    );
  }
}

注意:以上伺服器端Filter設定裡面
       response.setHeader("Access-Control-Allow-Origin", "*");  
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");  
        response.setHeader("Access-Control-Allow-Headers", "token,Access-Control-Allow-Origin,Access-Control-Allow-      Methods,Access-Control-Max-Age,authorization");

與前端頁面請求中

let formData = new FormData();
    formData.append("groupId", 1);

    var init = {
        method: 'POST',
        mode:'cors',
        cache: 'default',
        headers: {
          'Accept': 'application/x-www-form-urlencoded',
          'Access-Control-Allow-Origin':'*',
          'Access-Control-Allow-Methods': 'POST, GET, OPTIONS, PUT, DELETE',
          'token': 'Cl5bxjcUt+gDtIDJY4wDgZKNoYdMcgjKQQKXga/UKtmpfniucZTeKaf5mUjsm7uc'
        },
        body:formData
      };

的header要保持一致。

其中method 表示請求的方法。而允許哪些方法呢,在headers中是有設定的,伺服器端和前端都有設定,而且需要相同

'Access-Control-Allow-Methods': 'POST, GET, OPTIONS, PUT, DELETE',

response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE");

表示前端要在header中傳遞的引數和使用的引數

   response.setHeader("Access-Control-Allow-Headers", "token,Access-Control-Allow-Origin,Access-Control-Allow-   Methods,Access-Control-Max-Age,authorization");

mode 方式指定了使用的協議,這裡寫了使用 cors協議。mode 請求的方式 cors, no-cors, or same-origin

cache 快取 default, no-store, reload, no-cache, force-cache, or only-if-cached

headers 這個引數需要用到 Headers 物件。

'Access-Control-Allow-Origin':'*'  也是在伺服器端和前端中都有設定。 這個代表的意思是 允許訪問的域名。*  表示允許跨所有,為了安全起見,可以設定為固定的域名。 這樣 伺服器端與客戶端就進行了雙向的域名繫結。

REACT 使用POST方式進行提交

我們在請求http介面時候,通常都會使用get和post的方式,針對表單提交這類的請求,我們通常採用post方式。

那麼在RN中的Fetch API中post提交有哪些坑呢?讓我們擼起來。


我們先來說說Server端的程式碼,通常我們從Request獲取引數時的方法為:

  1. String paraValue = request.getParameter(paraName);  
我們下面說的判斷能不能獲取引數,就是按照這種方法來獲取。

在 RN中,通常我們會怎麼寫程式碼呢?
方案一(不推薦)
[javascript] view plain copy
  1. let url = "http://127.0.0.1:8080/api/testFetch”
  2. let params = "name=admin&password=admin123”;  
  3. fetch(url , {  
  4.   method: 'POST',  
  5.   headers: {},  
  6.   body: params,  
  7. }).then((response) => {  
  8.   if (response.ok) {  
  9.       return response.json();  
  10.   }  
  11. }).then((json) => {  
  12.   alert(JSON.stringify(json));  
  13. }).catch((error) => {  
  14.   console.error(error);  
  15. });  

此時我們發現在Server端無法獲取到name和password的值。
換成GET試試,將params追加到url後,發現ok。那這是什麼情況呢?下面講解。

好,不行,我們就再換一種方法試試唄。
方案二(不推薦):
[javascript] view plain copy在CODE上檢視程式碼片派生到我的程式碼片
  1. let params = {"name":"admin","password":"admin123"};  
  2. fetch(url , {  
  3.   method: 'POST',  
  4.   headers: {},  
  5.   body:JSON.stringify(params),  
  6. }).then((response) => {  
  7.   if (response.ok) {  
  8.       return response.json();  
  9.   }  
  10. }).then((json) => {  
  11.   alert(JSON.stringify(json));  
  12. }).catch((error) => {  
  13.   console.error(error);  
  14. });  
我們直接將params封裝成一個JSON物件,然後在body裡將JSON物件轉成字串傳過去,發現然並卵,Server端還是獲取不到值。


好,我們不兜圈子了,直接說明原因。
其實,方案一和方案二都是直接在body裡傳遞了一個字串,在Server端獲取body的方式如下:
  1. StringBuilder buffer = new StringBuilder();  
  2. BufferedReader reader = beat.getRequest().getReader();  
  3. String line;  
  4.  while ((line = reader.readLine()) != null) {  
  5.     buffer.append(line);  
  6.  }  
  7. String body = buffer.toString();  
通過這種方法我們可以獲取到傳入的字串。
既然能獲取到字串,那麼我們也可以拿到我們傳入的值了,可以轉JSON、或者按&切割字串,只不過這種解決方案好像有點挫啊!!!

也許你會問在jquery中,我們就是按照方案一這種方式做的啊,怎麼好使呢?
答:因為引數 "name=admin&password=admin123” 在jquery中,傳入物件框架會自動封裝成formData的形式,fetch沒有這個功能。

終極方案(推薦使用):

既然fetch不會自動轉FormData,那我們自己new一個FormData,直接傳給body。
在FormData中也可以傳遞位元組流實現上傳圖片的功能。參考:http://blog.csdn.net/codetomylaw/article/details/52446786
[javascript] view plain copy在CODE上檢視程式碼片派生到我的程式碼片
  1. let formData = new FormData();  
  2. formData.append("name","admin");  
  3. formData.append("password","admin123");  
  4. etch(url , {  
  5.  method: 'POST',  
  6.  headers: {},  
  7.  body: formData,  
  8. ).then((response) => {  
  9.  if (response.ok) {  
  10.      return response.json();  
  11.  }  
  12. ).then((json) => {  
  13.  alert(JSON.stringify(json));  
  14. ).catch((error) => {  
  15.  console.error(error);  
  16. );  
這樣我們就可以在Server端獲取到name和password的值了。

參考部落格:

https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch

https://www.giuem.com/html5-fetch-api/

http://www.cnblogs.com/snandy/p/5076512.html

http://blog.csdn.net/u012620506/article/details/52346264

http://blog.csdn.net/codetomylaw/article/details/52588493