西西軟件園多重安全檢測(cè)下載網(wǎng)站、值得信賴(lài)的軟件下載站!
軟件
軟件
文章
搜索

首頁(yè)編程開(kāi)發(fā)ASP.NET → 在ASP.NET Web API中是如何實(shí)現(xiàn)跨域資源共享?

在ASP.NET Web API中是如何實(shí)現(xiàn)跨域資源共享?

相關(guān)軟件相關(guān)文章發(fā)表評(píng)論 來(lái)源:Artech時(shí)間:2013/12/16 8:36:04字體大。A-A+

作者:Artech點(diǎn)擊:21次評(píng)論:1次標(biāo)簽: 跨域

  • 類(lèi)型:磁盤(pán)工具大小:18.8M語(yǔ)言:中文 評(píng)分:5.2
  • 標(biāo)簽:
立即下載

在《通過(guò)擴(kuò)展讓ASP.NET Web API支持W3C的CORS規(guī)范》中,我們通過(guò)自定義的HttpMessageHandler自行為ASP.NET Web API實(shí)現(xiàn)了針對(duì)CORS的支持,實(shí)際上ASP.NET Web API自身也是這么做的,該自定義HttpMessageHandler就是System.Web.Http.Cors.CorsMessageHandler。


   1: public class CorsMessageHandler : DelegatingHandler
   2: {
   3:     public CorsMessageHandler(HttpConfiguration httpConfiguration);
   4:     protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken);
   5:
   6:     public virtual Task<HttpResponseMessage> HandleCorsPreflightRequestAsync(HttpRequestMessage request, CorsRequestContext corsRequestContext, CancellationToken cancellationToken);
   7:     public virtual Task<HttpResponseMessage> HandleCorsRequestAsync(HttpRequestMessage request, CorsRequestContext corsRequestContext, CancellationToken cancellationToken);
   8: }


CorsMessageHandler的核心功能在于:提取預(yù)定義的CORS授權(quán)策略并對(duì)當(dāng)前請(qǐng)求實(shí)施授權(quán)檢驗(yàn),并根據(jù)授權(quán)檢驗(yàn)的結(jié)果為現(xiàn)有的響應(yīng)(針對(duì)簡(jiǎn)單跨域資源請(qǐng)求和繼預(yù)檢請(qǐng)求之后發(fā)送的真正跨域資源請(qǐng)求)或者新創(chuàng)建的響應(yīng)(針對(duì)預(yù)檢請(qǐng)求)添加相應(yīng)的CORS報(bào)頭。如上面的代碼片斷所示,CorsMessageHandler定義了HandleCorsPreflightRequestAsync和HandleCorsRequestAsync虛方法,它們分別實(shí)現(xiàn)針對(duì)預(yù)檢請(qǐng)求和非預(yù)檢請(qǐng)求的CORS授權(quán)檢驗(yàn)。

在實(shí)現(xiàn)的SendAsync方法中,當(dāng)CorsRequestContext根據(jù)表示當(dāng)前請(qǐng)求的HttpRequestMessage對(duì)象創(chuàng)建之后,會(huì)根據(jù)其IsPreflight屬性選擇調(diào)用方法HandleCorsPreflightRequestAsync或者HandleCorsRequestAsync。

CORS授權(quán)檢驗(yàn)

實(shí)現(xiàn)在CorsMessageHandler中的具體CORS授權(quán)檢驗(yàn)流程基本上體現(xiàn)在右圖中。它首先根據(jù)表示當(dāng)前請(qǐng)求的HttpRequestMessage對(duì)象創(chuàng)建CorsRequestContext對(duì)象。然后利用注冊(cè)的CorsProviderFactory得到對(duì)應(yīng)的CorsProvider對(duì)象,并利用后者得到針對(duì)當(dāng)前請(qǐng)求的資源授權(quán)策略,這是一個(gè)CorsPolicy對(duì)象。

