background

In order to make the format of the data content returned to the front end more consistent in the development process, we had better make a convention on the format of the returned data object in the development process, so that the convention in the development and docking process can be quickly achieved. This chapter will take you through how to design a unified return object and what you know about it.

Encapsulates the return result object

ResultVO object encapsulation

An interface developed through a RestFul interface generally contains the execution status of the interface (success, failure, failure description, successful data return object).

So we can define the structure of the returned object as follows:

@Data
@AllArgsConstructor
@NoArgsConstructor
@apiModel (value = "Return object for uniform request ")
public class ResultVO<T> {
    @apiModelProperty (value = "error code ")
    private Integer code;
    @apiModelProperty (value = "message ")
    private String msg;
    @apiModelProperty (value = "corresponding returned data ")
    private T data;
}
Copy the code

With a uniform return object defined, we generally need to consider the output and invocation of different scenarios; Such as success, failure, or other exceptions. If a failure operation is returned for some business reasons, it usually contains error codes and error information, and the winner contains the corresponding error stack exception details. Then we need to do better planning and design for error code;

Error code structure design

Here we design the following error code planning. First, we define an IErrorCode (the interface class of error code), which defines two methods, namely the interface method to get error code and error message, as follows

public interface IErrorCode {
    /** * description: error code *@date2020/11/21 * * /
    Integer getCode(a);
    /** * Description: Error message *@Author Hank
     **/
    String getMsg(a);
}
Copy the code

We define the interface class, and then we define the interface of ErrorCode to implement the enumeration; The following

public enum  ErrorCode implements IErrorCode {
    /*** * 1. To define the following error code, you need to communicate with the front-end in advance * 2. Error code Error code planning by module * 3. All error code enumeration classes need to implement error code interface class */
    SUCCESS(0."Operation successful"),
    SYSTEM_BUSY(10000."The system is busy, please try again later!"),
    FORM_VALIDATION_ERROR(10001."Form validation error"),
	// User login error code
    LOGIN_ERROR(101001."You haven't logged in yet. Please log in."),
    TOKEN_ERROR(101002."Login certificate has expired, please log in again");

   private Integer code;
   private String msg;

   ErrorCode(Integer code,String message){
       this.code=code;
       this.msg=message;
   }

    @Override
    public Integer getCode(a) {
        return code;
    }

    @Override
    public String getMsg(a) {
        returnmsg; }}Copy the code

A profound

Using this definition, we can use it in the corresponding method. We can return ResultVO directly; As follows:

@RestController
@RequestMapping(value = "/test")
@api (tags = "Basic module interface ")
public class IndexCtrl {

    @apiOperation (value = "hello interface ", Notes = "Hello interface description ")
    @GetMapping(value = "/index")
    public ResultVO hello(a){
        ResultVO rv=new ResultVO();
        rv.setCode(ErrorCode.SUCCESS.getCode());
        rv.setMsg(ErrorCode.SUCCESS.getMsg());
        returnrv; }}Copy the code

Is enhanced

As you can see from the above code, it’s a bit tedious to do such a return. It basically takes four lines of code to get a complete return, and the ErrorCode class is fixed.

We will try to modify the above code again, hoping to achieve the following effect:

  • Return objects can be used as simply as possible, or without feeling in development
  • Each module can define its own error code implementation class, which is integrated into the basic framework

ResultVO object enhancement

To achieve this, we first modify the ResultVO class to enrich the constructor and support the values passed by the enumeration method. The code looks like this:

@Data
@AllArgsConstructor
@NoArgsConstructor
@apiModel (value = "Return object for uniform request ")
public class ResultVO<T> {
    @apiModelProperty (value = "error code ")
    private Integer code;
    @apiModelProperty (value = "message ")
    private String msg;
    @apiModelProperty (value = "corresponding returned data ")
    private T data;

    public ResultVO(int code, String mesage) {
        setCode(code);
        setMsg(mesage);
    }
    
    public ResultVO(IErrorCode errorCode, T data) {
        setCodeMessage(errorCode);
        setData(data);
    }

    public ResultVO setCodeMessage(IErrorCode codeMessage) {
        setCode(codeMessage.getCode());
        setMsg(codeMessage.getMsg());
        return this; }}Copy the code

Encapsulation assistant tool

As mentioned above, we can also encapsulate a utility class RV for easy use:

public class RV {

    /*** * Successful return object *@param data
     * @return* /
    public static ResultVO success(Object data) {
        return new ResultVO(ErrorCode.SUCCESS,data);
    }

    /** * Failed return object *@Param: ErrCodeInterface
     * @return: [ResultVO]
     *
     **/
    public static ResultVO fail(IErrorCode errorCode) {
        return new ResultVO().setCodeMessage(errorCode);
    }

    /** * Description: Create a new object with the errorCode and data object parameters@param [errorCode, data]
     * @return: [ResultVO]
     **/
    public static ResultVO result(IErrorCode errorCode,Object data){
        return newResultVO(errorCode,data); }}Copy the code

