跨域請求: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獲取引數時的方法為:
- String paraValue = request.getParameter(paraName);
在 RN中,通常我們會怎麼寫程式碼呢?
方案一(不推薦):
[javascript] view plain copy
- let url = "http://127.0.0.1:8080/api/testFetch”
- let params = "name=admin&password=admin123”;
- fetch(url , {
- method: 'POST',
- headers: {},
- body: params,
- }).then((response) => {
- if (response.ok) {
- return response.json();
- }
- }).then((json) => {
- alert(JSON.stringify(json));
- }).catch((error) => {
- console.error(error);
- });
此時我們發現在Server端無法獲取到name和password的值。
換成GET試試,將params追加到url後,發現ok。那這是什麼情況呢?下面講解。
好,不行,我們就再換一種方法試試唄。
方案二(不推薦):
[javascript] view plain copy
- let params = {"name":"admin","password":"admin123"};
- fetch(url , {
- method: 'POST',
- headers: {},
- body:JSON.stringify(params),
- }).then((response) => {
- if (response.ok) {
- return response.json();
- }
- }).then((json) => {
- alert(JSON.stringify(json));
- }).catch((error) => {
- console.error(error);
- });
好,我們不兜圈子了,直接說明原因。
其實,方案一和方案二都是直接在body裡傳遞了一個字串,在Server端獲取body的方式如下:
- StringBuilder buffer = new StringBuilder();
- BufferedReader reader = beat.getRequest().getReader();
- String line;
- while ((line = reader.readLine()) != null) {
- buffer.append(line);
- }
- 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
- let formData = new FormData();
- formData.append("name","admin");
- formData.append("password","admin123");
- etch(url , {
- method: 'POST',
- headers: {},
- body: formData,
- ).then((response) => {
- if (response.ok) {
- return response.json();
- }
- ).then((json) => {
- alert(JSON.stringify(json));
- ).catch((error) => {
- console.error(error);
- );
參考部落格:
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