Controller layer logic and optimization

Under the MVC architecture, our web project structure will be divided into three layers, from bottom to top are the dao layer, service layer and controller layer. The controller layer is the control layer, which mainly handles external requests and calls the service layer.

Generally speaking, the controller layer should not contain business logic. The functions of the controller should have the following five points:

(1) Receive the request and parse the parameters

(2) Business logic execution responds successfully

⑶.Exception handling

⑷. Convert business objects

⑸, call the Service interface

Ordinary writing

@RestController
public class TestController {
    
    

    @Autowired
    private UserService userService;

    @PostMapping("/test")
 public Result service(@Validated  @RequesBody  UserRequestBo requestBo) throws Exception {
    
    
        Result result = new Result();
        // 参数校验
        if (StringUtils.isNotEmpty(requestBo.getId())
                || StringUtils.isNotEmpty(requestBo.getType())
                || StringUtils.isNotEmpty(requestBo.getName())
                || StringUtils.isNotEmpty(requestBo.getAge())) {
    
    
            throw new Exception("必输项校验失败");
        } else {
    
    
            // 调用service更新user,更新可能抛出异常,要捕获
            try {
    
    
                int count = 0;
                User user = userService.queryUser(requestBo.getId());
                if (ObjectUtils.isEmpty(user)) {
    
    
                    result.setCode("11111111111");
                    result.setMessage("请求失败");
                    return result;
                }
                // 转换业务对象
                UserDTO userDTO = new UserDTO();
                BeanUtils.copyProperties(requestBo, userDTO);
                if ("02".equals(user.getType())) {
    
    // 退回修改的更新
                    count = userService.updateUser(userDTO)
                }else if ("03".equals(user.getType())) {
    
    // 已生效状态,新增一条待复核
                    count = userService.addUser(userDTO);
                }
                // 组装返回对象
                result.setData(count);
                result.setCode("00000000");
                result.setMessage("请求成功");
            } catch (Exception ex) {
    
    
                // 异常处理
                result.setCode("111111111111");
                result.setMessage("请求失败");
            }
        }
        return result;
    }
}

Optimization ideas

1. Call the Service layer interface.
Under normal circumstances, the controller, as the control layer, calls the service layer interface and should not contain any business logic. All business operations are implemented in the service layer, and the relevant code of the controller layer is removed.
The controller layer becomes:

@RestController
public class TestController {
    
    

@Autowired
private UserService userService;

@PostMapping("/test")
public Result service(@Validated  @RequesBody  UserRequestBo requestBo) throws Exception {
    
    
    Result result = new Result();
    // 参数校验
    if (StringUtils.isNotEmpty(requestBo.getId())
            || StringUtils.isNotEmpty(requestBo.getType())
            || StringUtils.isNotEmpty(requestBo.getName())
            || StringUtils.isNotEmpty(requestBo.getAge())) {
    
    
        throw new Exception("必输项校验失败");
    } else {
    
    
        // 调用service更新user,更新可能抛出异常,要捕获
        try {
    
    
         // 转换业务对象
            UserDTO userDTO = new UserDTO();
            BeanUtils.copyProperties(requestBo, userDTO);
            int count = userService.updateUser(userDTO);
            // 组装返回对象
            result.setData(count);
            result.setCode("00000000");
            result.setMessage("请求成功");
        } catch (Exception ex) {
    
    
            // 异常处理
            result.setCode("EEEEEEEE");
            result.setMessage("请求失败");
        }
    }
    return result;
}

2. Parameter verification
In fact, most parameter verification is to detect null or empty string, then we can use @NotBlank and other annotations. Add the @NotBlank annotation to the name attribute in the UserRequestBo class

优化后如下:
@Data
public class UserRequestBo {
    
    

    @NotBlank
    private String id;

    @NotBlank
    private String type;

    @NotBlank
    private String name;

    @NotBlank
    private String age;
}

controller层就变成了:
@RestController
public class TestController {
    
    

    @Autowired
    private UserService userService;