Simplicity validation

Using the above application code as an example, the final code is modified as follows

@RestController
@RequestMapping(value = "/test")
@api (tags = "Basic module interface ")
public class IndexCtrl {

    @apiOperation (value = "hello interface ", Notes = "Hello interface description ")
    @GetMapping(value = "/index")
    public ResultVO hello(a){
        return RV.success(null);; }}Copy the code

Scalability verification

Here we describe the front so much, how to understand the so-called extensibility, here the so-called extensibility, more is in different business system, the expansion of the definition of error code, such as a, our project is divided into a number of different modules, but the realization of each module are encapsulated in a common packet tool depends on the foundation; For error codes, it is not possible to define all module error codes in common at once; So when we design the IErrorCode interface library class, we deliberately define the IErrorCode interface library class, which is implemented by ErrorCode by default.

In other words, the classes defined in the common package are not modified in special cases, so what can we do to define our own error codes in other business modules?

As follows, we only need to define our own error code enumeration class in the corresponding business module, such as the device management module

/** Java * new-retail * <p> * error code definition range 10101-10200 * </p> * @author Hank * @since 2020-11-21 */ public enum DeviceErrorCode implements IErrorCode {DEVICE_OFFLINE(10101," device is offline "), COMMAND_ERROR(10102," command error "); private Integer code; private String msg; DeviceErrorCode(Integer code,String message){ this.code=code; this.msg=message; } @Override public Integer getCode() { return code; } @Override public String getMsg() { return msg; }}Copy the code

Application in Controller

We defined the corresponding error enumeration class according to the device management module, which means that when doing the actual business of the module, we can directly throw the corresponding error code, without considering the adaptation of other modules. Its applications are as follows;

@PostMapping(value = "/restart")
public ResultVO restart(@RequestBody DeviceInfo deviceInfo) {
    load(deviceInfo);
    if(! deviceInfo.online()) {return RV.result(DeviceErrorCode.DEVICE_OFFLINE, deviceInfo);
    }
    return RV.success(deviceInfo);
}
Copy the code

Application in exceptions

In the previous section we covered the encapsulation of the BusinessException exception, but we simply inherited RuntimeException and didn’t go any further; Then we will combine the error code mentioned in this chapter to improve and enhance

/** * new-retail-lesson * <p> * Custom business exception class * </p> *@author Hank
 * @sinceThe 2020-10-31 * /
public class BusinessException extends RuntimeException {
    private int code;
    private String detailMessage;

    public BusinessException(int code, String message) {
        super(message);
        this.code = code;
        this.detailMessage = message;
    }

    public BusinessException(IErrorCode errorCode) {
        this(errorCode.getCode(),errorCode.getMsg());
    }

    public int getCode(a) {
        return code;
    }

    public String getDetailMessage(a) {
        returndetailMessage; }}Copy the code

From the above code, we can see that we added two variables code,detailMessage; In addition, we have diversified the constructor. It is worth noting that we have added the IErrorCode interface parameter in the constructor.

Since we have enhanced the exception, we also need to do the response processing in the exception interception. Find the global exception class GlobalExceptionHander defined before, and do the corresponding processing in the intercepting BusinessException, as follows

@RestControllerAdvice
public class GlobalException {
    /** * Description: Service exception interception *@param
     * @date 2020/10/31
     * @Author Hank
     **/
    @ExceptionHandler(value = BusinessException.class)
    public ResultVO businessException(BusinessException e){
        ResultVO rv= new ResultVO(e.getCode(),e.getDetailMessage());
        returnrv; }}Copy the code

With that groundwork done, let’s look at how it can be used in coding;

  1. When a business exception is thrown in the Controller, take our example, the code adjustment is as follows, instead of direct return, can directly throw the corresponding business exception, by GlobalExceptionHander to cover the line
@PostMapping(value = "/restart")
public ResultVO restart(@RequestBody DeviceInfo deviceInfo) {
    load(deviceInfo);
    if(! deviceInfo.online()) {throw new BusinessException(DeviceErrorCode.DEVICE_OFFLINE);
    }
    return RV.success(deviceInfo);
}
Copy the code
  1. In business code, the corresponding business exception can be thrown directly, and the GlobalException will take care of it

Of course, in the exception handling part, we can define different types of exceptions according to the needs of the scenario, and the structure is similar to the above to achieve the same effect

summary

Above we mainly introduced

  1. Encapsulation of the ResultVO object
  2. Then the design of uniform error code interface class and error enumeration class is introduced
  3. And combine the ResultVO object with the IErrorCode interface
  4. Combined with IErrorCode and custom exception, combined with global exception interception and ResultVO object to intercept global exception.
  5. Finally, it shows how exceptions can be extended by combining the defined ResultVO object with the IErrorCode interface class.

This is all part of this chapter. I hope you found it helpful. If you have different better practices in terms of uniform return object encapsulation and exception encapsulation, please leave a message in the comments section.