SpringBoot入门二

SpringBoot入门二

表单验证

表单验证,就是对用户的输入数据进行有效性检查

例如在添加某个实体Girl时,要满足其年龄必须大于18岁,否则不能提交

此时可以对Girl的age属性,使用@Min注解

    @Min(value = 18, message = "年龄未满18岁")
    private Integer age;

同时对于添加数据的方法,需添加:

  • @Valid - 表示验证的对象
  • BindingResult - 获取验证的结果

如下:

    @PostMapping(value = "/girls")
    public Girl girlAdd(@Valid Girl girl, BindingResult bindingResult){

        if (bindingResult.hasErrors()){
            System.out.println(bindingResult.getFieldError().getDefaultMessage());
            return null;
        }

        return girlReponsitory.save(girl);
    }

此时,如果我们添加一个age小于18的对象,此时控制台会输出年龄未满18岁

其它验证注解,参考Spring Boot 表单验证篇

Bean Validation 规范,运行时的数据验证框架。它是 JSR 303 规范,Hibernate Validator 实现了这套规范,并扩展了一些注解,如下:
@Null 被注释的元素必须为 null
@NotNull 被注释的元素必须不为 null
@Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Size(max, min) 被注释的元素的大小必须在指定的范围内
@Email 被注释的元素必须是电子邮箱地址
@Length 被注释的字符串的大小必须在指定的范围内
@NotEmpty 被注释的字符串的必须非空
@Range 被注释的元素必须在合适的范围内

AOP

AOP是一种编程范式,与语言无关,是一种编程设计思想。将通用逻辑从业务逻辑中分离出来

AOP统一处理请求日志

步骤:
1.在pom.xml中添加依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

2.创建处理文件HttpAspect,添加@Aspect@Component注解

扫描二维码关注公众号,回复: 887892 查看本文章
@Aspect
@Component
public class HttpAspect {

    @Before("execution(public * com.wz.girl.controller.GirlController.*(..))")
    public  void logBefore(){
        System.out.println("logBefore");
    }

    @After("execution(public * com.wz.girl.controller.GirlController.*(..))")
    public  void logAfter(){
        System.out.println("logAfter");
    }

}

上面的代码中,execution(public * com.wz.girl.controller.GirlController.*(..))是重复的,可以这样优化:

@Aspect
@Component
public class HttpAspect {

    //表示切点
    @Pointcut("execution(public * com.wz.girl.controller.GirlController.*(..))")
    public void log(){

    }

    @Before("log()")
    public  void logBefore(){
        System.out.println("logBefore");
    }

    @After("log()")
    public  void logAfter(){
        System.out.println("logAfter");
    }

}

此时,如果调用某个http://localhost:8083/girl/girls/1,控制台会输出:

logBefore
Hibernate: select girl0_.id as id1_0_0_, girl0_.age as age2_0_0_, girl0_.cup_size as cup_size3_0_0_ from girl girl0_ where girl0_.id=?
logAfter

3.使用日志输出

@Aspect
@Component
public class HttpAspect {

    private final  static Logger logger = LoggerFactory.getLogger(HttpAspect.class);

    //表示切点
    @Pointcut("execution(public * com.wz.girl.controller.GirlController.*(..))")
    public void log(){

    }

    @Before("log()")
    public  void logBefore(){
        logger.info("logBefore");
    }

    @After("log()")
    public  void logAfter(){
        logger.info("logAfter");
    }

}

此时输出日志的形式为:

2017-11-15 21:23:03.582  INFO 63856 --- [nio-8083-exec-1] com.wz.girl.aspect.HttpAspect            : logBefore
Hibernate: select girl0_.id as id1_0_0_, girl0_.age as age2_0_0_, girl0_.cup_size as cup_size3_0_0_ from girl girl0_ where girl0_.id=?
2017-11-15 21:23:03.683  INFO 63856 --- [nio-8083-exec-1] com.wz.girl.aspect.HttpAspect            : logAfter

4.记录Http请求

    @Before("log()")
    public  void logBefore(JoinPoint joinPoint){
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        //url
        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());

    }

    //获取返回的内容
    @AfterReturning(returning = "object", pointcut = "log()")
    public void doAfterReturning(Object object){
        logger.info("response={}", object);
    }

有某一个请求时,控制台输出为:

2017-11-15 21:38:26.536  INFO 64813 --- [nio-8083-exec-1] com.wz.girl.aspect.HttpAspect            : url=http://localhost:8083/girl/girls/1
2017-11-15 21:38:26.537  INFO 64813 --- [nio-8083-exec-1] com.wz.girl.aspect.HttpAspect            : method=GET
2017-11-15 21:38:26.537  INFO 64813 --- [nio-8083-exec-1] com.wz.girl.aspect.HttpAspect            : ip=0:0:0:0:0:0:0:1
2017-11-15 21:38:26.539  INFO 64813 --- [nio-8083-exec-1] com.wz.girl.aspect.HttpAspect            : class_method=com.wz.girl.controller.GirlController.girlFindOne
2017-11-15 21:38:26.539  INFO 64813 --- [nio-8083-exec-1] com.wz.girl.aspect.HttpAspect            : args=1
Hibernate: select girl0_.id as id1_0_0_, girl0_.age as age2_0_0_, girl0_.cup_size as cup_size3_0_0_ from girl girl0_ where girl0_.id=?
2017-11-15 21:38:26.627  INFO 64813 --- [nio-8083-exec-1] com.wz.girl.aspect.HttpAspect            : logAfter
2017-11-15 21:38:26.627  INFO 64813 --- [nio-8083-exec-1] com.wz.girl.aspect.HttpAspect            : response=Girl{id=1, cupSize='C', age=20}

