@ExceptionHandler возвращает много журналов из RuntimeException при обработке пользовательского исключения

Я изучаю среду загрузки Spring. Я пытаюсь реализовать обработку исключений на уровне контроллера с помощью @ExceptionHandler. Я могу обработать выброшенное пользователем исключение, которое расширяет RuntimeException, но ответ имеет гораздо больше, чем я хотел увидеть. Он содержит причину, трассировку стека из класса исключения времени выполнения, но я не хочу, чтобы те в ответе.

Ниже приведен код для @ExceptionHandler

@ExceptionHandler(InvalidPassengerNameException.class)
    public ResponseEntity<InvalidPassengerNameException> handleInvalidPassengerNameException(InvalidPassengerNameException exc) {
        InvalidPassengerNameException iPNE =  new InvalidPassengerNameException();
        iPNE.setErrorCode("E400");
        iPNE.setErrorMessage("PAssenger name should be more than 8 characters");
        return new ResponseEntity<InvalidPassengerNameException>(iPNE, HttpStatus.NOT_ACCEPTABLE);
    }

Ниже приведен мой класс пользовательских исключений:

public class InvalidPassengerNameException extends RuntimeException{
private String errorCode;
private String errorMessage;
public String getErrorCode() {
    return errorCode;
}
public void setErrorCode(String errorCode) {
    this.errorCode = errorCode;
}
public String getErrorMessage() {
    return errorMessage;
}
public void setErrorMessage(String errorMessage) {
    this.errorMessage = errorMessage;
}
}

Когда исключение обрабатывается определенным обработчиком, журнал содержит намного больше того, что я намеревался увидеть:

