解决WebApi入参时多对象的问题
我们的项目是用WebApi提供数据服务,且WebPage跟APP中都有调用到。
WebApi提供的接口一多,就发现一个问题,我们项目中有很多接口是接收POST(安全原因,我们采用的是https)请求的,而且入参基本也就是一两个参数。在开发的后期发现WebApi中有很多对象,多到已经快到了难以管理的地步了。
比如我们的WebApi如下:

对应的入参对象是:

很难想象如果再有别的Controller的话,Models中会有多少对象,而这些对象往往使用一次,或者在一个方法中使用了一下就不再用了。
这显然令我非常不爽!
经过反复推敲,发现如果采用这种写法:

显然能减少不少对象,而且接口也相对更加清晰了,不用再按F12进入到对象里面看看里面到底有几个参数,每个参数又表示什么。
但是!WebApi不支持这种请求,这种把参数写在方法前的只能接受GET方式的请求,就是参数在url中的比如:http://localhost:8080/api/People/GetPeoplesByID?ID=1
这显然把我们的参数暴露了出来,https就没有卵用了!
怎么让WebApi接收这种请求那?
机智的我给出下面两种解决方法:
方法一
我们注意到POST请求的过来的参数在Form Data中是这么写的:name=tlzzu&age=18,
相对的,而以GET方式请求过来的参数是这样的:http://localhost:端口号/api/People/ GetPeoplesByID? name=tlzzu&age=18
有没有注意到参数都是name=tlzzu&age=18 这样的,所以我们是不是可以把POST方式过来的Form Data 拼接到url中去,这样WebApi在后面解析的时候就能把他当成GET方式过来的参数了。
那么问题又来了,在哪里介入WebApi的请求处理过程呐?
我们知道ASP.NET WebApi是一个消息处理管道,这个管道是一组HttpMessageHandler的有序组-----引用自【ASP.NET Web API标准的"管道式"设计】
那也就是说我们可以写一个Handler来把POST过来的参数设置到GET里面。说干就干
新建一个SetPostToGetHandler.cs类
public
class
SetPostToGetHandler:System.Net.Http.DelegatingHandler
{
protected
override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
//如果不是POST请求,就不处理
if (request.Method !=
HttpMethod.Post)
return
base.SendAsync(request, cancellationToken);
//必须显示的指定 ContentType是application/x-www-form-urlencoded,如果是application/json则不处理
if (request.Content.Headers.ContentType ==null||string.IsNullOrWhiteSpace(request.Content.Headers.ContentType .MediaType)||!request.Content.Headers.ContentType.MediaType.Contains("application/x-www-form-urlencoded"))
return
base.SendAsync(request, cancellationToken);
//获取POST请求过来的参数
var formStr =request.Content.ReadAsFormDataAsync().Result.ToString();
if (!string.IsNullOrWhiteSpace(formStr))
{
var url =
string.IsNullOrWhiteSpace(request.RequestUri.Query) ?
string.Format("{0}?{1}", request.RequestUri.AbsoluteUri, formStr) : string.Format("{0}&{1}", request.RequestUri.AbsoluteUri, formStr);
//给request设置新的RequestUri对象
request.RequestUri =
new
Uri(url);
}
return
base.SendAsync(request, cancellationToken);
}
}
然后添加到WebApi MessageHandlers中

