本文将概述在WebAPI方式下将如何将参数绑定到一个action方法,包括参数是如何被读取,一系列规则决定特定环境采用的那种绑定方式,文章最后将给出一些实际的例子。

Parameter binding说到底是接到一个Http请求,将其转换成.NET类型使得action方法的签名更易于理解。

请求消息(request message)包括了请求的所有信息,如带查询字符串的请求地址(URL),内容主体(content body)及头部信息(header)。在没有采用parameter binding

的情况下,每个action方法将需要接收request message,并手动从中提取出参数,如下所示:

public object MyAction(HttpRequestMessage request)
{
// make explicit calls to get parameters from the request object
int id = int.Parse(request.RequestUri.ParseQueryString().Get("id")); // need error logic!
Customer c = request.Content.ReadAsAsync<Customer>().Result; // should be async!
// Now use id and customer
}

很显然,这样的方式丑陋,易出错,代码重复,而且难以单元测试。我们希望action的签名类似以下的形式:

public object MyAction(int id, Customer c) { }

那么WebAPI是如何将request message转换成像id和customer这样的参数的呢?

Model Binding vs. Formatters

参数绑定有两种技术:Model Binding和Formatters。实际上,WebAPI使用model binding读取查询字符串(query string)内容进行参数绑定,使用Formatters读取主体内容

(body content)进行参数的绑定。

  • Using Model Binding:

ModelBinding和MVC中的此概念是一致的,更多内容见Here。通常有一个"ValuePeoviders"提供数据片断如查询字符串参数,model binder将这些片断组合成一个对象。

  • Using Formatters:

Formatters(如MediaTypeFormatter类所示)实际上是包含额外元数据的序列化程序。WebAPI从HttpConfiguration中获取一个formatters的列表,然后通过request信息

中的content-type来判断采用具体合适的formatter。WebAPI有不少默认的formatters。默认的JSON formatter是JSON.NET。还有Xml formatter和采用JQuery语法的

FormUrl formatter。

其中Formatters的核心方法是MediaTypeFormatter.ReadFromStreamAsync,如下所示:

public virtual Task<object> ReadFromStreamAsync(
Type type,
Stream stream,
HttpContentHeaders contentHeaders,
IFormatterLogger formatterLogger)

其中,Type指代参数的类型,将传递给序列化器serializer。Stream是请求信息的content stream。Read方法将读取stream,将其实例化为一个对象,然后返回它。

HttpContentType来自请求信息。IFormatterLogger是一个回调接口,fomatter正是通过此接口来记录读取中的错误。

model binding和formatter都支持验证和错误信息记录。然后,相比formatter而言model binding更灵活。

何时采用特定的参数绑定方式?(when do we use which?)

以下这些基本原则决定了parameter是通过modelbinding还是formatter来读取的:

如果参数未添加任何特性字段[attribute]标明,那么这将由参数的.NET类型来决定具体采用何种方式。简单类型"Simple types"采用model binding。复杂类型"Complex types"

  1. 采用formatters。简单类型包括:primitives,Timespan,DateTime,Guid,Decimal,String,或者通过类型转换器(TypeConverter)从字符串转换过来的类型。
  2. 通过使用[FormBody]特性标示特定参数应该从body中取值。
  3. 通过使用[ModelBinder]特性来标示参数或参数类型应该使用model bound方式。这个特性也可以用来配置model binder。[FromUri]是一个从[ModelBinder]继承而来的
    实体,用于配置model binder只应用到URL中的数据。
  4. body只能被读取一次。如果签名中有两个负责类型,至少其中一个必须添加[ModelBinder]特性标注。

以下是使得这些原则得以稳定并可预测的关键设计。

(body 只能被读取一次)Only one thing can read the body

MVC和WebAPI之间的一个关键不同点在于MVC缓存请求主体(request body)。这意味着MVC的参数绑定可以反复从body中查找参数片断。然而,在WebAPI中,请求主体(HttpContent)

