一:Valid表单验证
作用:在参数(例如girl类)前添加注解 @Valid 表示对这个参数进行验证
而需要验证的条件则是写在参数中(girl中的年龄等属性),而验证结果则是由bindingResult对象返回:
实体类:
@Min(value = 18,message = "此女生未成年")
private Integer age;
更多限制属性注解:http://blog.java1234.com/blog/articles/336.html
(网上找的大佬总结)
Controller类:
/**调用时直接可以传age和cupSize参数即可,valid验证实体类*/
@PostMapping(value = "/girls")
public girl insert(@Valid girl girl, BindingResult bindingResult){
/**获取验证结果,如果有错误,输出错误*/
if(bindingResult.hasErrors()){
/**简单输出错误信息,可用枚举深入设置*/ System.out.println(bindingResult.getFieldError().getDefaultMessage());
return null;
}
girl.setAge(girl.getAge());
girl.setCupSize(girl.getCupSize());
return girlJPA.save(girl);
}
二:AOP处理请求
AOP这种程序设计思想不是只局限于Java,.net,c#等都有
而AOP的思想,在我看来就是,面向切面编程,意思就是将程序的公共逻辑代码提取出来构成一个切面
(tip:http请求发送到Controller中,由spirng容器注入的bean实例化的时候,因为程序启动之时,这些被注入的bean(类 )已经实例化了,所以不会执行我们Controller类里的构造方法)
/**maven导包*/
<dependency>
<groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
例如有一个场景,在Controller类中每个mapping业务需求都需要采集访问接口的ip地址、端口等信息,与其在每个mapping中写一样的逻辑代码,倒不如把这些逻辑看作一个公共部分,每个mapping都会走,直接单独提取出来(这里也可强调了代码的复用性和不冗余性),这时候AOP就起到了关键性的作用
具体操作:
1:定义一个单独的AOP类,类前添加注解 @Aspect和 @Component
2:因为逻辑有请求前的逻辑代码也有请求后的,所以有多个方法,也就是有多个注解(例如 @Before和 @After)
但是Before和After里我们如果都需要获取到前后的ip等信息作为对比呢,这时候直接的想法是两个方法都通过RequestContextHolder.getRequestAttributes().getRequest()获取到的httpServletRequest来得到,明显的,代码又重复了,本着有重复代码就优化的原则(这个原则蛮重要),我们可以在这里这么做
定义一个方法
//代表GirlController这个控制器里的方法都会被拦截并且在这里做出处理
@Pointcut("execution(public * com.Controller.*(..))")
//括号里表示Controller类里的所有方法(任何参数)都进行拦截
public void log(){ [1] }
@Before("log()")
然后在注解里添加log()方法,@Pointcut就是切点注解了
而在[1]中也就可以做一些事了
三:统一异常处理
在公司实习的过程中,不难发现,后端大佬,都是在接口返回参数中,有一个统一的标准,例如这样:
{
"success": true,
"resultMessage": "",
"resultCode": "0000",
"result": []
}
json格式
这样的好处在于,当因为前后端分离时,前端人员拿到你这个接口,立马能清楚的知道返回信息而直接撸他的代码
所以这种统一的异常处理,讲真,现在不学,到了工作岗位,迟早要学,而且事半功倍,何乐而不为呢
所以这里也制定了下异常处理统一格式
{
“code”: 0,
“msg”: “”,
“data”: null
}
既然这样指定了格式,如果一个个参数的new然后传,未免显得很Low,肯定得创建一个Result类进行规范,而data方面因为参数类型千奇百怪,所以泛型肯定是要有的
public class Result<T> {
/** 错误码. */
private int code;
/** 提示信息. */
private String msg;
/** 具体信息. */
private T data;
//get和set方法略
这里提一个问题,如果让你判断一个女孩的年龄(写在service层),如果小于10岁上小学,大于10小于16上初中,这两个条件因为太小了直接pass然后直接返回信息,大于16岁有加钱等操作,这样该怎么做呢
如果单纯返回给Controller1、2、3这样的然后再在Controller方法中进行if判断,未免显得繁琐,所以这里我们需要统一异常判断
不过Exception在抛异常的时候只能返回message,所以如果要加入错误码code,需要写自己的Exception
(这里注意继承的是RuntimeException,因为如果继承Exception,spring框架只对RuntimeException抛出的异常进行回滚)
public class GirlException extends RuntimeException {
private int code;
public GirlException(ResultEnum resultEnum) {
super(resultEnum.getMsg());
this.code = resultEnum.getCode();
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
}
而抛出的异常中,错误码目前是我们自己定义的,比如
错误码code(例如100) ———对应—— 你可能还在上小学吧
这样到后期,等涉及的错误异常越来越多,非常不利于管理和记录,所以枚举是必不可少的
public enum ResultEnum {
UNKNOWN_ERROR(-1,"未知错误"),
SUCCESS(100,"成功"),
PRIMARY_SCHOOL(101,"你可能还在上小学吧"),
MIDDLE_SCHOOL(102,"初中生吧"),
PROPERTY_ERROR(103,"自定义")
;
private int code;
private String msg;
ResultEnum(int code, String msg) {
this.code = code;
this.msg = msg;
}
public int getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
调用时直接ResultEnum.PRIMARY_SCHOOL表示括号里的内容,而ResultEnum.PROPERTY_ERROR.getCode()表示具体的code
四:单元测试
Idea 单元测试可以直接右键方法选择Go To –> Test –> 创建一个Test
然后编写单元测试 右键Run
不过进行单元测试的类上要添加注解 @RunWith(SpringRunner.class) 和 @SpringBootTest
特殊的对api(Controller类)进行测试时,
使用三个注解 @RunWith(SpringRunner.class)
@SpringBootTest @AutoConfigureMockMvc
@Autowired
private MockMvc mvc;
@Test
public void getGirls() throws Exception {
mvc.perform(MockMvcRequestBuilders.get("/girls"))
.andExpect(MockMvcResultMatchers.status().isOk());
//判断以get方式对girls这个路径返回的status进行测试
}