6

WebApi 中请求的 JSON 数据字段作为 POST 参数传入

 3 years ago
source link: http://www.cnblogs.com/oangs/p/14273813.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

使用 POST 方式请求 JSON 数据到服务器 WebAPI 接口时需要将 JSON 格式封装成数据模型接收参数。即使参数较少,每个接口仍然需要单独创建模型接收。下面方法实现了将 JSON 参数中的字段作为接口参数接收。实现的并不完美,现在只支持 JSON 格式顶级结构字段作为参数使用,且未处理复杂格式参数。

每个接收参数都会进行 JSON 反序列化操作,故参数多的情况下创建模型接收显然更节省资源。

以下方法只进行过简单测试,如有问题,还请大佬们自行解决,解决后希望能在此留言与诸位网友分享。

POST 提交的 JSON 数据演示:

{'Code':10000,Msg:'中文测试'}

WebApi 中获取方式:

        [HttpPost]
        public string Post([FromJson]int Code,[FromJson]string Msg)
        {
            return "OK";
        }

YRbqayb.png!mobile

FromJson 类:

    public class FromJsonAttribute : Attribute, IBindingSourceMetadata
    {
        public BindingSource BindingSource
        {
            get
            {
                return BindingSource.Custom;
            }
        }
    }

ModelBuilder 类:

    public class JsonParameterModelBinder : IModelBinder
    {
        private readonly BodyModelBinder bodyModelBinder;

        public JsonParameterModelBinder(IList<IInputFormatter> formatters, IHttpRequestStreamReaderFactory readerFactory)
        {
            bodyModelBinder = new BodyModelBinder(formatters, readerFactory);
        }

        public Task BindModelAsync(ModelBindingContext bindingContext)
        {
            if (bindingContext == null)
                throw new ArgumentNullException(nameof(bindingContext));

            var postStr = string.Empty;
            using (StreamReader sr = new StreamReader(bindingContext.HttpContext.Request.Body))
                postStr = sr.ReadToEndAsync().Result;

            var jobj = Newtonsoft.Json.JsonConvert.DeserializeObject<Newtonsoft.Json.Linq.JObject>(postStr);
            var val = jobj[bindingContext.FieldName]?.ToObject<object>();

            if (val != null)
                bindingContext.Result = ModelBindingResult.Success(Convert.ChangeType(val, bindingContext.ModelType, null));

            //将流重新写回 Request.Body 不然第二个参数再操作时会报错
            bindingContext.HttpContext.Request.Body = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(postStr));

            return Task.CompletedTask;
        }
    }

Provider 类:

    public class JsonParameterModelBinderProvider : IModelBinderProvider
    {
        private readonly IList<IInputFormatter> _formatters;

        public JsonParameterModelBinderProvider(IList<IInputFormatter> Formatters)
        {
            _formatters = Formatters;
        }

        public IModelBinder GetBinder(ModelBinderProviderContext context)
        {
            if (context == null)
                throw new ArgumentNullException(nameof(context));

            if (context.BindingInfo.BindingSource != null && context.BindingInfo.BindingSource.CanAcceptDataFrom(BindingSource.Custom))
                return new JsonParameterModelBinder(_formatters, context.Services.GetRequiredService<IHttpRequestStreamReaderFactory>());

            return null;
        }
    }

修改 Startup.cs 的 void ConfigureServices(IServiceCollection services) 方法,增加:

            services.AddMvc(options =>
            {
                options.ModelBinderProviders.Insert(0, new JsonParameterModelBinderProvider(options.InputFormatters));
            });

插入到 0 位置,优先处理,尽量不要使用 Add 方法插入到末位,否则可能不会被处理。

以上代码在 .Net 5 下测试通过。其他框架版本未测试。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK