HttpActionDescriptor,ASP.NET Web API又一个重要的描述对象

通过前面对“HttpController的激活”的介绍我们已经知道了ASP.NET Web API通过HttpControllerDescriptor来描述HttpController。对于定义在HttpController中的每一个Action方法则通过一个类型为HttpActionDescriptor的对象来描述,Action方法基本的元数据信息可以在对应的HttpActionDescriptor对象中找到。[本文已经同步到《How ASP.NET Web API Works?》]

目录 
HttpActionDescriptor 
ReflectedHttpActionDescriptor 
ActionNameAttribute 
方法名称与支持的HTTP方法 
ActionHttpMethodProvider

HttpActionDescriptor

如下面的代码片断所示,HttpActionDescriptor是一个抽象类型。我们列出了定义其中的部分属性成员,包括代表Action名称的ActionName属性和表示当前HttpConfiguration的Configuration属性,它来源于指定的HttpControllerDescriptor对象的同名属性。属性ControllerDescriptor表示描述所在HttpController的HttpControllerDescriptor对象,而另一个属性ReturnType代表Action方法的返回类型。

   1: public abstract class HttpActionDescriptor
   2: {
   3:     //其他成员
   4:     public abstract string                               ActionName { get; }
   5:     public HttpConfiguration                            Configuration { get; set; }
   6:     public HttpControllerDescriptor                     ControllerDescriptor { get; set; }
   7:     public abstract Type                                ReturnType { get; }
   8:     public virtual ConcurrentDictionary<object, object> Properties { get; }
   9:     public virtual Collection<HttpMethod>               SupportedHttpMethods { get; } 
  10:  
  11:     public abstract Collection<HttpParameterDescriptor> GetParameters();   
  12: }

和HttpControllerDescriptor类型一样,HttpActionDescriptor也定义了一个的属性Properties属性,其类型为ConcurrentDictionary<object, object>,所以我们可以利用它将任意对象附加到某个HttpActionDescriptor对象上。

在默认情况下每一个Action方法仅仅支持一种唯一的HTTP方法,不过我们通过一些编程技巧让一个Action方法可以同时支持多种HTTP方法。某个Action方法支持的HTTP方法列表可以通过对应HttpActionDescriptor的SupportedHttpMethods来获取。如果一个Action方法仅仅支持HTTP-GET,那么对于一个HTTP-POST的请求,该Action方法将不会被选择。

ASP.NET Web API最终能够执行目标Action方法的前提是能够正确从请求中提取相应的数据并将其绑定到方法对应的参数上。参数绑定的正常执行依赖于一些用于描述参数的元数据,ASP.NET Web API通过HttpParameterDescriptor对象来描述Action方法的参数,而HttpActionDescriptor定了一个具有如下定义的GetParameters方法用于获取描述其所有参数的HttpParameterDescriptor对象列表。

   1: public abstract class HttpActionDescriptor
   2: {
   3:     //其他成员
   4:      public abstract Collection<HttpParameterDescriptor> GetParameters();   
   5: }

由于基于自定义特性的编程方法在ASP.NET Web API中被广泛采用,ASP.NET Web API框架本身借助于一些应用在Action方法上的特性来控制目标Action的执行,而我们出于对框架扩展的目的也可能将自定义的一些特性应用到相应的Action方法上。为了方便我们获取应用到Action方法上的特性列表,HttpActionDescriptor为我们定义了如下两个GetCustomAttributes<T>方法重载。

   1: public abstract class HttpActionDescriptor
   2: {
   3:     //其他成员
   4:     public virtual Collection<T> GetCustomAttributes<T>() where T: class;
   5:     public virtual Collection<T> GetCustomAttributes<T>(bool inherit) where T: class;
   6: }

ReflectedHttpActionDescriptor

HttpActionDescriptor仅仅是一个抽象类型 ,在默认情况下ASP.NET Web API使用的具体HttpActionDescriptor类型为ReflectedHttpActionDescriptor。顾名思义,ReflectedHttpActionDescriptor通过反射的方式来获取用于描述目标Action方法的元数据。

   1: public class ReflectedHttpActionDescriptor : HttpActionDescriptor
   2: {
   3:     //其他成员
   4:     public ReflectedHttpActionDescriptor(HttpControllerDescriptor controllerDescriptor, MethodInfo methodInfo);
   5:  
   6:     public override Collection<HttpParameterDescriptor> GetParameters();
   7:     public override Collection<T> GetCustomAttributes<T>(bool inherit) where T: class;
   8:     
   9:     public MethodInfo                          MethodInfo { get; set; }
  10:     public override string                     ActionName { get; }
  11:     public override Type                       ReturnType { get; }
  12:     public override Collection<HttpMethod>     SupportedHttpMethods { get; }
  13: }

