Location>code7788 >text

How to handle return values elegantly

Popularity:136 ℃/2024-09-19 11:22:16

We already know.How to elegantly calibrate incoming parametersSo how does the back-end server accomplish returning data to the front-end?

Return Format

The back-end returns to the front-end we generally use JSON body way, the definition is as follows:

{
    # Return the status code
    code:string, # return message description
    # Returns the message description
    message:string, #returns the message description
    # Returns the value
    data:object
}

CODE Status Code

Code returns the status code, which is generally added to whatever is needed at the time of development.

If the interface wants to return a user privilege exception, we add a status code of 101, and the next time we want to add a data parameter exception, we add a status code of 102. Although this can meet the business as usual, but the status code is too messy.

Here you can refer toAlibaba Development Manual The front-end and back-end protocols and the contents of the exception log.

U denotes the user, and the next 4 digits are numbered as
#U1000 to U1999 indicates user parameter related.
#U2000 to U2999 indicates user-related interface exceptions.
...

In this way, the front-end developer can know what the error is according to the status code after getting the return value, and then according to the description of the Message related information, it can be quickly located.

Message

This field is relatively simple to understand, and is how to be friendly when an error occurs. The general design is to design with Code status code, such as:

@Data
public class Result {

    private String retCode;
    private String retMsg;
 
    //Default Success Status Code
    public static final String SUCCESSCODE = "S0000";
    public static final String SUCCESSMSG = "successes";
    //Default Failure Status Code
    public static final String ERROR_CODE = "E2222";
    public static final String ERROR_MSG = "fail (e.g. experiments)";

    public static final String COMMENT_CODE = "E3333";
    public static final String RUNNING_ERROR_MSG = "run-time error (in computing),Please contact the administrator";

    private Result() {
        this(SUCCESSCODE, SUCCESSMSG);
    }
}

Define, again in the enumeration, the status code:

@Getter
public enum UserResultConstants implements ResultConstats{

    NOT_ID("U1001","User not entered correctlyid"),
    ADD_FAIL("U1002","Failed to add user"),
    UPDATE_FAIL("U1003","Failed to update user"),
    DELETE_FAIL("U1004","Failed to delete user"),
    USERNAME_EXIST("U1005","Username already exists"),
    USER_NOT_EXIST("U1006","The user does not exist")
    ;

    private String code;
    private String message;

    private UserResultConstants(String code, String message){
         = code;
         = message;
    }

}

The status code and the message will then correspond to each other, which is better maintained.

Data

Return data body, JSON format, according to different business and different JSON body.

We are going to design a return body class Result:

@Data
public class Result {

    private String retCode;
    private String retMsg;
    private Object data;

    public static final String SUCCESSCODE = "0000";
    public static final String SUCCESSMSG = "successes";
    public static final String ERROR_CODE = "2222";
    public static final String ERROR_MSG = "fail (e.g. experiments)";

    public static final String COMMENT_CODE = "3333";
    public static final String RUNNING_ERROR_MSG = "run-time error (in computing),Please contact the administrator";

    public Result() {
        this(SUCCESSCODE, SUCCESSMSG, null);
    }

    public Result(Object data) {
        this(SUCCESSCODE, SUCCESSMSG, data);
    }

    public Result(String retCode, String retMsg, Object data) {
         = retCode;
         = retMsg;
         = data;
    }

    public Result(String retCode, String retMsg) {
        this(retCode, retMsg, null);
    }
}

Control Layer Controller

We will process the business request at the Controller level and return it to the front-end, in the case of user management:

@RestController
@RequestMapping("/user")
@Slf4j
@Validated
@RequiredArgsConstructor
public class UserController {

    private final UserService userService;

    /**
     * Query all user information
     *
     * @return Return all user information
     */
	@GetMapping(value = "/list")
    public Result userList() {
        List<UserVo> users = ();
        Result result = new Result(users);
        ("userListThe queried user information is:{}", (users));
        return result;
    }
}

We see that after we get the users object, we use the Result constructor to wrap the assignment and return it. The@RestController annotationIt's @Controller and @ResponseBody in one!It is a REST style controller, which is more suitable for the current architecture of front-end and back-end separation, and returns JSON data format, indicating that it is a controller bean, and the return value of the function is filled directly into the HTTP response body.

Did you find that the constructor methods are not cumbersome to wrap like this, and could be optimized.

Aesthetic Optimization

We can add static methods to the Result class, at a glance:

@Data
public class Result {

