用thinkjs也有一小段時間了,和其它國產框架一樣,起初是處於觀望態度。當然我最先的選擇也不是thinkjs而是選的express,用到后面發現實現一個能讓自己用着比較順手的博客還是一件蠻困難或者說是繁瑣的事情。遠沒有自己起初所想的那樣簡單,再次感嘆一切貴在堅持...
express雖然足夠簡潔,不過想要重頭開始實現一個相對完善的博客程序無疑是需要很大工作量的,想到馬上過期的虛擬機,thinkjs這樣更加全面和強大的mvc框架變成了我理想的選擇,其中的諸多特性(靈活的路由,方便的MVC,完善的數據校驗,面向未來的Es6/7支持,簡潔的數據庫CURD操作等等)足夠自己使用了。
-- 。--~啰嗦了,回到正題,由於后台管理界面我選擇了前后端分離的模式,而且分成了兩塊不同的構建項目(有點折騰,不過個人覺得兩個攪和在一起作為一個項目進行構建會更讓我頭大哈哈,畢竟現在前端也變得復雜,有自己一套獨立的構建工具流),自然也有了跨域的需求,好在官方文檔有提供相對完善的配置說明,我用了以下設置:
this.header("Access-Control-Allow-Origin", this.header("origin") || "*");
似乎簡單的一句就解決了跨域問題,得益於標准這確實解決了我們一些跨域場景需求。 上邊提到的CORS是一個W3C標准,全稱是"跨域資源共享"(Cross-origin resource sharing)
進一步的需求(支持跨域cookie)
跨域的同時我又遇到了需要給客戶端cookie,CORS同樣可以做到, 可以像下面這樣:
this.header('Access-Control-Allow-Credentials',true); // 在服務端設置
// 客戶端同樣設置,這里以Jquery為例(約定好規則 😅類似一個願打一個願挨)
// 跨域設置cokkie $.ajax({ url: a_cross_domain_url, xhrFields: { withCredentials: true } });
還可以跨域設置cookie的需求我們也解決了。
進進一步的需求(支持REST API)
當我以為一切OK的時候,REST的DELETE和PUT請求卻唱起了反調,這是要給好處費嗎....因為用到了REST API,僅使用上邊的設置還不能實現DELETE及PUT的跨域請求,表示我內心當時是崩潰的,折騰了兩天,總算找到了問題所在,起先找的Thinkjs官網文檔,有提供例子,不看還好,這一看徹底掉坑里了。
Thinkjs官網文檔
// 如果是在 REST API,那么可以放在 __call 方法里判斷,如: export default class extends think.controller.base { __call(){ let method = this.http.method.toLowerCase(); if(method === "options"){ this.setCorsHeader(); this.end(); return; } this.setCorsHeader(); return super.__call(); } setCorsHeader(){ this.header("Access-Control-Allow-Origin", this.header("origin") || "*"); this.header("Access-Control-Allow-Headers", "x-requested-with"); this.header("Access-Control-Request-Method", "GET,POST,PUT,DELETE"); this.header("Access-Control-Allow-Credentials", "true"); } }
爬出坑的設置
// 服務端代碼 async __before() { this.header("Access-Control-Allow-Origin", this.header("origin") || "*"); this.header("Access-Control-Allow-Headers", "x-requested-with"); this.header("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE"); this.header('Access-Control-Allow-Credentials',true); let method = this.http.method.toLowerCase(); if(method === "options"){ this.end(); return; } let isLogin = await this.session('userInfo'); if(!isLogin){ this.fail('AUTH_FAILED'); } }
this.header("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
就是這家伙了,隱藏得比較深,不過本質不壞,就靠它干活。
回顧並梳理
REST請求可以分成兩種,一種是簡單請求,GET,POST及HEAD都算其中。另一種就是非簡單請求,顧名思意它要復雜一些,按鈕w3c規范它會先發一次預請求(Preflighted requests),去詢問下服務器我能不能滿足要求,滿足的話會返回一系列CORS頭信息設置,返回請求后客戶端同樣會驗證,這兩家伙表示彼此都不信任對方0。0,客戶端會受瀏覽器的同源策略影響,因為相對GET POST HEAD請求,PUT DELETE請求安全性要求會更高些,驗證更嚴格也是理所應當的。其中Orgin會自動被服務端獲取,客戶端可以不用配置,默認包含在請求頭中發送,其它的設置基本都需要兩兩對應設置。
兼容性
現代瀏覽器均支持,IE10有完整實現,IE8 9需要通過 XDomainRequest 對象來實現CORS(¬_¬)。
圖示
簡單請求
cokkie設置
非簡單請求(預請求+主請求)
綜合
Tips:想要更詳細的了解可以查看底下列出的參考文檔!不知覺中表示寫的太多了點....