How to Prevent Stack Trace from Spring Rest API Exception Handler returning HTTP status 400
Asked Answered
L

7

10

I am trying to handle Validation exception in Spring Rest service like code given below:

@Produces("application/json")
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public @ResponseBody ResponseEntity<ValidationErrorDTO> processValidationError(MethodArgumentNotValidException ex) {
        BindingResult result = ex.getBindingResult();
        List<FieldError> fieldErrors = result.getFieldErrors();
        ResponseEntity<ValidationErrorDTO> re = new ResponseEntity<>(processFieldErrors(fieldErrors), HttpStatus.BAD_REQUEST);   
        return re;
    }

However when this code executes, it returns stack trace appended to the JSON response. I want to get rid of this stack trace and only return JSON error response. Is this possible with Spring 3.1?

{"fieldErrors":    
[{"field":"type","message":"Pattern.shippingAddress.type"}]}&lt;pre&gt;javax.servlet.ServletException: 400 Bad Request null
        at com.ebay.raptor.kernel.dispatcher.HttpDispatchCommand.execute(HttpDispatchCommand.java:142)
        at com.ebay.ebox.pres.cmd.preshandler.CommandDispatchHandler.handleRequest(CommandDispatchHandler.java:59)
        at com.ebay.ebox.pres.cmd.preshandler.CommandDispatchHandler.handleRequest(CommandDispatchHandler.java:13)
        at com.ebay.ebox.pres.cmd.preshandler.CommandHandlerFactory$CalHandler.handleRequest(CommandHandlerFactory.java:114)
        at com.ebay.ebox.pres.cmd.preshandler.CommandHandlerFactory$CalHandler.handleRequest(CommandHandlerFactory.java:75)
        at com.ebay.kernel.pipeline.RequestPipeline.invoke(RequestPipeline.java:18)
        at com.ebay.kernel.pipeline.RequestPipeline.invoke(RequestPipeline.java:12)
        at com.ebay.kernel.pipeline.BasePipeline.callHandler(BasePipeline.java:75)
        at com.ebay.kernel.pipeline.BasePipeline.execute(BasePipeline.java:53)
        at com.ebay.ebox.pres.stage.BaseCommandRequestStageImpl.doWork(BaseCommandRequestStageImpl.java:64)
        at com.ebay.kernel.stage.StageDriver.execute(StageDriver.java:55)
        at com.ebay.ebox.pres.cmd.WebCommandImpl.execute(WebCommandImpl.java:30)
        at com.ebay.raptor.web.RaptorFrontController.process(RaptorFrontController.java:338)
        at com.ebay.raptor.web.RaptorFrontController.doPost(RaptorFrontController.java:554)
        at com.ebay.raptor.web.RaptorGenericController.service(RaptorGenericController.java:64)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
        at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:749)
        at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:487)
        at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:379)
        at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:339)
        at com.ebay.raptor.kernel.filter.RaptorDispatchFilter.doFilter(RaptorDispatchFilter.java:52)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
        at com.thetransactioncompany.cors.CORSFilter.doFilter(CORSFilter.java:195)
        at com.thetransactioncompany.cors.CORSFilter.doFilter(CORSFilter.java:266)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
        at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
        at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1023)
        at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
        at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
        at java.lang.Thread.run(Thread.java:744)
    &lt;/pre&gt;
Lardner answered 14/7, 2014 at 7:29 Comment(5)
There must be somewhere a second filter that append the stack trace. I think, (Because of the stacktrace) that this filter is registered somewhere in the web.xml - maybe you post the web.xml, that one can have a look at this, and point at the spot.Anabaptist
There are no filters mapped in web.xml and also no servlets except Spring's dispatcher servlet.Lardner
what is: com.ebay.raptor.kernel.dispatcher.HttpDispatchCommandAnabaptist
It's an internal framework class.Lardner
I verified that if I do not use @ResponseBody annotation and instead write to response output print stream directly this stack trace is not added. It is only added when I use this ResponseBody Annotation and set HttpStatus to anything other than 200.Lardner
S
9

You can just add the application property server.error.include-stacktrace=never which responds for including stack trace into 400 responses.

Sweaty answered 5/12, 2019 at 22:45 Comment(1)
by default, server.error.include-stacktrace=neverLissie
A
2

You can do like this:

@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ModelAndView methodArgumentError(HttpServletRequest req, Exception exception) {
    return new JsonError(exception.getMessage()).asModelAndView();
}
Anthologize answered 6/1, 2015 at 12:57 Comment(0)
C
1

You could in your MethodArgumentNotValidException set the stack trace to null if the error is only being returned to the user and not be used for debugging.

Or alternatively, you could just override the getStackTrace() with a @JsonIgnore annotation

@JsonIgnore
@Override
public StackTraceElement[] getStackTrace() {
    return super.getStackTrace();
}

EDIT: I tried all the other solutions but none worked for me.

Conscious answered 2/12, 2020 at 11:48 Comment(0)
A
1
@Order(Ordered.HIGHEST_PRECEDENCE)

Use @Order(Ordered.HIGHEST_PRECEDENCE) on your method it will work

Argo answered 16/3, 2021 at 5:4 Comment(0)
A
0

It is uncommon to use both: @ResponseBody and ResponseEntity. I think you should clearly decide what you want to do. - And then you should check your internal framework class com.ebay.raptor.kernel.dispatcher.HttpDispatchCommand, because this is the one that throws the exception.

Anabaptist answered 16/7, 2014 at 6:36 Comment(1)
I tried without ResponseEntity also and the problem is same. As you pointed out I am currently analyzing this framework class com.ebay.raptor.kernel.dispatcher.HttpDispatchCommand. Right now it's highly suspected to be the culprit.Lardner
M
0

Another simpler way to do it

public static void main(String[] args) {


System.setProperty("server.error.include-message", "never");
System.setProperty("server.error.include-stacktrace", "never");

//You can also change other important properties with this method, for example:

// System.setProperty("server.port", "8081");
// System.setProperty("server.servlet.context-path", "/api");

SpringApplication.run(YourApplication.class, args);

}

The response will look like this;

{
    "timestamp": "2021-12-30T19:23:24.002+00:00",
    "status": 400,
    "error": "Bad Request",
    "path": "/test"
}
Meetinghouse answered 30/12, 2021 at 19:27 Comment(0)
C
-1

You can use Aspectj for these purposes. Example http://www.mkyong.com/spring3/spring-aop-aspectj-annotation-example/

You should write an aspect, which will catch your exception.

Example:

@Aspect
public class AroundExample {
 @Around
 pointcut = "execution(* your.package.Class.processValidationError(..))",
      throwing= "error")
 public Object doSomeStuff(ProceedingJoinPoint pjp, Throwable error) throws Throwable {

    Object[] args = joinPoint.getArgs(); // change the args if you want to
    Object retVal = pjp.proceed(args); // run the actual method (or don't)
    return retVal; // return the return value (or something else)
  }

}
Cloy answered 14/7, 2014 at 15:32 Comment(3)
The @ExceptionHandler(MethodArgumentNotValidException.class) allready catches the exception, so there's no need for AspectJ here.Farnsworth
But it doesnt return needed value.Cloy
No exception is thrown in processValidationError ! So your advice would never been called. It is allready an exception handler for exception occuring during the treatment of a request - read spring framework documentation about ExceptionHandler ...Farnsworth

© 2022 - 2024 — McMap. All rights reserved.