讓ASP.NET Web API的Action方法
在今天編輯推薦的《Hello Web API系列教程——Web API與國際化》一文中,作者透過自定義的HttpMessageHandler的方式根據請求的Accep-Language報頭設定當前執行緒UI Culture的方式來解決Localization的問題。如果你對ASP.NET Web API的執行機制有足夠了解的話,你會發現實際上有很多種解決方案。不過這些解決方案都不夠完美,原因很簡單:ASP.NET Web API的整個框架均採用基於Task的並行程式設計模式,所以每個可擴充套件元件均可以在不同的執行緒中執行,這樣會導致我們沒有辦法100%控制目標方法真正執行的執行緒的UI Culture。不過在預設情況下,大部分元件是按照同步的方式執行的,所以我們之需要在目標Action方法執行之前設定當前執行緒的UI Culture即可。
目錄
一、兩個輔助的擴充套件方法
二、第1種方案:自定義ActionFilter
三、第2種方案:自定義HttpActionDescriptor
四、第3種方案:自定義HttpActionInvoker
五、第4種方案:為HttpController建立一個基類
一、兩個輔助的擴充套件方法
我們針對HttpRequestMessage定義瞭如下兩個擴充套件方法。SetCurrentUICulture從請求的Accpet-Language報頭提取客戶端接受的語言並據此設定當前執行緒的UI Culture。在這之前,它會將當前執行緒的UI Culture儲存到HttpRequestMessage物件中。ResetCurrentUICulture方法將這個CultureInfo物件從HttpRequestMessage其中提取出來,將當前執行緒的UI Cuilture回覆到之前的狀態。
1: public static class HttpRequestMessageExtensions
2: {
3: public static void SetCurrentUICulture(this HttpRequestMessage request)
4: {
5: StringWithQualityHeaderValue acceptCultureHeader = request.Headers.AcceptLanguage.OrderByDescending(header => header.Quality).FirstOrDefault();
6: if (null != acceptCultureHeader)
7: {
8: request.Properties["__CurrentCulture"] = Thread.CurrentThread.CurrentUICulture;
9: Thread.CurrentThread.CurrentUICulture = new CultureInfo(acceptCultureHeader.Value);
10: }
11: }
12:
13: public static void ResetCurrentUICulture(this HttpRequestMessage request)
14: {
15: object culture;
16: if (request.Properties.TryGetValue("__CurrentCulture", out culture))
17: {
18: Thread.CurrentThread.CurrentUICulture = (CultureInfo)culture;
19: }
20: }
21: }
二、第1種方案:自定義ActionFilter
我想這應該是大家最容易想到的解決方案,因為ActionFilter可以註冊一些回撥操作在目標Action方法執行前後被自動呼叫。為此我們定義瞭如下一個繼承自ActionFilterAttribute的UseAcceptCultureAttribute型別。我們分別在重寫的OnActionExecuting和OnActionExecuted方法中利用上面定義的兩個擴充套件方法對當前執行緒的UI Culture進行設定和恢復。
1: public class UseAcceptCultureAttribute: ActionFilterAttribute
2: {
3: public override void OnActionExecuting(HttpActionContext actionContext)
4: {
5: actionContext.Request.SetCurrentUICulture();
6: }
7:
8: public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
9: {
10: actionExecutedContext.Request.ResetCurrentUICulture();
11: }
12: }
為了驗證這個ActionFilterAttribute特性,我們定義瞭如下一個繼承自ApiController的HelloController。唯一的Action方法返回的字串是從資原始檔中提取的(型別Resources為資原始檔自動生成的型別),而ActionFilterAttribute就應用在這個Get方法上。
1: public class HelloController : ApiController
2: {
3:
[UseAcceptCulture]
4: public string Get()
5: {
6: return Resources.HelloWorld;
7: }
8: }
我們定義了兩個資原始檔,一個為語言文化中性的Resources.resx,另一個則是針對中文的Resources.zh.resx。唯一的資源項HelloWorld分別在所在的檔案中以英文和中文進行定義,而上面定義的Get方法返回的正式它們的值。
在啟動之後,我們利用Fiddler來呼叫定義在HelloController中的Action方法Get,並手工設定Accept-Language報頭的值。如下圖所示,當請求的Accept-Language報頭被分別設定為“en-US;q=1.0, zh-CN;q=0.8”和“en-US;q=0.8, zh-CN;q=1.0”時(即給en-US和zh-CN分配不同的Quality),返回的內容分別是英文和中文。
三、第2種方案:自定義HttpActionDescriptor
HttpActionDescriptor用於描述定義在HttpController中的Action,預設的HttpActionDescriptor型別為ReflectedHttpActionDescriptor。Action方法的執行最終實現在HttpActionDescriptor的ExecuteAsync方法中,我們可以透過自定義的HttpActionDescriptor的方式在目標Action方法執行前後對當前執行緒的UI Culture進行設定和恢復。為此,我們定義瞭如下一個ExtendedReflectedHttpActionDescriptor型別。在重寫的ExecuteAsync方法中,我們呼叫基類的同名方法執行目標Action方法,並在這前後分別呼叫當前HttpRequestMessage的兩個擴充套件方法設定和恢復當前執行緒的UI Culture。
1: public class ExtendedReflectedHttpActionDescriptor : ReflectedHttpActionDescriptor
2: {
3: public ExtendedReflectedHttpActionDescriptor(ReflectedHttpActionDescriptor actionDescriptor)
4: : base(actionDescriptor.ControllerDescriptor, actionDescriptor.MethodInfo)
5: { }
6: public override Task<object> ExecuteAsync(HttpControllerContext controllerContext, IDictionary<string, object> arguments, CancellationToken cancellationToken)
7: {
8: controllerContext.Request.SetCurrentUICulture();
9: Task<object> task = base.ExecuteAsync(controllerContext, arguments, cancellationToken);
10: controllerContext.Request.ResetCurrentUICulture();
11: return task;
12: }
13: }
ASP.NET Web API利用一個名為HttpActionSelector的物件來選擇與當前請求匹配的HttpActionDescriptor,要讓我們自定義的ExtendedReflectedHttpActionDescriptor被使用,我們得對應的HttpActionSelector。ASP.NET Web API預設使用的HttpActionSelector型別為ApiControllerActionSelector,我們自定義的ExtentedApiControllerActionSelector就繼承於它。如下面的程式碼片斷所示,在重寫的SelectAction方法中,我們呼叫基類的同名方法得到一個ReflectedHttpActionDescriptor 物件,並根據它建立一個ExtendedReflectedHttpActionDescriptor 物件並返回。
1: public class ExtentedApiControllerActionSelector: ApiControllerActionSelector
2: {
3: public override HttpActionDescriptor SelectAction(HttpControllerContext controllerContext)
4: {
5: ReflectedHttpActionDescriptor actionDescriptor = (ReflectedHttpActionDescriptor) base.SelectAction(controllerContext);
6: return new ExtendedReflectedHttpActionDescriptor(actionDescriptor);
7: }
8: }
自定義的ExtentedApiControllerActionSelector可以在Global.asax中按照如下的方式進行註冊。
1: public class WebApiApplication : System.Web.HttpApplication
2: {
3: protected void Application_Start()
4: {
5: GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpActionSelector), new ExtentedApiControllerActionSelector());
6: //...
7: }
8: }
四、第3種方案:自定義HttpActionInvoker
目標Action的執行是透過一個名為HttpActionInvoker驅動執行的(它呼叫HttpActionDescriptor的ExecuteAsync方法),預設的HttpActionInvoker型別為ApiControllerActionInvoker。我們可以繼承它,並在執行目標Action方法前後設定和恢復當前執行緒的UI Culture。為此我定義瞭如下一個ExtendedApiControllerActionInvoker,在重寫的InvokeActionAsync方法中,我們呼叫基類的同名方法執行目標Action方法,並在這前後分別呼叫當前HttpRequestMessage的兩個擴充套件方法設定和恢復當前執行緒的UI Culture。
1: public class ExtendedApiControllerActionInvoker: ApiControllerActionInvoker
2: {
3: public override Task<HttpResponseMessage> InvokeActionAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
4: {
5: actionContext.Request.SetCurrentUICulture();
6: Task < HttpResponseMessage > task = base.InvokeActionAsync(actionContext, cancellationToken);
7: actionContext.Request.ResetCurrentUICulture();
8: return task;
9: }
10: }
自定義的ExtendedApiControllerActionInvoker可以在Global.asax中按照如下的方式進行註冊。
1: public class WebApiApplication : System.Web.HttpApplication
2: {
3: protected void Application_Start()
4: {
5: GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpActionInvoker), new ExtendedApiControllerActionInvoker());
6: //...
7: }
8: }
五、第4種方案:為HttpController建立一個基類
HttpActionInvoker的最終又是在執行HttpController時被呼叫的,所以我們可以在執行HttpController上作文章。所以我們定義瞭如下一個繼承自ApiController的ExtendedApiController 型別。在重寫的ExecuteAsync方法中,我們呼叫基類同名方法前後對當前執行緒的UI Culture進行了設定和恢復。
1: public abstract class ExtendedApiController : ApiController
2: {
3: public override Task<HttpResponseMessage> ExecuteAsync(HttpControllerContext controllerContext, CancellationToken cancellationToken)
4: {
5: controllerContext.Request.SetCurrentUICulture();
6: Task < HttpResponseMessage > task = base.ExecuteAsync(controllerContext, cancellationToken);
7: controllerContext.Request.ResetCurrentUICulture();
8: return task;
9: }
10: }
那麼我們的HelloController只需要繼承自ExtendedApiController 即可。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4650/viewspace-2817870/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- ASP.NET Core Web API 與 SSLASP.NETWebAPI
- ASP.NET Core Web API 介面限流ASP.NETWebAPI
- IoC在ASP.NET Web API中的應用ASP.NETWebAPI
- 如何在 ASP.NET Core 5 中過載 Action 方法ASP.NET
- ASP.NET Web API 中使用 swagger 來管理 API 文件ASP.NETWebAPISwagger
- ASP.NET Core Web API 整合測試ASP.NETWebAPI
- ASP.NET Core Web Api之JWT(一)ASP.NETWebAPIJWT
- ASP.NET Core Web API中使用SwaggerASP.NETWebAPISwagger
- ASP.NET Core Web API 教程 - Project ConfigurationASP.NETWebAPIProject
- 透過擴充套件讓ASP.NET Web API支援W3C的CORS規範套件ASP.NETWebAPICORS
- 使用靜態基類方案讓 ASP.NET Core 實現遵循 HATEOAS Restful Web APIASP.NETRESTWebAPI
- 使用 ASP.NET Core 和 MongoDB 建立 Web APIASP.NETMongoDBWebAPI
- 針對ASP.NET Core Web API的先進架構ASP.NETWebAPI架構
- Asp.Net中的Action和Func委託ASP.NET
- ASP.NET Core Web API 索引 (更新Redis in .NET Core)ASP.NETWebAPI索引Redis
- ASP.NET Core Web API 流式返回,逐字顯示ASP.NETWebAPI
- Asp.Net web api基於自定義Filter的安全認證ASP.NETWebAPIFilter
- 【ASP.NET Core】體驗一下 Mini Web APIASP.NETWebAPI
- Asp.NET Web API 2系列(二):靈活多樣的路由配置ASP.NETWebAPI路由
- ASP.NET Web API自身對CORS的支援:從例項開始ASP.NETWebAPICORS
- ASP.NET Core Web API 整合測試中使用 Bearer TokenASP.NETWebAPI
- ASP.NET Core Web Api之JWT重新整理Token(三)ASP.NETWebAPIJWT
- ASP.NET Core Web Api之JWT VS Session VS Cookie(二)ASP.NETWebAPIJWTSessionCookie
- 舊 WCF 專案成功遷移到 asp.net core web apiASP.NETWebAPI
- 呼叫ASP.NET Web API不能傳送PUT/DELETE請求ASP.NETWebAPIdelete
- 【Spark Java API】Action(3)—foreach、fSparkJavaAPI
- 【Spark Java API】Action(4)—sortBy、taSparkJavaAPI
- 使用 dynamic 型別讓 ASP.NET Core 實現 HATEOAS 結構的 RESTful API型別ASP.NETRESTAPI
- ASP.NET Web API 2系列(四):基於JWT的token身份認證方案ASP.NETWebAPIJWT
- 【ASP.NET Core】設定 Web API 響應資料的格式——FormatFilter特性篇ASP.NETWebAPIORMFilter
- 【ASP.NET Core】設定Web API 響應的資料格式——Produces 特性篇ASP.NETWebAPI
- ASP.NET Core Web API Swagger 按標籤Tags分組排序顯示ASP.NETWebAPISwagger排序
- 《Java 8 in Action》Chapter 12:新的日期和時間APIJavaAPTAPI
- 使用 ASP.NET Core MVC 建立 Web API——響應資料的內容協商(七)ASP.NETMVCWebAPI
- 在 ASP.NET Core Web API中使用 Polly 構建彈性容錯的微服務ASP.NETWebAPI微服務
- Web API中的EventWebAPI
- ASP.NET Core Web API設定響應輸出的Json資料格式的兩種方式ASP.NETWebAPIJSON
- ASP.NET Core 實戰:使用 ASP.NET Core Web API 和 Vue.js,搭建前後端分離框架ASP.NETWebAPIVue.js後端框架