在今天编辑推荐的《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 即可。

如何让ASP.NET Web API的Action方法在希望的Culture下执行的更多相关文章

  1. 8 种提升 ASP.NET Web API 性能的方法

    ASP.NET Web API 是非常棒的技术.编写 Web API 十分容易,以致于很多开发者没有在应用程序结构设计上花时间来获得很好的执行性能. 在本文中,我将介绍8项提高 ASP.NET Web ...

  2. 8种提升ASP.NET Web API性能的方法

    英文原文:8 ways to improve ASP.NET Web API performance ASP.NET Web API 是非常棒的技术.编写 Web API 十分容易,以致于很多开发者没 ...

  3. [转载]8 种提升 ASP.NET Web API 性能的方法

    http://www.oschina.net/translate/8-ways-improve-asp-net-web-api-performance 英文原文:8 ways to improve A ...

  4. 8 种提升 ASP.NET Web API 性能的方法 (转)

    出处:http://www.oschina.net/translate/8-ways-improve-asp-net-web-api-performance ASP.NET Web API 是非常棒的 ...

  5. 8 种提升ASP.NET Web API性能的方法

    ASP.NET Web API 是非常棒的技术.编写 Web API 十分容易,以致于很多开发者没有在应用程序结构设计上花时间来获得很好的执行性能. 在本文中,我将介绍8项提高 ASP.NET Web ...

  6. 8种提升 ASP.NET Web API 性能的方法

  7. 新作《ASP.NET Web API 2框架揭秘》正式出版

    我觉得大部分人都是“眼球动物“,他们关注的往往都是目光所及的东西.对于很多软件从业者来说,他们对看得见(具有UI界面)的应用抱有极大的热忱,但是对背后支撑整个应用的服务却显得较为冷漠.如果我们将整个“ ...

  8. ASP.NET Web API 2框架揭秘

    ASP.NET Web API 2框架揭秘(.NET领域再现力作顶级专家精讲微软全新轻量级通信平台) 蒋金楠 著   ISBN 978-7-121-23536-8 2014年7月出版 定价:108.0 ...

  9. MVC项目实践,在三层架构下实现SportsStore-09,ASP.NET MVC调用ASP.NET Web API的查询服务

    ASP.NET Web API和WCF都体现了REST软件架构风格.在REST中,把一切数据视为资源,所以也是一种面向资源的架构风格.所有的资源都可以通过URI来唯一标识,通过对资源的HTTP操作(G ...

随机推荐

  1. 用Eclipse搭建ssh框架

    问:ssh是哪三大框架,以及他们的作用是什么? 答:分别是struts,spring,hibernate. struts的作用是:是web层,其核心是mvc模式,他可以自动获取参数,自动类型转换,自动 ...

  2. dedecms功能性函数封装(XSS过滤、编码、浏览器XSS hack、字符操作函数)

    dedecms虽然有诸多漏洞,但不可否认确实是一个很不错的内容管理系统(cms),其他也不乏很多功能实用性的函数,以下就部分列举,持续更新,不作过多说明.使用时需部分修改,你懂的 1.XSS过滤. f ...

  3. Unity自动寻路Navmesh之高级

    隔离层自动生成寻路网格 (源码scene1.unity) 1.创建Plane实例P1,P2,两者之间出现一条鸿沟.直接控制角色位移是无法通过的. 2.打开Navigation窗口,分别选中P1,P2, ...

  4. mysql 数据表中查找重复记录

    select mobile_phone,count(*) as count from lawyer group by mobile_phone having count>1;

  5. Visual Studio 2015 各版本对比及下载地址

    2015年7月20日23时30分,微软举行了Visual Studio 2015的发布会,跟随者Visual Studio 2015 而来的是,.net 开源,C#支持wp,ios,android三大 ...

  6. [转]html5表单上传控件Files API

    表单上传控件:<input type="file" />(IE9及以下不支持下面这些功能,其它浏览器最新版本均已支持.) 1.允许上传文件数量 允许选择多个文件:< ...

  7. 自写函数VB6 STUFF函数 和 VB.net 2010 STUFF函数 详解

    '*************************************************************************'**模 块 名:自写函数VB6 STUFF函数 和 ...

  8. [转]Android通过NDK调用JNI,使用opencv做本地c++代码开发配置方法

    原文地址:http://blog.csdn.net/watkinsong/article/details/9849973 有一种方式不需要自己配置所有的Sun JDK, Android SDK以及ND ...

  9. 浅析 Linux 初始化 init 系统

    近年来,Linux 系统的 init 进程经历了两次重大的演进,传统的 sysvinit 已经逐渐淡出历史舞台,新的 UpStart 和 systemd 各有特点,越来越多的 Linux 发行版采纳了 ...

  10. Net作业调度(四)—quartz.net持久化和集群

    介绍 在实际使用quartz.net中,持久化能保证实例重启后job不丢失. 集群能均衡服务器压力和解决单点问题. quartz.net在这两方面配置都比较简单. 持久化 quartz.net的持久化 ...