[英]ASP.NET Core and formdata binding with file and json property
我有以下型號:
public class MyJson
{
public string Test{get;set;}
}
public class Dto
{
public IFormFile MyFile {get;set;}
public MyJson MyJson {get;set;}
}
在客戶端,我想發送一個文件和一個json。 所以我使用以下鍵在formData中發送它:
var formData = new FormData();
formData["myFile"] = file//here is my file
formData["myJson"] = obj; // object to be serialized to json.
我的動作如下:
public void MyAction(Dto dto) // or with [FromForm], doesn't really matter
{
//dto.MyJson is null here
//dto.myFile is set correctly.
}
如果我將dto.MyJson
更改為字符串然后它完全正常,但我必須在操作中手動將其反序列化為我的對象。 將它作為一個字符串的第二個問題是我不能使用swagger UI來正確處理它,因為它會問我一個json字符串而不是對象,無論如何將它作為一個字符串聽起來不正確。 是否有一種本機方式來處理json並在操作參數中正確歸檔,而不是使用Request.Form
手動解析它?
這可以使用自定義模型綁定器來完成:
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
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.