理解 ASP.NET Web API 中的 HttpParameterBinding
背景
问题的起因是这样的。群里面一个哥们儿发现在使用 ASP.NET WebAPI 时,不能在同一个方法签名中使用多次 FromBodyAttribute 这个 Attribute 。正好我也在用 WebAPI,不过我还没有这种需求。所以就打算研究一下。
异常信息
当使用多个 FromBodyAttribute 时,会收到下面的异常信息:
{
"Message": "An error has occurred.",
"ExceptionMessage": "Can't bind multiple parameters ('a' and 'b') to the request's content.",
"ExceptionType": "System.InvalidOperationException",
"StackTrace": " 在 System.Web.Http.Controllers.HttpActionBinding.ExecuteBindingAsync(HttpActionContext actionContext, CancellationToken cancellationToken)\r\n 在 System.Web.Http.Controllers.ActionFilterResult.<ExecuteAsync>d__2.MoveNext()\r\n--- 引发异常的上一位置中堆栈跟踪的末尾 ---\r\n 在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n 在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n 在 System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()"
}
意思就是不能参数 a 和 b 绑定到当前请求。
源代码追踪
通过异常信息可以发现是在 HttpActionBinding 这个类里面抛出了这个异常。立马去源代码中找这个类。下面是源代码:
通过 1,2,3 这三个点,发现是参数绑定类里面的验证失败。接着看了 HttpParameterBinding 是个抽象类。没有看到太多可用信息。去 FromBodyAttribute 里面看看有没有什么可用信息。
发现这里需要提供一个 HttpParameterBinding 的实例。从箭头标记的方法根进去接着看。
这里可以看到返回了一个 FormatterParameterBinding 的实例。并且需要三个参数。
- parameter:从命名可以看出来是参数描述信息;
- formatters:这个应该比较熟悉了,是格式化器;
- bodyModelValidator:这个是对应参数的验证器;
以上三个参数的意义基本就是看命名+大概阅读源代码得到的(所以写代码,命名很重要)。接着进入 FormatterParameterBinding 的源代码。这个类里面的代码也就 100 多行,逻辑就是从 HttpContent 中读取内容并设置为当前参数的值。
到了这儿算是理清了一点:原来在参数上打的这些 Attribute 都是从 ParameterBindingAttribute 继承的,又通过实现 GetBinding(HttpParameterDescriptor parameter); 方法将请求的参数与方法的参数进行绑定。
但是,在哪儿标记了不能使用多个 FromBodyAttribute 呢?既然是在 HttpActionBinding 中进行的验证,那就顺着 HttpActionBinding 往上找。通过 HttpActionBinding 的构造函数,发现只有 DefaultActionValueBinder 调用了它。接着往下看,看谁使用了这个 new 出来的实例。紧挨着就看到了 EnsureOneBodyParameter 这个方法,有点儿可疑,进去看一下。
这个地方的 WillReadBody 如果为 true 并且 idxFromBody 大于 0 ,就会给 ParameterBinding 设置错误消息。看了一下消息内容,就是最上面的异常消息的模板。到这里应该算是找到根儿上了。
现在来梳理一下:也就是说 HttpParameterBinding 的 WillReadBody 如果返回 true 就不能在一个方法的签名中使用多次,一旦使用多次,就会把这个错误。刚才上面看到的 FormatterParameterBinding 里面的 WillReadBody 是直接返回的 true ,而且是只读的,并且在执行绑定时是直接读取的 HttpContent 的内容,设置为当前参数的值了。假如要执行的 action 的方法签名中有多个参数就绑定不成功了。
定制开发
知道了这个原理,那么想在一个有多个参数的 action 中进行参数的灵活绑定,就有了办法。分两步走:
- 自定义一个 Attribute 从 ParameterBindingAttribute 继承;
- 自定义一个 ParameterBinding 从 HttpParameterBinding 继承;在 ExecuteBindingAsync 方法中绑定 action 的参数的值。并把这个自定义的类的 WillReadBody 设置为 false 。
理解 ASP.NET Web API 中的 HttpParameterBinding的更多相关文章
- ASP.NET Web API中的参数绑定总结
ASP.NET Web API中的action参数类型可以分为简单类型和复杂类型. HttpResponseMessage Put(int id, Product item) id是int类型,是简单 ...
- Asp.Net Web Api中使用Swagger
关于swagger 设计是API开发的基础.Swagger使API设计变得轻而易举,为开发人员.架构师和产品所有者提供了易于使用的工具. 官方网址:https://swagger.io/solutio ...
- ASP.NET Web API中的Controller
虽然通过Visual Studio向导在ASP.NET Web API项目中创建的 Controller类型默认派生与抽象类型ApiController,但是ASP.NET Web API框架本身只要 ...
- 在ASP.NET Web API中使用OData
http://www.alixixi.com/program/a/2015063094986.shtml 一.什么是ODataOData是一个开放的数据协议(Open Data Protocol)在A ...
- ASP.NET Web API 中的异常处理(转载)
转载地址:ASP.NET Web API 中的异常处理
- 【ASP.NET Web API教程】6.2 ASP.NET Web API中的JSON和XML序列化
谨以此文感谢关注此系列文章的园友!前段时间本以为此系列文章已没多少人关注,而不打算继续下去了.因为文章贴出来之后,看的人似乎不多,也很少有人对这些文章发表评论,而且几乎无人给予“推荐”.但前几天有人询 ...
- Asp.Net Web API 2第十三课——ASP.NET Web API中的JSON和XML序列化
前言 阅读本文之前,您也可以到Asp.Net Web API 2 系列导航进行查看 http://www.cnblogs.com/aehyok/p/3446289.html 本文描述ASP.NET W ...
- ASP.NET WEB API 中的路由调试与执行过程跟踪
路由调试 RouteDebugger 是调试 ASP.NET MVC 路由的一个好的工具,在ASP.NET WEB API中相应的有 WebApiRouteDebugger ,Nuget安装 Inst ...
- 能省则省:在ASP.NET Web API中通过HTTP Headers返回数据
对于一些返回数据非常简单的 Web API,比如我们今天遇到的“返回指定用户的未读站内短消息数”,返回数据就是一个数字,如果通过 http response body 返回数据,显得有些奢侈.何不直接 ...
随机推荐
- Celery结合Django使用
一.Celery介绍 Celery 是一个 基于python开发的分布式异步消息任务队列,通过它可以轻松的实现任务的异步处理, 如果你的业务场景中需要用到异步任务,就可以考虑使用celery, 举几个 ...
- PHP-不同Str 拼接方法性能对比 参考自https://www.cnblogs.com/xiaoerli520/p/9624309.html
问题 在PHP中,有多种字符串拼接的方式可供选择,共有: 1 . , .= , sprintf, vprintf, join, implode 那么,那种才是最快的,或者那种才是最适合业务使用的,需要 ...
- CentOS7.5实践快速部署LAMP+Tomcat成功运行阿里云或者腾讯云
安装一定要按照顺序来 1 先安装JDK+TOMCAT 点击看这里 2 在安装LAMP 点击看这里 3 最关键的就是这里 LAMP+Tomcat整合 我们不用源码编译安装,而是使用yum命令来完成. ...
- RTP实时传输协议
RTP协议是包括一对协议:RTP和RTCP. RTP传输数据,RTCP传输控制信息. 一般基于UDP,RTP使用偶数端口,RTCP使用下一个奇数端口. 层次关系: APP -> RTP -> ...
- PAT 乙级 1044 火星数字 (20 分)
1044 火星数字 (20 分) 火星人是以 13 进制计数的: 地球人的 0 被火星人称为 tret. 地球人数字 1 到 12 的火星文分别为:jan, feb, mar, apr, may, j ...
- Ubuntu 16.04 安装Go 1.9.2
系统环境 Ubuntu: 16.04 Go: 1.9.2 安装步骤 $ curl -O https://storage.googleapis.com/golang/go1.9.linux-amd64. ...
- MySQL Group Replication-MGR集群
简介 MySQL Group Replication(简称MGR)字面意思是mysql组复制的意思,但其实他是一个高可用的集群架构,暂时只支持mysql5.7和mysql8.0版本. 是MySQL官方 ...
- Troubleshooting 10g and 11.1 Clusterware Reboots (文档 ID 265769.1)
Troubleshooting 10g and 11.1 Clusterware Reboots (文档 ID 265769.1) This document is intended for DBA' ...
- VS2015创建类库项目后添加不了WPF资源字典,窗口,用户控件处理办法
打开项目工程文件在PropertyGroup标签最后加上下面3行: <ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FA ...
- Bootstrap关闭子类页面,刷新父类页面
关闭子类页面,刷新父类页面 function doBack() { var index = parent.layer.getFrameIndex(window.name) //获取窗口索引值 pare ...