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

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

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

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

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

  • 類型:磁盤工具大小:18.8M語言:中文 評分:5.2
  • 標(biāo)簽:
立即下載

在《通過擴(kuò)展讓ASP.NET Web API支持W3C的CORS規(guī)范》中,我們通過自定義的HttpMessageHandler自行為ASP.NET Web API實(shí)現(xiàn)了針對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)策略并對當(dāng)前請求實(shí)施授權(quán)檢驗(yàn),并根據(jù)授權(quán)檢驗(yàn)的結(jié)果為現(xiàn)有的響應(yīng)(針對簡單跨域資源請求和繼預(yù)檢請求之后發(fā)送的真正跨域資源請求)或者新創(chuàng)建的響應(yīng)(針對預(yù)檢請求)添加相應(yīng)的CORS報(bào)頭。如上面的代碼片斷所示,CorsMessageHandler定義了HandleCorsPreflightRequestAsync和HandleCorsRequestAsync虛方法,它們分別實(shí)現(xiàn)針對預(yù)檢請求和非預(yù)檢請求的CORS授權(quán)檢驗(yàn)。

在實(shí)現(xiàn)的SendAsync方法中,當(dāng)CorsRequestContext根據(jù)表示當(dāng)前請求的HttpRequestMessage對象創(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)前請求的HttpRequestMessage對象創(chuàng)建CorsRequestContext對象。然后利用注冊的CorsProviderFactory得到對應(yīng)的CorsProvider對象,并利用后者得到針對當(dāng)前請求的資源授權(quán)策略,這是一個(gè)CorsPolicy對象。

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

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

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

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

為了讓讀者朋友們對實(shí)現(xiàn)在CorsMessageHandler中的具體CORS資源授權(quán)流程具有更加深刻的認(rèn)識(shí),我們現(xiàn)在將這樣的授權(quán)檢驗(yàn)邏輯實(shí)現(xiàn)在一個(gè)自定義的HttpMessageHandler中。為此我們定義了如下一個(gè)MyCorsMessageHandler類型,由于它僅僅用于模擬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)前請求創(chuàng)建CorsRequestContext
   6:         CorsRequestContext context = request.CreateCorsRequestContext();
   7:
   8:         //針對非預(yù)檢請求:將請求傳遞給消息處理管道后續(xù)部分繼續(xù)處理,并得到響應(yīng)
   9:         HttpResponseMessage response = null;
  10:         if (!context.IsPreflight)
  11:         {
  12:             response = await base.SendAsync(request, cancellationToken);
  13:         }
  14:
  15:         //利用注冊的CorsPolicyProviderFactory得到對應(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:         //獲取注冊的CorsEngine
  21:         //利用CorsEngine對請求實(shí)施CORS資源授權(quán)檢驗(yàn),并得到表示檢驗(yàn)結(jié)果的CorsResult對象
  22:         ICorsEngine engine = configuration.GetCorsEngine();
  23:         CorsResult result = engine.EvaluatePolicy(context, policy);
  24:
  25:         //針對預(yù)檢請求
  26:         //如果請求通過授權(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:         //針對非預(yù)檢請求
  41:         //CORS報(bào)頭只有在通過授權(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)前請求的HttpRequestMessge對象創(chuàng)建出表示針對CORS的跨域資源請求上下文的CorsRequestContext對象。

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

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

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

通過CorsResult的IsValid屬性表示當(dāng)前請求是否通過CORS資源授權(quán)檢驗(yàn)。對于預(yù)檢請求,在請求通過授權(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)頭集合中。如果請求沒有通過授權(quán)檢驗(yàn),我們會(huì)返回一個(gè)狀態(tài)為“400, Bad Request”的響應(yīng),通過CorsResult的ErrorMessage屬性提取的錯(cuò)誤消息(表示授權(quán)失敗的原因)會(huì)作為響應(yīng)的主體內(nèi)容。

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

如下所示的是分別針對HttpRequestMessage和HttpResponseMessage定義的兩個(gè)擴(kuò)展方法,其中CreateCorsRequestContext方法根據(jù)HttpRequestMessage創(chuàng)建CorsRequestContext對象,而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提供針對CORS的支持,我們直接將其應(yīng)用到《同源策略與JSONP》創(chuàng)建的演示實(shí)例中。我們通過上面介紹的方式為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方法開啟ASP.NET Web API針對CORS的支持,而是采用如下的方式將創(chuàng)建的CorsMessageHandler對象添加到消息處理管道中。如果現(xiàn)在運(yùn)行ASP.NET MVC程序,通過調(diào)用Web API以跨域Ajax請求得到的聯(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方法

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


   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對象,一個(gè)AttributeBasedPolicyProviderFactory對象會(huì)被創(chuàng)建出來并注冊到HttpConfiguration上。而指定的CorsPolicyProvider實(shí)際上會(huì)作為AttributeBasedPolicyProviderFactory對象的DefaultPolicyProvider屬性。

    相關(guān)評論

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

    • 8 喜歡喜歡
    • 3 頂
    • 1 難過難過
    • 5 囧
    • 3 圍觀圍觀
    • 2 無聊無聊

    熱門評論

    最新評論

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

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