只能被读取一次,不被缓存,只能向前读取的流。这意味着parameter binding需要谨慎对待stream,除非在需要绑定参数的情况下,否则stream不能被读取。

以下的action方法想直接读取stream,因而导致WebAPI不能保证其拥有用于参数绑定的stream。考虑如下的action方法:

        // Action saves the request’s content into an Azure blob
public Task PostUploadfile(string destinationBlobName)
{
// string should come from URL, we’ll read content body ourselves.
Stream azureStream = OpenAzureStorage(destinationBlobName); // stream to write to azure
return this.Request.Content.CopyToStream(azureStream); // upload body contents to azure.
}

参数是一个简单类型,因而这将从查询字符串中读取。由于action签名中并不包含任何 负责类型,WebAPI将永远不会读取request content stream,因而这里的action方法可以读取它。

示例

以下给出一些不同请求的示例说明它们将如何映射到特定action签名:

/?id=123&name=bob 
void Action(int id, string name) // 所有参数都是简单类型,因而都将来自url

/?id=123&name=bob 
void Action([FromUri] int id, [FromUri] string name) // 同上

void Action([FromBody] string name); //[FormBody]特性显示标明读取整个body为一个字符串作为参数

public class Customer {   // 定义的一个复杂对象类型 
  public string Name { get; set; } 
  public int Age { get; set; } 
}

/?id=123 
void Action(int id, Customer c) // 参数id从query string中读取,参数c是一个复杂Customer对象类戏,通过formatter从body中读取

void Action(Customer c1, Customer c2) // 出错!多个参数都是复杂类型,都试图从body中读取,而body只能被读取一次

void Action([FromUri] Customer c1, Customer c2) // 可以!不同于上面的action,复杂类型c1将从url中读取,c2将从body中读取

void Action([ModelBinder(MyCustomBinder)] SomeType c) // 标示使用特定的model binder来解析参数

[ModelBinder(MyCustomBinder)] public class SomeType { } // 通过给特定类型SomeType声明标注[ModelBidner(MyCustomBinder)]特性使得所有SomeType类型参数应用此规则

void Action(SomeType c) // 由于c的类型为SomeType,因而应用SomeType上的特性决定其采用model binding

与MVC的区别

以下是MVC和WebAPI在参数绑定上的一些不同点:

  1. MVC只具有model binding,而没有formatters。这是由于MVC将对request body也应用model bind 进行解析,而WebAPI对request body将使用serializer来解析。
  2. MVC将缓存request body,因而能够很容易的将其传递给model binding。WebAPI不缓存request body,因而默认将不对request body应用model binding解析。
  3. WebAPI的绑定完全可以通过action签名的类型来决定。比如:在WebAPI中,我们知道一个参数最终将从body还是query string中读取绑定。然而,在MVC中,model binding
    系统将同时查找body和query string数据进行解析绑定。

原文:How WebAPI does Parameter Binding

