Controller层代码优化

Controller层逻辑

controller层意为控制层,主要作用是用来处理外部请求,调用service层处理业务逻辑。所以一般情况下,controller层不应该包含业务逻辑,而是进行请求接收、参数解析、异常处理、调用service层、响应结果这一系列流程。

基础写法

@RestController
public class TestController {

    @Autowired
    private TestServiceI testServiceI;

    @PostMapping("/test")
    public Result testFun(@RequestBody TestBO testBO){
        Result result = new Result("1001","成功!");
        //参数校验
        if(StringUtils.isBlank(testBO.getId())
        || StringUtils.isBlank(testBO.getSize())
        || StringUtils.isBlank(testBO.getColor())){
            return new Result("1004","参数校验失败!");
        }

        try {
            //转换业务对象
            TestDTO testDTO = new TestDTO();
            BeanUtils.copyProperties(testBO,testDTO);
            int count = testServiceI.addTest(testDTO);

            //组装返回对象
            result.setResult(count);
        } catch (BeansException e) {
            result.setCode("1002");
            result.setMessage("请求失败!");
        }
        return result;
    }
}

在我刚开始学习和工作的时候,我只会这样写,后来随着工作和学习,逐渐觉得这样写有点臃肿,开始试着优化简洁自己的代码。

优化思路

1.参数校验

参数校验可以通过@NotBlank等注解来解决,不需要在代码里自己写校验的逻辑。

@Data
public class TestBO {
    @NotNull
    private String id;
    @NotNull
    private String size;
    @NotNull
    private String color;
}

这样一来controller层的逻辑就变成了这样。

@RestController
public class TestController {

    @Autowired
    private TestServiceI testServiceI;

    @PostMapping("/test")
    public Result testFun(@Validated @RequestBody TestBO testBO){
        Result result = new Result("1001","成功!");

        try {
            //转换业务对象
            TestDTO testDTO = new TestDTO();
            BeanUtils.copyProperties(testBO,testDTO);
            int count = testServiceI.addTest(testDTO);

            //组装返回对象
            result.setResult(count);
        } catch (BeansException e) {
            result.setCode("1002");
            result.setMessage("请求失败!");
        }
        return result;
    }
}

2.统一封装返回对象

通常在代码开发的过程中,各个接口的返回对象应该都是统一的,因此我们可以将Result类进行优化。

@Data
public class Result<T> {
    private String code;
    private T result;
    private String message;

    public Result(String code, String message, T result) {
        this.code = code;
        this.result = result;
        this.message = message;
    }

    // 请求成功,指定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 TestServiceI testServiceI;

    @PostMapping("/test")
    public Result testFun(@Validated @RequestBody TestBO testBO){

        try {
            //转换业务对象
            TestDTO testDTO = new TestDTO();
            BeanUtils.copyProperties(testBO,testDTO);
            int count = testServiceI.addTest(testDTO);

            //组装返回对象
            return Result.success(count);
        } catch (BeansException e) {
            return Result.failed(e.getMessage());
        }
    }
}

3.统一的异常捕获

接下来我们可以通过@RestControllerAdvice注解来定义controller层的切面,同时使用@ExceptionHandler来对异常进行捕获和处理。

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

    @Autowired
    private HttpServletRequest request;

    private void logErrorRequest(Exception e){
        //组装日志内容
        String logInfo = String.format("报错API URL: %S, error = ", request.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 TestServiceI testServiceI;

    @PostMapping("/test")
    public Result testFun(@Validated @RequestBody TestBO testBO){
        //转换业务对象
        TestDTO testDTO = new TestDTO();
        BeanUtils.copyProperties(testBO,testDTO);
        int count = testServiceI.addTest(testDTO);

        //组装返回对象
        return Result.success(count);
    }
}

4.转换业务对象

在代码中可能有很多地方需要进行业务对象的转换,比如TestBO转为TestDTO,可以理解为这是TestBO的一种能力,我们可以参考充血模式的思想,在TestBO内部定义转换方法。

@Data
public class TestBO {
    @NotNull
    private String id;
    @NotNull
    private String size;
    @NotNull
    private String color;

    /**
     * TestBO对象为TestDTO
     * */
    public TestDTO convertToTestDTO(){
        TestDTO testDTO = new TestDTO();
        BeanUtils.copyProperties(this, testDTO);
        return testDTO;
    }
}

这样一来,我们就可以把controller的代码写成:

@RestController
public class TestController {

    @Autowired
    private TestServiceI testServiceI;

    @PostMapping("/test")
    public Result testFun(@Validated @RequestBody TestBO testBO){
        return Result.success(testServiceI.addTest(testBO.convertToTestDTO()));
    }
}

本文意在提供一种优化的思想,但是一味的追求代码的简洁也是不可取的,一定要保证代码的可读性再考虑优化的问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值