常用的前端跨域的几种方式
前端跨域
前端跨域有多少種情況
- CORS 跨域
- jsonp 跨域
- postMessage
- document.domain 一般情況下我們使用比較多的就是 Cross-Origin和 jsonp 這兩種方式。postMessage 和 document.domain 使用的不是很多。下面我會介紹他們如何使用和使用的場景還有一些不常見的問題。
CORS
全拼 cross-origin resource sharing,意思是跨域資源共享。我們先看看瀏覽器的兼容性,如下圖:
注意: 所以要使用 CORS 進行跨域的話,必須注意客戶端和服務器必須同時支持。
瀏覽器將 CORS 請求分成兩類:簡單請求(simple request)和非簡單請求(not-so-simple request),我們看看如何區分
1.請求方法是以下三種方法之一:
- HEAD
- GET
- POST
2.HTTP的頭信息不超出以下幾種字段:
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type :只限于三個值 application/x-www-form-urlencoded 、multipart/form-data、text/plain
簡單請求(simple request)
-
對于簡單請求,瀏覽器直接發出CORS請求。具體來說,就是在頭信息之中,增加一個Origin字段。Origin字段用來說明,本次請求來自哪個源(協議 + 域名 + 端口)。服務器根據這個值,決定是否同意這次請求。
-
如果 Origin 指定的源,不在許可范圍內,服務器會返回一個正常的 HTTP 回應。瀏覽器發現,這個回應的頭信息沒有包含 Access-Control-Allow-Origin 字段(詳見下文),就知道出錯了,從而拋出一個錯誤,被 XMLHttpRequest 的 onerror 回調函數捕獲。注意,這種錯誤無法通過狀態碼識別,因為 HTTP 回應的狀態碼有可能是200。(如果我們使用的 fetch,那么 fetch 會自動觸發一個錯誤,那么這個時候我們可以通過 promise.catch() 方法可捕獲這個錯誤信息)。
-
如果 Origin 指定的域名在許可范圍內,服務器返回的響應,會多出幾個頭信息字段
需要注意的是,如果要發送 Cookie,Access-Control-Allow-Origin就不能設為*,必須指定明確的、與請求網頁的域名一致。
非簡單請求(not-so-simple request)
-
非簡單請求的 CORS 請求,會在正式通信之前,增加一次 HTTP 查詢請求,稱為 預檢請求(preflight)。
-
預檢請求 用的請求方法是 OPTIONS,表示這個請求是用來詢問的。頭信息里面,關鍵字段是 Origin,表示請求來自哪個源。
-
除了 Origin 字段,"預檢"請求的頭信息包括兩個特殊字段
- 服務器收到預檢請求以后,檢查了 Origin、Request-Method 和 Request-Headers 字段以后,確認允許跨源請求,就可以做出回應。如果瀏覽器否定了"預檢"請求,會返回一個正常的 HTTP 回應,但是沒有任何 CORS 相關的頭信息字段。這時,瀏覽器就會認定,服務器不同意預檢請求,因此觸發一個錯誤,被 XMLHttpRequest 對象的 onerror 回調函數捕獲
一旦服務器通過了"預檢"請求,以后每次瀏覽器正常的 CORS 請求,就都跟簡單請求一樣,會有一個 Origin 頭信息字段。服務器的回應,也都會有一個 Access-Control-Allow-Origin 頭信息字段。
我們以 koa為列,在demo中這么設置就可以了
// 下面是對cors進行的設置,在返回的headers中添加如下字段,也可以使用 koa2-cors app.use(async (ctx, next) => {// 允許來自所有域名請求,請求攜帶了cookie就不能設置為 * ctx.set("Access-Control-Allow-Origin", "http://10.105.16.162:3000");// 設置所允許的HTTP請求方法ctx.set("Access-Control-Allow-Methods", "GET,POST");// 它也是一個逗號分隔的字符串,表明服務器支持的所有頭信息字段.ctx.set("Access-Control-Allow-Headers", "x-requested-with,accept,origin,content-type");// 該字段可選。它的值是一個布爾值,表示是否允許發送Cookie。默認情況下,Cookie不包括在CORS請求之中。// 當設置成允許請求攜帶cookie時,需要保證"Access-Control-Allow-Origin"是服務器有的域名,而不能是"*";ctx.set("Access-Control-Allow-Credentials", true);await next();}); 復制代碼jonsp 跨域
jonsp跨域的原理是通過動態創建script的標簽的形式來實現跨域的,因為script不受同源策略的影響。但是jsonp只能接受get方法。 看看下面的代碼是如何封裝的,并做了超時設置。
const defaultOptions = {timeout: 15000,uid: 0,perfix: '_jsp',name: 'callBack', } const jsp = (url, params, options = {}) => {let timer = null;const options = Object.assign({}, defaultOptions, options);const callBackName = `${options.perfix}_${options.name}_${options.uid++}`;const paramsKeys = Object.entries(params);url += `?${callback}=${callBackName}`;url = paramsKeys.reduce((initUrl, [k, v]) => {return `${initUrl}&${k}=${v}`;}, url);const body = document.getElementByTagName(body)[0];const script = document.createElement('script');script.src = url;body.appendChild(script);const clean = () => {if (timer) {clearInterval(timer);timer = null;body.removeChild(script);window[callBackName] = null;}};return new Promise((resolve, reject) => {window[callBackName] = function(data) {clear();return resolve(data);};timer = setTimeout(() => {clear();return reject('請求超時了');}, options.time);}); }復制代碼postMessage (這是HTML5提供的方法,IE8+才支持) 可以實現兩個窗口之間的通信,是不是同源不重要
它可用于解決以下方面的問題:
- 頁面和其打開的新窗口的數據傳遞
- 多窗口之間消息傳遞
- 頁面與嵌套的iframe消息傳遞
使用方法:postMessage(data, origin) 方法接受兩個參數 data html5規范支持任意基本類型或可復制的對象,但部分瀏覽器只支持字符串,所以傳參時最好用 JSON.stringify()序列化。IE10才開始支持對象形式。 origin 協議+主機+端口號,也可以設置為 *,表示可以傳遞給任意窗口,如果要指定和當前窗口同源的話設置為 /。
// 發送窗口代碼:const ifr = document.createElement('iframe');ifr.src = 'http://localhost:3001';ifr.display = 'none';document.body.append(ifr);ifr.onload = function() {const doc = ifr.contentWindow;// 目標源 可以限定具體的 協議 + 域名 + 端口 或者是 *const targetOrigin = 'http://localhost:3001';// 發送消息 doc可以是window.open() 也可以是嵌套的iframe// 發送的消息可以是 string 或者 object(IE10才支持)doc.postMessage('hello world', targetOrigin);}// 接收窗口代碼:window.addEventListener('message', (e)=> { // 必須是window不能是document監聽// 數據const data = e.data;// 消息是從哪個源發送來的const origin = e.origin;// 發送消息窗口 const source = e.source;// 通過source回傳消息 同樣在發送消息的窗口也需要監聽source.postMessage('hi shenxuxiang', 'http://localhost:3000');},false)復制代碼document.domain
此方案僅限主域相同,子域不同的跨域應用場景。實現原理:兩個頁面都通過js強制設置document.domain為基礎主域,就實現了同域。 之后就可以共享window下的屬性。
// www.a.com/a.html頁面中的代碼document.domain = 'a.com';const ifr = document.createElement('iframe');ifr.src = 'http://www.script.a.com/b.html';ifr.display = 'none';document.body.append(ifr);ifr.onload = function() {const doc = ifr.contentWindow;console.log(doc.name); // shenxuxiang}// www.script.a.com/b.html頁面中的代碼document.domain = 'a.com';window.name = 'shenxuxiang';復制代碼上面一共介紹了4種前端跨域的方法,jsonp 和 cors 可以和后臺進行跨域,而 postMessage 和 domain 適合頁面間的通訊。頁面間的跨域還有可以使用 window.name | location.hash。如果想了解的可以點擊這里
參考
Cross-Origin Resource Sharing MDN
轉載于:https://juejin.im/post/5ceded75f265da1b5e72d4d3
總結
以上是生活随笔為你收集整理的常用的前端跨域的几种方式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 用Python的smtp模块发邮件的示例
- 下一篇: 2017年html5行业报告,云适配发布