Unified return and exception handling commonly used in Java projects

Keywords: Java Spring Spring Boot

First create a crud project.

controller calls service and mapper

The following is replaced by simple code

  • controller
    @GetMapping("/getUserById")
    public String getUserById(String id){
        String userById = userService.getUserById(id);
        return userById;
    }
  • service
    @Override
    public String getUserById(String id) {
        // Simulated service
        User user = userMapper.selectById(id);
        return user.toString();
    }

Although the above code returns the data to the foreground, it does not handle exceptions and does not have a clear identification to tell the front end whether it succeeded or failed. At this time, we need to encapsulate a unified success or failure flag to tell the foreground how to process the returned data.

A CommonResponse class is simply encapsulated using Lombok

@Data
public class CommonResponse {
    /**
     * The returned business code is used to judge success or failure
     * 200 success
     * 500 fail
     */
    private String code;

    /** describe */
    private String massage;

    /** describe */
    private Object date;

    public CommonResponse(String code, String massage, Object date) {
        this.code = code;
        this.massage = massage;
        this.date = date;
    }

    public static CommonResponse succeed(){
        return getCommonResponse(CodeEnum.SUCCESS.getCode(), CodeEnum.SUCCESS.getMassage(), null);
    }
    public static CommonResponse succeed(Object date){
        return getCommonResponse(CodeEnum.SUCCESS.getCode(), CodeEnum.SUCCESS.getMassage(), date);
    }
    public static CommonResponse succeed(String massage,Object date){
        return getCommonResponse(CodeEnum.SUCCESS.getCode(), massage, date);
    }

    public static CommonResponse error(String massage){
        return getCommonResponse(CodeEnum.ERROR.getCode(), massage, null);
    }
    public static CommonResponse error(String code,String massage){
        return getCommonResponse(code, massage, null);
    }
    public static CommonResponse error(){
        return getCommonResponse(CodeEnum.ERROR.getCode(), CodeEnum.ERROR.getMassage(), null);
    }

    public static CommonResponse getCommonResponse(String code, String massage, Object date){
        return new CommonResponse(code,massage,date);
    }
}

The returned controller uses a unified CommonResponse

    @GetMapping("/getUserById")
    public CommonResponse getUserById(String id){
        String userById = userService.getUserById(id);
        return CommonResponse.succeed(userById);
    }

return

{
    "code": "200",
    "massage": "success",
    "date": "User(id=1, username=Jiawen 00, password=1000000, age=5555)"
}

The above returns are basically in line with expectations, but what happens when an unknown exception occurs in the program.
service transformation

    @Override
    public String getUserByIdException(String id) {
        User user = userMapper.selectById(id);
        // Simulation service exception
        int i=5/0;
        return user.toString();
    }

controller transformation

@GetMapping("/getUserById")
    public CommonResponse getUserById(String id){
        try{
            String userById = userService.getUserById(id);
            return CommonResponse.succeed(userById);
        }catch(Exception e){
            e.printStackTrace();
            log.error(e.getMessage());
            return CommonResponse.error(e.getMessage());
        }
    }

The above can also be used to judge how to return through trycatch. However, a large number of try catch es will appear in the code. Isn't it very ugly? Can we add a unified try? The answer is yes.

Use the unified exception handling provided by spring
spring provides three exception capture methods, which I recommend

@Slf4j
@ControllerAdvice
public class ExceptionHandle {
    /**
     * Handle unknown exception
     * @param e
     * @return
     */
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public CommonResponse handleException(Exception e){
        log.error("System exception:{}",e.getMessage());
        return CommonResponse.error(e.getMessage());
    }

    /**
     * Handle self thrown custom exceptions
     * @param e
     * @return
     */
    @ExceptionHandler(BusinessException.class)
    @ResponseBody
    public CommonResponse handleBusinessException(BusinessException e){
 
        log.error("Custom exception:{}",e.getErrMassage());
        return CommonResponse.error(e.getErrCode(),e.getErrMassage());
    }
}