[译]WebAPI下的如何实现参数绑定的更多相关文章

  1. WebAPI下的如何实现参数绑定

    本文将概述在WebAPI方式下将如何将参数绑定到一个action方法,包括参数是如何被读取,一系列规则决定特定环境采用的那种绑定方式,文章最后将给出一些实际的例子. Parameter binding ...

  2. WebAPI路由、参数绑定

    ​ 一.测试Web API a)测试Web API可以用来检测请求和返回数据是否正常,可以使用Fiddler.Postman等工具.以Fiddler为例,这是一个http协议调试代理工具,它能够记录客 ...

  3. asp.net webapi 参数绑定总结

    首先必须得更正下自己一直以来对于get请求和post请求理解的一个误区:get请求只能通过url传参,post请求只能通过body传参. 其实上面的理解是错误的,翻阅了不少资料及具体实践,正确理解应该 ...

  4. WebApi 参数绑定方法

    WebAPI 2参数绑定方法   简单类型参数 Example 1: Sending a simple parameter in the Url 01 02 03 04 05 06 07 08 09 ...

  5. ASP.NET WebAPI 05 参数绑定

    ParameterBindingAttribute 在上一篇中重点讲了ModelBinderAttribute的使用场景.这一篇详细的讲一下ModelBinder背后的参数绑定原理. ModelBin ...

  6. webapi简介及参数绑定

    介绍:WebAPI用来开发系统间接口的技术,基于HTTP协议,返回默认是json格式.比wcf简单 更通用,更轻量级,更省流量(json格式):WebAPI尽可能复用MVC路由.ModelBinder ...

  7. asp.net webapi参数绑定

    content={"content": [{"comb_id": "100323","comb_name": " ...

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

    导航 阅读本文之前,您也可以到Asp.Net Web API 2 系列导航进行查看 http://www.cnblogs.com/aehyok/p/3446289.html. 本文主要来讲解以下内容: ...

  9. 细说 Web API参数绑定和模型绑定

    今天跟大家分享下在Asp.NET Web API中Controller是如何解析从客户端传递过来的数据,然后赋值给Controller的参数的,也就是参数绑定和模型绑定. Web API参数绑定就是简 ...

随机推荐

  1. PHP-循环结构-数组(难)

    今日目标: (1)循环结构 —— do..while.. —— 掌握 (2)循环结构 —— for —— 重点 (3)数组 —— 重点 1.PHP中的循环结构 —— do..while... do: ...

  2. 1.MFC架构分析

    1.架构代码文件的结构 主要由四个部分组成 1.资源文件Resource.h:主要定义资源的ID 2.预编译文件:stdafx.h 可以用来解决头文件包含冲突的问题,定义一些需要全局性包含的文件. 3 ...

  3. docker-compose安装redis-sentinel集群(1主+2副+2哨兵)

    前提:本试验环境已经提前安装了docker和docker-compose 说明:本次部署是单机伪集群,想要部署真正的集群,需要将秒个主件拆分到各个机器上去部署,只修改ip地址 1.下载redis的相关 ...

  4. 10.22 tcpdump:监听网络流量

    [功能说明] tcpdump命令是一个截获网络数据包的包分析工具.tcpdump可以将网络中传送的数据包的“头”完全截获下来以提供分析.它支持针对网络层.协议.主机.端口等的过滤,并支持与.或.非逻辑 ...

  5. IDEA中MyBaits的Mapper文件颜色问题

    IDEA中MyBaits的Mapper文件颜色问题 在IDEA中Mapper文件的展示 包含的警告及其解决方案 然后我们就完成了,效果如下 在IDEA中Mapper文件的展示 在IDEA中,Mappe ...

  6. HDMI EDID 处理过程

    DDC的参数 EDID是一种VESA 标准数据格式,其中包含有关监视器及其性能的参数,包括供应商信息.最大图像大小.颜色设置.厂商预设置.频率范围的限制以及显示器名和序列号的字符串.EDID数据标准: ...

  7. Papers | 图像/视频增强 + 深度学习

    目录 I. ARCNN 1. Motivation 2. Contribution 3. Artifacts Reduction Convolutional Neural Networks (ARCN ...

  8. 给uniGUI的表格控件uniDBGrid加上记录序号的列

    uniDBGrid使用起来还是很方便的,但就是没有显示记录序号的功能,必须自己加,参照老外给的解决方案如下: 方案1: 1- 在UniDBGrid建一个第一列 (列的名字起“NO”) 2- 在 Uni ...

  9. 去除swagger ui的红色 error 错误提示

    去除swagger ui的红色 error 错误提示 自定义js文件中加入以下的代码. 加入自定义的js方法看这里 http://www.cnblogs.com/wang2650/archive/20 ...

  10. Runtime之成员变量&属性&关联对象

    上篇介绍了Runtime类和对象的相关知识点,在4.5和4.6小节,也介绍了成员变量和属性的一些方法应用.本篇将讨论实现细节的相关内容. 在讨论之前,我们先来介绍一个很冷僻但又很有用的一个关键字:@e ...