從壹開(kāi)始前后端分離【 .NET Core2.0 +Vue2.0 】框架之十二 || 三種跨域方式比較齐唆,DTOs(數(shù)據(jù)傳輸對(duì)象)初探

代碼已上傳Github+Gitee,文末有地址

今天忙著給小伙伴們提出的問(wèn)題解答冻河,時(shí)間上沒(méi)把握好箍邮,都快下班了,感覺(jué)發(fā)布:書(shū)說(shuō)上文《從壹開(kāi)始前后端分離【 .NET Core2.0 +Vue2.0 】框架之十一 || AOP自定義篩選叨叙,Redis入門(mén) 11.1》锭弊,昨天咱們說(shuō)到了分布式緩存鍵值數(shù)據(jù)庫(kù),主要講解了如何安裝擂错,使用味滞,最后遺留了一個(gè)問(wèn)題,同步+Redis緩存還是比較簡(jiǎn)單钮呀,如何使用異步泛型存取Redis剑鞍,還是一直我的心結(jié),希望大家有會(huì)的爽醋,可以不吝賜教蚁署,本系列教程已經(jīng)基本到了尾聲,今天就說(shuō)兩個(gè)小的知識(shí)點(diǎn)子房,既然本系列是講解前后端分離的形用,那一定會(huì)遇到跨域的問(wèn)題,沒(méi)錯(cuò)证杭,今天將說(shuō)下跨域田度!然后順便說(shuō)一下DTOs(數(shù)據(jù)傳輸對(duì)象),這些東西大家都用過(guò)解愤,比如镇饺,在MVC中定義一個(gè)ViewModel,是基于Model實(shí)體類(lèi)的送讲,然后做了相應(yīng)的變化奸笤,以適應(yīng)前端需求,沒(méi)錯(cuò)哼鬓,就是這個(gè)监右,如果大型的實(shí)體類(lèi),一個(gè)個(gè)復(fù)雜的話會(huì)稍顯費(fèi)力异希,今天就是用一個(gè)自動(dòng)映射工具——AutoMapper健盒。

零、今天完成左下角的深紫色部分

image

一称簿、為什么會(huì)出現(xiàn)跨域的問(wèn)題

跨域問(wèn)題由來(lái)已久扣癣,主要是來(lái)源于瀏覽器的”同源策略”。
? 何為同源憨降?只有當(dāng)協(xié)議父虑、端口、和域名都相同的頁(yè)面授药,則兩個(gè)頁(yè)面具有相同的源士嚎。只要網(wǎng)站的 協(xié)議名protocol、 主機(jī)host悔叽、 端口號(hào)port 這三個(gè)中的任意一個(gè)不同航邢,網(wǎng)站間的數(shù)據(jù)請(qǐng)求與傳輸便構(gòu)成了跨域調(diào)用,會(huì)受到同源策略的限制骄蝇。 ? 同源策略限制從一個(gè)源加載的文檔或腳本如何與來(lái)自另一個(gè)源的資源進(jìn)行交互膳殷。這是一個(gè)用于隔離潛在惡意文件的關(guān)鍵的安全機(jī)制。瀏覽器的同源策略九火,出于防范跨站腳本的攻擊赚窃,禁止客戶(hù)端腳本(如 JavaScript)對(duì)不同域的服務(wù)進(jìn)行跨站調(diào)用(通常指使用XMLHttpRequest請(qǐng)求)。

所以說(shuō)我們?cè)趙eb中岔激,我們無(wú)法去獲取跨域的請(qǐng)求勒极,常見(jiàn)的就是無(wú)法通過(guò)js獲取接口(這里要說(shuō)下我的以前使用的經(jīng)驗(yàn):在同源系統(tǒng)下,前端js去調(diào)用后端接口虑鼎,然后后端C#去調(diào)取跨域接口辱匿,這是我以前采用的辦法键痛,但是前后端分離,這個(gè)辦法肯定就是不行了匾七,因?yàn)槟菚r(shí)候已經(jīng)沒(méi)有了前后端之分絮短,是兩個(gè)項(xiàng)目),所以我們只要合理使用同源策略昨忆,就可以達(dá)到跨域訪問(wèn)的目的丁频。

二、如何達(dá)到跨域的目的——三種跨域方式 之JsonP

我自己建立了一個(gè)一個(gè)靜態(tài)頁(yè)面邑贴,用來(lái)模擬前端訪問(wèn)席里,具體如下步驟:

1、新建一個(gè)Html頁(yè)面拢驾,使用Jquery來(lái)發(fā)送請(qǐng)求(文件在項(xiàng)目的WWW文件夾下奖磁,大家可以自己下載,或者Copy下邊代碼)繁疤。

一共三種跨域方法

<!DOCTYPE html>
<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("http://localhost:58427/api/Login/jsonp?callBack=?", function (data) {
                    $("#data-jsonp").html("數(shù)據(jù): " + data.value);
                });
            });

            $("#cors").click(function () {
                $.get("http://localhost:58427/api/Login/Token", function (data, status) {
                    $("#status-cors").html("狀態(tài): " + status);
                    $("#data-cors").html("數(shù)據(jù): " + data);
                });
            });

        }); </script>
