source

스프링 부트 404 에러 커스텀에러 응답 Re세인트

topadmin 2023. 3. 1. 11:07

스프링 부트 404 에러 커스텀에러 응답 Re세인트

REST API 호스팅을 위해 Spring boot을 사용하고 있습니다.표준 에러 응답 대신 브라우저가 URL이나 커스텀 데이터 구조에 액세스 하고 있어도, 항상 JSON 응답을 송신하고 싶다고 생각하고 있습니다.

커스텀 예외의 경우 @ControllerAdvice 및 @ExceptionHandler를 사용하여 이를 수행할 수 있습니다.그러나 404 및 401과 같은 표준 오류 및 처리 오류에 대해서는 좋은 방법을 찾을 수 없습니다.

어떻게 하는지 좋은 패턴이 있나요?

Spring Boot 않은 2 @EnableWebMvc

application.properties

server.error.whitelabel.enabled=false
spring.mvc.throw-exception-if-no-handler-found=true
spring.resources.add-mappings=false

컨트롤러 어드바이스

@RestControllerAdvice
public class ExceptionResolver {

    @ExceptionHandler(NoHandlerFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public HashMap<String, String> handleNoHandlerFound(NoHandlerFoundException e, WebRequest request) {
        HashMap<String, String> response = new HashMap<>();
        response.put("status", "fail");
        response.put("message", e.getLocalizedMessage());
        return response;
    }
}

원천

스프링 부트에서 @RestControllerAdvice를 사용할 경우 사용할 수 있습니다.

spring.mvc.throw-exception-if-no-handler-found=true
server.error.whitelabel.enabled=false
spring.resources.add-mappings=false

@RestControllerAdvice
public class ErrorHandlerController {

@ExceptionHandler(NoHandlerFoundException.class)
@ResponseStatus(value = HttpStatus.NOT_FOUND )
public String handleNotFoundError(NoHandlerFoundException ex) {
    return "path does not exists";
}
}

404건에 대한 응답을 오버라이드하는 방법에 대한 샘플 솔루션을 제공했습니다.솔루션은 매우 심플하고 샘플 코드를 투고하고 있습니다만, Spring Boot Rest - How to configure 404 - resource not found (스프링 부트레스트 - 404 구성 방법 - 리소스를 찾을 수 없습니다)에 대한 자세한 내용은 원본 스레드에서 확인할 수 있습니다.

번째: 오류 사례를 처리하고 응답을 재정의하는 컨트롤러를 정의합니다.

@ControllerAdvice
public class ExceptionHandlerController {

    @ExceptionHandler(NoHandlerFoundException.class)
    @ResponseStatus(value= HttpStatus.NOT_FOUND)
    @ResponseBody
    public ErrorResponse requestHandlingNoHandlerFound() {
        return new ErrorResponse("custom_404", "message for 404 error code");
    }
}

번째: 404(핸들러를 해결할 수 없음)의 경우 예외를 발생시키도록 스프링에 지시해야 합니다.

@SpringBootApplication
@EnableWebMvc
public class Application {

    public static void main(String[] args) {
        ApplicationContext ctx = SpringApplication.run(Application.class, args);

        DispatcherServlet dispatcherServlet = (DispatcherServlet)ctx.getBean("dispatcherServlet");
        dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
    }
}

모든 답변과 의견을 종합해보면, 가장 좋은 방법은...

우선, 「 Boot」에 는, 「Spring Boot」에 합니다.application.properties

spring.mvc.throw-exception-if-no-handler-found=true

나서 " " " " 를 처리합니다.NoHandlerFoundException를 참조해 주세요.는 이것을 한다.

@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(NoHandlerFoundException.class)
    public void handleNotFoundError(HttpServletResponse response, NoHandlerFoundException ex) {
        ErrorDto errorDto = Errors.URL_NOT_FOUND.getErrorDto();
        logger.error("URL not found exception: " + ex.getRequestURL());
        prepareErrorResponse(response, HttpStatus.NOT_FOUND, errorDto);
    }
}

Swagger를 사용하는 경우, 이 예외 핸들러에서 스웨거 URL을 제외하기 위한 다른 답변을 볼 수 있습니다.

404 오류는 Dispatcher Servlet에 의해 처리됩니다.덮어쓸 수 있는 속성 throwExceptionIfHandlerFound가 있습니다.

응용 프로그램 클래스에서 새 빈을 만들 수 있습니다.

