以干净的方式在 Asp.Net Core API 中实现 ProblemDetails 的最佳方法
Posted
技术标签:
【中文标题】以干净的方式在 Asp.Net Core API 中实现 ProblemDetails 的最佳方法【英文标题】:Best way to implement the ProblemDetails in Asp.Net Core API in clean way 【发布时间】:2022-01-03 16:22:48 【问题描述】:我需要使用ProblemDetails 来处理验证错误。它按预期工作。但是这里有个大问题,我必须在所有的action方法中写一个类似的代码,我认为这不是一个好主意。
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
【问题讨论】:
嗨@vivek nuna,我想你可以尝试自定义扩展ValidationProblemDetails
的类。参考:***.com/a/67916802/11398810
@Rena 是的,我尝试过使用类似的解决方法。完成后我会发布。谢谢你的建议。
@Rena 我已经添加了我的答案,请查看并提供您的反馈:)
【参考方案1】:
我可以通过在 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);
;
);
我不得不使用一种技巧来传递错误代码和消息,方法是在扩展的ValidationAttribute
的IsValid
方法中使用这样的:
分隔符。
return new ValidationResult("01:Proprty 1");
如果有人有更好的方法或建议,请添加评论。我很高兴知道。
【讨论】:
以上是关于以干净的方式在 Asp.Net Core API 中实现 ProblemDetails 的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章
详解ASP.NET Core API 的Get和Post请求使用方式
仅在文件上传中出现 Asp.Net Core API CORS 策略错误
ASP.NET MVC Core API 将枚举序列化为字符串