接下來(lái),CorsMessageHandler會(huì)獲取注冊(cè)的CorsEngine。此前得到的CorsRequestContext和CorsPolicy對(duì)象會(huì)作為參數(shù)調(diào)用CorsEngine的EvaluatePolicy方法,CORS資源授權(quán)檢驗(yàn)由此開(kāi)始。授權(quán)檢驗(yàn)結(jié)束之后,CorsMessageHandler會(huì)得到表示檢驗(yàn)結(jié)果的CorsResult對(duì)象。

對(duì)于預(yù)檢請(qǐng)求,CorsMessageHandler會(huì)直接創(chuàng)建HttpResponseMessage對(duì)象予以響應(yīng)。具體來(lái)說(shuō),如果預(yù)檢請(qǐng)求通過(guò)了授權(quán)檢驗(yàn),一個(gè)狀態(tài)為“200, OK”的HttpResponseMessage會(huì)被創(chuàng)建出來(lái),通過(guò)CorsResult得到CORS響應(yīng)報(bào)頭會(huì)被添加到這個(gè)HttpResponseMessage對(duì)象的報(bào)頭集合中。如果授權(quán)檢驗(yàn)失敗,創(chuàng)建的HttpResponseMessage具有的狀態(tài)為“400, Bad Request”,CorsResult攜帶的錯(cuò)誤響應(yīng)會(huì)作為響應(yīng)的主體內(nèi)容。

對(duì)于非預(yù)檢請(qǐng)求,它會(huì)將當(dāng)前請(qǐng)求傳遞給消息處理管道的后續(xù)部分進(jìn)行進(jìn)一步處理,并最終得到表示響應(yīng)消息的HttpResponseMessage。只有在請(qǐng)求通過(guò)授權(quán)檢查的情況下,由CorsResult得到的CORS響應(yīng)報(bào)頭才會(huì)被添加到此HttpResponseMessage的報(bào)頭集合中。

實(shí)例演示:創(chuàng)建MyCorsMessageHandler模擬具體采用的授權(quán)檢驗(yàn)

為了讓讀者朋友們對(duì)實(shí)現(xiàn)在CorsMessageHandler中的具體CORS資源授權(quán)流程具有更加深刻的認(rèn)識(shí),我們現(xiàn)在將這樣的授權(quán)檢驗(yàn)邏輯實(shí)現(xiàn)在一個(gè)自定義的HttpMessageHandler中。為此我們定義了如下一個(gè)MyCorsMessageHandler類(lèi)型,由于它僅僅用于模擬CorsMessageHandler大體實(shí)現(xiàn)邏輯,所以我們會(huì)忽略很多細(xì)節(jié)上(比如異常處理)的代碼。


   1: public class MyCorsMessageHandler: DelegatingHandler
   2: {
   3:     protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
   4:     {
   5:         //根據(jù)當(dāng)前請(qǐng)求創(chuàng)建CorsRequestContext
   6:         CorsRequestContext context = request.CreateCorsRequestContext();
   7:
   8:         //針對(duì)非預(yù)檢請(qǐng)求:將請(qǐng)求傳遞給消息處理管道后續(xù)部分繼續(xù)處理,并得到響應(yīng)
   9:         HttpResponseMessage response = null;
  10:         if (!context.IsPreflight)
  11:         {
  12:             response = await base.SendAsync(request, cancellationToken);
  13:         }
  14:
  15:         //利用注冊(cè)的CorsPolicyProviderFactory得到對(duì)應(yīng)的CorsPolicyProvider
  16:         //借助于CorsPolicyProvider得到表示CORS資源授權(quán)策略的CorsPolicy
  17:         HttpConfiguration configuration = request.GetConfiguration();
  18:         CorsPolicy policy = await configuration.GetCorsPolicyProviderFactory().GetCorsPolicyProvider(request).GetCorsPolicyAsync(request,cancellationToken);
  19:
  20:         //獲取注冊(cè)的CorsEngine
  21:         //利用CorsEngine對(duì)請(qǐng)求實(shí)施CORS資源授權(quán)檢驗(yàn),并得到表示檢驗(yàn)結(jié)果的CorsResult對(duì)象
  22:         ICorsEngine engine = configuration.GetCorsEngine();
  23:         CorsResult result = engine.EvaluatePolicy(context, policy);
  24:
  25:         //針對(duì)預(yù)檢請(qǐng)求
  26:         //如果請(qǐng)求通過(guò)授權(quán)檢驗(yàn),返回一個(gè)狀態(tài)為“200, OK”的響應(yīng)并添加CORS報(bào)頭
  27:         //如果授權(quán)檢驗(yàn)失敗,返回一個(gè)狀態(tài)為“400, Bad Request”的響應(yīng)并指定授權(quán)失敗原因
  28:         if (context.IsPreflight)
  29:         {
  30:             if (result.IsValid)
  31:             {
  32:                 response = new HttpResponseMessage(HttpStatusCode.OK);
  33:                 response.AddCorsHeaders(result);
  34:             }
  35:             else
  36:             {
  37:                 response = request.CreateErrorResponse(HttpStatusCode.BadRequest,string.Join(" |", result.ErrorMessages.ToArray()));
  38:             }
  39:         }
  40:         //針對(duì)非預(yù)檢請(qǐng)求
  41:         //CORS報(bào)頭只有在通過(guò)授權(quán)檢驗(yàn)情況下才會(huì)被添加到響應(yīng)報(bào)頭集合中
  42:         else if (result.IsValid)
  43:         {
  44:             response.AddCorsHeaders(result);
  45:         }
  46:         return response;
  47:     }
  48: }


