在ASP.NET核心API中干净地实现ProblemDetails的最佳方式 [英] Best way to implement the ProblemDetails in Asp.Net Core API in clean way

查看:27
本文介绍了在ASP.NET核心API中干净地实现ProblemDetails的最佳方式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要使用ProblemDetails表示验证错误。它的工作情况和预期的一样。但这里有一个很大的问题,我必须在所有操作方法中编写类似的代码,我认为这不是一个好主意。

public async Task<ActionResult<SampleResponse>> Post([FromBody] SampleRequest getRateApiRequest)
{
    try
    {
        if (ModelState.IsValid == false)
        {
            ProblemDetails problemDetails = new ProblemDetails();
            problemDetails.Detail = "Detail";
            problemDetails.Instance = "Instance";
            problemDetails.Status = StatusCodes.Status400BadRequest;
            problemDetails.Title = "Title";
            problemDetails.Type = "Type";

            List<FieldCodeMessage> codeMessages = new List<FieldCodeMessage>();
            foreach (var modelState in ModelState)
            {
                if (modelState.Value.ValidationState == Microsoft.AspNetCore.Mvc.ModelBinding.ModelValidationState.Invalid)
                {
                    MemberInfo property = typeof(TradeBookingRequestAPI).GetProperty(modelState.Key);
                    var attribute = property.GetCustomAttributes(typeof(DisplayNameAttribute), true).Cast<DisplayNameAttribute>().Single();
                    string displayName = attribute.DisplayName;
                    switch (modelState.Key)
                    {
                        case "Property1":
                            codeMessages.Add(new FieldCodeMessage(field: displayName, code: "01", message: modelState.Value.Errors.Select(a => a.ErrorMessage).FirstOrDefault()));
                            break;
                        case "Property2":
                            codeMessages.Add(new FieldCodeMessage(field: displayName, code: "02", message: modelState.Value.Errors.Select(a => a.ErrorMessage).FirstOrDefault()));
                            break;
                        case "Property3":
                            codeMessages.Add(new FieldCodeMessage(field: displayName, code: "03", message: modelState.Value.Errors.Select(a => a.ErrorMessage).FirstOrDefault()));
                            break;
                        case "Property4":
                            codeMessages.Add(new FieldCodeMessage(field: displayName, code: "04", message: modelState.Value.Errors.Select(a => a.ErrorMessage).FirstOrDefault()));
                            break;
                        case "Property5":
                            codeMessages.Add(new FieldCodeMessage(field: displayName, code: "05", message: modelState.Value.Errors.Select(a => a.ErrorMessage).FirstOrDefault()));
                            break;
                        case "Property6":
                            codeMessages.Add(new FieldCodeMessage(field: displayName, code: "06", message: modelState.Value.Errors.Select(a => a.ErrorMessage).FirstOrDefault()));
                            break;
                    }
                }
            }

            problemDetails.Extensions.Add("Invalid Fields", codeMessages);

            return BadRequest(problemDetails);
        }
    }
    catch (Exception)
    {
        ...
    }
}

那么有没有办法在一个集中的地方(如中间件或其他东西)处理这个问题。

预期响应:

{
    "type": "Type",
    "title": "Title",
    "status": 400,
    "detail": "Detail",
    "instance": "Instance",
    "Invalid Fields": [
        {
            "field": "Proprty 1",
            "code": "01",
            "message": "Invalid Proprty 1"
        },
        {
            "field": "Property 2",
            "code": "02",
            "message": "Invalid Property 2"
        }
    ]
}

我扩展了ValidationAttribute来实现所有属性的验证逻辑,下面是Property1的实现。

protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
{
    try
    {
        if (value != null)
        {
            propertyDisplayName = validationContext.DisplayName;
            long property1 = (Int64)value;
            Match match = Regex.Match($"{property1}", @"^d+$", RegexOptions.IgnoreCase);

            if (!string.IsNullOrWhiteSpace($"{property1}") && match.Success)
            {
                return ValidationResult.Success;
            }
            else
            {
                return new ValidationResult($"Invalid {propertyDisplayName}");
            }

        }
        else
        {
            return new ValidationResult($"Invalid {propertyDisplayName}");
        }
    }
    catch (Exception ex)
    {
        ...
    }
}

如果也有办法在扩展的ValidationAttribute类中处理此方案,那也适用于我。

注意:目标框架为.Net5

推荐答案

我可以通过使用Startup.cs的ConfigureServices方法中的以下代码来解决此问题。

services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Latest).ConfigureApiBehaviorOptions(options =>
{
    options.InvalidModelStateResponseFactory = c =>
    {
        ProblemDetails problemDetails = new ProblemDetails();
        problemDetails.Status = StatusCodes.Status400BadRequest;
        problemDetails.Title = "One or more validation errors occurred.";
        problemDetails.Type = "https://tools.ietf.org/html/rfc7231#section-6.5.1";
        
        List<FieldCodeMessage> codeMessages = new List<FieldCodeMessage>();
        foreach (var modelState in c.ModelState)
        {
            if (modelState.Value.ValidationState == Microsoft.AspNetCore.Mvc.ModelBinding.ModelValidationState.Invalid)
            {
                string[] errorMessageCode = modelState.Value.Errors.Select(a => a.ErrorMessage).FirstOrDefault().Split(':');
                string code = errorMessageCode[0];
                string message = errorMessageCode[1];

                codeMessages.Add(new FieldCodeMessage(field: modelState.Key, code: code, message: message));
            }
        }

        problemDetails.Extensions.Add("Invalid Fields", codeMessages);

        return new BadRequestObjectResult(problemDetails);
    };
});

我必须使用一个技巧通过使用扩展ValidationAttributeIsValid方法中的:分隔符将错误代码与消息一起传递。

return new ValidationResult("01:Proprty 1");

如果有人有更好的方法或建议,请添加评论。我很乐意知道。

这篇关于在ASP.NET核心API中干净地实现ProblemDetails的最佳方式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