自從前后端開發(fā)實現(xiàn)了越來越徹底的分離厚骗,開發(fā)中遇到的跨域問題也隨之越來越多痛黎;
而無論是跨域請求JSONP狸相,CORS或者window跨域window.name
桩匪,window.postMessage
怕篷,在實際開發(fā)使用中的表現(xiàn)都不夠完美历筝。
相對來說 CORS 是官方的功能比較完善的方案,但除了需要服務(wù)器和瀏覽器雙方同時支持外廊谓,還有很多限制梳猪,比如Access-Control-Allow-Origin:*
不能發(fā)送cookie等,而且如果服務(wù)器設(shè)置不當也存在著一些安全隱患蒸痹。
當然春弥,我寫這篇的重點不是吐槽,而且解決問題的叠荠。
更多關(guān)于跨域的資料請自行查閱匿沛,相關(guān)內(nèi)容
<h4 id="design"> 設(shè)計</h4>
我并不是一個前端開發(fā),在之前的很長時間里我都在做著后端開發(fā)的工作榛鼎;
一個偶然的機會接觸到了angularJS的前端路由逃呼,當時我就想到了一個點子——做一個index.html
頁面,這個頁面只有一段js腳本者娱,腳本的功能是將另外一個存放于靜態(tài)存儲服務(wù)器的html頁面整個拉過來抡笼,寫到當前頁面中。
舉個栗子
比如我有一個服務(wù)器是這樣的:
api服務(wù)站點:www.xxx.com
靜態(tài)存儲服務(wù):static.xxx.com
登錄頁面:static.xxx.com/20170420/login.html (中間的數(shù)字表示版本肺然,下面會講到)
登錄接口:www.xxx.com/api/user/login
index.html
很顯然蔫缸,在登錄頁面調(diào)用登錄接口是一個跨域的行為;
所以我現(xiàn)在在www.xxx.com中放一個index.html頁面
內(nèi)容如下:
<script src="http://static.xxx.com/config.js"></script>
<script>
(function (url) {
if (/[?&]supportreload\b/i.test(location.search)) {
window.loadPage = arguments.callee;
}
if (url) {
var xhr = window.XMLHttpRequest ?
new XMLHttpRequest() :
new ActiveXObject('Microsoft.XMLHTTP');
url += [(url.indexOf("?") < 0 ? "?" : "&"), "_", new Date().getTime()].join("");
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
var base = ['<base href="', url, '" />'].join("");
var html = xhr.responseText;
html = html.replace(/(\<head[^>]*\>)/, "$1" + base);
if (html === xhr.responseText) {
html = base + html;
}
document.open();
document.write(html);
document.close();
} else {
document.write("'" + url + "' 加載失敗(" + xhr.statusText + ")...");
}
}
}
xhr.open("GET", url, true);
xhr.send(null);
}
})(window["index.page"]);
</script>
其中引入了一個//static.xxx.com/config.js
际起,內(nèi)容如下:
window["index.page"] = "http://static.xxx.com/20170420/login.html"
流程
流程大致是這樣的
可以看到拾碌,在index.html頁面被加載的同時吐葱,我引用了一個config.js,這個js也是存放在靜態(tài)資源服務(wù)器的校翔,里面聲明了一個參數(shù)
window["index.page"]
弟跑,而index.html頁面會用這個變量中聲明的url拉取頁面,并write到當前頁面中防症。
發(fā)布版本控制
可以看到導(dǎo)入的頁面是"http://static.xxx.com/20170420/login.html"孟辑,中間的數(shù)字可以看做是版本,前端每次發(fā)布都可以創(chuàng)建一個新的文件夾蔫敲,保留之前的發(fā)布內(nèi)容饲嗽,如果遇到問題需要回滾之類的操作,只需要將config.js
中的window["index.page"]
指向新的版本頁面即可奈嘿;
頁內(nèi)資源
另外頁面中的css貌虾,js等資源的問題,我在load page操作的時候會在頁面中加入<base >
標簽裙犹,保證大部分的相對引用資源都是沒問題的(js代碼中的地址不受base影響尽狠,如ajax);
本地調(diào)試
為了方便本地調(diào)試叶圃,特別加了這一段
if (/[?&]supportreload\b/i.test(location.search)) {
window.loadPage = arguments.callee;
}
當訪問 “www.xxx.com/index.html?supportreload”的時候袄膏,會注冊一個全局方法window.loadPage
,可以load本地調(diào)試頁面掺冠,如:
window.loadPage("localhost:8080/login.html");
頁面跳轉(zhuǎn)
目前頁面跳轉(zhuǎn)有2個方案:
-
前端處理
前端使用angularjs的路由方式沉馆,使用hash切換頁面,首頁永遠不變赫舒;
這種方式對前端有一定要求悍及,后端代碼不需要任何修改闽瓢;
-
后端處理
后端使用url重寫功能接癌,將一個固定路徑的所有子路徑全部映射到index.html
,如:
config.Routes.MapHttpRoute(
name: "webhtml",
routeTemplate: "web/{*pattern}",
defaults: new { controller = "webhtml", action = "index" }
);
前端使用相對路徑的方式切換頁面扣讼,config.js
部分代碼做一些簡單的調(diào)整缺猛;
如:
window["index.page"] = "http://static.xxx.com/20170420" + location.pathname
現(xiàn)在就可以動態(tài)拉取頁面了,比如訪問
www.xxx.com/web/login.html的時候椭符,會拉取并展示"//static.xxx.com/20170420/login.html"
www.xxx.com/web/user/manager/info.html的時候荔燎,會拉取并展示"//static.xxx.com/20170420/user/manager/info.html"