我们的项目是用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入参时多对象的问题的更多相关文章

  1. .NET WebAPI 自定义 NullableConverter 解决请求入参 “”空字符触发转换异常问题

    最近在项目中启用了Nullable 可为空的类型,这个特性确实很好用,在 WebAPI 的入参上可以直接采用 ? 来标记一个字段是否允许为空,但是使用过程中遇到了如下一个问题,比如创建部门接口 我们定 ...

  2. 字节码编程,Javassist篇二《定义属性以及创建方法时多种入参和出参类型的使用》

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 在上一篇 Helloworld 中,我们初步尝试使用了 Javassist字节编程的 ...

  3. 命令行通过入参调用jar包

    命令行通过入参调用jar包 最近因为项目需要,需要实现一个功能,即定时执行服务器上的一个脚本去对数据库的数据进行业务处理,要操作的数据库有很多种,mysql.db2.oracle.sqlserver等 ...

  4. URL传参时中文参数乱码的解决方法

    URL传参时,中文参数乱码的解决: 今天在工作中遇到了这样的一个问题,在页面之间跳转时,我将中文的参数放入到url中,使用location进行跳转传参,但是发现接收到的参数值是乱码.我的代码是这样写的 ...

  5. js的replace函数入参为function时的疑问

    近期在写js导出excel文件时运用到replace方法,此处详细的记录下它各个参数所代表的的意义. 定义和用法 replace() 方法用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式 ...

  6. C#构造函数在继承时必须要求与父类型构造函数入参相同怎么办?

    摘要 我们都知道,C#中,在类型继承时,由于构造子类必须先构造其父类型的内容,因此,必须子类型的构造函数中调用父类型的构造函数(无参数的不需要显式声明). 但是往往我们会出现,子类型本身的构造函数大于 ...

  7. TFS签入代码时,自动修改工作项的状态为“已解决”

    Visual Studio中有一个很酷的功能,就是签入代码到TFS库时,可以关联相应的工作项,实现代码与工作项(需求.任务.Bug等)的关联,从而实现代码的跟踪. 在关联工作项的过程中,如果工作项具备 ...

  8. robot:接口入参为图片时如何发送请求

    https://www.cnblogs.com/changyou615/p/8776507.html 接口是上传图片,通过F12抓包获得如下信息 由于使用的是RequestsLibrary,所以先看一 ...

  9. RobotFramework:发现一个大坑,当post接口入参为json时,千万不能用sojson转化后的json串(ride解析会有异常,非sojson工具问题),直接用浏览器粘过来的就行

    问题背景: 和以往一样愉快的进行着自动化测试,突然就不停的提示我,“程序异常”,查看log发现data中的json变为了数组?????? 那算了,我不先组装入参数据直接data=json入参吧,wha ...

随机推荐

  1. Spring Rabbitmq HelloWorld实例

    之前的博客和大家分享了Rabbitmq的基本框架,及其工作原理,网址为 < http://www.cnblogs.com/jun-ma/p/4840869.html >.今天呢,想和大家一 ...

  2. 一个App完成入门篇-终结篇(八)- 应用收官

    经过以上几步的学习,我们终于来到最后一个步骤了,应用APP也接近尾声. 通过之前的几节教程,不知道您对使用DeviceOne开发一个应用是不是已经得心应手了,本节教程将教会大家如何在开发完成之后通过D ...

  3. 用python实现的百度音乐下载器-python-pyqt-改进版

    之前写过一个用python实现的百度新歌榜.热歌榜下载器的博文,实现了百度新歌.热门歌曲的爬取与下载.但那个采用的是单线程,网络状况一般的情况下,扫描前100首歌的时间大概得到40来秒.而且用Pyqt ...

  4. JVM字节码指令

    invokevirtual 调用实例方法 invokespecial 调用父类构造,实例初始化方法,私有方法 dup 复制栈顶数值,并且复制值进栈,pop/pop2为栈顶值出栈 aload_0 加载第 ...

  5. 工作任务:模拟淘宝登录和购物车功能:使用cookie记录登录名,下次登录时能够记得上次的登录名,使用cookie模拟购物车功能,使用session记住登录信息并验证是否登录,防止利用url打开网站,并实现退出登录功能

    登入界面<% Cookie[] cks =request.getCookies(); String str=null; for(Cookie ck:cks) { if(ck.getName(). ...

  6. MongoDB 使用Index

    Index 能够提高查询的性能,如果没有Index,MongoDB必须扫描整个collection,从collection的第一个doc开始,直到最后一个doc,即使第一个doc之后的所有doc都不满 ...

  7. 对Big Table进行全表更新,导致 Replication 同步数据的过程十分缓慢

    在Publisher database中更新一个big table,数据行数是3.4亿多.由于没有更新 clustered Index key,因此,只产生了3.4亿多个Update Commands ...

  8. 记住密码超简单实现(C#)

    实现效果如下 实现过程 [Serializable] class User { //记住密码 private string loginID; public string LoginID { get { ...

  9. javascript动画系列第五篇——模拟滚动条

    × 目录 [1]原理介绍 [2]数字加减 [3]元素尺寸[4]内容滚动 前面的话 当元素内容溢出元素尺寸范围时,会出现滚动条.但由于滚动条在各浏览器下表现不同,兼容性不好.所以,模拟滚动条也是很常见的 ...

  10. 应用程序框架实战三十:表现层及ASP.NET MVC介绍(一)

    本文将介绍表现层及ASP.NET MVC的一些要点,特别是ASP.NET MVC的一些抽象和封装技巧,如果你对MVC还不了解,可以参考<ASP.NET MVC4 高级编程>,作者Jon G ...