ASP.NET Core Web API中的流畅验证自定义响应。

4

我正在开发一个ASP.NET Core 6 Web API项目,我们使用Fluent Validation。如何返回自定义响应?请给予建议。

我想在不同的验证错误(例如页面、限制、日期等)上发送自定义响应。

这是我默认收到的响应:

  {
       "type": "",
       "title": "One or more validation errors occurred.",
       "status": 400,
       "traceId": "00-097c689850af328c49705cea77fc6fbd-0c7defe165d9693f-00",
       "errors": {
         "Page": [
           "Page value should be greater than 0"
         ],
         "Limit": [
           "Limit value should be greater than 0"
         ]
       }
}

以下是我希望得到的回复:

{
  "traceId": "e45b3398-a2a5-43eb-8851-e45de1184021",
  "timestamp": "2021-06-28T00:32:06.548Z",
  "errors": [
    {
      "errorCode": "Contract Violation",
      "message": "Page should be greater than 0",
      "priority": "MEDIUM",
      "properties": {
        "name": "string",
        "value": "string"
      }
    }
  ]
}

这是我的代码:
public async Task<IActionResult> Get([FromQuery] DataParameter input) 
{

}

public class DataParameter 
{
    public DateTime? StartDate { get; set; }
    public DateTime? EndDate { get; set; }
    public int Page { get; set; }
    public int Limit { get; set; }
}

public class QueryParameterValidator : AbstractValidator<QueryParameter>
{
    public QueryParameterValidator()
    {
        RuleFor(model => model.Page)
            .GreaterThan(0)
            .WithMessage("Page value should be greater than 0");
        RuleFor(model => model.Limit)
            .GreaterThan(0)
            .WithMessage("Limit value should be greater than 0");
    
        RuleFor(model => model.StartDate)
            .Matches(@"^\d{4}\-(0[1-9]|1[012])\-(0[1-9]|[12][0-9]|3[01])$")
            .WithMessage("Transaction from date should be in the pattern of YYYY-MM-DD");
        RuleFor(model => model.EndDate)
            .Matches(@"^\d{4}\-(0[1-9]|1[012])\-(0[1-9]|[12][0-9]|3[01])$")
            .WithMessage("Transaction to date should be in the pattern of YYYY-MM-DD");
    }
}

嗨 @itmannz,响应包含两个错误,但您预期的响应只有一个错误,您是否只想显示一个错误?我建议您可以自定义一个继承自“ValidationProblemDetails”的类。 - Rena
嗨,Rena,谢谢。我可以有一个例子吗? - itmannz
1个回答

9
这里有一个完整的可供您参考的工作演示:
模型:
public class ErrorDetail
{
    public string traceId { get; set; }
    public DateTime timestamp { get; set; }
    public List<Error> errors { get; set; } = new List<Error>();
}

public class Error
{
    public string errorCode { get; set; }
    public string message { get; set; }
    public string priority { get; set; }
    public Properties properties { get; set; }=new Properties();
}

public class Properties
{
    public string name { get; set; }
    public string value { get; set; }
}

静态类:

public static class CustomProblemDetails
{
    public static ErrorDetail ErrorDetail { get; set; } =new ErrorDetail();
    public static IActionResult MakeValidationResponse(ActionContext context)
    {
        var problemDetails = new ValidationProblemDetails(context.ModelState)
        {
            Status = StatusCodes.Status400BadRequest,
        };
        foreach (var keyModelStatePair in context.ModelState)
        {
            var errors = keyModelStatePair.Value.Errors;
            if (errors != null && errors.Count > 0)
            {
                if (errors.Count == 1)
                {
                    var errorMessage = GetErrorMessage(errors[0]);
                    ErrorDetail.errors.Add(new Error()
                    {
                        errorCode= "Contract Violation",
                        message = errorMessage,
                        priority= "MEDIUM",
                        properties= new Properties() { 
                            name = "string", 
                            value = "string" 
                        }
                    });
                }
                else
                {
                    var errorMessages = new string[errors.Count];
                    for (var i = 0; i < errors.Count; i++)
                    {
                        errorMessages[i] = GetErrorMessage(errors[i]);
                        ErrorDetail.errors.Add(new Error()
                        {
                            errorCode = "Contract Violation",
                            message = errorMessages[i],
                            priority = "MEDIUM"
                        });
                    }
                }
            }
        }
        ErrorDetail.traceId = context.HttpContext.TraceIdentifier;
        ErrorDetail.timestamp = DateTime.Now;

        var result = new BadRequestObjectResult(ErrorDetail);

        result.ContentTypes.Add("application/problem+json");

        return result;
    }
    static string GetErrorMessage(ModelError error)
    {
        return string.IsNullOrEmpty(error.ErrorMessage) ?
        "The input was not valid." :
        error.ErrorMessage;
    }
}

注册:

builder.Services.AddControllers().AddFluentValidation().ConfigureApiBehaviorOptions(options =>
{
    options.InvalidModelStateResponseFactory = CustomProblemDetails.MakeValidationResponse;
}); 

结果:

在此输入图像描述


嗨@Rena,感谢您的帮助。为什么要使用静态类CustomProblemDetails和静态ErrorDetail、IActionResult?此外,如果我在Swagger中点击执行按钮,它会添加到“errors”数组中。 - itmannz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接