在 ASP.NET Core Web API 中上传文件和 JSON [英] Upload files and JSON in ASP.NET Core Web API

查看:40
本文介绍了在 ASP.NET Core Web API 中上传文件和 JSON的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何使用分段上传将文件(图像)列表和 json 数据上传到 ASP.NET Core Web API 控制器?

How can I upload a list of files (images) and json data to ASP.NET Core Web API controller using multipart upload?

我可以成功接收文件列表,上传的multipart/form-data内容类型如下:

I can successfully receive a list of files, uploaded with multipart/form-data content type like that:

public async Task<IActionResult> Upload(IList<IFormFile> files)

当然,我可以使用默认的 JSON 格式化程序成功接收格式化为我的对象的 HTTP 请求正文:

And of course I can successfully receive HTTP request body formatted to my object using default JSON formatter like that:

public void Post([FromBody]SomeObject value)

但是我怎样才能将这两者结合在一个控制器动作中呢?如何上传图像和 JSON 数据并将它们绑定到我的对象?

But how can I combine these two in a single controller action? How can I upload both images and JSON data and have them bind to my objects?

推荐答案

显然没有内置的方法可以做我想做的事.所以我最终编写了自己的 ModelBinder 来处理这种情况.我没有找到任何关于自定义模型绑定的官方文档,但我使用了 这篇文章 作为参考.

Apparently there is no built in way to do what I want. So I ended up writing my own ModelBinder to handle this situation. I didn't find any official documentation on custom model binding but I used this post as a reference.

Custom ModelBinder 将搜索用 FromJson 属性修饰的属性,并将来自多部分请求的字符串反序列化为 JSON.我将我的模型包装在另一个具有模型和 IFormFile 属性的类(包装器)中.

Custom ModelBinder will search for properties decorated with FromJson attribute and deserialize string that came from multipart request to JSON. I wrap my model inside another class (wrapper) that has model and IFormFile properties.

IJsonAttribute.cs:

public interface IJsonAttribute
{
    object TryConvert(string modelValue, Type targertType, out bool success);
}

FromJsonAttribute.cs:

using Newtonsoft.Json;
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class FromJsonAttribute : Attribute, IJsonAttribute
{
    public object TryConvert(string modelValue, Type targetType, out bool success)
    {
        var value = JsonConvert.DeserializeObject(modelValue, targetType);
        success = value != null;
        return value;
    }
}

JsonModelBinderProvider.cs:

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

        if (context.Metadata.IsComplexType)
        {
            var propName = context.Metadata.PropertyName;
            var propInfo = context.Metadata.ContainerType?.GetProperty(propName);
            if(propName == null || propInfo == null)
                return null;
            // Look for FromJson attributes
            var attribute = propInfo.GetCustomAttributes(typeof(FromJsonAttribute), false).FirstOrDefault();
            if (attribute != null) 
                return new JsonModelBinder(context.Metadata.ModelType, attribute as IJsonAttribute);
        }
        return null;
    }
}

JsonModelBinder.cs:

public class JsonModelBinder : IModelBinder
{
    private IJsonAttribute _attribute;
    private Type _targetType;

    public JsonModelBinder(Type type, IJsonAttribute attribute)
    {
        if (type == null) throw new ArgumentNullException(nameof(type));
        _attribute = attribute as IJsonAttribute;
        _targetType = type;
    }

    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        if (bindingContext == null) throw new ArgumentNullException(nameof(bindingContext));
        // Check the value sent in
        var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
        if (valueProviderResult != ValueProviderResult.None)
        {
            bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult);
            // Attempt to convert the input value
            var valueAsString = valueProviderResult.FirstValue;
            bool success;
            var result = _attribute.TryConvert(valueAsString, _targetType, out success);
            if (success)
            {
                bindingContext.Result = ModelBindingResult.Success(result);
                return Task.CompletedTask;
            }
        }
        return Task.CompletedTask;
    }
}

用法:

public class MyModelWrapper
{
    public IList<IFormFile> Files { get; set; }
    [FromJson]
    public MyModel Model { get; set; } // <-- JSON will be deserialized to this object
}

// Controller action:
public async Task<IActionResult> Upload(MyModelWrapper modelWrapper)
{
}

// Add custom binder provider in Startup.cs ConfigureServices
services.AddMvc(properties => 
{
    properties.ModelBinderProviders.Insert(0, new JsonModelBinderProvider());
});

这篇关于在 ASP.NET Core Web API 中上传文件和 JSON的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