瀏覽器出於防止潛在安全風險的考慮,使用了同源策略,這一方面保證了咱們數據的安全, 另外一方面卻又限制了咱們的手腳,基於此,開發者們與標準制定組織提供了不一樣的解決方案, 這裏主要說說CORS與jsonp。javascript
同源指的是兩個域須要協議,子域名,主域名與端口號都保持一致,四者有一個不一樣,即屬於跨域。 注意: http://localhost:8080與http://127.0.0.1:8080不屬於同源,也就是說,即便IP地址一致,可是一個是域名,一個是IP地址,也不屬於同源。html
CORS:跨域資源共享,是W3C制定的一個草案,定義了在必須訪問跨源資源時,瀏覽器和服務器該怎麼溝通。java
CORS的實現原理是,瀏覽器發出請求報文中會額外包含一個Origin頭部,該頭部的值是當前頁面的源信息(協議,域名和端口),服務器收到請求報文後,若是贊成這個跨源請求,就在響應報文的頭部添加Access-Control-Allow-Origin,值與請求報文中的Origin頭部的值一致,若是響應報文中沒有這個頭部或者有,可是值不一致,此次的跨源請求就會失敗。express
瀏覽器原生支持CORS,可是不一樣的瀏覽器支持的方式不一樣。 IE8及以上版本引用了XDR(XDomainRequest)類型,這個對象和XHR對象相似,使用這個對象,能夠實現安全可靠的跨域通訊。 XDR的使用與XHR相似,使用過程以下:npm
var xdr = new XDomainRequest();
複製代碼
xdr.open('get','#');
複製代碼
xdr.send(null);
複製代碼
xdr.onload=function(){
console.log(xdr.resonseText);
}
複製代碼
4.2 error事件 致使XDR請求失敗的因素不少,全部爲每一個xdr對象綁定該事件的處理函數是頗有必要的,可是該事件拋出的信息有限,咱們只能肯定請求失敗了,並不能得知請求失敗的緣由。json
xdr.onerror=function(){
console.log('get a error');
}
複製代碼
4.3 timeout事件 xdr對象有一個timeout屬性,該屬性代表請求自發出多久後超時,當爲其賦值後,在給定的時間內還沒接受到響應,就會觸發timeout事件。跨域
xdr.ontimeout=function(){
alert('time is too long');
}
複製代碼
使用XDR須要注意的點:瀏覽器
其餘瀏覽器對CORS的實現 其餘主流瀏覽器經過XHR對象原生支持CORS,在嘗試打開不一樣來源的資源時,XML能夠自動觸發這個行爲,有一點不一樣的是,open方法中的URL參數須要傳入絕對路徑。緩存
使用XHR對象進行跨域通訊的注意點:安全
這個給出一個例子,本地文件是index.html,服務端文件爲server.js,須要注意的是,咱們須要使用http-server(其餘的也能夠)搭建一個靜態資源服務器,關於http-server的使用,點擊這裏,這樣可使用http協議訪問index.html文件,使用file協議沒法使用CORS。
// index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div>
<a href="#">clcik me</a>
</div>
</body>
<script>
let aElement = document.getElementsByTagName('a')[0]
aElement.addEventListener('click',cors)
function cors(){
var xhr = new XMLHttpRequest();
xhr.onreadystatechange=(req,res)=>{
if(xhr.readyState==4){
if((xhr.status>=200&& xhr.status<300)||xhr.status==304) {
console.log(xhr.responseText);
console.log(xhr.getResponseHeader('Connection')); //null
console.log(xhr.getResponseHeader('content-type')) //null
console.log(xhr.getResponseHeader('date')) //null
} else {
console.log('Request was unsuccessful: '+xhr.statusText);
}
}
};
xhr.open('get','http://127.0.0.1:3000/',true);
xhr.send(null);
}
</script>
</html>
複製代碼
//server.js
const express = require("express"); //記得安裝express
const app = express();
app.get("/", function (req, res) {
res.setHeader('Access-Control-Allow-Origin','http://localhost:8080');
res.end('hello world!');
});
app.listen(3000, function () {
console.log("app is listening 3000");
});
複製代碼
CORS支持在跨域請求的過程當中,使用自定義的頭部信息,post和GET以外的方法,不一樣類型的主體內容和提升憑據(cookie)。在使用這些高級選項發送請求時,瀏覽器會首先發送一個Prefight請求,這種請求使用options方法,這是一種透明的服務器驗證機制,開發者不須要作這些。 該請求須要包含如下的頭部:
服務器在接收到這個請求後,若是贊成此次跨域請求,就會在響應中設置響應的頭部信息。 這些頭部信息包括:
這裏給一個完整的例子: 一樣使用http-server搭建靜態文件服務器
//index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div>
<a href="#">clcik me</a>
</div>
</body>
<script>
let aElement = document.getElementsByTagName('a')[0]
aElement.addEventListener('click',cors)
function cors(){
var xhr = new XMLHttpRequest();
document.cookie = "name=wang"; //BOM提供的接口,用於設置當前頁面所在的域的cookie
xhr.withCredentials = true; //容許在此次請求中攜帶cookie
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
if ((xhr.status >= 200) & (xhr.status < 300) || xhr.status == 304) {
console.log(xhr.responseText);
console.log('name:'+xhr.getResponseHeader("name")); //成功接收到服務器端設置的自定義響應頭
} else {
console.log("Request was unsuccessful: " + xhr.status);
}
}
};
xhr.open("PUT", "http://127.0.0.1:5000/getData", true); //使用put方法
xhr.setRequestHeader("age", 12);
xhr.send(null);
}
</script>
</html>
複製代碼
//server1.js
//server2.js
const Koa = require('koa');
const router = require('koa-router')();
const app = new Koa();
app.use(async (ctx, next) => {
console.log(`Process ${ctx.request.method}: ${ctx.request.url}`)
await next();
})
app.use(async (ctx, next) => {
ctx.response.set('Access-Control-Allow-Origin', 'http://localhost:8080');
ctx.response.set('Access-Control-Allow-Headers', "age"); //自定義的頭部信息
ctx.response.set('Access-Control-Allow-Methods', "PUT");
ctx.response.set('Access-Control-Allow-Credentials', true);
ctx.response.set('Access-Control-Allow-Max-Age', 6);
ctx.response.set('Access-Control-Expose-Headers', 'name');
// 以上這些響應頭都需設置
ctx.body = 'I am a OPTION method request' //爲option請求報文設置響應
await next();
})
router.put('/getData',async (ctx,next)=>{
console.log('request header:'+ctx.request.header.age);
ctx.response.set('name','wang'); //設置自定義響應頭
ctx.body='success put request';
await next();
})
app.use(router.routes());
app.listen(5000, () => {
console.log('app is listening at port 5000');
});
複製代碼
運行以上server1.js件,訪問http://localhost:8080。
1.服務器端運行結果以下,能夠看到,服務器端在客戶端一次跨域請求中,接收到兩條不一樣方法的請求,且成功接收到客戶端自定義的頭部信息。
總結:CORS的高級用法其實就是在真正與服務器進行跨域通訊時,瀏覽器會先發送一個options方法的請求,幫咱們跟服務器進行‘溝通’,基於溝通結果,決定咱們真正須要的跨域通訊的成功或失敗。
jsonp利用script標籤能夠不受限制的從其餘域加載資源的能力,進行跨域通訊。 jsonp由兩部分組成:回調函數和數據。回調函數是響應帶來時,應該調用的函數,它須要在URL中指定;數據就是服務器返回給瀏覽器的響應。
jsonp的使用:
//index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<script> var script = document.createElement('script'); //回調函數 var blog = function(str){ console.log(str); } script.src='http://localhost:3000/?callback=blog'; document.body.appendChild(script); </script>
</body>
</html>
複製代碼
//server.js
const Koa = require('koa');
const app = new Koa();
const router = require('koa-router')();
router.get('/',async(ctx,next)=>{
var name = ctx.request.querystring.split('=')[1];
console.log(name);
var value='hello world!';
ctx.body = `${name}('${value}')`;
})
app.use(router.routes());
app.listen(3000,()=>{
console.log('app is running at port 3000');
})
複製代碼
其實jsonp的內在邏輯很簡單,在script標籤中聲明的函數是屬於全局的,當服務器返回字符串後,這個字符串會被當作JavaScript代碼執行,也就是調用以前聲明的函數。 ##總結 CORS屬於瀏覽器原生支持,支持全部類型的HTTP請求,是跨域通訊的根本解決方案。 jsonp是開發者們爲了繞開同源策略的權宜之計,雖然只支持get方法,可是使用簡單。
CORS屬於瀏覽器原生支持,支持全部類型的HTTP請求,是跨域通訊的根本解決方案。 jsonp是開發者們爲了繞開同源策略的權宜之計,雖然只支持get方法,可是使用簡單。