如何反序列化 422 无法处理的实体错误模型并将错误绑定到 asp.net core razor pages 模型状态

Posted

技术标签:

【中文标题】如何反序列化 422 无法处理的实体错误模型并将错误绑定到 asp.net core razor pages 模型状态【英文标题】:How to deserialize 422 unprocessable entity error model and bind the errors to asp.net core razor pages model state 【发布时间】:2020-07-09 16:11:04 【问题描述】:

我的Asp.Net Core 3.1 API返回422 Unprocessable Entity错误响应如下图:


  "type": "https://test.com/modelvalidationproblem",
  "title": "One or more model validation errors occurred.",
  "status": 422,
  "detail": "See the errors property for details.",
  "instance": "/api/path",
  "traceId": "8000003f-0001-ec00-b63f-84710c7967bb",
  "errors": 
    "FirstName": [
      "The FirstName field is required."
    ]
  

如何反序列化此响应并将错误添加到Asp.Net Core 3.1 Razor Pages 中的模型验证错误?

我尝试创建如下所示的模型,

错误模型:

public class UnprocessableEntity

    public string Type  get; set; 
    public string Title  get; set; 
    public int Status  get; set; 
    public string Detail  get; set; 
    public string Instance  get; set; 
    public string TraceId  get; set; 
    public Errors Errors  get; set; 


public class Errors

    ....// what should I need to add here? Keys and messages will be dynamic

但是我应该在Errors 类中添加什么?错误键和消息将是动态的。

一旦知道了以上内容,我就可以在我的 razor 页面中添加模型状态错误,如下所示,

using var responseStream = await response.Content.ReadAsStreamAsync();
var errorResponse = await JsonSerializer.DeserializeAsync<UnprocessableEntity>(responseStream);

foreach (var error in errorResponse.errors)

    ModelState.AddModelError(error.key, error.Message[0]); // I'm stuck here

【问题讨论】:

【参考方案1】:

我已经修改了@Yongqing Yu 的答案并使其通用。

422 错误模型:

public class UnprocessableEntity<T>

    [JsonPropertyName("type")]
    public string Type  get; set; 

    [JsonPropertyName("title")]
    public string Title  get; set; 

    [JsonPropertyName("status")]
    public int Status  get; set; 

    [JsonPropertyName("detail")]
    public string Detail  get; set; 

    [JsonPropertyName("instance")]
    public string Instance  get; set; 

    [JsonPropertyName("traceId")]
    public string TraceId  get; set; 

    [JsonPropertyName("errors")]
    public T Errors  get; set; 

型号:

public class EventRegisterViewModel

    [Required]
    [MaxLength(200)]
    [RegularExpression(ValidationConstants.ALPHABETSWITHSPACE, ErrorMessage = "Enter only alphabets")]
    [Display(Name = "First Name")]
    public string FirstName  get; set; 

    [Required]
    [MaxLength(200)]
    [RegularExpression(ValidationConstants.ALPHABETSWITHSPACE, ErrorMessage = "Enter only alphabets")]
    [Display(Name = "Last Name")]
    public string LastName  get; set; 

    [MinLength(10, ErrorMessage = "Please Enter 10 digit Mobile Number")]
    [MaxLength(10)]
    [RegularExpression(ValidationConstants.NUMBERSONLY, ErrorMessage = "Invalid Mobile Number")]
    [Display(Name = "Mobile Number")]
    public string MobileNumber  get; set; 

    [Required]
    [MaxLength(200)]
    [EmailAddress]
    [Display(Name = "Email Address")]
    public string EmailAddress  get; set; 

错误模型:

public class EventRegistrationValidationErrors

    public IEnumerable<string> FirstName  get; set; 
    public IEnumerable<string> LastName  get; set; 
    public IEnumerable<string> MobileNumber  get; set; 
    public IEnumerable<string> EmailAddress  get; set; 

在反序列化时,

using var responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
var errorResponse = await JsonSerializer.DeserializeAsync<UnprocessableEntity<EventRegistrationValidationErrors>>(responseStream).ConfigureAwait(false);
var errorFieldNames = typeof(EventRegistrationValidationErrors)
                        .GetProperties()
                        .Select(field => field.Name)
                        .ToList();

foreach (var errorField in errorFieldNames)

    var errorLists = (List<string>)errorResponse.errors
                        .GetType()
                        .GetProperty(errorField)
                        .GetValue(errorResponse.errors);

    if (!(errorLists is null))
    
        ModelState.AddModelError($"Event.errorField", errorLists[0]);
    

希望这对那里的人有所帮助。

【讨论】:

【参考方案2】:

首先,您应该确保关键字段名称与从 api(pay attention to case) 返回的 json 相同。

 public class UnprocessableEntity

    public string type  get; set; 
    public string tTitle  get; set; 
    public int status  get; set; 
    public string detail  get; set; 
    public string instance  get; set; 
    public string traceId  get; set; 
    public Errors errors  get; set; 

那么Errors类的字段应该包含验证类的所有字段,并且字段名称要一致,但是需要define their type as an array to receive,因为json中errors返回的每个字段都是一个数组. (这里我创建了一个名为StudInfo 的简单验证类):

    public class StudInfo
    
        [Key]
        public int Id  get; set; 
        [Required]
        public string Name  get; set; 
    


    public class Errors
    
        public List<string> Id  get; set; 
        public List<string> Name  get; set; 
    

然后你可以使用如下代码:

       using var responseStream = await response.Content.ReadAsStreamAsync();
       var errorResponse = await JsonSerializer.DeserializeAsync<UnprocessableEntity>(responseStream);
        var t = typeof(Errors);
        var fieldNames = typeof(Errors).GetProperties()
                        .Select(field => field.Name)
                        .ToList();
        foreach (var name in fieldNames)
        
            List<string> errorLists = (List<string>)errorResponse.errors.GetType().GetProperty(name).GetValue(errorResponse.errors);
            if (errorLists != null)
            
                ModelState.AddModelError(name, errorLists[0]); 
            

        

【讨论】:

以上是关于如何反序列化 422 无法处理的实体错误模型并将错误绑定到 asp.net core razor pages 模型状态的主要内容,如果未能解决你的问题,请参考以下文章

Rails API 422 无法处理的实体:没有可用的验证密钥,heroku

422 无法处理的实体错误。我尝试上传 Excel 文件以导入数据,但它不工作

Spring框架中的Jackson反序列化错误处理

如何从实体框架中存在数据模型的json中反序列化对象?

Paypal 创建支付令牌 API 返回 422 无法处理的实体

PayPal Orders v2 在传递金额折扣时创建返回 422(无法处理的实体)