一、博友@落幕殘情童鞋說到了,Nginx反向代理實現跨域,由於我目前尚未使用到,給忽略了,此次記錄下,爲下次補充。此坑已填css
二、提示:跨域的姊妹篇——《三十三║ ⅖ 種方法實現完美跨域》html
今天忙着給小夥伴們提出的問題解答,時間上沒把握好,都快下班了,趕忙發佈:書說上文《從壹開始先後端分離【 .NET Core2.0 +Vue2.0 】框架之十一 || AOP自定義篩選,Redis入門 11.1》,昨天我們說到了分佈式緩存鍵值數據庫,主要講解了如何安裝,使用,最後遺留了一個問題,同步+Redis緩存仍是比較簡單,如何使用異步泛型存取Redis,仍是一直個人心結,但願你們有會的,能夠不吝賜教,本系列教程已經基本到了尾聲,今天就說兩個小的知識點,既然本系列是講解先後端分離的,那必定會遇到跨域的問題,沒錯,今天將說下跨域!而後順便說一下DTOs(數據傳輸對象),這些東西你們都用過,好比,在MVC中定義一個ViewModel,是基於Model實體類的,而後作了相應的變化,以適應前端需求,沒錯,就是這個,若是大型的實體類,一個個複雜的話會稍顯費力,今天就是用一個自動映射工具——AutoMapper。前端
跨域問題由來已久,主要是來源於瀏覽器的」同源策略」。
何爲同源?只有當協議、端口、和域名都相同的頁面,則兩個頁面具備相同的源。只要網站的 協議名protocol、 主機host、 端口號port 這三個中的任意一個不一樣,網站間的數據請求與傳輸便構成了跨域調用,會受到同源策略的限制。 同源策略限制從一個源加載的文檔或腳本如何與來自另外一個源的資源進行交互。這是一個用於隔離潛在惡意文件的關鍵的安全機制。瀏覽器的同源策略,出於防範跨站腳本的攻擊,禁止客戶端腳本(如 JavaScript)對不一樣域的服務進行跨站調用(一般指使用XMLHttpRequest請求)。jquery
因此說咱們在web中,咱們沒法去獲取跨域的請求,常見的就是沒法經過js獲取接口(這裏要說下個人之前使用的經驗:在同源系統下,前端js去調用後端接口,而後後端C#去調取跨域接口,這是我之前採用的辦法,可是先後端分離,這個辦法確定就是不行了,由於那時候已經沒有了先後端之分,是兩個項目),因此咱們只要合理使用同源策略,就能夠達到跨域訪問的目的。nginx
我本身創建了一個靜態頁面,用來模擬前端訪問,具體以下步驟:git
新建一個Html頁面,使用Jquery來發送請求(文件在項目的WWW文件夾下,你們能夠本身下載,或者Copy下邊代碼)。github
一共三種跨域方法:web
<html> <head> <meta charset="utf-8"> <title>Blog.Core</title> <script src="https://cdn.bootcss.com/jquery/1.10.2/jquery.min.js"></script> <style> div { margin: 10px; word-wrap: break-word; } </style> <script> $(document).ready(function () { $("#jsonp").click(function () { $.getJSON("/api/Login/jsonp?callBack=?", function (data) { $("#data-jsonp").html("數據: " + data.value); }); }); $("#cors").click(function () { $.get("/api/Login/Token", function (data, status) { console.log(data); $("#status-cors").html("狀態: " + status); $("#data-cors").html("數據: " + data? data.token:"失敗"); }); }); $("#cors-post").click(function () { let postdata = { "bID": 10, "bsubmitter": "222", "btitle": "33333", "bcategory": "4444", "bcontent": "5555", "btraffic": 0, "bcommentNum": 0, "bUpdateTime": "2018-11-08T02:36:26.557Z", "bCreateTime": "2018-11-08T02:36:26.557Z", "bRemark": "string" }; $.ajax({ type: 'post', url: '/api/Values', contentType: 'application/json', data: JSON.stringify(postdata), success: function (data, status) { console.log(data); $("#status-cors-post").html("狀態: " + status); $("#data-cors-post").html("數據: " + JSON.stringify(data)); } }); //$.ajax({ // type: "POST", // url: "/api/Values", // success: function (data, status) { // console.log(data); // $("#status-cors-post").html("狀態: " + status); // $("#data-cors-post").html("數據: " + data); // } //}); }); }); </script> </head> <body> <h3>經過JsonP實現跨域請求</h3> <button id="jsonp">發送一個 GET </button> <div id="status-jsonp"></div> <div id="data-jsonp"></div> <hr /> <h3>添加請求頭實現跨域</h3> 無 <hr /> <h3>經過CORS實現跨域請求,另須要在服務器端配置CORE</h3> <button id="cors">發送一個 GET </button> <div id="status-cors"></div> <div id="data-cors"></div> <hr /> <button id="cors-post">發送一個 POST </button> <div id="status-cors-post"></div> <div id="data-cors-post"></div> <hr /> </body> </html>
注意:這裏必定要注意jsonp的前端頁面請求寫法,要求很嚴謹ajax
一、其實只須要當前Blog.Core 項目配置了靜態文件中間件,直接訪問就能夠
數據庫
好比個人在線地址:查看右側公告欄
二、單獨部署:將這個頁面部署到本身的IIS中(拷貝到文件裏,直接在iis添加該文件,訪問剛剛的Html文件目錄就行)
在咱們的項目 LoginController 中,設計Jsonp接口,Core調用的接口咱們已經有了,就是以前獲取Token的接口GetJWTStr
[HttpGet] [Route("jsonp")] public void Getjsonp(string callBack, long id = 1, string sub = "Admin", int expiresSliding = 30, int expiresAbsoulute = 30) { TokenModel tokenModel = new TokenModel(); tokenModel.Uid = id; tokenModel.Sub = sub; DateTime d1 = DateTime.Now; DateTime d2 = d1.AddMinutes(expiresSliding); DateTime d3 = d1.AddDays(expiresAbsoulute); TimeSpan sliding = d2 - d1; TimeSpan absoulute = d3 - d1; string jwtStr = BlogCoreToken.IssueJWT(tokenModel, sliding, absoulute);
//重要,必定要這麼寫 string response = string.Format("\"value\":\"{0}\"", jwtStr); string call = callBack + "({"+response+"})"; Response.WriteAsync(call); }
注意:這裏必定要注意jsonp的接口寫法,要求很嚴謹
四、點擊」經過JsonP實現跨域請求「按鈕,發現已經有數據了,證實Jsonp跨域已經成功,你能夠換成本身的域名試一試,可是Cors的還不行
這裏我沒有寫到代碼裏,是在通常處理程序裏以前用到的
public void ProcessRequest(HttpContext context) { //接收參數 string uName = context.Request["name"]; string data = "{\"name\":\"" + uName + "\",\"age\":\"18\"}"; //只需在服務端添加如下兩句 context.Response.AddHeader("Access-Control-Allow-Origin", "*"); //跨域能夠請求的方式 context.Response.AddHeader("Access-Control-Allow-Methods", "POST,GET"); context.Response.Write(data); }
function ashxRequest() { $.post("http://localhost:5551/ashxRequest.ashx", { name: "halo" }, function (data) { for (var i in data) { alert(data[i]); } }, "json") }
你們感興趣能夠本身實驗下。有問題請留言
前端的代碼在jsonp的時候已經寫好,請往上看第二節,後端接口也是Token接口
剩下的就是配置跨域了,很簡單!
在ConfigureServices中添加
#region CORS //跨域第二種方法,聲明策略,記得下邊app中配置 services.AddCors(c => { //↓↓↓↓↓↓↓注意正式環境不要使用這種全開放的處理↓↓↓↓↓↓↓↓↓↓ c.AddPolicy("AllRequests", policy => { policy .AllowAnyOrigin()//容許任何源 .AllowAnyMethod()//容許任何方式 .AllowAnyHeader()//容許任何頭 .AllowCredentials();//容許cookie }); //↑↑↑↑↑↑↑注意正式環境不要使用這種全開放的處理↑↑↑↑↑↑↑↑↑↑ //通常採用這種方法 c.AddPolicy("LimitRequests", policy => { policy .WithOrigins("http://127.0.0.1:1818", "http://localhost:8080", "http://localhost:8021", "http://localhost:8081", "http://localhost:1818")//支持多個域名端口,注意端口號後不要帶/斜杆:好比localhost:8000/,是錯的 .AllowAnyHeader()//Ensures that the policy allows any header. .AllowAnyMethod(); }); }); //注意還須要在下邊 Configure方法 中進行中間件配置 這裏有歧義,部分讀者可能沒看懂,請看下邊解釋 //services.AddCors(); #endregion
歧義解釋:
可能有些讀者會說,你這裏寫錯了,應該是 app.UseCors() ,我確定是知道的,那爲啥還要這麼寫呢,是由於這裏我提供了兩套 Cors 跨域寫法:
一、配置在 configureServices 中,而後再在管道中開啓中間件,就是上邊的寫法;
二、還有一個是,只在 configureServices 中,開啓服務,而後在中間件中,具體的配置:
//跨域第一種版本,請要ConfigureService中配置服務 services.AddCors(); // app.UseCors(options => options.WithOrigins("http://localhost:8021").AllowAnyHeader() //.AllowAnyMethod());
基本註釋都有,你們都能看的懂,就這麼簡單!
注意:在定義策略 LimitRequests 的時候,源域名應該是客戶端請求的端口域名,不是當前API的域名端口。
感謝博友 @學弱 提醒:CORS的配置必定要放在AutoFac前面,不然builder.Populate(services);後,你再進行配置會沒有效果。
在啓動文件 的Configure 配置方法裏,添加啓用Cors中間件服務
感謝博友@kiritio_ooo的提醒,Git已更新
注意:若是你使用了 app.UserMvc() 或者 app.UseHttpsRedirection()這類的中間件,必定要把 app.UseCors() 寫在它們的上邊,先進行跨域,再進行 Http 請求,不然會提示跨域失敗。
由於這兩個都是涉及到 Http請求的,若是你不跨域就直接轉發或者mvc,那確定報錯。
至此,跨域的問題已經完成辣
重要:若是你想查看效果,個人最新的Github上代碼已經給你們寫好了,你們clone之後,只須要執行 http://localhost:8081/corspost.html ,就能看到各類效果了。固然若是懶得下載,能夠看個人在線效果:查看右側公告欄
注意:這裏要說下,若是遇到了跨域失敗的提示,好比這樣:
這個並不必定是沒有配置好致使的跨域失敗,還有多是接口有錯誤,好比 500了,致使的接口異常,因此就提示訪問有錯誤。
請參考個人文章:
nginx是一個高性能的web服務器,經常使用做反向代理服務器。nginx做爲反向代理服務器,就是把http請求轉發到另外一個或者一些服務器上。
經過把本地一個url前綴映射到要跨域訪問的web服務器上,就能夠實現跨域訪問。
對於瀏覽器來講,訪問的就是同源服務器上的一個url。而nginx經過檢測url前綴,把http請求轉發到後面真實的物理服務器。並經過rewrite命令把前綴再去掉。這樣真實的服務器就能夠正確處理請求,而且並不知道這個請求是來自代理服務器的。
簡單說,nginx服務器欺騙了瀏覽器,讓它認爲這是同源調用,從而解決了瀏覽器的跨域問題。又經過重寫url,欺騙了真實的服務器,讓它覺得這個http請求是直接來自與用戶瀏覽器的。
這樣,爲了解決跨域問題,只須要動一下nginx配置文件便可。
三種辦法其實都能達到目的,可是優缺點也很明顯
一、手動建立JSONP跨域
優勢:無瀏覽器要求,能夠在任何瀏覽器中使用此方式
缺點:格式要求很嚴格,只支持get請求方式,請求的後端出錯不會有提示,形成不能處理異常
二、添加請求頭實現跨域
優勢:支持任意請求方式,而且後端出錯會像非跨域那樣有報錯,能夠對異常進行處理
缺點:兼容性不是很好,IE的話 <IE10 都不支持此方式
雖然CORS的方法有點兒相似請求頭,可是封裝,兼容性,靈活性都要好的不少,強烈推薦。
請看如下實體類
//數據庫實體類 public class Author { public string Name { get; set; } } public class Book { public string Title { get; set; } public Author Author { get; set; } } //頁面實體類 public class BookViewModel { public string Title { get; set; } public string Author { get; set; } } //api調用 BookViewModel model = new BookViewModel { Title = book.Title, Author = book.Author.Name }
上面的例子至關的直觀了,咱們平時也是這麼用的基本,可是問題也隨之而來了,咱們能夠看到在上面的代碼中,若是一旦在Book對象裏添加了一個額外的字段,然後想在前臺頁面輸出這個字段,那麼就須要去在項目裏找到每一處有這樣BookViewModel轉換字段的地方,這是很是繁瑣的。另外,BookViewModel.Author是一個string類型的字段,可是Book.Author屬性倒是Author對象類型的,咱們用的解決方法是經過Book.Auther對象來取得Author的Name屬性值,而後再賦值給BookViewModel的Author屬性,這樣看起行的通,可是想想,若是打算在之後的開發中把Name拆分紅兩個-FisrtName和LastName,個人天吶!咱們得去把原來的ViewModel對象也拆分紅對應的兩個字段,而後在項目中找到全部的轉換,而後替換。
那麼有什麼辦法或者工具來幫助咱們可以避免這樣的狀況發生呢?AutoMapper正是符合要求的一款插件。只需一鍵操做,就能一勞永逸,解決全部問題,而後經過依賴注入,快速使用:
//AutoMapper自動映射 //Mapper.Initialize(cfg => cfg.CreateMap<BlogArticle, BlogViewModels>()); //BlogViewModels models = Mapper.Map<BlogArticle, BlogViewModels>(blogArticle); BlogViewModels models = IMapper.Map<BlogViewModels>(blogArticle);//就這一句話徹底搞定全部轉換
今天由於時間的關係,沒有說到Automapper,明天再見吧~