    @PostMapping("/test")
    public Result service( @Validated  @RequesBody  UserRequestBo requestBo) throws Exception {
    
    
        Result result = new Result();
        // 调用service更新user,更新可能抛出异常,要捕获
        try {
    
    
         // 转换业务对象
            UserDTO userDTO = new UserDTO();
            BeanUtils.copyProperties(requestBo, userDTO);
            int count = userService.updateUser(userDTO);
            // 组装返回对象
            result.setData(count);
            result.setCode("00000000");
            result.setMessage("请求成功");
        } catch (Exception ex) {
    
    
            // 异常处理
            result.setCode("EEEEEEEE");
            result.setMessage("请求失败");
        }
        return result;
    }
}

备注:@NotNull@NotBlank@NotEmpty的区别,也适用于代码中的校验方法
@NotNull: 平常用于基本数据的包装类(IntegerLongDouble等等),如果@NotNull 注解被使用在 String 类型的数据上,则表示该数据不能为 Null,但是可以为空字符串(“”),空格字符串(“ ”)等。

@NotEmpty: 平常用于 StringCollection集合、Map、数组等等,@NotEmpty 注解的参数不能为 Null 或者 长度为 0,如果用在String类型上,则字符串也不能为空字符串(“”), 但是空格字符串(“ ”)不会被校验住。

@NotBlank: 平常用于 String 类型的数据上,加了@NotBlank 注解的参数不能为 Null ,不能为空字符串(“”), 也不能会空格字符串(“ ”),多了一个trim()得到效果。(推荐使用)

3. Unified encapsulation of return objects.
Whether the business succeeds or fails in the code, the return objects need to be encapsulated. Currently, they are encapsulated wherever they are used in the code.

We can uniformly encapsulate the return object

After optimization, it is as follows:

@Data
public class Result<T> {
    
    

    private String code;

    private String message;

    private T data;

 // 请求成功,指定data
    public static <T> Result<T> success(T data) {
    
    
        return new Result<>(ResultEnum.SUCCESS.getCode(), ResultEnum.SUCCESS.getMessage(), data);
    }
    
 // 请求成功,指定data和指定message
    public static <T> Result<T> success(String message, T data) {
    
    
        return new Result<>(ResultEnum.SUCCESS.getCode(), message, data);
    }
    
 // 请求失败
    public static Result<?> failed() {
    
    
        return new Result<>(ResultEnum.COMMON_FAILED.getCode(), ResultEnum.COMMON_FAILED.getMessage(), null);
    }
    
 // 请求失败,指定message
    public static Result<?> failed(String message) {
    
    
        return new Result<>(ResultEnum.COMMON_FAILED.getCode(), message, null);
    }
    
    // 请求失败,指定code和message
    public static Result<?> failed(String code, String message) {
    
    
        return new Result<>(code, message, null);
    }
}

controller层就变成了:
@RestController
public class TestController {
    
    

    @Autowired
    private UserService userService;

    @PostMapping("/test")
    public Result service(@Validated  @RequesBody  UserRequestBo requestBo) throws Exception {
    
    
        // 调用service更新user,更新可能抛出异常,要捕获
        try {
    
    
         // 转换业务对象
            UserDTO userDTO = new UserDTO();
            BeanUtils.copyProperties(requestBo, userDTO);
            int count = userService.updateUser(userDTO);
            // 组装返回对象
            Result.success(count);
        } catch (Exception ex) {
    
    
            // 异常处理
            Result.failed(ex.getMessage());
        }
    }
} 


4. There are a lot of try-catch in the unified exception catching Controller layer and service, which are repeated codes and do not look elegant. You can add aspects to the controller layer methods to handle exceptions uniformly.

Use @ControllerAdvice annotation (@RestControllerAdvice is also available) to define the aspects of the controller layer. Method execution in the class annotated with @Controller will enter this aspect. At the same time, we can use @ExceptionHandler to capture and handle different exceptions. For captured exceptions, we can log and encapsulate the returned object.

After optimization, it is as follows:

// @RestControllerAdvice(basePackages = "com.ruoyi.web.controller.demo.test"), 指定包路径进行切面
// @RestControllerAdvice(basePackageClasses = TestController.class) , 指定Contrller.class进行切面
// @RestControllerAdvice 不带参数默认覆盖所有添加@Controller注解的类
@RestControllerAdvice(basePackageClasses = TestController.class)
public class TestControllerAdvice {
    
    

