zoukankan      html  css  js  c++  java
  • 解决WebApi入参时多对象的问题

    我们的项目是用WebApi提供数据服务,且WebPage跟APP中都有调用到。

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

    比如我们的WebApi如下:

    对应的入参对象是:

    很难想象如果再有别的Controller的话,Models中会有多少对象,而这些对象往往使用一次,或者在一个方法中使用了一下就不再用了。

    这显然令我非常不爽!

    经过反复推敲,发现如果采用这种写法:

    显然能减少不少对象,而且接口也相对更加清晰了,不用再按F12进入到对象里面看看里面到底有几个参数,每个参数又表示什么。

    但是!WebApi不支持这种请求,这种把参数写在方法前的只能接受GET方式的请求,就是参数在url中的比如:http://localhost:8080/api/People/GetPeoplesByID?ID=1

    这显然把我们的参数暴露了出来,https就没有卵用了!

    Tech.Moonlight 指出,在https协议下会加密信息中包含了url,故https://xxx.com/index.html?a=123也是安装的!

    怎么让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实在翻不过去)搜了很久才找到的国外的这个大神写的办法,引用自这里

    完整代码如下 源码下载

  • 相关阅读:
    BERT模型fine-tuning代码解析(一)
    使用BERT获取中文词向量
    使用BERT获取中文词向量
    中文情感分类任务如何对bert语言模型微调,微调后的模型如何使用
    Tencent_AILab_ChineseEmbedding使用(×××××)
    中文自然语言处理数据集
    Anaconda环境的创建/激活/删除/管理
    thymeleaf和vue的关系
    模拟测试20191013
    模拟测试20191011-2
  • 原文地址:https://www.cnblogs.com/tlzzu/p/4772783.html
Copyright © 2011-2022 走看看