如下面的代码片断所示,在构建一个ReflectedHttpActionDescriptor对象的时候除了需要指定用于描述所在HttpController的HttpControllerDescriptor对象之外,还需要指定描述对应Action方法的MethodInfo对象(属性MethodInfo返回该对象),相关的元数据就来源于此。

ReflectedHttpActionDescriptor利用指定的MethodInfo获得Action方法的返回类型,并将其作为ReturnType属性值。如果Action方法返回类型为void,此ReturnType属性返回Null。在默认的情况下,表示Action名称ActionName属性返回Action方法的名称。如果我们需要指定不同的名称,可以借助于ActionNameAttribute这个特性。

ActionNameAttribute

对于一个有效的Action方法来说,其方法名称默认作为Action的名称。采用怎样的URL对于REST这种架构风格来说显得尤为重要,出于简洁性、可读性以及SEO优化等方面的考虑,我们往往需要对方法某个Action方法的URL进行精心设计。如果Action名称会作为请求URL模板的一部分,而Action名称的内容总是对应着Action方法的名称,这将使我们不能对URL进行独立的设计。

为了解决Action名称与方法名称的分离,我们可以在Action方法上应用ActionNameAttribute特性为其指定一个我们希望的名称。ActionNameAttribute的定义如下,表示Action名称的Name属性直接在构造函数中指定。

   1: [AttributeUsage(AttributeTargets.Method, AllowMultiple=false, Inherited=true)]
   2: public sealed class ActionNameAttribute : Attribute
   3: {
   4:     public ActionNameAttribute(string name);
   5:     public string Name { get; }
   6: }

在DemoController中,我们在Action方法Xxx上通过应用的ActionNameAttribute特性将Action名称设置为“Yyy”。

   1: public class DemoController: ApiController
   2: {     

3:     [ActionName(“Yyy”)]

   4:     public void Xxx();
   5: }

方法名称与支持的HTTP方法

定义在某个HttpController中的Action方法默认只支持一种唯一的HTTP方法,并且这个支持的HTTP方法决定于Action方法的名称。具体来说,如果Action方法名称具有如下的前缀(不区分大小写),那么对应的HTTP方法将默认被支持。比如说,如果Action方法被命名为“GetXxx”,那么默认支持HTTP-GET。如果Action方法名称不具有如此前缀,那么默认支持HTTP-POST。

  • GET
  • POST
  • PUT
  • DELETE
  • HEAD
  • OPTIONS
  • PATCH

为了让读者朋友们对“Action方法名称决定支持的HTTP方法”这个默认的HTTP方法选择策略具有更加深刻的了解,我们来做一个简单的实例演示。我们定义了如下一个继承自ApiController的DemoController,它具有8个合法的Action方法成员,除了最后一个Other方法之外,其余7个Action方法名称均以相应的HTTP方法名称作为前缀。

   1: public class DemoController : ApiController
   2: {
   3:     public void GetXxx() {}
   4:     public void PostXxx() {}
   5:     public void PutXxx() {}
   6:     public void DeleteXxx() {}
   7:     public void HeadXxx() {}
   8:     public void OptionsXxx() {}
   9:     public void PatchXxx() {}
  10:     public void Other() {}
  11: }

我们在一个空的ASP.NET MVC应用中定义了如下一个HomeController。在默认的Action方法Index中,我们针对定义在DemoController的8个方法创建了相应的ReflectedHttpActionDescriptor对象。8个ReflectedHttpActionDescriptor对象对应的Action方法名称和支持的HTTP方法名称列表被转换成一个Dictionary<string, string[]>对象并呈现在默认的View中。

   1: public class HomeController : Controller
   2: {
   3:     public ActionResult Index()
   4:     {
   5:         HttpControllerDescriptor controllerDescriptor = new HttpControllerDescriptor(new HttpConfiguration(), "demo", typeof(DemoController));
   6:         Dictionary<string, string[]> supportedHttpMethods = new Dictionary<string, string[]>();
   7:         foreach (MethodInfo method in typeof(DemoController).GetMethods())
   8:         {
   9:             if (method.DeclaringType == typeof(DemoController))
  10:             {
  11:                 ReflectedHttpActionDescriptor actionDescritor = new ReflectedHttpActionDescriptor(controllerDescriptor, method);
  12:                 supportedHttpMethods.Add(method.Name, actionDescritor.SupportedHttpMethods.Select(httpMethod=>httpMethod.Method).ToArray());
  13:             }
  14:         }
  15:         return View(supportedHttpMethods);
  16:     }
  17: }

