见过几千行代码的 controller吗我见过。见过全是 try catch 的 controller 吗我见过。见过全是字段校验的 controller 吗我见过。见过全是业务代码的 controller 吗不好意思我们公司很多业务写在 controller 的。看见这些我真的血压高。不优雅的 controllerRestController RequestMapping(/user/test) public class UserController { private static Logger logger LoggerFactory.getLogger(UserController.class); Autowired private UserService userService; Autowired private AuthService authService; PostMapping public CommonResult userRegistration(RequestBody UserVo userVo) { if (StringUtils.isBlank(userVo.getUsername())){ return CommonResult.error(用户名不能为空); } if (StringUtils.isBlank(userVo.getPassword())){ return CommonResult.error(密码不能为空); } logger.info(注册用户{} , userVo.getUsername()); try { userService.registerUser(userVo.getUsername()); return CommonResult.ok(); }catch (Exception e){ logger.error(注册用户失败{}, userVo.getUsername(), e); return CommonResult.error(注册失败); } } PostMapping(/login) PermitAll ApiOperation(使用账号密码登录) public CommonResultAuthLoginRespVO login(RequestBody AuthLoginReqVO reqVO) { if (StringUtils.isBlank(reqVO.getUsername())){ return CommonResult.error(用户名不能为空); } if (StringUtils.isBlank(reqVO.getPassword())){ return CommonResult.error(密码不能为空); } try { return success(authService.login(reqVO)); }catch (Exception e){ logger.error(注册用户失败{}, reqVO.getUsername(), e); return CommonResult.error(注册失败); } } }优雅的controllerRestController RequestMapping(/user/test) public class UserController1 { private static Logger logger LoggerFactory.getLogger(UserController1.class); Autowired private UserService userService; Autowired private AuthService authService; PostMapping(/userRegistration) public CommonResult userRegistration(RequestBody Valid UserVo userVo) { userService.registerUser(userVo.getUsername()); return CommonResult.ok(); } PostMapping(/login) PermitAll ApiOperation(使用账号密码登录) public CommonResultAuthLoginRespVO login(RequestBody Valid AuthLoginReqVO reqVO) { return success(authService.login(reqVO)); } }代码量直接减一半呀这还不算上有些直接把业务逻辑写在 controller 的看到这些我真的直接吐血改造流程校验方式这个 if 校验看得我哪哪都不爽。好歹给我写一个断言吧。Assert.notNull(userVo.getUsername(), 用户名不能为空);这不香吗确实不香。使用 spring 提供的Valid在入参时使用Valid注解并且在 vo 中使用校验注解如AuthLoginReqVO复制代码ApiModel(value 管理后台 - 账号密码登录 Request VO) Data NoArgsConstructor AllArgsConstructor Builder public class AuthLoginReqVO { ApiModelProperty(value 账号, required true, example user) NotEmpty(message 登录账号不能为空) Length(min 4, max 16, message 账号长度为 4-16 位) Pattern(regexp ^[A-Za-z0-9]$, message 账号格式为数字以及字母) private String username; ApiModelProperty(value 密码, required true, example password) NotEmpty(message 密码不能为空) Length(min 4, max 16, message 密码长度为 4-16 位) private String password; }Valid在SpringBoot中Valid是一个非常有用的注解主要用于数据校验。以下是关于Valid的一些详细信息为什么使用 Valid 来验证参数在编写接口时我们经常需要验证请求参数。通常我们可能会写大量的 if 和 if else 代码来进行判断。但这样的代码不仅不优雅而且如果存在大量的验证逻辑这会使代码看起来混乱大大降低代码可读性。为了简化这个过程我们可以使用 Valid 注解来帮助我们简化验证逻辑。Valid 注解的作用Valid 的主要作用是用于数据效验可以在定义的实体中的属性上添加不同的注解来完成不同的校验规则而在接口类中的接收数据参数中添加 valid 注解这时你的实体将会开启一个校验的功能。Valid 的相关注解在实体类中不同的属性上添加不同的注解就能实现不同数据的效验功能。使用 Valid 进行参数效验步骤整个过程如下用户访问接口然后进行参数效验因为 Valid 不支持平面的参数效验直接写在参数中字段的效验所以基于 GET 请求的参数还是按照原先方式进行效验而 POST 则可以以实体对象为参数可以使用 Valid 方式进行效验。如果效验通过则进入业务逻辑否则抛出异常交由全局异常处理器进行处理。Validated与Valid的区别Validated是Valid的变体。通过声明实体中属性的groups再搭配使用Validated就能决定哪些属性需要校验哪些不需要校验。全局异常处理这个全局异常处理可以根据自己的异常自定义异常处理并设置一个兜底的异常处理ResponseBody RestControllerAdvice public class ExceptionHandlerAdvice { protected Logger logger LoggerFactory.getLogger(getClass()); ExceptionHandler(MethodArgumentNotValidException.class) public CommonResultObject handleValidationExceptions(MethodArgumentNotValidException ex) { logger.error([handleValidationExceptions], ex); StringBuilder sb new StringBuilder(); ex.getBindingResult().getAllErrors().forEach(error - { String fieldName ((org.springframework.validation.FieldError) error).getField(); String errorMessage error.getDefaultMessage(); sb.append(fieldName).append(:).append(errorMessage).append(;); }); return CommonResult.error(sb.toString()); } /** * 处理系统异常兜底处理所有的一切 */ ExceptionHandler(value Exception.class) public CommonResult? defaultExceptionHandler(Throwable ex) { logger.error([defaultExceptionHandler], ex); // 返回 ERROR CommonResult return CommonResult.error(INTERNAL_SERVER_ERROR.getCode(), INTERNAL_SERVER_ERROR.getMsg()); } }就这么多搞定这样就拥有了漂流优雅的 controller 了在日常开发中还有那些血压飙升瞬间我拿出下图阁下如何面对这个阁下又如何面对我不说你能知道这个什么吗【狗头】总结不是很明白为什么有些喜欢在 controller 写业务逻辑的曾经有个同事问我就是喜欢在 controller 写业务的你这个接口写在那里我需要调一下你这个接口。我满脸问号不是隔壁的模块吗为什么要调我的接口直接引用的我的 service 去调方法就好了。这个就是痛点各写各的冗余代码一堆。曾经看到一个同事写一个保存的方法虽然逻辑挺多我滑动了好久都还没有方法还没有结束。一个方法整整几百行……看过 spring 源码都知道spring 源码难啃就是因为 spring 无限往下套娃基本每个方法干每个方法的事情。比如我保存用户时就只是保存用户至于什么校验丢给校验的方法处理什么发送消息丢给发送消息处理这些就不能耦合在一起。对于看到一些 if 下面一丢逻辑然后 if 再一丢逻辑看代码时很多情况不需要知道这个逻辑怎么实现的知道入参出参就大概这里做什么了。即使想知道详细情况点进去就知道了。突出这个当前方法要做的事情就好了。阿里的开发手册就推荐一个方法不能超过 80 行超过可以根据业务具体调整一下。