Handle Spring Exceptions Like A Pro

Typical Exception Handling In Java ️

It’s a common thing in Java to try-catch parts of our code that we except to fail for some reason

  • Missing files, corrupt data, etc…
try{  
    buggyMethod();  
    return "Done!";  
}catch (RuntimeException e){  
    return "An error happened!";  
}

Enter fullscreen mode Exit fullscreen mode


Exception Handling In Spring

Let’s view the workflow of Spring as a web framework:

  1. Listen to requests from the client.
  2. Take some actions based on our business logic.
  3. Return a response to the client containing the result of our work.

Now, we ideally want to catch any exception (error) that might arise at level 2 (action taking).
We can write a try catch block at every controller method that handles the exceptions in a standard way

@RestController
@RequiredArgsConstructor  
public class TestController  
{
    private final ExceptionHandler exceptionHandler;

    @GetMapping("/test1")  
    public void test1(){  
        try{  
          // test 1 things 
      }catch (Exception e){  
          exceptionHandler.handleException(e);  
      }  
    }  
    @GetMapping("/test2")  
    public void test2(){  
        try{  
          // test 2 things 
      }catch (Exception e){  
         exceptionHandler.handleException(e);  
      }  
    }
}

Enter fullscreen mode Exit fullscreen mode

The problem with this approach however is that it get quite tedious when we have many more controller methods.


Why capture all exceptions? and not just let them occur?🤷

  • We want our application to be user friendly and handle all edge cases, thus we want it to return responses with standard format.
  • We might also want to log those exceptions in a backlog to get back to them and investigate them, or do whatever we like with them.

@ControllerAdvice To The Rescue

The idea is that we declare a method that will handle any unhandled exceptions in the application.

How to do it?

First, we need to declare a class and annotate it with @ControllerAdvice.
Then, we declare methods, each handling a class of exception.

@ControllerAdvice @Slf4j  
public class GlobalErrorHandler  
{  
    @ResponseStatus(INTERNAL_SERVER_ERROR)  
    @ResponseBody  
    @ExceptionHandler(Exception.class)  
    public String methodArgumentNotValidException(Exception ex) {  
        // you can take actions based on the exception 
        log.error("An unexpected error has happened", ex);  
        return "An internal error has happened, please report the incident";  
  }
    @ResponseStatus(BAD_REQUEST)  
    @ResponseBody  
    @ExceptionHandler(InvalidParameterException.class)  
    public String invalidParameterException(InvalidParameterException ex){  
        return "This is a BAD REQUEST";  
   }  
}

Enter fullscreen mode Exit fullscreen mode

What does the above code do?️

  • Declares two methods that will be run whenever an exception of class Exception, InvalidParameterException (or subclass of them) is thrown and not handled locally in their thread of execution.
  • They return a string response back the client.

Note that we can specify more than one handler in the class annotated with @ControllerAdvice.


Now, let’s code some endpoints for us to validate against. Let’s code three endpoints

  • One that handles the exception thrown.
  • The other two leave the handling to the global exception handler
@RestController @RequiredArgsConstructor  
public class TestController  
{  
    @GetMapping("/buggyMethod")  
    public String testMeWithExceptionHandler(){  
        try{  
            buggyMethod();  
            return "Done!";  
      }catch (RuntimeException e){  
            return "An error happened!";  
        }  
    }  
    @GetMapping("/potentialBuggyMethod")  
    public String testMeWithoutExceptionHandler(){  
        undercoverBuggyMethod();  
        return "Done!";  
      }  
    @PostMapping("/invalidParamMethod")  
    public String testForInvalidParam(){  
        buggyParameters();  
        return "Done";  
    }  
    private void buggyMethod(){  
        throw new RuntimeException();  
    }  
    private void undercoverBuggyMethod(){  
        throw new RuntimeException("oops");  
    }  
    private void buggyParameters(){  
        throw new InvalidParameterException();  
    } 
}

Enter fullscreen mode Exit fullscreen mode


Let’s Verify It With Some Tests 🧠

@WebMvcTest(controllers = TestController.class)  
public class GlobalExceptionHandlerTest  
{  
  @Autowired  
  private MockMvc mockMvc;  

  @Test  
  public void givenAGetRequestToBuggyEndPoint_DetectErrorMessage() throws Exception  
    {  
        MvcResult mvcResult = mockMvc  
                .perform(get("/buggyMethod"))  
                .andExpect(status().isOk())  
                .andReturn();  
        String response = mvcResult.getResponse().getContentAsString();  
        assertEquals(response, "An error happened!");  
  }  
    @Test  
  public void givenAGetRequestToPotentialBuggyMethod_DetectErrorMessage() throws Exception  
    {  
        MvcResult mvcResult = mockMvc  
                .perform(get("/potentialBuggyMethod"))  
                .andExpect(status().is5xxServerError())  
                .andReturn();  
        String response = mvcResult.getResponse().getContentAsString();  
        assertEquals(response, "An internal error has happened, please report the incident");  
  }
  @Test  
public void givenAPostRequestToBuggyMethod_DetectInvalidParameterErrorMessage() throws Exception  
{  
    MvcResult mvcResult = mockMvc  
            .perform(post("/invalidParamMethod"))  
            .andExpect(status().isBadRequest())  
            .andReturn();  
      String response = mvcResult.getResponse().getContentAsString();  
      assertEquals(response, "This is a BAD REQUEST");  
    }  
}

Enter fullscreen mode Exit fullscreen mode


Conclusion

Unexpected and general errors should be handled elegantly to sustain a smooth experience for our application clients. This is best done using Spring’s ControllerAdvice.


Check this article for more details Error Handling for REST with Spring


Check the code on GitHub🥷

原文链接:Handle Spring Exceptions Like A Pro

© 版权声明
THE END
喜欢就支持一下吧
点赞6 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容