c# - JObject.ToObject 与模型状态错误
问题描述
在一个 ASP.NET 核心项目中,我已经从使用切换ResourceModel
到使用JObject
作为我的[FromBody]
参数。然后我传递JObject.ToObject<ResourceModel>()
到一个服务中,但想为类似的功能维护它JObject
本身。ContainsKey
如果JObject.ToObject<T>
成功,我可以使用TryValidateModel()
,如果失败,则BadRequest(ModelState)
足够简单地返回。但是,我遇到的问题是何时JObject.ToObject<T>
引发异常 - 我不确定如何在ModelState
.
例子:
public class Person {
public string FirstName {get;set;}
public string LastName {get;set;}
public uint Age {get;set;}
}
public class CommonParameters {
[FromQuery] public string? Fields {get;set;}
public string UserName {get;set;}
public IPAddress? RequestorIp {get;set;}
public JObject? JsonBody {get;set;}
}
public class PersonController : ControllerBase {
private readonly PersonService _personService;
// this is how I had been doing it
[HttpPatch("obsolete/{id}")] // this method doesn't actually exist, but is shown for the example
public async Task<IActionResult> UpdatePerson([FromRoute] int id, [FromBody] Person parameters, [FromHeader] CommonParameters commonParameters) {
SetCommonParameters(commonParameters, parameters);
// if parameters.Age in the json body was a string, BadRequest is returned before this method even starts
_personService.Set(parameters);
await _personService.SaveChangesAsync();
return NoContent();
}
// this is how I'm trying to do it now, so that I only update Age if JObject.ContainsKey("Age")
// instead of parameters.Age != default, etc.
[HttpPatch("{id}")]
public async Task<IActionResult> UpdatePerson([FromRoute] int Id, [FromBody] JObject parameters, [FromHeader] CommonParameters commonParameters) {
SetCommonParameters(commonParameters, parameters);
// if parameters.Age is a string in this version, a JsonReaderException
// or JsonSerializationException is thrown when I call JObject.ToObject<>()
// and I have to validate the model separately after that
// what this means is, a BadRequest with the ModelState errors is not
// returned if there's an issue with the json.
var model = parameters.ToObject<Person>();
if (!TryValidateModel(model)) return BadRequest(ModelState);
_personService.Set(person);
await _personService.SaveChangesAsync();
return NoContent();
}
private void SetCommonParameters(CommonParameters commonParameters, JObject? jsonBody = null) {
commonParameters.JsonBody = jsonBody;
commonParameters.UserName = User.Identity?.Name;
commonParameters.RequestorIp = Request.HttpContext.Connection.RemoteIpAddress;
}
}
编辑:正如 Guru Stron 所问的,这是一个无效模型/json 对象的示例。
模型:
public class ContactInfo {
// PhoneNumber being a struct representing an 11-digit phone number
// PhoneType being an enum of Cell, Work, Home, etc.
public Dictionary<PhoneType, PhoneNumber[]> PhoneNumbers {get;set;} = new();
public string? Apartment {get;set;}
public int AddressNumber {get;set;}
public string StreetName {get;set;}
public string City {get;set;}
// State being an enum of the 50 United States of America
public State State {get;set;}
}
无效的 json
{
"phoneNumbers": {
"work": [ "1-111-111-1111" ],
"home": [ "2-222-222-2222" ],
"cell": [ "1-800-CALL-NOW" ]
},
"apartment": null,
"addressNumber": "3a",
"streetName": "Imagination St.",
"city": "Atlantis",
"state": "Atlantic"
}
在这种情况下,将上述 json for 传递ContactInfo
给请求参数的控制器方法ContactInfo
将返回一个错误请求,指示状态无效、地址号码无效和电话号码无效 - 无需专门对此进行测试,例如以下。
{
"errors": {
"phoneNumbers": [
"Could not parse phone number \"1-800-CALL-NOW\". The value must be an 11-digit numeric value."
],
"addressNumber": [
"The value could not be parsed as int."
],
"state": [
"\"Atlantic\" is an invalid value for State. The following values are valid: ..."
]
}
}
相反,如果控制器要求,JObject
然后我调用JObject.ToObject
并捕获异常,我将得到其中一个错误 - 例如Could not parse phone number "1-800-CALL-NOW". The value must be an 11-digit numeric value.
其他错误将被忽略,直到该错误被修复并且用户再次尝试。
解决方案
我终于弄明白了……我需要做的就是提供一个 JsonSerializerJObject.ToObject<>
并包含一个错误处理程序,将错误标记为已处理。例如:
var serializer = new JsonSerializer();
serializer.Error += (sender, args) =>
{
if (args.ErrorContext.Error is JsonException)
{
ModelState.AddModelError(args.ErrorContext.Path, args.ErrorContext.Error.Message);
args.ErrorContext.Handled = true;
}
};
var value = parameters.ToObject<Person>(serializer);
if (!TryValidateModel(value)) return ValidationProblem(ModelState);
我的理解是默认Microsoft.AspNetCore.Mvc.Formatters.NewtonsoftJsonInputFormater
执行类似的错误处理;我仍然不确定如何直接使用该处理程序,但我在这里所拥有的正在满足我的需要。
推荐阅读
- java - 使用从 long 到 int 的 mod 操作时,类型转换不起作用
- linux - 如何使用 C 中的 RAW 套接字创建 ICMP 源淬火消息包?
- python-3.x - 如何在 Python 中使用 OpenCV 正确过滤此图像
- php - 我的 Laravel 代码中的 Slug Route Return 404
- python - 如何从图像中读取“/”、“_”和“\”等标点符号
- react-native - Redux saga:我如何确保只有我的 saga 能够更新某个状态?
- java - 获得整数的最小幂数为 2?
- mongodb - 如何多次推送到嵌套数组
- python - 导入自制包问题
- java - 如何使用索引调试每个循环?