统一异常处理

现在假设返回统一格式的信息,如下结构所示:

{
    "code": 0,
    "msg": "成功",
    "data": {
        "id": 6,
        "cupSize": "D",
        "age": 18
    }
}

所以先需要定义一个Result类,来表示返回的结果:

public class Result<T> {
    //错误码
    private Integer code;
    //错误信息
    private String msg;
    //数据
    private T data;
    //getter setter方法
    .....
}

//Result工具类
public class ResultUtil {

    public  static Result success(Object object){
        Result result = new Result();
        result.setCode(0);
        result.setMsg("成功");
        result.setData(object);
        return result;
    }

    public  static Result success(){
        return success(null);
    }

    public  static Result error(Integer code, String msg){
        Result result = new Result();
        result.setCode(code);
        result.setMsg(msg);
        return result;
    }

}

但如果调用某个方法抛出了某个异常,此时返回的数据格式,可能就会如下:

{
    "timestamp": 1510755505374,
    "status": 404,
    "error": "Not Found",
    "exception": "java.lang.Exception",
    "message": "你可能还在上小学",
    "path": "/girl/girls/getAge/4"
}

由此可见,需要一种统一处理异常的方式,来返回相同格式的数据,示例如下:

1.创建一个枚举,对应错误码和错误信息

public enum ResultEnum {

    UNKONW_ERROR(-1, "未知错误"),
    SUCCESS(0, "成功"),
    PRIMARY_SCHOOL(100, "你可能还在上小学"),
    MIDDLE_SCHOOL(101, "你可能在上中学")
    ;


    private Integer code;
    private  String msg;

    ResultEnum(Integer code, String msg){
        this.code = code;
        this.msg = msg;
    }

    public Integer getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }
}

2.自定义一个GirlException异常类,Spring只会捕捉RuntimeException异常,所以要继承RuntimeException

public class GirlException extends  RuntimeException{

    private Integer code;

    public GirlException(ResultEnum resultEnum) {
        super(resultEnum.getMsg());
        this.code = resultEnum.getCode();
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }
}

3.在某个方法抛出异常

    public void getAge(Integer id) throws Exception{
        Girl girl = girlReponsitory.findOne(id);
        Integer age = girl.getAge();
        if (age < 10){
            throw new GirlException(ResultEnum.PRIMARY_SCHOOL);
        }else if(age > 10 && age < 16){
            throw new GirlException(ResultEnum.MIDDLE_SCHOOL);
        }
    }

4.新建一个异常处理类ExceptionHandle,捕获异常,返回自定义结果。在这里统一处理异常

@ControllerAdvice
public class ExceptionHandle {

    private final static Logger logger = LoggerFactory.getLogger(ExceptionHandle.class);

    @ExceptionHandler(value = GirlException.class)
    @ResponseBody
    public Result handler(Exception e){
        if (e instanceof GirlException){
            GirlException girlException = (GirlException) e;
            return ResultUtil.error(girlException.getCode(), e.getMessage());
        }
        logger.error("[系统异常]{}", e);
        return ResultUtil.error(-1, "位置错误");
    }

}

@ControllerAdvice注解用来处理异常:

5.此时,如果调用某个接口抛出了异常,则会返回如下结构的json数据:

{
    "code": 101,
    "msg": "你可能在上中学",
    "data": null
}

单元测试

参考:

Service测试

Service测试,需添加@RunWith(SpringRunner.class)@SpringBootTest注解

@RunWith(SpringRunner.class)
@SpringBootTest
public class GirlServiceTest {

    @Autowired
    private GirlService girlService;

    @Test
    public void findOneTest(){
        Girl girl = girlService.findOne(1);
        Assert.assertEquals(new Integer(20), girl.getAge());
    }

}

Controller测试

创建单元测试还可以右键,有IDE来生成单元测试类:

这里写图片描述

需额外添加@AutoConfigureMockMvc注解

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class GirlControllerTest {

    @Autowired
    private MockMvc mvc;


    @Test
    public void girlList() throws Exception {
        mvc.perform(MockMvcRequestBuilders.get("/girls"))
                .andExpect(MockMvcResultMatchers.status().isOk())
        .andExpect(MockMvcResultMatchers.content().string("abc"));
    }

}

其它

教程:

猜你喜欢

转载自blog.csdn.net/winfredzen/article/details/78544338