如下所示的是Action方法Index对应View的定义,这是一个以IDictionary<string, string[]>对象作为Model的强类型View。在该View中,我们将Action方法的名称和支持的HTTP方法列表以表格的形式呈现出来。

   1: @model IDictionary<string, string[]>
   2: <html>
   3: <head>
   4:     <title>Action方法名称与支持的HTTP方法</title>
   5: </head>
   6: <body>
   7:     <table>
   8:         <thead>
   9:             <tr>
  10:                 <th>Action方法名称</th>
  11:                 <th>支持HTTP方法</th>
  12:             </tr>
  13:         </thead>
  14:         <tbody>
  15:             @foreach (var item in Model)
  16:             {
  17:                 <tr>
  18:                     <td rowspan="@item.Value.Length">@item.Key</td>
  19:                     <td>@item.Value[0]</td>
  20:                 </tr>
  21:                 for (int i = 1; i < item.Value.Length; i++)
  22:                 { 
  23:                     <tr><td>@item.Value[i]</td></tr>
  24:                 }
  25:             }
  26:         </tbody>
  27:     </table>
  28: </body>
  29: </html>

直接运行该程序后会在浏览器中得到如右图所示的输出结果。我们可以清楚地看到定义在DemoController的前面7个Action方法支持的HTTP方法对应于其方法名称采用的前缀,而最后一个Action方法Other则采用默认的HTTP-POST作为唯一支持的HTTP方法。

这种由Action方法名来决定它所支持的HTTP方法的策略虽然在一定程度上是我们的编程变得简单,但是在很多情况下与我们的期望是不相符的。比如我们定义一个名为RetrieveContacts的Action方法来获取相应的联系人,它所支持的HTTP方法应该是HTTP-GET,但是按照实际支持的HTTP方法却是HTTP-POST。

除此之外,默认情况下一个Action方法仅仅支持一种唯一的HTTP方法,但是很多情况下我们希望一个Action方法能够支持多种HTTP方法。比如我们定义一个名为UpdateContact的Action方法,它既支持针对新联系人的添加也支持现有联系人的修改,所以我们希望它同时支持HTTP-POST和HTTP-PUT这两种HTTP方法。这两种情况下都可以通过在Action方法上应用相应的ActionHttpMethodProvider特性来解决。

ActionHttpMethodProvider

顾名思义,ActionHttpMethodProvider被相应的Action用于提供所支持的HTTP方法,所有的ActionHttpMethodProvider实现了System.Web.Http.Controllers.IActionHttpMethodProvider接口。如下面的代码片断所示,IActionHttpMethodProvider接口定义了一个唯一的只读属性HttpMethods返回其提供的HTTP方法集合。

   1: public interface IActionHttpMethodProvider
   2: {
   3:     Collection<HttpMethod> HttpMethods { get; }
   4: }

ActionHttpMethodProvider在以特性的方式应用于相应的Action方法上。ASP.NET Web API为我们定义了两种实现了IActionHttpMethodProvider接口的特性,其中一个是具有如下定义的AcceptVerbsAttribute。当我们将AcceptVerbsAttribute特性应用到某个Action方法上的时候,可以直接以字符串的形式在构造函数中指定它所支持的HTTP方法名称(“GET”、“POST”、“PUT”、“DELETE”、“HEAD”、“OPTIONS”、“PATCH”等,指定的HTTP方法名称是不区分大小写的)。

   1: [AttributeUsage(AttributeTargets.Method, AllowMultiple=true, Inherited=true)]
   2: public sealed class AcceptVerbsAttribute : Attribute, IActionHttpMethodProvider, ...
   3: {
   4:     //其他成员
   5:     public AcceptVerbsAttribute(params string[] methods);
   6:     public Collection<HttpMethod> HttpMethods { get; }
   7: }