用Fidder测试,完美解决问题
方法二
新建SimplePostVariableParameterBinding类
public
class
SimplePostVariableParameterBinding : HttpParameterBinding
{
private
const
string MultipleBodyParameters =
"MultipleBodyParameters";
public SimplePostVariableParameterBinding(HttpParameterDescriptor descriptor) : base(descriptor) { }
/// <summary>
/// Check for simple binding parameters in POST data. Bind POST
/// data as well as query string data
/// </summary>
/// <param name="metadataProvider"></param>
/// <param name="actionContext"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public
override
Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider, HttpActionContext actionContext, CancellationToken cancellationToken)
{
string stringValue =
null;
try
{
NameValueCollection col = TryReadBody(actionContext.Request);
if (col !=
null)
stringValue = col[Descriptor.ParameterName];
// try reading query string if we have no POST/PUT match
if (stringValue ==
null)
{
var query = actionContext.Request.GetQueryNameValuePairs();
if (query !=
null)
{
var matches = query.Where(kv => kv.Key.ToLower() == Descriptor.ParameterName.ToLower());
if (matches.Count() >
0)
stringValue = matches.First().Value;
}
}
object value = StringToType(stringValue);
// Set the binding result here 给字段挨个赋值
SetValue(actionContext, value);
// now, we can return a completed task with no result
TaskCompletionSource<AsyncVoid> tcs =
new
TaskCompletionSource<AsyncVoid>();
tcs.SetResult(default(AsyncVoid));
return tcs.Task;
}
catch (Exception ex)
{
throw ex;
return
null;
}
}
/// <summary>
/// Method that implements parameter binding hookup to the global configuration object's
/// ParameterBindingRules collection delegate.
///
/// This routine filters based on POST/PUT method status and simple parameter
/// types.
/// </summary>
/// <example>
/// GlobalConfiguration.Configuration.
/// .ParameterBindingRules
/// .Insert(0,SimplePostVariableParameterBinding.HookupParameterBinding);
/// </example>
/// <param name="descriptor"></param>
/// <returns></returns>
public
static
HttpParameterBinding HookupParameterBinding(HttpParameterDescriptor descriptor)
{
try
{
var supportedMethods = descriptor.ActionDescriptor.SupportedHttpMethods;
// Only apply this binder on POST operations
if (supportedMethods.Contains(HttpMethod.Post))
{
var supportedTypes =
new
Type[] { typeof(string),
typeof(int),
typeof(long),
typeof(long?),
typeof(decimal),
typeof(double),
typeof(bool),
typeof(DateTime),
typeof(byte[])
};
if (supportedTypes.Where(typ => typ == descriptor.ParameterType).Count() >
0)
return
new
SimplePostVariableParameterBinding(descriptor);
}
}
catch (Exception ex)
{
throw ex;
}
return
null;
}
private
object StringToType(string stringValue)
{
object value =
null;
try
{
if (stringValue ==
null)
value =
null;
else
if (Descriptor.ParameterType ==
typeof(string))
value = stringValue;
else
if (Descriptor.ParameterType ==
typeof(int))
value =
int.Parse(stringValue, CultureInfo.CurrentCulture);
else
if (Descriptor.ParameterType ==
typeof(Int32))
value =
Int32.Parse(stringValue, CultureInfo.CurrentCulture);
else
if (Descriptor.ParameterType ==
typeof(Int64))
value =
Int64.Parse(stringValue, CultureInfo.CurrentCulture);
else
if (Descriptor.ParameterType ==
typeof(decimal))
value =
decimal.Parse(stringValue, CultureInfo.CurrentCulture);
else
if (Descriptor.ParameterType ==
typeof(double))
value =
double.Parse(stringValue, CultureInfo.CurrentCulture);
else
if (Descriptor.ParameterType ==
typeof(DateTime))
value =
DateTime.Parse(stringValue, CultureInfo.CurrentCulture);
else
if (Descriptor.ParameterType ==
typeof(bool))
{
value =
false;
if (stringValue ==
"true"
|| stringValue ==
"on"
|| stringValue ==
"1")
value =
true;
}
else
value = stringValue;
}
catch (Exception ex)
{
throw ex;
}
return value;
}
/// <summary>
/// Read and cache the request body
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
private
NameValueCollection TryReadBody(HttpRequestMessage request)
{
object result =
null;
try
{
if (!request.Properties.TryGetValue(MultipleBodyParameters, out result))
{
var contentType = request.Content.Headers.ContentType.MediaType.ToLower();
if (contentType ==
null)
{
result =
null;
}
else
if (contentType.Contains("application/x-www-form-urlencoded"))
{
result = request.Content.ReadAsFormDataAsync().Result;
}
else
if (contentType.Contains("application/json"))//解决json问题
{
var jsonStr = request.Content.ReadAsStringAsync().Result;//{"Name":"tongl","Age":22}
var json =
JsonConvert.DeserializeObject<IDictionary<string, string>>(jsonStr);
if (json !=
null
|| json.Count >
0)
{
var nvc =
new
NameValueCollection();
foreach (var item in json)
{
nvc.Add(item.Key, item.Value);
}
result = nvc;
}
}
else
{
result =
null;
}
request.Properties.Add(MultipleBodyParameters, result);
}
}
catch (Exception ex)
{
throw ex;
}
return result as
NameValueCollection;
}
private
struct
AsyncVoid
{
}
}
这是我用bing(技术渣google实在翻不过去)搜了很久才找到的国外的这个大神写的办法,引用自这里
完整代码如下 源码下载
解决WebApi入参时多对象的问题的更多相关文章
- .NET WebAPI 自定义 NullableConverter 解决请求入参 “”空字符触发转换异常问题
最近在项目中启用了Nullable 可为空的类型,这个特性确实很好用,在 WebAPI 的入参上可以直接采用 ? 来标记一个字段是否允许为空,但是使用过程中遇到了如下一个问题,比如创建部门接口 我们定 ...
- 字节码编程,Javassist篇二《定义属性以及创建方法时多种入参和出参类型的使用》
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 在上一篇 Helloworld 中,我们初步尝试使用了 Javassist字节编程的 ...
- 命令行通过入参调用jar包
命令行通过入参调用jar包 最近因为项目需要,需要实现一个功能,即定时执行服务器上的一个脚本去对数据库的数据进行业务处理,要操作的数据库有很多种,mysql.db2.oracle.sqlserver等 ...
- URL传参时中文参数乱码的解决方法
URL传参时,中文参数乱码的解决: 今天在工作中遇到了这样的一个问题,在页面之间跳转时,我将中文的参数放入到url中,使用location进行跳转传参,但是发现接收到的参数值是乱码.我的代码是这样写的 ...
- js的replace函数入参为function时的疑问
近期在写js导出excel文件时运用到replace方法,此处详细的记录下它各个参数所代表的的意义. 定义和用法 replace() 方法用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式 ...
- C#构造函数在继承时必须要求与父类型构造函数入参相同怎么办?
摘要 我们都知道,C#中,在类型继承时,由于构造子类必须先构造其父类型的内容,因此,必须子类型的构造函数中调用父类型的构造函数(无参数的不需要显式声明). 但是往往我们会出现,子类型本身的构造函数大于 ...
- TFS签入代码时,自动修改工作项的状态为“已解决”
Visual Studio中有一个很酷的功能,就是签入代码到TFS库时,可以关联相应的工作项,实现代码与工作项(需求.任务.Bug等)的关联,从而实现代码的跟踪. 在关联工作项的过程中,如果工作项具备 ...
- robot:接口入参为图片时如何发送请求
https://www.cnblogs.com/changyou615/p/8776507.html 接口是上传图片,通过F12抓包获得如下信息 由于使用的是RequestsLibrary,所以先看一 ...
- RobotFramework:发现一个大坑,当post接口入参为json时,千万不能用sojson转化后的json串(ride解析会有异常,非sojson工具问题),直接用浏览器粘过来的就行
问题背景: 和以往一样愉快的进行着自动化测试,突然就不停的提示我,“程序异常”,查看log发现data中的json变为了数组?????? 那算了,我不先组装入参数据直接data=json入参吧,wha ...
随机推荐
- ABP理论学习之Nuget包
返回总目录 本篇目录 框架 测试基 ABP已经发布在Nuget上,这里是所有包的列表. 框架 Abp Abp系统的核心包.所有其他的包都依赖这个包. Abp.Web 提供了MVC和Web API都使用 ...
- HTML和CSS经典布局1
如下图: 需求: 1. 如图 2. 可以从body标签开始. <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xht ...
- [nRF51822] 15、穿戴式设备上电量检测装置的设计及细节技术点(偏专业硬件文章)
穿戴式 设备如智能手环.智能手表一般采用几百毫安时的锂离子电池来供电.因此,与之配套的充电电路.稳压电路和电池电量检测电路便必不可少!本文主要谈谈该类消费类电子内部电池电量检测的一般方法及其优缺点. ...
- ASP.NET MVC 过滤器(三)
ASP.NET MVC 过滤器(三) 前言 本篇讲解行为过滤器的执行过程,过滤器实现.使用方式有AOP的意思,可以通过学习了解过滤器在框架中的执行过程从而获得一些AOP方面的知识(在顺序执行的过程中, ...
- Fig 应用编排
Fig是Docker的应用编排工具,主要用来跟 Docker 一起来构建基于 Docker 的复杂应用,Fig 通过一个配置文件来管理多个Docker容器,非常适合组合使用多个容器进行开发的场景. 说 ...
- Atitit GRASP(General Responsibility Assignment Software Patterns),中文名称为“通用职责分配软件模式”
Atitit GRASP(General Responsibility Assignment Software Patterns),中文名称为"通用职责分配软件模式" 1. GRA ...
- iOS-数据持久化详细介绍
1.iOS-数据解析XML解析的多种平台介绍 2.iOS-数据持久化基础-JSON与XML数据解析 3.iOS-数据持久化基础-沙盒机制 4. 数据持久化的几种方式: 1)plist(XML属性列 ...
- 浅析Yii2的view层设计
Yii2.0的view层提供了若干重要的功能:assets资源管理,widgets小组件,layouts布局... 下面将通过对Yii2.0代码直接进行分析,看一下上述功能都是如何实现的,当然细枝末节 ...
- FontAwesome 4.4.0 中完整的585个图标样式CSS参考
做一记录,免得每次都去网上搜图标对应的class. 在线版本:http://dnt.dkill.net/dnt/font/
- 【WP开发】不同客户端之间传输加密数据
在上一篇文章中,曾说好本次将提供一个客户端之间传输加密数据的例子.前些天就打算写了,只是因一些人类科技无法预知的事情发生,故拖到今天. 本示例没什么技术含量,也没什么亮点,Bug林立,只不过提供给有需 ...