前端跨域新方案嘗試

簡書原文css

自從先後端開發實現了愈來愈完全的分離,開發中遇到的跨域問題也隨之愈來愈多;
而不管是跨域請求JSONPCORS或者window跨域window.namewindow.postMessage,在實際開發使用中的表現都不夠完美。
相對來講 CORS 是官方的功能比較完善的方案,但除了須要服務器和瀏覽器雙方同時支持外,還有不少限制,好比Access-Control-Allow-Origin:*不能發送cookie等,並且若是服務器設置不當也存在着一些安全隱患。html

固然,我寫這篇的重點不是吐槽,並且解決問題的。
更多關於跨域的資料請自行查閱,相關內容前端

《瀏覽器的同源策略》
《跨域資源共享 CORS 詳解》
《 深刻理解前端跨域方法和原理》angularjs

設計

我並非一個前端開發,在以前的很長時間裏我都在作着後端開發的工做;
一個偶然的機會接觸到了angularJS的前端路由,當時我就想到了一個點子——作一個index.html頁面,這個頁面只有一段js腳本,腳本的功能是將另一個存放於靜態存儲服務器的html頁面整個拉過來,寫到當前頁面中web

舉個栗子

好比我有一個服務器是這樣的:
api服務站點:www.xxx.com
靜態存儲服務:static.xxx.com
登陸頁面:static.xxx.com/20170420/login.html (中間的數字表示版本,下面會講到)
登陸接口:www.xxx.com/api/user/loginajax

index.html

很顯然,在登陸頁面調用登陸接口是一個跨域的行爲;
因此我如今在www.xxx.com中放一個index.html頁面
內容以下:後端

<script src="//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,內容以下:api

window["index.page"] = "//static.xxx.com/20170420/login.html"

流程

流程大體是這樣的

能夠看到,在index.html頁面被加載的同時,我引用了一個config.js,這個js也是存放在靜態資源服務器的,裏面聲明瞭一個參數window["index.page"],而index.html頁面會用這個變量中聲明的url拉取頁面,並write到當前頁面中。跨域

發佈版本控制

能夠看到導入的頁面是"//static.xxx.com/20170420/login.html",中間的數字能夠看作是版本,前端每次發佈均可以建立一個新的文件夾,保留以前的發佈內容,若是遇到問題須要回滾之類的操做,只須要將config.js中的window["index.page"]指向新的版本頁面便可;瀏覽器

頁內資源

另外頁面中的css,js等資源的問題,我在load page操做的時候會在頁面中加入<base >標籤,保證大部分的相對引用資源都是沒問題的(js代碼中的地址不受base影響,如ajax);

本地調試

爲了方便本地調試,特別加了這一段

if (/[?&]supportreload\b/i.test(location.search)) {
    window.loadPage = arguments.callee;
}

當訪問 「www.xxx.com/index.html?supportreload」的時候,會註冊一個全局方法window.loadPage,能夠load本地調試頁面,如:

window.loadPage("localhost:8080/login.html");

頁面跳轉

目前頁面跳轉有2個方案:

  1. 前端處理
    前端使用angularjs的路由方式,使用hash切換頁面,首頁永遠不變;
    這種方式對前端有必定要求,後端代碼不須要任何修改;
  2. 後端處理
    後端使用url重寫功能,將一個固定路徑的全部子路徑所有映射到index.html,如:
config.Routes.MapHttpRoute(
        name: "webhtml",
        routeTemplate: "web/{*pattern}",
        defaults: new { controller = "webhtml", action = "index" }
);

前端使用相對路徑的方式切換頁面,config.js部分代碼作一些簡單的調整;
如:

window["index.page"] = "//static.xxx.com/20170420" +  location.pathname

如今就能夠動態拉取頁面了,好比訪問
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"


相關文章
相關標籤/搜索