在如下所示的代码片断中,我们将AcceptVerbsAttribute特性应用到Action方法Update中,使之可以同时支持HTTP-PUT和HTTP-POST两种HTTP方法的请求。

   1: public class ContactController : ApiController
   2: {
   3:     //其他成员
   4:     [AcceptVerbsAttribute("PUT","POST")] 
   5:     public void Update(Contact contact)
   6:     { 
   7:         //省略实现
   8:     }
   9: }

如果使用AcceptVerbsAttribute特性,我们只能以字符串的形式指定目标Action方法所支持的HTTP方法,为了避免指定错误HTTP方法名称,ASP.NET Web API为我们定义了另一种类型的ActionHttpMethodProvider特性,即具有如下定义的HttpVerbAttribute。

   1: public abstract class HttpVerbAttribute : Attribute, IActionHttpMethodProvider, ...
   2: {
   3:     //其他成员
   4:     protected HttpVerbAttribute(HttpMethod httpMethod);
   5:     public Collection<HttpMethod> HttpMethods { get; }
   6: }

通过HttpVerbAttribute的定义可以看出它是一个抽象类型,针对上面我们介绍的7种常用的HTTP方法,ASP.NET Web API通过继承HttpVerbAttribute定义了相应的特性类型。这些针对具体某种HTTP方法的特性类型均定义在System.Web.Http命名空间下,它们分别是:

  • HttpGetAttribute
  • HttpPostAttrubute
  • HttpPutAttribute
  • HttpDeleteAttribute
  • HttpHeadAttribute
  • HttpOptionsAttribute
  • HttpPatchAttribute

上面我们通过应用AcceptVerbsAttribute特性实现了目标Action方法对HTTP-POST和HTTP-POST这两种HTTP方法的支持,我们可以以如下的方式在目标Action方法上应用HttpPutAttribute和HttpPostAttribute实现相同的效果。

   1: public class ContactController : ApiController
   2: {
   3:     //其他成员
   4:     [HttPut] 
   5:     [HttpPost] 
   6:     public void Update(Contact contact)
   7:     { 
   8:         //省略实现
   9:     }
  10: }

上面我们通过实现演示了默认情况下基于Action方法名称的HTTP方法支持策略,现在我们按照如下的方式将HttpGetAttribute和HttpPostAttribute应用到定义在DemoController中的8个Action方法上,使所有的Action方法均提供对HTTP-GET和HTTP-POST请求的支持。

   1: public class DemoController : ApiController
   2: {
   3:     [HttpGet]
   4:     [HttpPost]
   5:     public void GetXxx() {}
   6:  
   7:     [HttpGet]
   8:     [HttpPost]
   9:      public void PostXxx(){}
  10:  
  11:     [HttpGet]
  12:     [HttpPost]
  13:      public void PutXxx(){}
  14:  
  15:     [HttpGet]
  16:     [HttpPost]
  17:     public void DeleteXxx(){}
  18:  
  19:     [HttpGet]
  20:     [HttpPost]
  21:     public void HeadXxx(){}
  22:  
  23:     [HttpGet]
  24:     [HttpPost]
  25:     public void OptionsXxx() {}
  26:  
  27:     [HttpGet]
  28:     [HttpPost]
  29:     public void PatchXxx() {}
  30:  
  31:     [HttpGet]
  32:     [HttpPost]
  33:     public void Other() {}
  34: }

再次运行该程序后会在浏览器中呈现出如下图所示的结果。我们可以清楚地看到定义在DemoController中的8个Action方法均提供对HTTP-GET和HTTP-POST这两种HTTP方法的支持。

作者:Artech
出处:http://artech.cnblogs.com/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
 