</head>
<body>

    <h3>通過(guò)JsonP實(shí)現(xiàn)跨域請(qǐng)求</h3>
    <button id="jsonp">發(fā)送一個(gè) GET </button>

    <div id="status-jsonp"></div>
    <div id="data-jsonp"></div>
    <hr />

    <h3>添加請(qǐng)求頭實(shí)現(xiàn)跨域</h3>
    <hr />

    <h3>通過(guò)CORS實(shí)現(xiàn)跨域請(qǐng)求署穗,另需要在服務(wù)器段配置CORE</h3>
    <button id="cors">發(fā)送一個(gè) GET </button>

    <div id="status-cors"></div>
    <div id="data-cors"></div>
    <hr />
</body>
</html>

注意:這里一定要注意jsonp的前端頁(yè)面請(qǐng)求寫(xiě)法,要求很?chē)?yán)謹(jǐn)

2嵌洼、將這個(gè)頁(yè)面部署到自己的IIS中(拷貝到文件里案疲,直接在iis添加該文件,訪問(wèn)剛剛的Html文件目錄就行)

image

3麻养、在我們的項(xiàng)目 LoginController 中褐啡,設(shè)計(jì)Jsonp接口,Core調(diào)用的接口我們已經(jīng)有了鳖昌,就是之前獲取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);

        //重要备畦,一定要這么寫(xiě) string response = string.Format("\"value\":\"{0}\"", jwtStr); string call = callBack + "({"+response+"})";
            Response.WriteAsync(call);
        }

注意:這里一定要注意jsonp的接口寫(xiě)法,要求很?chē)?yán)謹(jǐn)

image

4许昨、點(diǎn)擊”通過(guò)JsonP實(shí)現(xiàn)跨域請(qǐng)求“按鈕懂盐,發(fā)現(xiàn)已經(jīng)有數(shù)據(jù)了,證明Jsonp跨域已經(jīng)成功糕档,你可以換成自己的域名試一試莉恼,但是Cors的還不行

image

三、如何達(dá)到跨域的目的——三種跨域方式 之添加請(qǐng)求頭實(shí)現(xiàn)跨域

這里我沒(méi)有寫(xiě)到代碼里速那,是在一般處理程序里之前用到的

** 后端**

        public void ProcessRequest(HttpContext context)
        { //接收參數(shù)
            string uName = context.Request["name"]; string data = "{\"name\":\"" + uName + "\",\"age\":\"18\"}"; //只需在服務(wù)端添加以下兩句
            context.Response.AddHeader("Access-Control-Allow-Origin", "*"); //跨域可以請(qǐng)求的方式
            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")
        }

大家感興趣可以自己實(shí)驗(yàn)下俐银。有問(wèn)題請(qǐng)留言

四、如何達(dá)到跨域的目的——三種跨域方式 之 高效CROS

1端仰、前端的代碼在jsonp的時(shí)候已經(jīng)寫(xiě)好捶惜,請(qǐng)往上看第二節(jié),后端接口也是Token接口

剩下的就是配置跨域了荔烧,很簡(jiǎn)單吱七!

