Only one step is needed to unify the Restful API return value format and handle exceptions in Spring Boot

Keywords: Java Spring

Unified Return Value

Today, with the separation of front-end and back-end, a unified return value format can not only make our interface look more beautiful, but also enable the front-end to handle a lot of things in a unified way, avoiding many problems.

The more general return value format is as follows:

public class Result<T> {
    // Interface call success or failure
    private Integer code = 0;
    // Specific code for failure
    private String errorCode = "";
    // Information to be transmitted, such as error messages
    private String msg;
    // Data to be transferred
    private T data;
    ...
}

The original interface is as follows:

    @GetMapping("/test")
    public User test() {
        return new User();
    }

When we need to unify the return values, we may use such a method:

    @GetMapping("/test")
    public Result test() {
        return Result.success(new User());
    }

This method does achieve the goal of unified interface return value, but several new problems arise:

  • The return value of the interface is not obvious, and the return value of the interface can not be seen at a glance.
  • Each interface requires additional code.

Fortunately, Spring Boot has provided us with a better solution. Just add the following code to the project to unify the global return value for us senselessly.

/**
 * Unified Encapsulation of Global Return Values
 */
@EnableWebMvc
@Configuration
public class GlobalReturnConfig {

    @RestControllerAdvice
    static class ResultResponseAdvice implements ResponseBodyAdvice<Object> {
        @Override
        public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
            return true;
        }

        @Override
        public Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
            if (body instanceof Result) {
                return body;
            }
            return new Result(body);
        }
    }
}


Our interface only needs to be written in the most primitive form.

    @GetMapping("/test")
    public User test() {
        return new User();
    }

Unified exception handling

When encapsulating return values uniformly, we do not consider the case when the interface throws an exception. When an exception is thrown by an interface, it must be unfriendly for the user to see the exception on the server directly, and it is impossible for us to try/catch every interface for processing. At this time, we only need to use the @ExceptionHandler annotation to deal with the exception universally and senselessly.

@RestControllerAdvice
public class GlobalExceptionHandler {

    private static final Logger LOG = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    /**
     * Global exception handling
     */
    @ExceptionHandler
    public JsonData handleException(HttpServletRequest request, HttpServletResponse response, final Exception e) {
        LOG.error(e.getMessage(), e);
        if (e instanceof AlertException) {//Anomalies that can be found at the front end of Alert
            if (((AlertException) e).getRetCode() != null) {//Predefined anomaly
                return new Result(((AlertException) e).getRetCode());
            } else {
                return new Result(1, e.getMessage() != null ? e.getMessage() : "");
            }
        } else {//Other anomalies
            if (Util.isProduct()) {//If it's a formal environment, uniform tips
                return new Result(RetCode.ERROR);
            } else {//Test environment, alert exception information
                return new Result(1, StringUtils.isNotBlank(e.getMessage()) ? e.getMessage() : e.toString());
            }
        }
    }

}

AlertException is our custom exception, so when errors need to be thrown in the business, you can manually throw AlertException.

These are the two steps of unified processing of return values and exception.

Posted by sureshp on Wed, 21 Aug 2019 05:28:37 -0700