C# JObject.ToObject与模型状态之间存在错误

C# JObject.ToObject与模型状态之间存在错误,c#,json,asp.net-core,json.net,bad-request,C#,Json,Asp.net Core,Json.net,Bad Request,在ASP.NET核心项目中,我已从使用ResourceModel切换到使用JObject作为我的[FromBody]参数。然后,我将JObject.ToObject()传递到一个服务中,但希望为ContainsKey和类似功能维护JObject本身 如果JObject.ToObject成功,我可以使用TryValidateModel()如果失败,返回BadRequest(ModelState)。但是,我遇到的问题是当JObject.ToObject抛出异常时-我不确定如何捕获ModelState

在ASP.NET核心项目中,我已从使用
ResourceModel
切换到使用
JObject
作为我的
[FromBody]
参数。然后,我将
JObject.ToObject()
传递到一个服务中,但希望为
ContainsKey
和类似功能维护
JObject
本身

如果
JObject.ToObject
成功,我可以使用
TryValidateModel()
如果失败,返回
BadRequest(ModelState)
。但是,我遇到的问题是当
JObject.ToObject
抛出异常时-我不确定如何捕获
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;
    }
}
在这种情况下,将上述用于
ContactInfo
的json传递给请求
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
并捕获异常,我将得到其中一个错误,例如
无法解析电话号码“1-800-call-NOW”。该值必须是11位数字。
其他值将被忽略,直到该值被修复且用户重试。

您可以捕获异常并使用
ModelState.AddModelError(“,…”)将其文本添加为模型级错误
不幸的是,这将只捕获一个异常(第一个解析错误),而不是可能发生的所有异常。请提供一些无效json和预期错误输出的示例。@GuruStron我已更新文章,在底部包含一个示例。您的意思是要获取ModelState的所有错误吗?如果是,您可以使用
foreach(ModelState中的var项)
来foreach ModelState的错误。
{
  "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"
}
{
  "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: ..."
    ]
  }
}