본문 바로가기

백엔드/닷넷

에이피아이 - 에러응답 형식 통일하기

반응형

각 에러의 종류마다 반환되는 에러 메시지에 포함된 내용과 형식이 다릅니다. 이 글에서는 반환되는 에러 메시지의 형식을 통일하여 프론트엔드 개발자가 보다 쉽게 활용가능하도록 하는 방법을 보겠습니다

 

먼저 몇몇 에러메시지의 응답 형식을 보겠습니다

404
500
400
400

구현하기

1) .NET MVC 컨트롤러 베이스 에러코드

MVC 컨트롤러의 에러코드 반환 메서드의 경우 매개변수를 통해 원하는 형식의 에러 메시지를 반환가능합니다.

 

에이피아이 프로젝트 안에 에러를 처리하는 폴더를 만들고

공통된 형식으로 에러메시지를 반환해 줄 클래스를 생성합니다

생성된 클래스에 아래와 같이 컨스트럭터, 속성, 메서드를 추가하고

// constructor
public ErrorRes(int statusCode, string massage = null)
{
  this.StatusCode = statusCode;      
  this.Message = massage ?? GetStatusCodeMessage(statusCode);
}

// properties
public int StatusCode { get; set; }
public string Message { get; set; }

// method
private string GetStatusCodeMessage(int statusCode)
{
  return statusCode switch
  {
    400 => "bad request",
    401 => "Unauthorized",
    404 => "Resource not found",
    500 => "Server error",
    _ => null,
  };
}

생성된 클래스를 MVC 에러 코드에 매개변수로 사용합니다

2) 존재하지 않는 엔드포인트

존재하지 않는 경로를 처리하기 위해 컨트롤러를 추가합니다

생성한 컨트롤러에 아래와 같이 코드를 작성합니다

[ApiController]
[Route("errors/{code}")]
[ApiExplorerSettings(IgnoreApi = true)]
public class NotFoundController : ControllerBase
{
  public IActionResult Error(int code)
  {
    return new ObjectResult(new ErrorRes(code));
  }
}

Program.cs 파일로 이동하여 존재하지 않는 엔드포인트 접근시도 시 위에서 설정한 컨트롤러경로로 전환되게 설정합니다

app.UseStatusCodePagesWithReExecute("/errors/{0}");

3) 에러메시지 추가하기

반환되는 에러 정보에 추가정보를 더하는 클래스를 생성하고 

에러클래스를 상속한 뒤 추가로 필요한 속성을 추가합니다 (보기에서 'Details')

public class ErrorSource : ErrorRes
{
  public ErrorSource(
    int statusCode,
    string massage = null,
    string details = null
    ) : base(statusCode, massage)
  {
    this.Details = details;
  }

  public string Details { get; set; }
}

미들웨어 폴더를 생성

예외를 처리할 클래스 생성

아래 코드를 삽입합니다

private readonly RequestDelegate _next;
private readonly ILogger<ExceptionMiddleware> _logger;
private readonly IHostEnvironment _env;
public ExceptionMiddleware(
  RequestDelegate next,
  ILogger<ExceptionMiddleware> logger,
  IHostEnvironment env)
{
  this._next = next;
  this._logger = logger;
  this._env = env;
}

public async Task InvokeAsync(HttpContext context)
{
  try
  {
    await _next(context);
  }
    catch (Exception ex)
  {
    _logger.LogError(ex, ex.Message);
    context.Response.ContentType = "application/json";
    context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
    var res = _env.IsDevelopment() ? new ErrorSource(
      (int)HttpStatusCode.InternalServerError,
      ex.Message,
      ex.StackTrace.ToString()
      ) : new ErrorSource((int)HttpStatusCode.InternalServerError);
    var options = new JsonSerializerOptions
    {
      PropertyNamingPolicy = JsonNamingPolicy.CamelCase
    };
    var json = JsonSerializer.Serialize(res, options);
    await context.Response.WriteAsync(json);
  }
}

Program.cs 파일로 이동하여 설정한 미들웨어를 등록합니다

4) 객체 스트링 배열로 변환하기

기본 에러메시지는 [ApiController] 메타태그에서 제공합니다. 따라서 반환되는 메시지의 형태변경은 해당 메타태그에 옵션을 변경하여 구현합니다

반환되는 객체를 스트링 배열로 변환할 클래스를 생성하고 

public class ErrorValidation : ErrorRes
{
  public ErrorValidation() : base(400)
  {        
  }

  public IEnumerable<string> Errors { get; set; }
}

Program.cs 파일로 이동하여 아래와 같이 컨트롤러 [ApiController]의 설정을 변경하는 서비스를 등록합니다

builder.Services.Configure<ApiBehaviorOptions>(opt =>
{
  opt.InvalidModelStateResponseFactory = actionContext =>
  {
    var errors = actionContext.ModelState
    .Where(e => e.Value.Errors.Count > 0)
    .SelectMany(x => x.Value.Errors)
    .Select(x => x.ErrorMessage).ToArray();

    var errorRes = new ErrorValidation
    {
      Errors = errors
    };

    return new BadRequestObjectResult(errorRes);
  };
});

이상으로 반환되는 에러메시지의 형식을 통일하는 방법에 대해서 알아보았습니다

 

 

728x90
반응형