如上面的代碼片斷所示,我們首選在實(shí)現(xiàn)的SendAsync方法中調(diào)用自定義的擴(kuò)展方法CreateCorsRequestContext根據(jù)表示當(dāng)前請(qǐng)求的HttpRequestMessge對(duì)象創(chuàng)建出表示針對(duì)CORS的跨域資源請(qǐng)求上下文的CorsRequestContext對(duì)象。

然后我們根據(jù)CorsRequestContext的IsPreflight屬性判斷當(dāng)前是否是一個(gè)預(yù)檢請(qǐng)求。對(duì)于預(yù)檢請(qǐng)求,我們會(huì)直接調(diào)用基類(lèi)的同名方法將請(qǐng)求傳遞給消息處理管道的后續(xù)環(huán)節(jié)作進(jìn)一步處理,并最終得到表示響應(yīng)的HttpResponse對(duì)象。

我們接下來(lái)從表示當(dāng)前請(qǐng)求的HttpRequestMessge對(duì)象中直接獲取當(dāng)前HttpConfiguration對(duì)象,并調(diào)用擴(kuò)展方法GetCorsPolicyProviderFactory得到注冊(cè)在它上面的CorsPolicyProviderFactory,進(jìn)而得到由它提供的GetCorsPolicyProvider。通過(guò)調(diào)用此GetCorsPolicyProvider的方法GetCorsPolicyAsync,我們會(huì)得到目標(biāo)Action方法采用的CORS資源授權(quán)策略,這是一個(gè)CorsPolicy對(duì)象。

在這之后,我們調(diào)用HttpConfiguration對(duì)象的另一個(gè)擴(kuò)展方法GetCorsEngine得到注冊(cè)其上的CorsEngine,并將此前得到的CorsRequestContext和CorsPolicy對(duì)象作為參數(shù)調(diào)用它的方法EvaluatePolicy由此開(kāi)始針對(duì)當(dāng)前請(qǐng)求的CORS資源授權(quán)檢驗(yàn),并最終得到表示檢驗(yàn)結(jié)果的CorsResult。

