众所周知WebApi的入参形式是很让人烦心的事情,[FromBody]、[FromUri]、参数实体封装(泛滥成灾),这些入参方式就是在墙奸程序员,至于框架的这种设计初衷无法去揣测,还好有伟大的程序员,无意中查到国外一大牛对此做了改造,表示感谢!
SimplePostVariableParameterBinding.cs
using log4net;
using Newtonsoft.Json;
using SSXLX.Api.Common;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http.Controllers;
using System.Web.Http.Metadata;
namespace SSXLX.WebApi
{
public class SimplePostVariableParameterBinding : HttpParameterBinding
{
private const string MultipleBodyParameters = "MultipleBodyParameters";
protected static ILog logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
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;
TaskCompletionSource<AsyncVoid> tcs = new TaskCompletionSource<AsyncVoid>();
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
tcs.SetResult(default(AsyncVoid));
}
catch (Exception ex)
{
logger.LogException("ExecuteBindingAsync", ex);
//throw ex;
}
return tcs.Task;
}
/// <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.Count(typ => typ == descriptor.ParameterType) > 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
{
}
}
}
通过NameValueCollection获取参数集合,给每个字段参数赋值,重新绑定
在WebApiConfig.cs的Register中添加参数规则过滤
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Web.Http;
using Microsoft.Owin.Security.OAuth;
using Newtonsoft.Json.Serialization;
using System.Net.Http.Formatting;
namespace SSXLX.WebApi
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API 配置和服务
// 将 Web API 配置为仅使用不记名令牌身份验证。
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
// Web API 路由
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional, action = RouteParameter.Optional }
);
<strong>config.ParameterBindingRules.Add(SimplePostVariableParameterBinding.HookupParameterBinding);</strong>
}
}
}
如此接口的参数定义则可以定义为我们常规的习惯
public async Task<IHttpActionResult> Regist(string loginName, string password, string idCard, string validCode)
也满足接口强类型语言的初衷,避免了大量的参数实体的封装造成参数实体泛滥成灾。