Model Binding for multipart/form-data (File + JSON) post in ASP.NET Core 1.1

后端 未结 2 1936
旧时难觅i
旧时难觅i 2021-01-12 07:08

I\'m attempting to build an ASP.NET Core 1.1 Controller method to handle an HTTP Request that looks like the following:

POST https://localhost/api/data/uploa         


        
相关标签:
2条回答
  • 2021-01-12 07:37

    I'm not 100% clear on how this would work for ASP.NET Core but for Web API (so I assume a similar path exists here) you'd want to go down the road of a Media Formatter. Here's an example (fairly similar to your question) Github Sample with blog post

    Custom formatters might be the ticket? https://docs.microsoft.com/en-us/aspnet/core/mvc/advanced/custom-formatters

    0 讨论(0)
  • 2021-01-12 07:40

    The first problem here is that the data needs to be sent from the client in a slightly different format. Each property in your UploadPayload class needs to be sent in its own form part:

    const formData = new FormData();
    formData.append(`file`, file);
    formData.append('md5', JSON.stringify(md5));
    formData.append('sessionIds', JSON.stringify(sessionIds));
    

    Once you do this, you can add the [FromForm] attribute to the MD5 property to bind it, since it is a simple string value. This will not work for the SessionIds property though since it is a complex object.

    Binding complex JSON from the form data can be accomplished using a custom model binder:

    public class FormDataJsonBinder : IModelBinder
    {
        public Task BindModelAsync(ModelBindingContext bindingContext)
        {
            if(bindingContext == null) throw new ArgumentNullException(nameof(bindingContext));
    
            // Fetch the value of the argument by name and set it to the model state
            string fieldName = bindingContext.FieldName;
            var valueProviderResult = bindingContext.ValueProvider.GetValue(fieldName);
            if(valueProviderResult == ValueProviderResult.None) return Task.CompletedTask;
            else bindingContext.ModelState.SetModelValue(fieldName, valueProviderResult);
    
            // Do nothing if the value is null or empty
            string value = valueProviderResult.FirstValue;
            if(string.IsNullOrEmpty(value)) return Task.CompletedTask;
    
            try
            {
                // Deserialize the provided value and set the binding result
                object result = JsonConvert.DeserializeObject(value, bindingContext.ModelType);
                bindingContext.Result = ModelBindingResult.Success(result);
            }
            catch(JsonException)
            {
                bindingContext.Result = ModelBindingResult.Failed();
            }
    
            return Task.CompletedTask;
        }
    }
    

    You can then use the ModelBinder attribute in your DTO class to indicate that this binder should be used to bind the MyJson property:

    public class UploadPayload
    {
        public IFormFile File { get; set; }
    
        [Required]
        [StringLength(32)]
        [FromForm]
        public string Md5 { get; set; }
    
        [ModelBinder(BinderType = typeof(FormDataJsonBinder))]
        public List<string> SessionIds { get; set; }
    }
    

    You can read more about custom model binding in the ASP.NET Core documentation: https://docs.microsoft.com/en-us/aspnet/core/mvc/advanced/custom-model-binding

    0 讨论(0)
提交回复
热议问题