preface

When the project runs with an exception, if the exception is not caught and handled, the following page appears:

This is obviously extremely user unfriendly.

The back end should not return an error page directly, but should return a uniform error message, such as:

{
  "code": 500."data": null."message": "Service is abnormal. Please try again later."
}
Copy the code

The front end then displays a friendly prompt page based on the returned information.

Spring provides three ways to handle exceptions uniformly:

  1. @ExceptionHandler
  2. Implement the HandlerExceptionResolver interface
  3. @ControllerAdvice + @ExceptionHandler

So let’s actually do it.

The specific implementation

@ExceptionHandler

In this article, we define the base class BaseController for Controller, so as long as we use @ExceptionHandler in BaseController to handle exceptions, Other controllers inherit from BaseController. The implementation is as follows:

@Slf4j
public abstract class BaseController {

    /** * BusinessException */
    @ResponseBody
    @ExceptionHandler(BusinessException.class)
    public ApiResult businessExceptionHandler(BusinessException e) {
        log.error(e.getMessage(), e);
        // do something
        return ApiResult.fail(e.getMessage());
    }

    /** * Exception */
    @ResponseBody
    @ExceptionHandler(Exception.class)
    public ApiResult exceptionHandler(Exception e) {
        log.error(e.getMessage(), e);
        return ApiResult.fail("Service is abnormal. Please try again later."); }}Copy the code

BusinessException and Exception are handled here. BusinessException is the base class of the convention BusinessException. If the Exception is thrown actively, it is generally required to be a subclass of BusinessException. Will be handled by the Business CeptionHandler. Other exceptions, possibly unexpected exceptions, are handled by exceptionHandler.

After unified processing, the following result is displayed:

Implement the HandlerExceptionResolver interface

@Slf4j
@Component
public class GlobalHandlerExceptionResolver implements HandlerExceptionResolver {

    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object o, Exception e) {
        log.error(e.getMessage(), e);
        ApiResult apiResult;
        if (e instanceof BusinessException) {
            BusinessException be = (BusinessException) e;
            // do something
            apiResult = ApiResult.fail(be.getMessage());
        } else {
            apiResult = ApiResult.fail("Service is abnormal. Please try again later.");
        }
        WebUtils.writeJson(response, apiResult);
        return null; }}Copy the code

This approach requires implementing the HandlerExceptionResolver interface and then injecting the implementation class into the Spring container.

But in the first way, with the @responseBody annotation, Spring returns json data for us, which we need to do ourselves.

The WebUtils utility class is implemented to return JSON data as follows:

public class WebUtils {

    private static final Logger log = LoggerFactory.getLogger(WebUtils.class);

    private static Gson gson = new GsonBuilder().serializeNulls().create();

    /** * returns json data **@param response
     * @param object
     */
    public static void writeJson(HttpServletResponse response, int status, Object object) {
        response.setHeader("Content-Type"."application/json; charset=UTF-8");
        response.setContentType("application/json; charset=UTF-8");
        response.setStatus(status);
        PrintWriter out = null;
        try {
            String data = object instanceof String ? (String) object : gson.toJson(object);
            out = response.getWriter();
            out.print(data);
            out.flush();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        } finally {
            if(out ! =null) { out.close(); }}}/** * returns json data **@param response
     * @param object
     */
    public static void writeJson(HttpServletResponse response, Object object) {
        writeJson(response, HttpServletResponse.SC_OK, object);
    }

    /** * returns json data **@param response
     * @param object
     */
    public static void writeJson(ServletResponse response, Object object) {
        if (response instanceofHttpServletResponse) { writeJson((HttpServletResponse) response, object); }}}Copy the code

Gson is used in the utility class and needs to be referenced:

<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
</dependency>
Copy the code

@ControllerAdvice + @ExceptionHandler

This method is similar to the first method, as follows:

@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {

    /** * BusinessException */
    @ResponseBody
    @ExceptionHandler(BusinessException.class)
    public ApiResult businessExceptionHandler(BusinessException e) {
        log.error(e.getMessage(), e);
        // do something
        return ApiResult.fail(e.getMessage());
    }

    /** * Exception */
    @ResponseBody
    @ExceptionHandler(Exception.class)
    public ApiResult exceptionHandler(Exception e) {
        log.error(e.getMessage(), e);
        return ApiResult.fail("Service is abnormal. Please try again later."); }}Copy the code

conclusion

All three methods work well for uniform exception handling, but the @Controlleradvice + @ExceptionHandler method is generally recommended to separate exception handling from business logic and to avoid handling Json data returns.

The source code

Github.com/zhuqianchan…

Review past

  • Build backend frameworks from scratch – keep updating