通過(guò)CorsResult的IsValid屬性表示當(dāng)前請(qǐng)求是否通過(guò)CORS資源授權(quán)檢驗(yàn)。對(duì)于預(yù)檢請(qǐng)求,在請(qǐng)求通過(guò)授權(quán)檢驗(yàn)的情況下,我們會(huì)創(chuàng)建一個(gè)狀態(tài)為“200, OK”的HttpResponseMessage作為最終的響應(yīng),在返回之前我們調(diào)用自定義的擴(kuò)展方法AddCorsHeaders將從CorsResult得到的CORS響應(yīng)報(bào)頭添加到此HttpResponseMessage的報(bào)頭集合中。如果請(qǐng)求沒(méi)有通過(guò)授權(quán)檢驗(yàn),我們會(huì)返回一個(gè)狀態(tài)為“400, Bad Request”的響應(yīng),通過(guò)CorsResult的ErrorMessage屬性提取的錯(cuò)誤消息(表示授權(quán)失敗的原因)會(huì)作為響應(yīng)的主體內(nèi)容。

對(duì)于非預(yù)檢請(qǐng)求來(lái)說(shuō),只有在它通過(guò)了資源授權(quán)檢驗(yàn)的情況下,我們才會(huì)調(diào)用擴(kuò)展方法AddCorsHeaders將從CorsResult得到的CORS報(bào)頭添加響應(yīng)的報(bào)頭集合中。換句話說(shuō),對(duì)于未取得授權(quán)的非預(yù)檢跨域資源請(qǐng)求,MyCorsMessageHandler沒(méi)有對(duì)響應(yīng)作任何的改變。

如下所示的是分別針對(duì)HttpRequestMessage和HttpResponseMessage定義的兩個(gè)擴(kuò)展方法,其中CreateCorsRequestContext方法根據(jù)HttpRequestMessage創(chuàng)建CorsRequestContext對(duì)象,而AddCorsHeaders方法則將從CorsResult中獲取的CORS響應(yīng)報(bào)頭添加到指定的HttpResponseMessage中。


   1: public static class CorsExtensions
   2: {
   3:     public static CorsRequestContext CreateCorsRequestContext(this HttpRequestMessage request)
   4:     {
   5:         CorsRequestContext context = new CorsRequestContext
   6:         {
   7:             RequestUri = request.RequestUri,
   8:             HttpMethod = request.Method.Method,
   9:             Host = request.Headers.Host,
  10:             Origin = request.GetHeader("Origin"),
  11:             AccessControlRequestMethod = request.GetHeader("Access-Control-Request-Method")
  12:         };
  13:
  14:         string requestHeaders = request.GetHeader("Access-Control-Request-Headers");
  15:         if (!string.IsNullOrEmpty(requestHeaders))
  16:         {
  17:             Array.ForEach(requestHeaders.Split(','), header => context.AccessControlRequestHeaders.Add(header.Trim()));
  18:         }
  19:         return context;
  20:     }
  21:
  22:     public static void AddCorsHeaders(this HttpResponseMessage response, CorsResult result)
  23:     {
  24:         foreach (var item in result.ToResponseHeaders())
  25:         {
  26:             response.Headers.TryAddWithoutValidation(item.Key, item.Value);
  27:         }
  28:     }
  29:
  30:     private static string GetHeader(this HttpRequestMessage request, string name)
  31:     {
  32:         IEnumerable<string> headerValues;
  33:         if (request.Headers.TryGetValues(name, out headerValues))
  34:         {
  35:             return headerValues.FirstOrDefault();
  36:         }
  37:         return null;
  38:     }
  39: }


為了驗(yàn)證我們這個(gè)用于模擬CorsMessageHandler的自定義HttpMessageHandler是否能夠真正為ASP.NET Web API提供針對(duì)CORS的支持,我們直接將其應(yīng)用到《同源策略與JSONP》創(chuàng)建的演示實(shí)例中。我們通過(guò)上面介紹的方式為WebApi應(yīng)用安裝“Microsoft ASP.NET Web API 2 Cross-Origin Support”這個(gè)NuGet包后,將EnableCorsAttribute特性應(yīng)用到定義在ContactsController上并作如下的設(shè)置。


   1: [EnableCors("http://localhost:9527","*","*")]
   2: public class ContactsController : ApiController
   3: {
   4:     public IHttpActionResult GetAllContacts()
   5:     {
   6:         //省略實(shí)現(xiàn)
   7:     }
   8: }


