모든 ASP.NET Core Web API 프로젝트에 필요한 것 - 3부 - 예외 처리 미들웨어
19965 단어 aspnetcoredotnetarchitecturewebapi
누가 버그 없는 코드를 작성할 수 있습니까? 적어도 나는 아니다. 각 시스템에서 처리되지 않은 예외가 발생할 수 있지만 오류를 잡아서 기록하고 수정하고 클라이언트에 적절한 응답을 표시하는 것이 정말 중요합니다. 예외 처리 미들웨어는 단일 위치에서 예외를 포착하고 응용 프로그램을 통해 중복된 예외 처리 코드를 방지하는 데 도움이 됩니다.
1단계 - 예외 처리 미들웨어 구현
먼저
Infrastructure
폴더에 새 폴더를 추가하고 Middlewares
를 호출한 다음 새 파일ApiExceptionHandlingMiddleware.cs
을 추가합니다. 다음 코드를 추가합니다.public class ApiExceptionHandlingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<ApiExceptionHandlingMiddleware> _logger;
public ApiExceptionHandlingMiddleware(RequestDelegate next, ILogger<ApiExceptionHandlingMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task Invoke(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex)
{
await HandleExceptionAsync(context, ex);
}
}
private async Task HandleExceptionAsync(HttpContext context, Exception ex)
{
_logger.LogError(ex, $"An unhandled exception has occurred, {ex.Message}");
var problemDetails = new ProblemDetails
{
Type = "https://tools.ietf.org/html/rfc7231#section-6.6.1",
Title = "Internal Server Error",
Status = (int)HttpStatusCode.InternalServerError,
Instance = context.Request.Path,
Detail = "Internal server error occured!"
};
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
var result = JsonSerializer.Serialize(problemDetails);
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(result);
}
}
응답의 상태 코드를 500
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError
으로 설정하는 것과 함께 ProblemDetails
형식의 메시지가 응답 본문에 존재합니다.ASP.NET Core 2.2 이전의 HTTP 400(
BadRequest(ModelState)
에 대한 기본 응답 유형은 다음과 같습니다.{
"": [
"A non-empty request body is required."
]
}
IETF(Internet Engineering Task Force)RFC-7231 document에 따르면 ASP.NET Core 팀은 웹 API 응답의 오류를 지정하기 위해 기계가 읽을 수 있는 형식ProblemDetails을 구현했으며 RFC 7807 사양을 준수합니다.
2단계 - 미들웨어 등록
public static class MiddlewareExtensions
{
public static IApplicationBuilder UseApiExceptionHandling(this IApplicationBuilder app)
=> app.UseMiddleware<ApiExceptionHandlingMiddleware>();
}
Startup.cs
클래스를 열고 Configure
메서드에서 미들웨어를 추가합니다.public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IApiVersionDescriptionProvider provider)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
...
}
app.UseApiExceptionHandling();
아시다시피 미들웨어 구성 요소를 추가하는 순서가 중요합니다. 개발 환경에
UseDeveloperExceptionPage
를 사용하는 경우 그 뒤에 ApiExceptionHandling
미들웨어를 추가하십시오.3단계 - 비즈니스 오류를 도메인 예외로 변환
예외를 throw해야 하는 경우에 대해 많은 논쟁이 있지만 예외를 throw해야 하는 경우는 다음과 같습니다.
private async Task AddProductToBasketAsync(Guid productId)
{
var product = await _repository.GetProductByIdAsync(productId);
if(product == null)
throw new DomainException($"Product with id {productId} could not be found.");
// Or simply return null?
// Or return an error code or warping response into another object that has `Succeeded` property like `IdentityResult`?
// Or return a tuple (false, "Product with id {productId} could not be found.")?
예외 발생의 단점 중 하나는 예외에 성능 비용이 든다는 것입니다. 고성능 애플리케이션을 작성하는 경우 예외를 발생시키면 성능이 저하될 수 있습니다.
다시 말하지만, 예외는 정말로 예외적이어야 합니다.
오류 코드 또는 래퍼 개체를 사용할 때의 단점은 무엇입니까?
4단계 - DomainException 클래스 추가
Domain
한 다음 다른 폴더를 추가합니다Exception
.DomainException.cs
을 Exception
폴더에 추가:public class DomainException : Exception
{
public DomainException(string message)
: base(message)
{
}
}
DomainException
를 포착하고 변환합니다.private async Task HandleExceptionAsync(HttpContext context, Exception ex)
{
string result;
**if (ex is DomainException)
{
var problemDetails = new ValidationProblemDetails(new Dictionary<string, string[]> { { "Error", new[] { ex.Message } } })
{
Type = "https://tools.ietf.org/html/rfc7231#section-6.5.1",
Title = "One or more validation errors occurred.",
Status = (int)HttpStatusCode.BadRequest,
Instance = context.Request.Path,
};
context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
result = JsonSerializer.Serialize(problemDetails);
}**
else
{
_logger.LogError(ex, $"An unhandled exception has occurred, {ex.Message}");
var problemDetails = new ProblemDetails
{
Type = "https://tools.ietf.org/html/rfc7231#section-6.6.1",
Title = "Internal Server Error.",
Status = (int)HttpStatusCode.InternalServerError,
Instance = context.Request.Path,
Detail = "Internal Server Error!"
};
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
result = JsonSerializer.Serialize(problemDetails);
}
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(result);
}
처리되지 않은 예외와 마찬가지로
DomainException
를 ValidationProblemDetails
로 번역했습니다. 나중에 DomainException
를 사용하겠습니다. 작동 중인 도메인 예외를 테스트해 보겠습니다.[HttpGet("throw-domain-exception")]
public IActionResult ThrowDomainError()
{
throw new DomainException("Product could not be found");
}
Github에서 이 연습의 소스 코드를 찾을 수 있습니다.
Reference
이 문제에 관하여(모든 ASP.NET Core Web API 프로젝트에 필요한 것 - 3부 - 예외 처리 미들웨어), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/moesmp/what-every-asp-net-core-web-api-project-needs-part-3-exception-handling-middleware-3nif텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)