2汽久、在ConfigureServices中添加

        #region CORS services.AddCors(c => { //↓↓↓↓↓↓↓注意正式環(huán)境不要使用這種全開(kāi)放的處理↓↓↓↓↓↓↓↓↓↓
                c.AddPolicy("AllRequests", policy => {
                    policy
                    .AllowAnyOrigin()//允許任何源
                    .AllowAnyMethod()//允許任何方式
                    .AllowAnyHeader()//允許任何頭
                    .AllowCredentials();//允許cookie
 }); //↑↑↑↑↑↑↑注意正式環(huán)境不要使用這種全開(kāi)放的處理↑↑↑↑↑↑↑↑↑↑ //一般采用這種方法
                c.AddPolicy("LimitRequests", policy => {
                    policy
                    .WithOrigins("http://localhost:8020", "http://blog.core.xxx.com","")//支持多個(gè)域名端口
                    .WithMethods("GET", "POST", "PUT", "DELETE")//請(qǐng)求方法添加到策略
                    .WithHeaders("authorization");//標(biāo)頭添加到策略
 });

            }); #endregion

基本注釋都有,大家都能看的懂踊餐,就這么簡(jiǎn)單景醇!

3、在需要跨域的controller上市袖,增加特性(本文因?yàn)樵贚oginController啡直,所以在這個(gè)控制器里)烁涌,注意名稱(chēng)要寫(xiě)對(duì) LimitRequests

    [Produces("application/json")]
    [Route("api/Login")]
    [EnableCors("LimitRequests")]//就是這里 public class LoginController : Controller
    { //....
     }
image

** 4苍碟、好啦運(yùn)行調(diào)試,一切正常**

image

至此撮执,跨域的問(wèn)題已經(jīng)完成辣

五微峰、結(jié)語(yǔ)

三種辦法其實(shí)都能達(dá)到目的,但是優(yōu)缺點(diǎn)也很明顯

1抒钱、手動(dòng)創(chuàng)建JSONP跨域

優(yōu)點(diǎn):無(wú)瀏覽器要求蜓肆,可以在任何瀏覽器中使用此方式

缺點(diǎn):格式要求很?chē)?yán)格,只支持get請(qǐng)求方式谋币,請(qǐng)求的后端出錯(cuò)不會(huì)有提示,造成不能處理異常

2仗扬、添加請(qǐng)求頭實(shí)現(xiàn)跨域

優(yōu)點(diǎn):支持任意請(qǐng)求方式,并且后端出錯(cuò)會(huì)像非跨域那樣有報(bào)錯(cuò),可以對(duì)異常進(jìn)行處理

缺點(diǎn):兼容性不是很好蕾额,IE的話 <IE10 都不支持此方式

雖然CORS的方法有點(diǎn)兒類(lèi)似請(qǐng)求頭早芭,但是封裝,兼容性诅蝶,靈活性都要好的很多退个,強(qiáng)烈推薦。

六调炬、初探DTOs

請(qǐng)看以下實(shí)體類(lèi)

//數(shù)據(jù)庫(kù)實(shí)體類(lèi)
public class Author
{ public string Name { get; set; }
} public class Book
{ public string Title { get; set; } public Author Author { get; set; }
} //頁(yè)面實(shí)體類(lèi)
public class BookViewModel
{ public string Title { get; set; } public string Author { get; set; }
} //api調(diào)用
BookViewModel model = new BookViewModel
{
    Title = book.Title,
    Author = book.Author.Name
}

上面的例子相當(dāng)?shù)闹庇^了语盈,我們平時(shí)也是這么用的基本,但是問(wèn)題也隨之而來(lái)了缰泡,我們可以看到在上面的代碼中刀荒,如果一旦在Book對(duì)象里添加了一個(gè)額外的字段,而后想在前臺(tái)頁(yè)面輸出這個(gè)字段棘钞,那么就需要去在項(xiàng)目里找到每一處有這樣BookViewModel轉(zhuǎn)換字段的地方照棋,這是非常繁瑣的。另外武翎,BookViewModel.Author是一個(gè)string類(lèi)型的字段,但是Book.Author屬性卻是Author對(duì)象類(lèi)型的烈炭,我們用的解決方法是通過(guò)Book.Auther對(duì)象來(lái)取得Author的Name屬性值,然后再賦值給BookViewModel的Author屬性宝恶,這樣看起行的通符隙,但是想一想趴捅,如果打算在以后的開(kāi)發(fā)中把Name拆分成兩個(gè)-FisrtName和LastName,我的天吶!我們得去把原來(lái)的ViewModel對(duì)象也拆分成對(duì)應(yīng)的兩個(gè)字段霹疫,然后在項(xiàng)目中找到所有的轉(zhuǎn)換拱绑,然后替換。
那么有什么辦法或者工具來(lái)幫助我們能夠避免這樣的情況發(fā)生呢丽蝎?AutoMapper正是符合要求的一款插件猎拨。

