一、@Valid验证
场景:添加部门信息时,增加部门下的人数;满足一定人数可以被成功添加,否则返回NULL
//部门人数 @Min(value=18,message="不满足18个人,不能成立新部门!") private Integer count; //get/set 方法 @PostMapping(value="/saveDept") public DeptTemp saveDept(@Valid DeptTemp temp, //错误信息返回对象 BindingResult result){ if (result.hasErrors()) { //如果有错误信息,则打印 并返回 System.out.println(result.getFieldError().getDefaultMessage()); return null; } temp.setdName(temp.getdName()); temp.setLoc(temp.getLoc()); temp.setCount(temp.getCount()); return deptRespository.save(temp); }
由于,我们处理了验证不同过时的信息,如果验证不通过时会在控制台打印“自定义的验证信息,例如:不满足18个人,不能成立新部门!”
否则的话,此访问路径则会返回“500”错误,运行时异常;
在满足正确的条件下则可以被添加数据库,否则被拒之门外。
二、AOP统一处理日志
AOP是一种编程规范,与语言无关,是一种程序设计思想;
场景:记录下每次访问的请求
第一步:添加依赖
org.springframework.boot spring-boot-starter-aop
第二步:定义Aspect类
@Aspect @Component //该文件引用到spring 容器里 public class HttpAspect { @Before("execution(public * com.test.controller.DeptController.getList(..))") public void log(){ System.out.println("Aspect:log"); } }
该log方法将作用在getList方法上面,并且先于该方法之前打印出日志。
同样也会有After方法,方法同上。
//Spring自带的日志框架,底层实现是logback private final static Logger logger = LoggerFactory.getLogger(HttpAspect.class); @Pointcut("execution(public * com.test.controller.DeptController.getList(..))") public void log(){ } @Before("log()") public void doBefore(){ logger.info("Aspect:before:log"); } @After("log()") public void doAfter(){ logger.info("Aspect:after:log"); }
1.记录请求方法的url、method、ip等信息
@Before("log()") public void doBefore(JoinPoint joinPoint){ //请求的url ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); logger.info("url={}",request.getRequestURL()); //请求的method logger.info("method={}",request.getMethod()); //请求的ip logger.info("ip={}",request.getRemoteAddr()); //请求的方法 logger.info("class_method={}",joinPoint.getSignature().getDeclaringTypeName()+"."+joinPoint.getSignature().getName()); //请求的参数 logger.info("args={}",joinPoint.getArgs()); }
请求路径如下:http://localhost:8081/dept/getDept/2
2.获取返回的内容
@AfterReturning(returning = "object" , pointcut="log()") public void doAfterReturning(Object object){ logger.info("response={}",object); }
返回结果如下:
三、异常处理
场景:判断部门下的人数,小于10人,划分成一个小组;10-18建议划分小组;大于18,成立新小组。
在之前的添加部门的方法时有必输校验,如果没有填写该值则会报错,会触发后台异常;
解决方法:把异常信息返回在前台页面
第一步:改造添加部门方法
第二步:验证
1.非法输入参数
2.正常输入参数
这就带来一个问题,返回的格式不一致!这对接口调用方来说,很不方便,这个时候就需要定义个传输标准
例如:
##接口调用异常 { "code" : -1, "msg" : "XX必输", "data" : NULL } ##接口调用正常 { "code" : -1, "msg" : "XX必输", "data" : { "id" : "2", "dName" : "一部", "loc" : "北京", "count" : "20" } }
第一步:定义业务类
/** * version 1.0 * TODO http请求返回的最外层对象 * @param*/ public class ReturnMsg { /** 错误码 */ private Integer code; /** 提示的信息 */ private String msg; /** 具体的内容 */ private T data; //get/set 方法 }
第二步:定义返回成功、失败的公共方法
public class ReturnUtil { public static ReturnMsg success(Object obj){ ReturnMsg msg = new ReturnMsg(); msg.setCode(0); msg.setMsg("成功"); msg.setData(obj); return msg; } public static ReturnMsg success(){ return success(null); } public static ReturnMsg error(Integer code,String msg){ ReturnMsg returnMsg = new ReturnMsg(); returnMsg.setCode(code); returnMsg.setMsg(msg); return returnMsg; } }
第三步:改造新增时的方法
测试结果如下:
正确数据:
异常数据:
回到我们最一开始的业务场景,判断部门下的人数,小于10人,划分成一个小组;10-18建议划分小组;大于18,成立新小组。
常规做法:在service处理业务逻辑,进行if判断,但这个时候会带来一个问题:往往service不只是单纯处理的逻辑判断还会根据判断的结果进行更进一步的业务处理,这个时候就需要用到统一异常处理的类,来进行异常处理。
第一步:service定义方法
第二步:自定义Exception异常
@ControllerAdvice public class NoExceptionHandle { @ExceptionHandler(value = Exception.class) @ResponseBody public ReturnMsg handle(Exception e){ return ReturnUtil.error(100, e.getMessage()); } }
第三步:controller调用
@GetMapping(value = "/getNo/{no}") public void getNo(@PathVariable("no") Integer no) throws Exception{ deptService.getNo(no); }
调用结果如下:
现在已经基本完善,但还有一点不是很明确,就是返回的code,始终是100,这个时候对排查错误不是很友好,此时需要改造Exception
第一步:自定义异常类
//因为Spring框架只会对RuntimeException进行事务回滚 public class MyException extends RuntimeException{ private Integer code; public MyException(Integer code,String msg) { super(msg); this.code = code; } public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } }
第二步:改造service
第三步:改造handle
验证:
还需要进行改造的是:code和msg部分;方便统一管理
第一步:定义枚举
//定义code和msg public enum ResultEnum { UNKOWN_ERROR(-1,"未知错误"), SUCCESS(0,"成功"), LESS_TEAM(100,"人数不足,划分为一个小组!"), MID_TEAM(101,"人数过多,建议划分!"), NEW_TEAM(102,"人数过多,成立新小组") ; private Integer code; private String msg; //只需要get方法 }
第二步:改造MyException
第三步:改造service
结果同上。