天天看点

Asp.Net Web API 2第十六课——Parameter Binding in ASP.NET Web API(参数绑定)

导航

本文主要来讲解以下内容:

当Web API在一个控制器中调用一个方法的时候,它必须为参数设定值,这个过程就叫做绑定。这篇文章描述Web API如何绑定参数,以及如何自定义绑定过程。

  默认情况,Web API使用如下规则来绑定参数:

  1、如果参数一个"简单"类型,那么Web API试图从URI中获取值。简单的类型包含.NET的基元类型(int,bool,double等等)加上TimeSpan, DateTime, Guid, decimal, and string,再加上任何的能从字符串进行转换的类型。

例如,这是一个典型的Web API控制器方法:

这个“id”参数是一个“简单”类型,因此Web API试图从请求的URI中获取参数值,这个“item”参数是一个复杂类型,因此Web API试图使用一个媒体格式化器从请求消息体中来读取参数值。

 为了更好的让Web API从URI中读取复杂类型,添加【FormUri】属性到参数上。下面的例子定义了一个<code>GeoPoint</code> 的类型,紧接着一个控制器方法从URI中获得这个GetPoint参数。

这个客户端可以把Latitude和Longitude的值放进查询字符串中。Web API将用这两个参数来构造一个GeoPoint参数。例如:

 为了更好的让Web API 从消息体中读取一个简单类型。添加【FromBody】属性到参数上:

在这个例子中,Web API将使用媒体格式化器来读取消息体中的name值。这是一个客户端请求的例子:

当一个参数拥有【FromBody】属性的时候,Web API使用Content-Type header去选择一个格式化器。在这个例子中Content-Type是“application/json”,这个请求体是一个原始的Json字符串(而不是Json对象)。

至多一个参数被允许从消息体中读取值。因此如下这段将不会起作用:

对于这个规则的原因就是这个请求体被存储在只能被读取一次的非缓冲流中。

 你也可以让Web API对待一个class像一个简单的类型,通过创建一个TypeConverter 并提供一个字符串的转换。

接下来的代码展示了用一个GeoPoint类来表示一个地理位置。添加一个 TypeConverter来把字符串转换为GeoPoint实例。这个GeoPoint类用了一个TypeConverter属性来修饰,并且指定了这个TypeConverter的类型。

现在Web API可以把GeoPoint看做是一个简单类型。意味着它将可以从URI中绑定GeoPoint参数。在参数上你不需要添加【FromUri】属性。

客户端可以调用这个方法,例如如下的URI:

 比一个type converter更灵活的选项是创建一个自定义的模型绑定。有了模型绑定,你可以使用像HTTP请求,Action描述,以及路由数据中的原始值。

为了创建一个Model Binder,你需要实现IModelBinder 接口,这个接口中定义了一个方法,BindModel:

接下来为GeoPoint对象来创建一个Model Binder。

一个model binder从一个value provider中获得原始的录入值。这个设计分为两个独立的方法:

1、这个value provider接收到一个HTTP请求,并且填充一个键值对的字典。

2、然后model binder使用键值对的字典来填充model。

Web API中默认的value provider从路由数据和查询字符串中获取值。例如,这样一个URI:

value provider将会创建如下的键值对:

我们假设使用的是默认的路由模版。

被绑定的参数的名称被存储在ModelBindingContext.ModelName这个属性上。model binder在字典中寻找一个键的值。如果这个值存在,并且也能被转换成GeoPoint,这个model binder将分配这个值到ModelBindingContext.Model属性。

注意:Model Binder不会限制一个简单类型的转换,这个model binder首先会在已知位置的列表中寻找,如果查找失败,将会使用 type converter。

Setting the Model Binder

这里有几种方法去设置Model Binder.首先你可以添加一个【Model Binder】属性到参数上。

你也能添加【Model Binder】属性到这个参数类型上。Web API将指定这个model binder到这个类型的所有参数上。

Asp.Net Web API 2第十六课——Parameter Binding in ASP.NET Web API(参数绑定)

接下来的代码展示如何启用他们:

