这可以使用自定义模型绑定器来完成:
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;
}
}
然后您可以在 DTO 类中使用 ModelBinder 属性来指示应该使用此绑定器来绑定 MyJson 属性:
public class Dto
{
public IFormFile MyFile {get;set;}
[ModelBinder(BinderType = typeof(FormDataJsonBinder))]
public MyJson MyJson {get;set;}
}
请注意,您还需要在客户端中正确序列化您的 JSON 数据:
const formData = new FormData();
formData.append(`myFile`, file);
formData.append('myJson', JSON.stringify(obj));
上面的代码可以工作,但你也可以更进一步,定义一个自定义属性和一个自定义IModelBinderProvider,这样你就不需要每次都使用更冗长的ModelBinder属性。 .请注意,我为此重新使用了现有的 [FromForm] 属性,但您也可以定义自己的属性来代替。
public class FormDataJsonBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if(context == null) throw new ArgumentNullException(nameof(context));
// Do not use this provider for binding simple values
if(!context.Metadata.IsComplexType) return null;
// Do not use this provider if the binding target is not a property
var propName = context.Metadata.PropertyName;
var propInfo = context.Metadata.ContainerType?.GetProperty(propName);
if(propName == null || propInfo == null) return null;
// Do not use this provider if the target property type implements IFormFile
if(propInfo.PropertyType.IsAssignableFrom(typeof(IFormFile))) return null;
// Do not use this provider if this property does not have the FromForm attribute
if(!propInfo.GetCustomAttributes(typeof(FromForm), false).Any()) return null;
// All criteria met; use the FormDataJsonBinder
return new FormDataJsonBinder();
}
}
您需要将此模型绑定器提供程序添加到您的启动配置中,然后才会被拾取:
services.AddMvc(options =>
{
// add custom model binders to beginning of collection
options.ModelBinderProviders.Insert(0, new FormDataJsonBinderProvider())
});
那么你的 DTO 可以简单一点:
public class Dto
{
public IFormFile MyFile {get;set;}
[FromForm]
public MyJson MyJson {get;set;}
}
您可以在 ASP.NET Core 文档中阅读有关自定义模型绑定的更多信息:https://docs.microsoft.com/en-us/aspnet/core/mvc/advanced/custom-model-binding