对于这样的场景,需要一个自定义模型绑定器。该框架允许这种灵活性。
使用此处提供的演练
Custom model binder sample
并使其适应这个问题。
以下示例使用SalesRecord 模型上的ModelBinder 属性:
[ModelBinder(BinderType = typeof(SalesRecordBinder))]
[JsonConverter(typeof(JsonPathConverter))]
public class SalesRecord {
[JsonProperty("user.name.first")]
public string FirstName {get; set;}
[JsonProperty("user.name.last")]
public string LastName {get; set;}
[JsonProperty("payment.type")]
public string PaymentType {get; set;}
}
在前面的代码中,ModelBinder 属性指定了应该用于绑定SalesRecord 操作参数的IModelBinder 的类型。
SalesRecordBinder 用于绑定 SalesRecord 参数,尝试使用自定义 JSON 转换器解析发布的内容以简化反序列化。
class JsonPathConverter : JsonConverter {
public override object ReadJson(JsonReader reader, Type objectType,
object existingValue, JsonSerializer serializer) {
JObject jo = JObject.Load(reader);
object targetObj = Activator.CreateInstance(objectType);
foreach (PropertyInfo prop in objectType.GetProperties()
.Where(p => p.CanRead && p.CanWrite)) {
JsonPropertyAttribute att = prop.GetCustomAttributes(true)
.OfType<JsonPropertyAttribute>()
.FirstOrDefault();
string jsonPath = (att != null ? att.PropertyName : prop.Name);
JToken token = jo.SelectToken(jsonPath);
if (token != null && token.Type != JTokenType.Null) {
object value = token.ToObject(prop.PropertyType, serializer);
prop.SetValue(targetObj, value, null);
}
}
return targetObj;
}
public override bool CanConvert(Type objectType) {
// CanConvert is not called when [JsonConverter] attribute is used
return false;
}
public override bool CanWrite {
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value,
JsonSerializer serializer) {
throw new NotImplementedException();
}
}
Source: Can I specify a path in an attribute to map a property in my class to a child property in my JSON?
public class SalesRecordBinder : IModelBinder {
public Task BindModelAsync(ModelBindingContext bindingContext) {
if (bindingContext == null){
throw new ArgumentNullException(nameof(bindingContext));
}
// Try to fetch the value of the argument by name
var valueProviderResult = bindingContext.ValueProvider
.GetValue(bindingContext.ModelName);
if (valueProviderResult == ValueProviderResult.None){
return Task.CompletedTask;
}
var json = valueProviderResult.FirstValue;
// Check if the argument value is null or empty
if (string.IsNullOrEmpty(json)) {
return Task.CompletedTask;
}
//Try to parse the provided value into the desired model
var model = JsonConvert.DeserializeObject<SalesRecord>(json);
//Model will be null if unable to desrialize.
if (model == null) {
bindingContext.ModelState
.TryAddModelError(
bindingContext.ModelName,
"Invalid data"
);
return Task.CompletedTask;
}
bindingContext.ModelState.SetModelValue(bindingContext.ModelName, model);
//could consider checking model state if so desired.
//set result state of binding the model
bindingContext.Result = ModelBindingResult.Success(model);
return Task.CompletedTask;
}
}
从那里开始,现在应该是在动作中使用模型的简单问题
[HttpPost("create", Name = "CreateSalesRecord")]
public IActionResult Create([FromBody] SalesRecord record) {
if(ModelState.IsValid) {
//...
return Ok();
}
return BadRequest(ModelState);
}
免责声明:这尚未经过测试。可能还有一些问题需要解决,因为它基于上面提供的链接来源。