有了一个model-binding provider,你仍然需要添加一个[ModelBinder] 属性到参数上,它的目的就是告知Web API应该是用model binder,而不是使用媒体格式化器。但是现在你不需要在属性上指定这个model binder的类型。

 我前面提到过一个model binder是从value provider中获取值。写一个自定义的value provider,实现这个IValueProvider 接口。这个例子是从请求的cookie中获取值。

你也需要创建一个value provider 工厂通过继承自ValueProviderFactory 。

添加value provider 工厂到HttpConfiguration ,代码如下:

 Web API组合了所有的value provider,因此当一个model binder调用ValueProvider.GetValue,这个model binder接收从第一个value provider能提供它的值。

或者,通过使用ValueProvider属性你也能在参数级别上设置value provider 工厂,代码如下:

这将告诉Web API模型绑定使用指定的value  provider 工厂,不要用任何另外的被注册的value  provider。

 模型绑定是一个更加普遍机制的特性实例。如果你看到这个 [ModelBinder] 属性,你将明白它是派生自ParameterBindingAttribute 抽象类。这个类定义了一个单独的方法,并返回一个HttpParameterBinding 对象:

HttpParameterBinding 对象负责绑定一个值到一个参数。在[ModelBinder]修饰的情况下,这个属性返回一个HttpParameterBinding 的实现,它使用了一个IModelBinder 去展现真实的binding。你也可以实现自己的HttpParameterBinding 。

 例如,假定你想从请求的<code>if-match</code> 和 <code>if-none-match</code> 的header中获取ETags。开始我们将定义一个class来代替ETags 。

我们也来定义一个枚举指明是否从<code>if-match</code> 和 <code>if-none-match</code> 的header中获得了ETag 。

这里是HttpParameterBinding ,获取该 ETag 从所需的标头并将其绑定到类型的参数的 ETag:

ExecuteBindingAsync 方法来处理绑定。在此方法中,添加参数值到ActionArgument 字典中并在HttpActionContext中。

如果你的ExecuteBindingAsync 方法读取请求消息体。重写这个WillReadBody 属性去返回true。这个消息体可能是只能读一次的未缓冲的流。因此Web API施行了一个规则至多有一个绑定可以读取消息体。

应用一个自定义的HttpParameterBinding,你能定义一个派生自ParameterBindingAttribute 的属性,对于ETagParameterBinding,我们将定义两个属性:一个是对于<code>if-match</code>  Header的,一个是对于if-none-match Header。都派生自一个抽象的基类。

这是一个控制器方法 使用了<code>[IfNoneMatch]</code> 属性。

除了ParameterBindingAttribute 之外,对于添加一个自定义的HttpParameterBinding 有另外一个挂钩。在HttpConfiguration 对象上,ParameterBindingRules 是一个匿名方法类型(HttpParameterDescriptor -&gt; HttpParameterBinding)的集合。例如,你可以添加一个规则:在Get请求方法中任何ETag 参数使用<code>ETagParameterBinding</code> with <code>if-none-match。</code>

这个方法对于绑定不适用的参数应该返回null。

 整个参数绑定的过程被一个可插拔的服务控制,IActionValueBinder。IActionValueBinder 的默认实现将执行以下操作:

  1、在参数上查看ParameterBindingAttribute ,这包括 [FromBody], [FromUri], 和[ModelBinder], 或者是自定义的属性。

  2、否则,查看一个函数的HttpConfiguration.ParameterBindingRules ,它返回一个非null的HttpParameterBinding 。

  3、否则,使用我之前描述的默认规则。

    ①、如果参数类型是一个“简单”的,或者拥有一个type converter,将会从URI进行绑定。它等价于在参数上添加[FromUri]属性。

    ②、否则,试图从消息体中读取参数,这等价于在参数上添加[FromBody]属性。

 如果你需要,你可以用一个自定义的实现来替代整个IActionValueBinder 。

总结

本文主要来讲解参数绑定,但是通过上面也可以看出涉及到的知识点还是蛮多的,但是都是很实用的,例子也比较清晰。但是还是需要在项目中进行应用,才能更好的学习和掌握参数绑定的环节。