@Bean
DispatcherServlet dispatcherServlet () {
    DispatcherServlet ds = new DispatcherServlet();
    ds.setThrowExceptionIfNoHandlerFound(true);
    return ds;
}

...에서 NoHandlerFoundException 예외를 검출합니다.

@EnableWebMvc
@ControllerAdvice
public class GlobalControllerExceptionHandler {
    @ExceptionHandler
    @ResponseStatus(value=HttpStatus.NOT_FOUND)
    @ResponseBody
    public ErrorMessageResponse requestHandlingNoHandlerFound(final NoHandlerFoundException ex) {
        doSomething(LOG.debug("text to log"));
    }
}

하다를 해서 사용해도 됩니다.ResponseEntityExceptionHandler projectspring boot project에는 다음과 할 수 .

@ControllerAdvice
public class MyApiExceptionHandler extends ResponseEntityExceptionHandler {

    @Override
    public ResponseEntity<Object> handleBindException(BindException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        String responseBody = "{\"key\":\"value\"}";
        headers.add("Content-Type", "application/json;charset=utf-8");
        return handleExceptionInternal(ex, responseBody, headers, HttpStatus.NOT_ACCEPTABLE, request);
    }
}

http status 404-Not Found의 다른 예에서는

@ControllerAdvice
public class MyApiExceptionHandler extends ResponseEntityExceptionHandler {

    @Override
    public ResponseEntity<Object> handleNoHandlerFoundException(NoHandlerFoundException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        String responseBody = "{\"errormessage\":\"WHATEVER YOU LIKE\"}";
        headers.add("Content-Type", "application/json;charset=utf-8");
        return handleExceptionInternal(ex, responseBody, headers, HttpStatus.NOT_FOUND, request);
    }
}

404 not found 예외에 대해서는 기본 동작 대신 Dispatcher Servlet이 핸들러를 찾을 수 없는 경우 drow 및 exception을 설정해야 합니다.404에 관한 문제에 대해서는, 이 질문을 읽어 주세요.

같은 문제가 있었지만 다른 방법으로 수정했습니다.커스텀 응답으로 404, 401 및 기타 상태를 반환하려면 응답 상태를 커스텀 예외 클래스에 추가하고 예외 핸들러에서 호출합니다.

스프링 유틸리티 클래스 AnnotationUtils를 사용하면 findAnnotation 메서드를 사용하여 정의된 사용자 정의 예외의 상태를 가져올 수 있습니다.이러한 예외는 찾을 수 없음 등의 예외에 대해 정의한 주석을 사용하여 적절한 상태를 반환합니다.

여기 @RestControllerAdvice가 있습니다.

@RestControllerAdvice
public class MainExceptionHandler extends Throwable{