在Global.asax中,我們并不調(diào)用當(dāng)前HttpConfiguration的EnableCors方法開(kāi)啟ASP.NET Web API針對(duì)CORS的支持,而是采用如下的方式將創(chuàng)建的CorsMessageHandler對(duì)象添加到消息處理管道中。如果現(xiàn)在運(yùn)行ASP.NET MVC程序,通過(guò)調(diào)用Web API以跨域Ajax請(qǐng)求得到的聯(lián)系人列表依然會(huì)顯示在瀏覽器上。


   1: public class WebApiApplication : System.Web.HttpApplication
   2: {
   3:     protected void Application_Start()
   4:     {
   5:         GlobalConfiguration.Configuration.MessageHandlers.Add(new MyCorsMessageHandler());
   6:         //其他操作
   7:     }
   8: }


HttpConfiguration的EnableCors方法

通過(guò)上面的介紹我們知道針對(duì)ASP.NET Web API的CORS編程首先需要做的就是在程序啟動(dòng)之前調(diào)用當(dāng)前HttpConfiguration的擴(kuò)展方法EnableCors開(kāi)啟對(duì)CORS的支持,那么該方法中具體實(shí)現(xiàn)了怎樣操作呢?由于ASP.NET Web API針對(duì)CORS的支持最終是通過(guò)CorsMesssageHandler這個(gè)自定義的HttpMessageHandler來(lái)實(shí)現(xiàn)的,所以對(duì)于HttpConfiguration的擴(kuò)展方法EnableCors來(lái)說(shuō),其核心操作就是對(duì)CorsMesssageHandler予以注冊(cè)。


   1: public static class CorsHttpConfigurationExtensions
   2: {
   3:     public static void EnableCors(this HttpConfiguration httpConfiguration);
   4:     public static void EnableCors(this HttpConfiguration httpConfiguration, ICorsPolicyProvider defaultPolicyProvider);
   5: }
   6:
   7: public class AttributeBasedPolicyProviderFactory : ICorsPolicyProviderFactory
   8: {
   9:     //其他成員
  10:     public ICorsPolicyProvider DefaultPolicyProvider { get; set; }
  11: }


如上面的代碼片斷所示,HttpConfiguration具有兩個(gè)重載的EnableCors方法。其中一個(gè)可以指定一個(gè)默認(rèn)的CorsPolicyProvider,如果調(diào)用此方法并指定一個(gè)具體的CorsPolicyProvider對(duì)象,一個(gè)AttributeBasedPolicyProviderFactory對(duì)象會(huì)被創(chuàng)建出來(lái)并注冊(cè)到HttpConfiguration上。而指定的CorsPolicyProvider實(shí)際上會(huì)作為AttributeBasedPolicyProviderFactory對(duì)象的DefaultPolicyProvider屬性。

    相關(guān)評(píng)論

    閱讀本文后您有什么感想? 已有人給出評(píng)價(jià)!

    • 8 喜歡喜歡
    • 3 頂
    • 1 難過(guò)難過(guò)
    • 5 囧
    • 3 圍觀圍觀
    • 2 無(wú)聊無(wú)聊

    熱門(mén)評(píng)論

    最新評(píng)論

    發(fā)表評(píng)論 查看所有評(píng)論(1)

    昵稱(chēng):
    表情: 高興 可 汗 我不要 害羞 好 下下下 送花 屎 親親
    字?jǐn)?shù): 0/500 (您的評(píng)論需要經(jīng)過(guò)審核才能顯示)