    //...
    
    public static Result ok() {
        return new Result();
    }

    public static Result ok(Object data) {
        return new Result(data);
    }

    public static Result ok(String retCode, String retMsg) {
        return new Result(retCode, retMsg);
    }

    public static Result error(String retCode, String retMsg) {
        return new Result(retCode, retMsg);
    }

}

Remodel the Controller:

@RestController
@RequestMapping("/user")
@Slf4j
@Validated
@RequiredArgsConstructor
public class UserController {

    private final UserService userService;

    /**
     * Query all user information
     *
     * @return Return all user information
     */
    @GetMapping(value = "/list")
    public Result userList() {
        List<UserVo> users = ();
        ("userListThe queried user information is:{}", (users));
        return (users);
    }
}

Isn't the code more concise and aesthetically pleasing.

Elegant Optimization

Above we see the addition of static methods to the Result class, making the business processing code concise.

But have you noticed that there are several problems with this:

  • The return of each method is a Result wrapped object with no business implications.
  • In the business code, on success we call theException Error CallsIt's a lot of money. Isn't that redundant.

Our best way to return the real business object directly, preferably without changing the previous business approach, is as follows:

@RestController
@RequestMapping("/user")
@Slf4j
@Validated
@RequiredArgsConstructor
public class UserController {

    private final UserService userService;

    /**
     * Query all user information
     *
     * @return Return all user information
     */
    @GetMapping(value = "/list")
    public List<UserVo> userList() {
        List<UserVo> users = ();
        ("userListThe queried user information is:{}", (users));
        return users;
    }
}

This is the same as our usual code, which is very intuitive and returns the user object directly, so isn't that perfect.

And what's the realization plan?

Return Value Elegant Scheme Implementation

It's easy to optimize this code with the help of the SpringBoot-suppliedResponseBodyAdviceThat's enough.

ResponseBodyAdvice role: intercept the return value of the Controller method , unified processing of the return value / response body , generally used to unify the return format , encryption and decryption , signatures and so on.

Rewrite the return body

@RestControllerAdvice
@RequiredArgsConstructor
public class ResponseAdvice implements ResponseBodyAdvice<Object> {

    private final ObjectMapper objectMapper;

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return true;
    }

    @SneakyThrows
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        //in the event thatControllerDirect returnStringif (coming after a conditional clause),SpringBoot是Direct return,So we need to manually convertjson。
        if (body instanceof String) {
            return ((body));
        }
        //in the event that已经封装成Result(modal particle intensifying preceding clause),then there is no need to repackage the,Failure to do so will result in the appearance of multiple layers
        if (body instanceof Result) {
            return body;
        }
        return (body);
    }
}

The above code is to determine whether the return value packaging, if you need to pack directly. Here we only deal with the normal success of the packaging, if the method body reported an exception how to do?

Handling of unusual problems

At this point there is a problem, because there is no exception handling, when the called method once an exception occurs, then you can write a global exception to return the same format of Result through the global exception.

  • Defining Exception Classes
public class UserException extends RuntimeException {
    public UserException(String message) {
        super(message);
    }
}
  • global exception class (math.)
@RestControllerAdvice
@Slf4j
@Order(value = Ordered.HIGHEST_PRECEDENCE)
public class GlobalExceptionHandler {

    @ExceptionHandler(value = )
    public Result handlerUserException(UserException e, HttpServletRequest request) {
        String msg = ();
        ("requesting[ {} ] {} An error occurred in the parameter validation of the,error message:{}", (), (), msg, e);
        return (Result.COMMENT_CODE, msg);
    }
}

Rewrite Controller

@RestController
@RequestMapping("/user")
@Slf4j
@Validated
@RequiredArgsConstructor
public class UserController {

    private final UserService userService;

    /**
     * According to the useridQuery user information
     *
     * @param id subscribersid
     * @return 成功则返回subscribers信息
     */
    @GetMapping("/selectById")
    public UserVo selectById(@RequestParam(required = false) Integer id) {
        UserVo user = (id);
        ("selectByIdaccording toid:{}perform a search,查询的subscribers信息为:{}", id, (user));
        if (user == null) {
            throw new UserException("查询的subscribers不存在");
        }
        return user;
    }
}

To this return to the same format of the design of the idea is completed, is not yet simple and elegant.

About the Author.

From the front-line programmer Seven's exploration and practice, continuous learning iteration in the~

This article is included in my personal blog:https://

Public number: seven97, welcome to follow~