HttpActionDescriptor,ASP.NET Web API又一个重要的描述对象的更多相关文章

  1. 通过Knockout.js + ASP.NET Web API构建一个简单的CRUD应用

    REFERENCE FROM : http://www.cnblogs.com/artech/archive/2012/07/04/Knockout-web-api.html 较之面向最终消费者的网站 ...

  2. 在一个空ASP.NET Web项目上创建一个ASP.NET Web API 2.0应用

    由于ASP.NET Web API具有与ASP.NET MVC类似的编程方式,再加上目前市面上专门介绍ASP.NET Web API 的书籍少之又少(我们看到的相关内容往往是某本介绍ASP.NET M ...

  3. 【ASP.NET Web API教程】1.1 第一个ASP.NET Web API

    Your First ASP.NET Web API (C#)第一个ASP.NET Web API(C#) By Mike Wasson|January 21, 2012作者:Mike Wasson ...

  4. 一个ASP.NET Web API 2.0应用

    在一个空ASP.NET Web项目上创建一个ASP.NET Web API 2.0应用 由于ASP.NET Web API具有与ASP.NET MVC类似的编程方式,再加上目前市面上专门介绍ASP.N ...

  5. 第一个ASP.NET Web API (C#)程序

    本文翻自http://www.asp.net/web-api/overview/getting-started-with-aspnet-web-api 绝对手工制作,如有雷同,实属巧合. 转载请注明. ...

  6. 如何让ASP.NET Web API的Action方法在希望的Culture下执行

    在今天编辑推荐的<Hello Web API系列教程--Web API与国际化>一文中,作者通过自定义的HttpMessageHandler的方式根据请求的Accep-Language报头 ...

  7. Asp.Net Web API(三)

    Routing Tables路由表 在Asp.Net Web API中,一个控制器就是一个处理HTTP请求的类,控制器的public方法就被叫做action方法或简单的Action.当Web API接 ...

  8. ASP.NET Web API 管道模型

    ASP.NET Web API 管道模型 前言 ASP.NET Web API是一个独立的框架,也有着自己的一套消息处理管道,不管是在WebHost宿主环境还是在SelfHost宿主环境请求和响应都是 ...

  9. ASP.NET Web API 开篇示例介绍

    ASP.NET Web API 开篇示例介绍 ASP.NET Web API 对于我这个初学者来说ASP.NET Web API这个框架很陌生又熟悉着. 陌生的是ASP.NET Web API是一个全 ...

随机推荐

  1. Cocos发育Visual Studio下一个HttpClient开发环境设置

    Cocos2d-x 3.x相关类集成到网络通信libNetwork图书馆project于.这其中包括:HttpClient分类. 我们需要在Visual Studio溶液中加入libNetwork图书 ...

  2. duplicate symbol _*** in:

    duplicate symbol _kReachabilityChangedNotification in: 问题出在同一个文件被引用两次,在项目中找到引用的地方,删掉对应的引用

  3. TFTP server组态

    TFTP server组态 2014-10-31北京海淀区  张俊浩 一.TFTP(Trivial File Transfer Protocol,简单文件传输协议或称小型文件传输协议) 是一种简化的文 ...

  4. hibernate 一对多关联关系(具体分析)

    在领域模型中, 类与类之间最普遍的关系就是关联关系. 在 UML 中, 关联是有方向的.  以 Customer 和 Order 为例: 一个用户能发出多个订单, 而一个订单仅仅能属于一个客户. 从 ...

  5. Oracle利用存储过程性 实现分页

    分页的简单配置 在上一次已经说过了 这边说说怎么在存储过程中实现分页 首先建立存储过程 參考 http://www.cnblogs.com/gisdream/archive/2011/11/16/22 ...

  6. javascript如何解析json对javascript如何解析json对象并动态赋值到select列表象并动态赋值到select列表

    原文 javascript如何解析json对象并动态赋值到select列表 JSON(JavaScriptObject Notation)一种简单的数据格式,比xml更轻巧.JSON是JavaScri ...

  7. C# 获取与解析枚举类型的 DescriptionAttribute

    原文:C# 获取与解析枚举类型的 DescriptionAttribute System.ComponentModel.DescriptionAttribute 这个 Attribute,经常被用来为 ...

  8. Java Persistence with MyBatis 3(中国版) 第五章 与Spring集成

    MyBatis-Spring它是MyBatis子模块框.它用来提供流行的依赖注入框架Spring无缝集成. Spring框架是一个基于依赖注入(Dependency Injection)和面向切面编程 ...

  9. Android Material Design带UI变化

    谷歌Matias Duarte称,"Material Design是漂亮和大胆的.由于干净的排版和布局简单且easy理解.内容才是焦点. 谷歌I/O 014开发人员大会上宣布全新的设计语言& ...

  10. 异步提交form的时候利用jQuery validate实现表单验证

    异步提交form的时候利用jQuery validate实现表单验证相信很多人都用过jquery validate插件,非常好用,并且可以通过下面的语句来自定义验证规则    // 电话号码验证    ...