    @Autowired
    HttpServletRequest httpServletRequest;

    private void logErrorRequest(Exception e){
    
    
        // 组装日志内容
        String logInfo = String.format("报错API URL: %S, error = ", httpServletRequest.getRequestURI(), e.getMessage());
        // 打印日志
        System.out.println(logInfo);
    }

    /**
     * {@code @RequestBody} 参数校验不通过时抛出的异常处理
     */
    @ExceptionHandler({
    
    MethodArgumentNotValidException.class})
    public Result handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
    
    
        // 打印日志
        logErrorRequest(ex);
        // 组织异常信息,可能存在多个参数校验失败
        BindingResult bindingResult = ex.getBindingResult();
        StringBuilder sb = new StringBuilder("校验失败:");
        for (FieldError fieldError : bindingResult.getFieldErrors()) {
    
    
       sb.append(fieldError.getField()).append(":").append(fieldError.getDefaultMessage()).append(", ");
        }
        return Result.failed(ResultEnum.VALIDATE_FAILED.getCode(), sb.toString());
    }

    /**
     * 业务层异常,如果项目中有自定义异常则使用自定义业务异常,如果没有,可以和其他异常一起处理
     *
     * @param exception
     * @return
     */
    @ExceptionHandler(RuntimeException.class)
    protected Result serviceException(RuntimeException exception) {
    
    
        logErrorRequest(exception);
        return Result.failed(exception.getMessage());
    }

    /**
     * 其他异常
     *
     * @param exception
     * @return
     */
    @ExceptionHandler({
    
    HttpClientErrorException.class, IOException.class, Exception.class})
    protected Result serviceException(Exception exception) {
    
    
        logErrorRequest(exception);
        return Result.failed(exception.getMessage());
    }
}
controller层就变成了:

@RestController
public class TestController {
    
    

    @Autowired
    private UserService userService;

    @PostMapping("/test")
    public Result service( @Validated  @RequesBody  UserRequestBo requestBo) throws Exception {
    
    
        UserDTO userDTO = new UserDTO();
        BeanUtils.copyProperties(requestBo, userDTO);
        // 调用service层接口
        int count = userService.updateUser(userDTO);
        //组装返回对象
        return Result.success(count);
    }
}

5.
There may be many places in the business object conversion code to convert the same business object. The input parameter UserRequestBo can be converted into userDTO, which can be understood as a feature or ability of UserRequestBo. We can refer to the idea of ​​congestion mode and define it in UserRequestBo convertToUserDTO method, our purpose is to convert business objects. As for the conversion method, the caller does not care. We are currently using BeanUtils.copyProperties(). If one day we want to modify it to use Mapstruct for object conversion, we only need to modify UserRequestBo. The convertToUserDTO method is sufficient and does not involve modification of business code.

Optimized code:

@Data
public class UserRequestBo {
    
    

    @NotBlank
    private String id;

    @NotBlank
    private String type;

    @NotBlank
    private String name;

    @NotBlank
    private String age;

    /**
     * UserRequestBo对象为UserDTO
     * */
    public UserDTO convertToUserDTO(){
    
    
        UserDTO userDTO = new UserDTO();
        // BeanUtils.copyProperties要求字段名和字段类型都要保持一致,如果有不一样的字段,需要单独set
        BeanUtils.copyProperties(this, userDTO);
        userDTO.setType(this.getType());
        return userDTO;
    }
}
controller层就变成了:

@RestController
public class TestController {
    
    

    @Autowired
    private UserService userService;

    @PostMapping("/test")
    public Result service(@Validated  @RequesBody  UserRequestBo requestBo) throws Exception {
    
    
        return Result.success(userService.updateUser(requestBo.convertToUserDTO()));
    }
}

Guess you like

Origin blog.csdn.net/weixin_43952429/article/details/131718880