如何在 Web API 中支持 json 和 urlencoded 内容类型

问题描述 投票:0回答:3

我想在我的控制器/操作中支持以下所有 3 种内容类型。

application/json
application/x-www-form-urlencoded
multipart/form-data

使用此签名,我可以支持 urlencoded 和表单数据,但是 JSON 有效负载不会绑定到

Message

[HttpPost]
public async Task<IActionResult> PostAsync(Message message)

如果我想正确地将 JSON 负载绑定到

Message
,我需要使用
FromBody
属性,如下所示:

[HttpPost]
public async Task<IActionResult> PostAsync([FromBody]Message message)

但是这样做会开始为我感兴趣的其他 2 种内容类型抛出 415 错误。

我的问题是,如何向我的客户提供单一 API 端点,并让他们能够灵活地以这 3 种内容类型中的任何一种发送数据。

asp.net-core asp.net-web-api
3个回答
2
投票

您应该使用 ConsumesAttribute 添加支持的不同内容类型,然后按如下方式更新您的操作;

[HttpPost]
[Consumes("application/json", "multipart/form-data", "application/x-www-form-urlencoded")]
public async Task<IActionResult> PostAsync([FromForm, FromBody, FromQuery]Message message)

2
投票

实际上,您可以有一个接受多种内容类型的端点:

[HttpPost]
[Consumes("application/json")]
public async Task<IActionResult> CommonEndpoint([FromBody] JObject payload)
{
    ...
}

[HttpPost]
[Consumes("application/x-www-form-urlencoded")]
public async Task<IActionResult> CommonEndpoint(/* this overload doesn't specify the payload in its signature */)
{
    var formFieldsDictionary = HttpContext.Request.Form.Keys
                .ToDictionary(k => k, k => HttpContext.Request.Form[k].FirstOrDefault());

    var payload = JsonConvert.SerializeObject(formFieldsDictionary);

    ...
}

0
投票

首先,您应该避免将

application/json
multipart/form-data
组合用于同一操作,这会使应用程序不稳定。

如果您坚持这样做,您需要按照以下步骤实施您自己的

ModelBinder

  1. MyComplexTypeModelBinder

    public class MyComplexTypeModelBinder : ComplexTypeModelBinder
    {
        public MyComplexTypeModelBinder(IDictionary<ModelMetadata, IModelBinder> propertyBinders, ILoggerFactory loggerFactory, bool allowValidatingTopLevelNodes) : base(propertyBinders, loggerFactory, allowValidatingTopLevelNodes)
        {
        }
    
        protected override Task BindProperty(ModelBindingContext bindingContext)
        {
            try
            {
                var result = base.BindProperty(bindingContext);
                if (bindingContext.Result.IsModelSet == false)
                {
                    var request = bindingContext.HttpContext.Request;
                    var body = request.Body;
    
                    request.EnableRewind();
    
                    var buffer = new byte[Convert.ToInt32(request.ContentLength)];
    
                    request.Body.Read(buffer, 0, buffer.Length);
    
                    var bodyAsText = Encoding.UTF8.GetString(buffer);
    
                    var jobject = JObject.Parse(bodyAsText);
                    var value = jobject.GetValue(bindingContext.FieldName, StringComparison.InvariantCultureIgnoreCase);                    
                    var typeConverter = TypeDescriptor.GetConverter(bindingContext.ModelType);
                    var model = typeConverter.ConvertFrom(
                        context: null,
                        culture: CultureInfo.InvariantCulture,
                        value: value.ToString());
                    bindingContext.Result = ModelBindingResult.Success(model);
                    request.Body.Seek(0, SeekOrigin.Begin);
    
                }
                return result;
            }
            catch (Exception ex)
            {
    
                throw;
            }
        }
    }
    
  2. MyComplexTypeModelBinderProvider

    public class MyComplexTypeModelBinderProvider : IModelBinderProvider
    {
        public IModelBinder GetBinder(ModelBinderProviderContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }
    
            if (context.Metadata.IsComplexType && !context.Metadata.IsCollectionType)
            {
                var propertyBinders = new Dictionary<ModelMetadata, IModelBinder>();
                for (var i = 0; i < context.Metadata.Properties.Count; i++)
                {
                    var property = context.Metadata.Properties[i];
                    propertyBinders.Add(property, context.CreateBinder(property));
                }
    
                var loggerFactory = context.Services.GetRequiredService<ILoggerFactory>();
                var mvcOptions = context.Services.GetRequiredService<IOptions<MvcOptions>>().Value;
                return new MyComplexTypeModelBinder(
                    propertyBinders,
                    loggerFactory,
                    mvcOptions.AllowValidatingTopLevelNodes);
            }
    
            return null;
        }
    
    }
    
  3. MyComplexTypeModelBinderProvider
     注册 
    Startup.cs

    services.AddMvc(options => {
        options.ModelBinderProviders.Insert(0, new MyComplexTypeModelBinderProvider());
    }).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
    
© www.soinside.com 2019 - 2024. All rights reserved.