 @ExceptionHandler(BaseException.class)
 ResponseEntity<ExceptionErrorResponse> exceptionHandler(GeneralMainException  e)
 {
  ResponseStatus status = AnnotationUtils.findAnnotation(e.getClass(),ResponseStatus.class);
  if(status != null)
  {
    return new ResponseEntity<>(new ExceptionErrorResponse(e.getCode(),e.getMessage()),status.code());
  }
 }

커스텀 패럴잘못된 요청 상태를 반환하는 예외

@ResponseStatus(value= HttpStatus.BAD_REQUEST)
public class CustomParamsException extends BaseException {
    private static final String CODE = "400";
    public CustomParamsException(String message) {
        super(CODE, message);
    }
}

찾을 수 없음 상태를 반환하는 세부 정보를 찾을 수 없습니다.

@ResponseStatus(value= HttpStatus.NOT_FOUND)
public class DetailsNotException extends BaseException {
    private static final String CODE = "400";
    public DetailsNotException(String message) {
        super(CODE, message);
    }
}

Excection을 확장하기 위한 General Main Exception

public class GeneralMainException extends Exception {
private String code;
private String message;

public GeneralMainException (String message) {
    super(message);
}

public GeneralMainException (String code, String message) {
    this.code = code;
    this.message = message;
}

public String getCode() {
    return code;
}

@Override
public String getMessage() {
    return message;
}
}

컨트롤러의 어드바이스에 포함시킴으로써 다른 시스템 예외를 처리하도록 결정할 수 있습니다.

@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
 @ExceptionHandler(Exception.class)
 ExceptionErrorResponse sysError(Exception e)
 {
  return new ExceptionErrorResponse(""1002", e.getMessage());
 }

적절한 주석이 달린 방법을 도입해야 할 것 같습니다.예를 들어 지원되지 않는 미디어 유형(415)의 경우 다음과 같습니다.

  @ExceptionHandler(MethodArgumentNotValidException)
  public ResponseEntity handleMethodArgumentNotValidException(HttpServletRequest req, MethodArgumentNotValidException e) {
    logger.error('Caught exception', e)
    def response = new ExceptionResponse(
            error: 'Validation error',
            exception: e.class.name,
            message: e.bindingResult.fieldErrors.collect { "'$it.field' $it.defaultMessage" }.join(', '),
            path: req.servletPath,
            status: BAD_REQUEST.value(),
            timestamp: currentTimeMillis()
    )
    new ResponseEntity<>(response, BAD_REQUEST)
  }

그러나 401과 404는 도달하기 전에 던질 수 있기 때문에 가능하지 않을 수 있습니다.DispatcherServlet- 이 경우ControllerAdvice동작하지 않습니다.

web.xml의 오류 페이지 정의에 해당하는 사용자 지정 ErrorPage 개체를 추가할 수 있습니다.Spring Boot의 예는 다음과 같습니다.

@Bean
public EmbeddedServletContainerCustomizer containerCustomizer(){
    return new MyCustomizer();
}

// ...

private static class MyCustomizer implements EmbeddedServletContainerCustomizer {

    @Override
    public void customize(ConfigurableEmbeddedServletContainer container) {
        container.addErrorPages(new ErrorPage(HttpStatus.UNAUTHORIZED, "/unauthorized.html"));
        container.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/not-found.html"));
    }

}

편집: 위의 방법은 에러 페이지를 컨트롤러에 남겨두면 효과가 있다고 생각합니다만, 보다 쉬운 방법은 다음과 같은 커스텀 Error Controller를 추가하는 것입니다.

@Bean
public ErrorController errorController(ErrorAttributes errorAttributes) {
    return new CustomErrorController(errorAttributes);
}

// ...

public class CustomErrorController extends BasicErrorController {

    public CustomErrorController(ErrorAttributes errorAttributes) {
        super(errorAttributes);
    }

    @Override
    @RequestMapping(value = "${error.path:/error}")
    @ResponseBody
    public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
        ResponseEntity<Map<String, Object>> error = super.error(request);
        HttpStatus statusCode = error.getStatusCode();

        switch (statusCode) {
        case NOT_FOUND:
            return getMyCustomNotFoundResponseEntity(request);
        case UNAUTHORIZED:
            return getMyCustomUnauthorizedResponseEntity(request);
        default:
            return error;
        }
    }
}

Spring Boot REST 서비스 예외 처리를 참조하십시오.디스패처 서블릿에 "no route found"의 예외를 송신하도록 지시하는 방법과 이러한 예외를 검출하는 방법에 대해 설명합니다.현재 저희(제가 일하는 곳)에서는 REST 서비스를 위해 이 제품을 생산하고 있습니다.

Spring 버전5부터는 클래스 ResponseStatusException을 사용할 수 있습니다.

@GetMapping("example")
public ResponseEntity example() {
    try {
        throw new MyException();
    } catch (MyException e) {
        throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "My Exception", e);
    }
}

가능한 모든 오류 시나리오에서 동일한 오류 형식(json) 구조를 사용하고 싶었기 때문에 AbstractErrorController에서 코드를 재사용하여 ErrorController를 등록했습니다.

@Controller
@RequestMapping(path = "/error", produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public class ErrorController extends AbstractErrorController {

    public ErrorController(ErrorAttributes errorAttributes, ObjectProvider<ErrorViewResolver> errorViewResolvers) {
        super(errorAttributes, errorViewResolvers.orderedStream().collect(Collectors.toUnmodifiableList()));
    }

    @RequestMapping
    public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
        final var status = getStatus(request);
        if (status == HttpStatus.NO_CONTENT) {
            return new ResponseEntity<>(status);
        }
        return new ResponseEntity<>(getErrorAttributes(request, ErrorAttributeOptions.defaults()), status);
    }

    @Override
    public String getErrorPath() {
        return null;
    }
}

이것에 의해, 컨트롤러의 어드바이스는 불필요합니다.기본적으로 모든 에러는 에러 방식으로 진행됩니다.

언급URL : https://stackoverflow.com/questions/30917782/spring-boot-404-error-custom-error-response-rest