Stepped pit:

  1. This place encountered a pit when writing. Because the return value after catching an exception is CommonResponse, the annotation @ ResponseBody should be added to facilitate format conversion.
  2. Two identical exceptions cannot be configured when configuring @ ExceptionHandler. Otherwise, you will not know which to use and report an error.

At this time, the controller is clear as follows: only handle the business call, and there is no need to handle the upward thrown exception.

    @GetMapping("/getUserByIdException")
    public CommonResponse getUserByIdException(String id){
        String userById = userService.getUserByIdException(id);
        return CommonResponse.succeed(userById);
    }
    @GetMapping("/getUserByIdBusinessException")
    public CommonResponse getUserByIdBusinessException(String id){
        String userById = userService.getUserByIdBusinessException(id);
        return CommonResponse.succeed(userById);
    }

Of course, sometimes we will encounter our own verification failure and terminate the program. We can throw an Exception, or we need to customize the return code, or customize an Exception class. The following is a simple example. You can customize it according to your business needs.

  • Custom exception class BusinessException
@Data
public class BusinessException extends RuntimeException{

    private static final long serialVersionUID = 918204099850898995L;

    private String errCode;

    private String errMassage;

    public BusinessException(String errCode,String errMassage){
        super(errMassage);
        this.errCode = errCode;
        this.errMassage = errMassage;
    }
}

  • Custom exception returned by service
    @Override
    public String getUserByIdBusinessException(String id) {
        User user = userMapper.selectById(id);
        // Simulation service exception
        if("1".equals(id)){
            throw new BusinessException("400","id Query is not supported for data with value 1.");
        }
        return user.toString();
    }

At this point, we get the return from the front end

## request
http://localhost:8088/getUserByIdBusinessException?id=1
## return

{
    "code": "400",
    "massage": "id Query is not supported for data with value 1.",
    "date": null
}

The above is unified exception capture and unified return.

In addition, in our actual project, in order to make the business exceptions beautiful and unified, we can define an enumeration to store our business exception information.

  1. Define an enumeration class
public enum ExceptionEnum {

    BUSINESS_NOT_ONE("400","id Query is not supported for data with value 1"),
    ERR_NOT_LOOK("401","Too handsome to see")
    // Accumulate back
    ;

    private String code;
    private String desc;

    ExceptionEnum(String code, String desc) {
        this.code = code;
        this.desc = desc;
    }
    public void ThrowException(){
        ThrowException(code,desc);
    }
    public void ThrowException(String errMassage){
        errMassage = desc +":"+errMassage;
        ThrowException(code,errMassage);
    }
    private BusinessException ThrowException(String code,String desc){
        throw new BusinessException(code,desc);
    }
}

  1. An enumeration exception is thrown in the service
    @Override
    public String getUserByIdBusinessExceptionByEnumOne(String id) {
        User user = userMapper.selectById(id);
        // Simulation service exception
        if("1".equals(id)){
            ExceptionEnum.BUSINESS_NOT_ONE.ThrowException();
        }
        return user.toString();
    }
    @Override
    public String getUserByIdBusinessExceptionByEnumTwo(String id) {
        User user = userMapper.selectById(id);
        // Simulation service exception
        if("look".equals(id)){
            // Can dynamically splice exception information
            ExceptionEnum.ERR_NOT_LOOK.ThrowException("Are you right"+id);
        }
        return user.toString();
    }

  1. Foreground return
{
    "code": "400",
    "massage": "id Query is not supported for data with value 1",
    "date": null
}

{
    "code": "401",
    "massage": "Too handsome to see:Are you right look",
    "date": null
}

The advantage of this approach is to facilitate management and know at a glance how many errors and exceptions there are in your project. However, some students will find it hard to write like this. They have to enumerate each time they throw an exception java Tutorial Class. Therefore, this practice depends on the project and members.

The above is my humble opinion. If there is anything inappropriate, please leave a message for correction.

Posted by savedlema on Sun, 19 Sep 2021 19:33:01 -0700