{
    "cause": null,
    "stackTrace": [
        {
            "methodName": "handleInvalidPassengerNameException",
            "fileName": "PassengerController.java",
            "lineNumber": 32,
            "className": "com.booking.application.controller.PassengerController",
            "nativeMethod": false
        },
        {
            "methodName": "invoke0",
            "fileName": "NativeMethodAccessorImpl.java",
            "lineNumber": -2,
            "className": "sun.reflect.NativeMethodAccessorImpl",
            "nativeMethod": true
        },
        {
            "methodName": "invoke",
            "fileName": "NativeMethodAccessorImpl.java",
            "lineNumber": 62,
            "className": "sun.reflect.NativeMethodAccessorImpl",
            "nativeMethod": false
        },
        {
            "methodName": "invoke",
            "fileName": "DelegatingMethodAccessorImpl.java",
            "lineNumber": 43,
            "className": "sun.reflect.DelegatingMethodAccessorImpl",
            "nativeMethod": false
        },
        {
            "methodName": "invoke",
            "fileName": "Method.java",
            "lineNumber": 498,
            "className": "java.lang.reflect.Method",
            "nativeMethod": false
        },
        {
            "methodName": "doInvoke",
            "fileName": "InvocableHandlerMethod.java",
            "lineNumber": 190,
            "className": "org.springframework.web.method.support.InvocableHandlerMethod",
            "nativeMethod": false
        },
        {
            "methodName": "invokeForRequest",
            "fileName": "InvocableHandlerMethod.java",
            "lineNumber": 138,
            "className": "org.springframework.web.method.support.InvocableHandlerMethod",
            "nativeMethod": false
        },
        {
            "methodName": "invokeAndHandle",
            "fileName": "ServletInvocableHandlerMethod.java",
            "lineNumber": 106,
            "className": "org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod",
            "nativeMethod": false
        },
        {
            "methodName": "doResolveHandlerMethodException",
            "fileName": "ExceptionHandlerExceptionResolver.java",
            "lineNumber": 407,
            "className": "org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver",
            "nativeMethod": false
        },
        {
            "methodName": "doResolveException",
            "fileName": "AbstractHandlerMethodExceptionResolver.java",
            "lineNumber": 61,
            "className": "org.springframework.web.servlet.handler.AbstractHandlerMethodExceptionResolver",
            "nativeMethod": false
        },
        {
            "methodName": "resolveException",
            "fileName": "AbstractHandlerExceptionResolver.java",
            "lineNumber": 141,
            "className": "org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver",
            "nativeMethod": false
        },
        {
            "methodName": "resolveException",
            "fileName": "HandlerExceptionResolverComposite.java",
            "lineNumber": 80,
            "className": "org.springframework.web.servlet.handler.HandlerExceptionResolverComposite",
            "nativeMethod": false
        },
        {
            "methodName": "processHandlerException",
            "fileName": "DispatcherServlet.java",
            "lineNumber": 1300,
            "className": "org.springframework.web.servlet.DispatcherServlet",
            "nativeMethod": false
        },
        {
            "methodName": "processDispatchResult",
            "fileName": "DispatcherServlet.java",
            "lineNumber": 1111,
            "className": "org.springframework.web.servlet.DispatcherServlet",
            "nativeMethod": false
        },
        {
            "methodName": "doDispatch",
            "fileName": "DispatcherServlet.java",
            "lineNumber": 1057,
            "className": "org.springframework.web.servlet.DispatcherServlet",
            "nativeMethod": false
        },
        {
            "methodName": "doService",
            "fileName": "DispatcherServlet.java",
            "lineNumber": 943,
            "className": "org.springframework.web.servlet.DispatcherServlet",
            "nativeMethod": false
        },
        {
            "methodName": "processRequest",
            "fileName": "FrameworkServlet.java",
            "lineNumber": 1006,
            "className": "org.springframework.web.servlet.FrameworkServlet",
            "nativeMethod": false
        },
        {
            "methodName": "doPost",
            "fileName": "FrameworkServlet.java",
            "lineNumber": 909,
            "className": "org.springframework.web.servlet.FrameworkServlet",
            "nativeMethod": false
        },
        {
            "methodName": "service",
            "fileName": "HttpServlet.java",
            "lineNumber": 660,
            "className": "javax.servlet.http.HttpServlet",
            "nativeMethod": false
        },
        {
            "methodName": "service",
            "fileName": "FrameworkServlet.java",
            "lineNumber": 883,
            "className": "org.springframework.web.servlet.FrameworkServlet",
            "nativeMethod": false
        },
        {
            "methodName": "service",
            "fileName": "HttpServlet.java",
            "lineNumber": 741,
            "className": "javax.servlet.http.HttpServlet",
            "nativeMethod": false
        },
        {
            "methodName": "internalDoFilter",
            "fileName": "ApplicationFilterChain.java",
            "lineNumber": 231,
            "className": "org.apache.catalina.core.ApplicationFilterChain",
            "nativeMethod": false
        },
        {
            "methodName": "doFilter",
            "fileName": "ApplicationFilterChain.java",
            "lineNumber": 166,
            "className": "org.apache.catalina.core.ApplicationFilterChain",
            "nativeMethod": false
        },
        {
            "methodName": "doFilter",
            "fileName": "WsFilter.java",
            "lineNumber": 53,
            "className": "org.apache.tomcat.websocket.server.WsFilter",
            "nativeMethod": false
        },
        {
            "methodName": "internalDoFilter",
            "fileName": "ApplicationFilterChain.java",
            "lineNumber": 193,
            "className": "org.apache.catalina.core.ApplicationFilterChain",
            "nativeMethod": false
        },
        {
            "methodName": "doFilter",
            "fileName": "ApplicationFilterChain.java",
            "lineNumber": 166,
            "className": "org.apache.catalina.core.ApplicationFilterChain",
            "nativeMethod": false
        },
        {
            "methodName": "doFilterInternal",
            "fileName": "RequestContextFilter.java",
            "lineNumber": 100,
            "className": "org.springframework.web.filter.RequestContextFilter",
            "nativeMethod": false
        },
        {
            "methodName": "doFilter",
            "fileName": "OncePerRequestFilter.java",
            "lineNumber": 119,
            "className": "org.springframework.web.filter.OncePerRequestFilter",
            "nativeMethod": false
        },
        {
            "methodName": "internalDoFilter",
            "fileName": "ApplicationFilterChain.java",
            "lineNumber": 193,
            "className": "org.apache.catalina.core.ApplicationFilterChain",
            "nativeMethod": false
        },
        {
            "methodName": "doFilter",
            "fileName": "ApplicationFilterChain.java",
            "lineNumber": 166,
            "className": "org.apache.catalina.core.ApplicationFilterChain",
            "nativeMethod": false
        },
        {
            "methodName": "doFilterInternal",
            "fileName": "FormContentFilter.java",
            "lineNumber": 93,
            "className": "org.springframework.web.filter.FormContentFilter",
            "nativeMethod": false
        },
        {
            "methodName": "doFilter",
            "fileName": "OncePerRequestFilter.java",
            "lineNumber": 119,
            "className": "org.springframework.web.filter.OncePerRequestFilter",
            "nativeMethod": false
        },
        {
            "methodName": "internalDoFilter",
            "fileName": "ApplicationFilterChain.java",
            "lineNumber": 193,
            "className": "org.apache.catalina.core.ApplicationFilterChain",
            "nativeMethod": false
        },
        {
            "methodName": "doFilter",
            "fileName": "ApplicationFilterChain.java",
            "lineNumber": 166,
            "className": "org.apache.catalina.core.ApplicationFilterChain",
            "nativeMethod": false
        },
        {
            "methodName": "doFilterInternal",
            "fileName": "CharacterEncodingFilter.java",
            "lineNumber": 201,
            "className": "org.springframework.web.filter.CharacterEncodingFilter",
            "nativeMethod": false
        },
        {
            "methodName": "doFilter",
            "fileName": "OncePerRequestFilter.java",
            "lineNumber": 119,
            "className": "org.springframework.web.filter.OncePerRequestFilter",
            "nativeMethod": false
        },
        {
            "methodName": "internalDoFilter",
            "fileName": "ApplicationFilterChain.java",
            "lineNumber": 193,
            "className": "org.apache.catalina.core.ApplicationFilterChain",
            "nativeMethod": false
        },
        {
            "methodName": "doFilter",
            "fileName": "ApplicationFilterChain.java",
            "lineNumber": 166,
            "className": "org.apache.catalina.core.ApplicationFilterChain",
            "nativeMethod": false
        },
        {
            "methodName": "invoke",
            "fileName": "StandardWrapperValve.java",
            "lineNumber": 202,
            "className": "org.apache.catalina.core.StandardWrapperValve",
            "nativeMethod": false
        },
        {
            "methodName": "invoke",
            "fileName": "StandardContextValve.java",
            "lineNumber": 96,
            "className": "org.apache.catalina.core.StandardContextValve",
            "nativeMethod": false
        },
        {
            "methodName": "invoke",
            "fileName": "AuthenticatorBase.java",
            "lineNumber": 526,
            "className": "org.apache.catalina.authenticator.AuthenticatorBase",
            "nativeMethod": false
        },
        {
            "methodName": "invoke",
            "fileName": "StandardHostValve.java",
            "lineNumber": 139,
            "className": "org.apache.catalina.core.StandardHostValve",
            "nativeMethod": false
        },
        {
            "methodName": "invoke",
            "fileName": "ErrorReportValve.java",
            "lineNumber": 92,
            "className": "org.apache.catalina.valves.ErrorReportValve",
            "nativeMethod": false
        },
        {
            "methodName": "invoke",
            "fileName": "StandardEngineValve.java",
            "lineNumber": 74,
            "className": "org.apache.catalina.core.StandardEngineValve",
            "nativeMethod": false
        },
        {
            "methodName": "service",
            "fileName": "CoyoteAdapter.java",
            "lineNumber": 343,
            "className": "org.apache.catalina.connector.CoyoteAdapter",
            "nativeMethod": false
        },
        {
            "methodName": "service",
            "fileName": "Http11Processor.java",
            "lineNumber": 367,
            "className": "org.apache.coyote.http11.Http11Processor",
            "nativeMethod": false
        },
        {
            "methodName": "process",
            "fileName": "AbstractProcessorLight.java",
            "lineNumber": 65,
            "className": "org.apache.coyote.AbstractProcessorLight",
            "nativeMethod": false
        },
        {
            "methodName": "process",
            "fileName": "AbstractProtocol.java",
            "lineNumber": 860,
            "className": "org.apache.coyote.AbstractProtocol$ConnectionHandler",
            "nativeMethod": false
        },
        {
            "methodName": "doRun",
            "fileName": "NioEndpoint.java",
            "lineNumber": 1591,
            "className": "org.apache.tomcat.util.net.NioEndpoint$SocketProcessor",
            "nativeMethod": false
        },
        {
            "methodName": "run",
            "fileName": "SocketProcessorBase.java",
            "lineNumber": 49,
            "className": "org.apache.tomcat.util.net.SocketProcessorBase",
            "nativeMethod": false
        },
        {
            "methodName": "runWorker",
            "fileName": "ThreadPoolExecutor.java",
            "lineNumber": 1149,
            "className": "java.util.concurrent.ThreadPoolExecutor",
            "nativeMethod": false
        },
        {
            "methodName": "run",
            "fileName": "ThreadPoolExecutor.java",
            "lineNumber": 624,
            "className": "java.util.concurrent.ThreadPoolExecutor$Worker",
            "nativeMethod": false
        },
        {
            "methodName": "run",
            "fileName": "TaskThread.java",
            "lineNumber": 61,
            "className": "org.apache.tomcat.util.threads.TaskThread$WrappingRunnable",
            "nativeMethod": false
        },
        {
            "methodName": "run",
            "fileName": "Thread.java",
            "lineNumber": 748,
            "className": "java.lang.Thread",
            "nativeMethod": false
        }
    ],
    "errorCode": "E400",
    "errorMessage": "Passenger name should be more than 8 characters",
    "localizedMessage": null,
    "message": null,
    "suppressed": []
}

