1. 程式人生 > >WebApi 中請求的 JSON 資料欄位作為 POST 引數傳入

WebApi 中請求的 JSON 資料欄位作為 POST 引數傳入

  使用 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";
        }

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 下測試通過。其他框架版本未測試。