SpringBoot入门二

来源:互联网 发布:windows 2003安装ad域 编辑:程序博客网 时间:2024/06/05 20:40

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注解

@Aspect@Componentpublic 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@Componentpublic 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,控制台会输出:

logBeforeHibernate: 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@Componentpublic 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            : logBeforeHibernate: 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/12017-11-15 21:38:26.537  INFO 64813 --- [nio-8083-exec-1] com.wz.girl.aspect.HttpAspect            : method=GET2017-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:12017-11-15 21:38:26.539  INFO 64813 --- [nio-8083-exec-1] com.wz.girl.aspect.HttpAspect            : class_method=com.wz.girl.controller.GirlController.girlFindOne2017-11-15 21:38:26.539  INFO 64813 --- [nio-8083-exec-1] com.wz.girl.aspect.HttpAspect            : args=1Hibernate: 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            : logAfter2017-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,捕获异常,返回自定义结果。在这里统一处理异常

@ControllerAdvicepublic 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注解用来处理异常:

  • Spring中添加@ControllerAdvice增强Controller
  • Spring:异常统一处理的三种方式与Rest接口异常的处理
  • 使用Spring MVC的@ControllerAdvice注解做Json的异常处理

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

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

单元测试

参考:

  • SpringBoot单元测试
  • Spring Boot构建RESTful API与单元测试

Service测试

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

@RunWith(SpringRunner.class)@SpringBootTestpublic 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@AutoConfigureMockMvcpublic 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"));    }}

其它

教程:

  • Spring Boot进阶之Web进阶
原创粉丝点击