То, что я хотел увидеть, это:

"errorCode": "E400",
"errorMessage": "Passenger name should be more than 8 characters",

Что мне не хватает? Я знаю, что глобальный @ControllerAdvice поможет. Но я хочу узнать, почему это происходит и как это можно реализовать, чтобы получить намеченный ответ как результат?

Всего 1 ответ


@ExceptionHandler работает нормально. Однако вы расширяете RuntimeException , которая поставляется с getCause() , getStackTrace() , .... Джексон сериализует все эти поля в JSON, поэтому вы видите stacktrace и поле cause .

Чтобы решить эту проблему, вы можете создать отдельное DTO для ваших ошибок, которое не выходит за RuntimeException , например:

public class ErrorMessageDTO {
    private final String errorCode;
    private final String errorMessage;

    // Constructors + Getters + Setters
}

И теперь вы можете использовать это в своем @ExceptionHandler :

@ExceptionHandler(InvalidPassengerNameException.class)
public ResponseEntity<ErrorMessageDTO> handleInvalidPassengerNameException(InvalidPassengerNameException exc) {
    ErrorMessageDTO message = new ErrorMessageDTO("E400", "Passenger name should be more than 8 characters");
    return new ResponseEntity<ErrorMessageDTO>(message, HttpStatus.NOT_ACCEPTABLE);
}

Кроме того, вы можете переопределить RuntimeException получения RuntimeException и аннотировать их с помощью @JsonIgnore :

public class InvalidPassengerNameException extends RuntimeException {
    // Fields + Getters + Setters

    @JsonIgnore
    @Override
    public Throwable getCause() {
        return super.getCause();
    }
}

В этом случае первое решение будет лучшим. Вы уже возвращаете новый объект в своем @ExceptionHandler , и больше нет причин для этого нового объекта расширяться от RuntimeException .


Есть идеи?

10000