wordpress分站,广东网络推广运营,长域名的优秀网站,龙岗网络公司controller层设计 Controller 层逻辑
MVC架构下#xff0c;我们的web工程结构会分为三层#xff0c;自下而上是dao层#xff0c;service层和controller层。controller层为控制层#xff0c;主要处理外部请求。调用service层#xff0c;一般情况下#xff0c;contro…controller层设计
§ Controller 层逻辑
MVC架构下我们的web工程结构会分为三层自下而上是dao层service层和controller层。controller层为控制层主要处理外部请求。调用service层一般情况下controller层不应该包含业务逻辑controller的功能应该有以下五点
⑴、接收请求并解析参数
⑵、业务逻辑执行成功做出响应
⑶、异常处理
⑷、转换业务对象
⑸、调用 Service 接口
§ 普通写法
RestController
public class TestController {Autowiredprivate 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;}
}§ 优化思路
1、调用 Service 层接口
一般情况下controller作为控制层调用service层接口不应该包含任何业务逻辑所有的业务操作都放在service层实现把controller层相关代码去掉
controller层就变成了
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、参数校验
其实大多数的参数校验就是判空或者空字符串那么我们可以用NotBlank等注解。在UserRequestBo类中name属性上加上NotBlank注解
优化后如下
Data
public class UserRequestBo {NotBlankprivate String id;NotBlankprivate String type;NotBlankprivate String name;NotBlankprivate String age;
}controller层就变成了
RestController
public class TestController {Autowiredprivate 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平常用于 String、Collection集合、Map、数组等等NotEmpty 注解的参数不能为 Null 或者 长度为 0如果用在String类型上则字符串也不能为空字符串(“”), 但是空格字符串“ ”不会被校验住。
NotBlank平常用于 String 类型的数据上加了NotBlank 注解的参数不能为 Null 不能为空字符串(“”), 也不能会空格字符串“ ”多了一个trim()得到效果。
3、统一封装返回对象
代码中无论是业务成功或者失败都需要封装返回对象目前代码中都是哪里用到就在哪里进行封装
我们可以统一封装返回对象
优化后如下
Data
public class ResultT {private String code;private String message;private T data;// 请求成功指定datapublic static T ResultT success(T data) {return new Result(ResultEnum.SUCCESS.getCode(), ResultEnum.SUCCESS.getMessage(), data);}// 请求成功指定data和指定messagepublic static T ResultT 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);}// 请求失败指定messagepublic static Result? failed(String message) {return new Result(ResultEnum.COMMON_FAILED.getCode(), message, null);}// 请求失败指定code和messagepublic static Result? failed(String code, String message) {return new Result(code, message, null);}
}controller层就变成了
RestController
public class TestController {Autowiredprivate 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、统一的异常捕获
Controller层和service存在大量的try-catch都是重复代码并且看起来也不优雅。可以给controller层的方法加上切面来统一处理异常。用ControllerAdvice注解(RestControllerAdvice也可以)用来定义controller层的切面添加Controller注解的类中的方法执行都会进入该切面同时我们可以使用ExceptionHandler来对不同的异常进行捕获和处理对于捕获的异常我们可以进行日志记录并且封装返回对象。
优化后如下
// RestControllerAdvice(basePackages com.ruoyi.web.controller.demo.test), 指定包路径进行切面
// RestControllerAdvice(basePackageClasses TestController.class) , 指定Contrller.class进行切面
// RestControllerAdvice 不带参数默认覆盖所有添加Controller注解的类
RestControllerAdvice(basePackageClasses TestController.class)
public class TestControllerAdvice {AutowiredHttpServletRequest 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 {Autowiredprivate 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、转换业务对象
代码中可能有很多个地方转换同一个业务对象入参UserRequestBo可以转换为userDTO可以理解为这是UserRequestBo的一个特性或者能力我们可以参考充血模式的思想在UserRequestBo中定义convertToUserDTO方法我们的目的是转换业务对象至于使用什么方式转换调用方并不关心现在使用的BeanUtils.copyProperties()如果有一天想修改成使用Mapstruct来进行对象转换只需要修改UserRequestBo的convertToUserDTO方法即可不会涉及到业务代码的修改。
优化后代码
Data
public class UserRequestBo {NotBlankprivate String id;NotBlankprivate String type;NotBlankprivate String name;NotBlankprivate String age;/*** UserRequestBo对象为UserDTO* */public UserDTO convertToUserDTO(){UserDTO userDTO new UserDTO();// BeanUtils.copyProperties要求字段名和字段类型都要保持一致如果有不一样的字段需要单独setBeanUtils.copyProperties(this, userDTO);userDTO.setType(this.getType());return userDTO;}
}controller层就变成了
RestController
public class TestController {Autowiredprivate UserService userService;PostMapping(/test)public Result service(Validated RequesBody UserRequestBo requestBo) throws Exception {return Result.success(userService.updateUser(requestBo.convertToUserDTO()));}
}优化结束打完收工。