只需一鍵操作,就能一勞永逸屠阻,解決所有問(wèn)題红省,然后通過(guò)依賴(lài)注入,快速使用:

      //AutoMapper自動(dòng)映射 //Mapper.Initialize(cfg => cfg.CreateMap<BlogArticle, BlogViewModels>()); //BlogViewModels models = Mapper.Map<BlogArticle, BlogViewModels>(blogArticle);
 BlogViewModels models = IMapper.Map<BlogViewModels>(blogArticle);//就這一句話完全搞定所有轉(zhuǎn)換

今天因?yàn)闀r(shí)間的關(guān)系国觉,沒(méi)有說(shuō)到Automapper吧恃,明天再見(jiàn)吧~

七、CODE

https://github.com/anjoy8/Blog.Core

https://gitee.com/laozhangIsPhi/Blog.Core

QQ群:
867095512 (blod.core)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末麻诀,一起剝皮案震驚了整個(gè)濱河市痕寓,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蝇闭,老刑警劉巖呻率,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異呻引,居然都是意外死亡礼仗,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)苞七,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)藐守,“玉大人,你說(shuō)我怎么就攤上這事蹂风÷В” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵惠啄,是天一觀的道長(zhǎng)慎恒。 經(jīng)常有香客問(wèn)我,道長(zhǎng)撵渡,這世上最難降的妖魔是什么融柬? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮趋距,結(jié)果婚禮上粒氧,老公的妹妹穿的比我還像新娘。我一直安慰自己节腐,他們只是感情好外盯,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布摘盆。 她就那樣靜靜地躺著,像睡著了一般饱苟。 火紅的嫁衣襯著肌膚如雪孩擂。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,165評(píng)論 1 299
  • 那天箱熬,我揣著相機(jī)與錄音类垦,去河邊找鬼。 笑死城须,一個(gè)胖子當(dāng)著我的面吹牛蚤认,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播酿傍,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼烙懦,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼驱入!你這毒婦竟也來(lái)了赤炒?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤亏较,失蹤者是張志新(化名)和其女友劉穎莺褒,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體雪情,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡遵岩,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了巡通。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片尘执。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖宴凉,靈堂內(nèi)的尸體忽然破棺而出誊锭,到底是詐尸還是另有隱情,我是刑警寧澤弥锄,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布丧靡,位于F島的核電站,受9級(jí)特大地震影響籽暇,放射性物質(zhì)發(fā)生泄漏温治。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一戒悠、第九天 我趴在偏房一處隱蔽的房頂上張望熬荆。 院中可真熱鬧,春花似錦绸狐、人聲如沸卤恳。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)纬黎。三九已至幅骄,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間本今,已是汗流浹背拆座。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留冠息,地道東北人挪凑。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像逛艰,于是被迫代替她去往敵國(guó)和親躏碳。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • 什么是跨域 跨域散怖,是指瀏覽器不能執(zhí)行其他網(wǎng)站的腳本菇绵。它是由瀏覽器的同源策略造成的,是瀏覽器對(duì)JavaScript實(shí)...
    他方l閱讀 1,064評(píng)論 0 2
  • 什么是跨域 跨域镇眷,是指瀏覽器不能執(zhí)行其他網(wǎng)站的腳本咬最。它是由瀏覽器的同源策略造成的,是瀏覽器對(duì)JavaScript實(shí)...
    HeroXin閱讀 836評(píng)論 0 4
  • 什么是跨域 跨域欠动,是指瀏覽器不能執(zhí)行其他網(wǎng)站的腳本永乌。它是由瀏覽器的同源策略造成的,是瀏覽器對(duì)JavaScript實(shí)...
    Yaoxue9閱讀 1,299評(píng)論 0 6
  • 1. 什么是跨域 跨域具伍,是指瀏覽器不能執(zhí)行其他網(wǎng)站的腳本翅雏。它是由瀏覽器的同源策略造成的,是瀏覽器對(duì)JavaScri...
    cbw100閱讀 6,328評(píng)論 2 86
  • 教育史上的經(jīng)典著作永遠(yuǎn)是教育工作者的最佳啟蒙人芽。 1.從絕對(duì)的意義上說(shuō)望几,心靈原來(lái)是一張白紙,沒(méi)有任何生命和觀念的形式...
    行走的教育閱讀 476評(píng)論 0 4