问题
I'm using app.UseExceptionHandler("/error"); to customize the output of any unhandled exceptions.
However, validation errors use the framework (ApiController) and automatically return a default formatted error.
I see that I can overwrite this using InvalidModelStateResponseFactory but is there a way to have one global location to handle all of these?
I understand that one is for 500 responses and the other is 400 responses but I'd like to consolidate if possible.
回答1:
If you just display simple error message,you could use UseStatusCodePagesWithReExecute and UseExceptionHandler middleware like below:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseStatusCodePagesWithReExecute("/errors/{0}");
app.UseExceptionHandler("/errors/500");
//...
}
ErrorsController:
[ApiController]
[Route("[controller]")]
public class ErrorsController : ControllerBase
{
[Route("{code}")]
public IActionResult InternalError(int code)
{
return new ObjectResult(new ApiResponse(code));
}
}
Custom 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;
}
}
}
How to test:
1.For 500:
[HttpGet]
public IActionResult Get()
{
throw new Exception("adasd");
}
2.For 400:
[HttpPost]
public IActionResult Get(TestModel model)
{
if (!ModelState.IsValid)
{
return BadRequest();//can't pass the ModelState to it
}
return Ok();
}
If you want to display the detailed ModelState error,you need to know that model validation is before action,so you should add an action filter based on my previous answer:
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));
}
}
}
Custom 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");
//...
}
Result:
Reference:
https://www.devtrends.co.uk/blog/handling-errors-in-asp.net-core-web-api
回答2:
You can using error middleware (refer my question here for that Or follow below code:
If you want to handle error in response then using OnActionExecuted insteed.
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
};
}
}
}
来源:https://stackoverflow.com/questions/62985803/global-customization-of-error-responses-from-bet-core-3-1-webapi