【问题标题】:Global customization of error responses from .bet core 3.1 webapi来自 .net core 3.1 web api 的错误响应的全局定制
【发布时间】:2020-07-19 21:19:31
【问题描述】:

我正在使用app.UseExceptionHandler("/error"); 自定义任何未处理异常的输出。

但是,验证错误使用框架 (ApiController) 并自动返回默认格式错误。

我发现我可以使用 InvalidModelStateResponseFactory 覆盖它,但是有没有办法让一个全局位置来处理所有这些?

我知道一个用于 500 个响应,另一个用于 400 个响应,但如果可能,我想合并。

【问题讨论】:

    标签: asp.net-core


    【解决方案1】:

    如果您只是显示简单的错误信息,您可以使用UseStatusCodePagesWithReExecuteUseExceptionHandler 中间件,如下所示:

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        app.UseStatusCodePagesWithReExecute("/errors/{0}");
        app.UseExceptionHandler("/errors/500");
        //...
    }
    

    错误控制器:

    [ApiController]
    [Route("[controller]")]
    public class ErrorsController : ControllerBase
    {
        [Route("{code}")]
        public IActionResult InternalError(int code)
        {
            return new ObjectResult(new ApiResponse(code));
        }
    }
    

    自定义 ApiResponse:

    public class ApiResponse
    {
        public int StatusCode { get; }
    
        public string Message { get; }
    
        public ApiResponse(int statusCode, string message = null)
        {
            StatusCode = statusCode;
            Message = message ?? GetDefaultMessageForStatusCode(statusCode);
        }
    
        private static string GetDefaultMessageForStatusCode(int statusCode)
        {
            switch (statusCode)
            {
    
            case 404:
                return "Resource not found";
            case 500:
                return "An unhandled error occurred";
            case 400:
                return "Model error";
            default:
                return null;
            }
        }
    }
    

    如何测试:

    1.对于 500:

    [HttpGet]
    public IActionResult Get()
    {
        throw new Exception("adasd");
    }
    

    2.对于 400:

    [HttpPost]
    public IActionResult Get(TestModel model)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest();//can't pass the ModelState to it
        }
        return Ok();
    }
    

    如果你想显示详细的ModelState错误,你需要知道模型验证是在动作之前,所以你应该根据我之前的回答添加一个动作过滤器:

    public class ApiValidationFilter : IActionFilter
    {
        public void OnActionExecuted(ActionExecutedContext context)
        {
        }
    
        public void OnActionExecuting(ActionExecutingContext context)
        {
            if (!context.ModelState.IsValid)
            {
                context.Result = new BadRequestObjectResult(new ApiBadRequestResponse(context.ModelState));
            }
        }
    }
    

    自定义 ApiBadRequestResponse:

    public class ApiBadRequestResponse : ApiResponse
    {
        public IEnumerable<string> Errors { get; }
    
        public ApiBadRequestResponse(ModelStateDictionary modelState)
            : base(400)
        {
            if (modelState.IsValid)
            {
                throw new ArgumentException("ModelState must be invalid", nameof(modelState));
            }
    
            Errors = modelState.SelectMany(x => x.Value.Errors)
                .Select(x => x.ErrorMessage).ToArray();
        }
    }
    

    Startup.cs:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers(options =>
        {
            options.Filters.Add(typeof(ApiValidationFilter));
        });
    }
    
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        app.UseStatusCodePagesWithReExecute("/errors/{0}");
        app.UseExceptionHandler("/errors/500");
    
        //...
    }
    

    结果:

    参考:

    https://www.devtrends.co.uk/blog/handling-errors-in-asp.net-core-web-api

    【讨论】:

    • 我认为这回答了我的问题并给了我一些选择。谢谢!
    【解决方案2】:

    您可以使用错误中间件(refer my question here 或按照以下代码:

    如果您想在响应中处理错误,请使用 OnActionExecuted。

    services.AddMvc(opt =>
    {
        opt.Filters.Add(typeof(ValidateModelStateAttributeFilter));
    })
    
    public class ValidateModelStateAttributeFilter : ActionFilterAttribute
        {
            /// <summary>
            /// 
            /// </summary>
            public ValidateModelStateAttributeFilter()
            {
            }
    
            /// <summary>
            /// 
            /// </summary>
            /// <param name="context"></param>
            public override void OnActionExecuting(ActionExecutingContext context)
            {
                if (!context.ModelState.IsValid)
                {
                    var errors = context.ModelState.Values.Where(v => v.Errors.Count > 0)
                            .SelectMany(v => v.Errors)
                            .Select(v => v.ErrorMessage)
                            .ToList();
    
                    string combinError = errors.Any() ? string.Join("\n", errors) : string.Empty;
    
                    var responseObj = new DomainExceptionContract
                    {
                        Key = "BadRequest",
                        Message = combinError
                    };
    
                    context.Result = new JsonResult(new { Data = responseObj })
                    {
                        StatusCode = 400
                    };
                }
            }
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-11-08
      • 1970-01-01
      • 2019-07-23
      • 2018-08-13
      • 1970-01-01
      • 2018-04-28
      • 1970-01-01
      相关资源
      最近更新 更多