springboot|springboot的web进阶知识(1)

以下笔记为慕课网上的springboot的web进阶课程学习完后写下来以后参考的。
代码可以参考教程老师的:https://gitee.com/liaoshixiong/girl
我的github里面有三个项目(没有专门的仓库,所以下载过程比较麻烦,你们还是下载老师的吧,有兴趣就留言我再创建,没有就不浪费了哈哈哈):girl:我自己在老师的基础上边看边修改的,还有个1和5版本是老师的;
https://github.com/momozpr/JavaCode/tree/com/springboot
1 表单验证
(1)将传入的参数封装在一个类中,在类中对变量进行条件限制
(2)标签如:@GeneratedValue、@NotBlank(message = “这个字段必传”)、@Min(value = https://www.it610.com/article/18, message = “未成年少女禁止入门”)、@Length()等
(3)在控制类中传入的类对象参数前面添加@Valid
(4)返回错误的信息,在控制层方法添加一个参数:BindingResult bindingResult
判断如下:注释的内容是其他测试输出的内容,请按自己需要的返回就行了

if(bindingResult.hasErrors()){ logger.info(bindingResult.getFieldError().getDefaultMessage()); //return 12/0; //return ResultUtils.error(1,bindingResult.getFieldError().getDefaultMessage()); return null; }

2 AOP的使用
AOP是spring的两大特性之一,所以一定要会使用。
添加日志输入请求信息:
(1) 创建一个切面类:添加@Aspect、@Component(引入容器,spring的另一个特性)
(2) 为了减少路径的重复,创建一个切面方法(路径为每个在GirlController下的方法)
如:
@Pointcut("execution(public * com.imooc.controller.GirlController.*(..))") public void log(){ }

(3) 定义日志输出对象
//使用log打印日志
private final static Logger logger = LoggerFactory.getLogger(HttpAspect.class);

(4) 添加访问方法前先执行的方法,如:
@Before("log()") public void doBefore(JoinPoint joinPoint){ ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); //url logger.info("url={}",request.getRequestURL()); //ip logger.info("ip={}",request.getRemoteAddr()); //method logger.info("method={}",request.getMethod()); //类方法 logger.info("class_method={}",joinPoint.getSignature().getDeclaringTypeName()+"."+joinPoint.getSignature().getName()); //参数 logger.info("args={}",joinPoint.getArgs()); }

控制台输出结果如下:
2019-01-22 10:19:41.015INFO 1412 --- [nio-8080-exec-2] com.imooc.aspect.HttpAspect: url=http://127.0.0.1:8080/girls/getAge/14 2019-01-22 10:19:41.015INFO 1412 --- [nio-8080-exec-2] com.imooc.aspect.HttpAspect: ip=127.0.0.1 2019-01-22 10:19:41.015INFO 1412 --- [nio-8080-exec-2] com.imooc.aspect.HttpAspect: method=GET 2019-01-22 10:19:41.015INFO 1412 --- [nio-8080-exec-2] com.imooc.aspect.HttpAspect: class_method=com.imooc.controller.GirlController.getAge 2019-01-22 10:19:41.015INFO 1412 --- [nio-8080-exec-2] com.imooc.aspect.HttpAspect: args=14

(5) 添加一个将结果输出在控制台的方法:返回的结果设置为Object,包含所有引用数据类型,如果参数写的是object而不是object.toString(),(toString()方法要在该类重写toString方法,输出才好看。),结果为null不会报错,反之则会在控制台报错。
@AfterReturning(returning = "object",pointcut = "log()") public void returnResult(Object object){ logger.info("response={}",object.toString()); }

3 统一异常处理
(1)创建自定义异常类,如:注意在springboot中创建的话最好继承RuntimeException
package com.imooc.exception; /** * @ClassName GirlException * @Description springboot只会对RuntimeException进行回滚,所以在自定义类的时候要继承RuntimeException * @Author zhoup * @Date 2019/1/2117:16 * @Version 1.0 **/ public class GirlException extends RuntimeException {private Integer code; public GirlException(Integer code,String message) { super(message); this.code = code; }public Integer getCode() { return code; }public void setCode(Integer code) { this.code = code; } }

【springboot|springboot的web进阶知识(1)】(2)创建异常处理类
package com.imooc.handle; import com.imooc.domain.Result; import com.imooc.enums.ResultEnums; import com.imooc.exception.GirlException; import com.imooc.utils.ResultUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; /** * @ClassName ExceptionHandle * @Description * @Author zhoup * @Date 2019/1/2117:24 * @Version 1.0 **/ @ControllerAdvice public class ExceptionHandle { private final static Logger logger = LoggerFactory.getLogger(ExceptionHandle.class); @ExceptionHandler(value = https://www.it610.com/article/Exception.class)//处理的都是Exception的子类,RuntimeException也是它的子类 @ResponseBody//异常返回给页面 public Result handle(Exception e){ if(e instanceof GirlException){ GirlException girlException = (GirlException) e; return ResultUtils.error(girlException.getCode(),girlException.getMessage()); }else{ logger.error("【系统异常】={}",e); return ResultUtils.error(ResultEnums.UNKNOW_ERROR.getCode(),ResultEnums.UNKNOW_ERROR.getMessage()); } } }

页面输出效果:
{ "code": 100, "message": "我猜你还在读小学吧", "data": null }

{ "code": -1, "message": "未知错误", "data": null }

补充结果输出工具类和异常输出枚举类:
package com.imooc.utils; import com.imooc.domain.Result; /** * @ClassName ResultYtils * @Description * @Author zhoup * @Date 2019/1/2116:44 * @Version 1.0 **/ public class ResultUtils {public static Result success(Object data){ Result result = new Result(); result.setCode(0); result.setMessage("成功"); result.setData(data); return result; }public static Result success(){ return success(null); }public static Result error(Integer code,String message){ Result result = new Result(); result.setCode(code); result.setMessage(message); result.setData(null); return result; } }

package com.imooc.enums; public enum ResultEnums { UNKNOW_ERROR(-1,"未知错误"), SUCCESS(0,"成功"), PRIMARY_ERROR(100,"我猜你还在读小学吧"), MIDDLE_ERROR(101,"我猜你还在读初中吧") ; private Integer code; private String message; ResultEnums(Integer code, String message) { this.code = code; this.message = message; }public Integer getCode() { return code; }public String getMessage() { return message; } }

4 单元测试
贴一个控制层的异常,觉得这个比较容易忘,参数比较复杂:控制层不是像service层一样直接注入service类就可以调用和判断,我们还需要判断路径访问和请求结果、返回内容等问题。
package com.imooc.controller; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; import static org.junit.Assert.*; @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("[{\"id\":1,\"cupSize\":\"A\",\"age\":12,\"money\":0.0},{\"id\":4,\"cupSize\":\"A\",\"age\":12,\"money\":0.0},{\"id\":5,\"cupSize\":\"A\",\"age\":35,\"money\":0.0},{\"id\":6,\"cupSize\":\"A\",\"age\":23,\"money\":0.0},{\"id\":7,\"cupSize\":\"C\",\"age\":23,\"money\":0.0},{\"id\":8,\"cupSize\":\"B\",\"age\":23,\"money\":0.0},{\"id\":9,\"cupSize\":\"A\",\"age\":23,\"money\":0.0},{\"id\":10,\"cupSize\":\"C\",\"age\":23,\"money\":0.0},{\"id\":11,\"cupSize\":\"C\",\"age\":14,\"money\":0.0},{\"id\":12,\"cupSize\":\"C\",\"age\":23,\"money\":150.0},{\"id\":13,\"cupSize\":\"C\",\"age\":23,\"money\":150.0},{\"id\":14,\"cupSize\":\"C\",\"age\":4,\"money\":150.0}]")); } }

service的方法的单元测试:直接生成测试方法比较方便(IDEA),在类或者方法的地方右键Go to (Test),自己添加测试的类。
package com.imooc.service; import com.imooc.domain.Girl; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import static org.junit.Assert.*; @RunWith(SpringRunner.class) @SpringBootTest public class GirlServiceTest { @Autowired private GirlService girlService; @Test public void findOne(){ Girl girl = girlService.findOne(10); Assert.assertEquals(new Integer(23),girl.getAge()); } }

当进行maven打包时,会自动运行我们创建的单元测试:mvn clean package; 当然在IDEA中不用使用命令行,直接找到maven视图就可以了,IDEA真的是超级强大,当然你也可以在打包时不选择测试: mvn clean package -Dmaven.test.skip=true(目测这个只能用命令行) 2019.1.23更新(新的慕课《SpringBoot 极速入门到整合》) 5 Json格式返回数据注解
当我们将json数据返回页面时,有时候我们可以对返回的数据进行处理,可以在pojo类中对字段进行加注解,(在类上面即整个类通用,单项则针对单项起作用),如密码项可以忽略,时间格式规范以及字段为空时忽略:
未加限制:
{ name: "zola", age: 17, birthday: "2019-01-23 02:27:52 下午" }

加了注解限制:(这里的格式是使用了工具类返回格式)
{ status: 200, msg: "OK", data: { name: "zola", age: 17, birthday: "2019-01-23 02:28:42 下午" }, ok: null }

附加工具类:来自https://github.com/leechenxiang/imooc-springboot-starter
package com.zpr.pojo; import java.util.List; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; /** * * @Title: LeeJSONResult.java * @Package com.lee.utils * @Description: 自定义响应数据结构 *这个类是提供给门户,ios,安卓,微信商城用的 *门户接受此类数据后需要使用本类的方法转换成对于的数据类型格式(类,或者list) *其他自行处理 *200:表示成功 *500:表示错误,错误信息在msg字段中 *501:bean验证错误,不管多少个错误都以map形式返回 *502:拦截器拦截到用户token出错 *555:异常抛出信息 * Copyright: Copyright (c) 2016 * Company:Nathan.Lee.Salvatore * * @author leechenxiang * @date 2016年4月22日 下午8:33:36 * @version V1.0 */ public class IMoocJSONResult {// 定义jackson对象 private static final ObjectMapper MAPPER = new ObjectMapper(); // 响应业务状态 private Integer status; // 响应消息 private String msg; // 响应中的数据 private Object data; private String ok; // 不使用public static IMoocJSONResult build(Integer status, String msg, Object data) { return new IMoocJSONResult(status, msg, data); }public static IMoocJSONResult ok(Object data) { return new IMoocJSONResult(data); }public static IMoocJSONResult ok() { return new IMoocJSONResult(null); }public static IMoocJSONResult errorMsg(String msg) { return new IMoocJSONResult(500, msg, null); }public static IMoocJSONResult errorMap(Object data) { return new IMoocJSONResult(501, "error", data); }public static IMoocJSONResult errorTokenMsg(String msg) { return new IMoocJSONResult(502, msg, null); }public static IMoocJSONResult errorException(String msg) { return new IMoocJSONResult(555, msg, null); }public IMoocJSONResult() {}//public static LeeJSONResult build(Integer status, String msg) { //return new LeeJSONResult(status, msg, null); //}public IMoocJSONResult(Integer status, String msg, Object data) { this.status = status; this.msg = msg; this.data = https://www.it610.com/article/data; }public IMoocJSONResult(Object data) { this.status = 200; this.msg ="OK"; this.data = https://www.it610.com/article/data; }public Boolean isOK() { return this.status == 200; }public Integer getStatus() { return status; }public void setStatus(Integer status) { this.status = status; }public String getMsg() { return msg; }public void setMsg(String msg) { this.msg = msg; }public Object getData() { return data; }public void setData(Object data) { this.data = data; }/** * * @Description: 将json结果集转化为LeeJSONResult对象 *需要转换的对象是一个类 * @param jsonData * @param clazz * @return * * @author leechenxiang * @date 2016年4月22日 下午8:34:58 */ public static IMoocJSONResult formatToPojo(String jsonData, Class clazz) { try { if (clazz == null) { return MAPPER.readValue(jsonData, IMoocJSONResult.class); } JsonNode jsonNode = MAPPER.readTree(jsonData); JsonNode data = https://www.it610.com/article/jsonNode.get("data"); Object obj = null; if (clazz != null) { if (data.isObject()) { obj = MAPPER.readValue(data.traverse(), clazz); } else if (data.isTextual()) { obj = MAPPER.readValue(data.asText(), clazz); } } return build(jsonNode.get("status").intValue(), jsonNode.get("msg").asText(), obj); } catch (Exception e) { return null; } }/** * * @Description: 没有object对象的转化 * @param json * @return * * @author leechenxiang * @date 2016年4月22日 下午8:35:21 */ public static IMoocJSONResult format(String json) { try { return MAPPER.readValue(json, IMoocJSONResult.class); } catch (Exception e) { e.printStackTrace(); } return null; }/** * * @Description: Object是集合转化 *需要转换的对象是一个list * @param jsonData * @param clazz * @return * * @author leechenxiang * @date 2016年4月22日 下午8:35:31 */ public static IMoocJSONResult formatToList(String jsonData, Class clazz) { try { JsonNode jsonNode = MAPPER.readTree(jsonData); JsonNode data = https://www.it610.com/article/jsonNode.get("data"); Object obj = null; if (data.isArray() && data.size() > 0) { obj = MAPPER.readValue(data.traverse(), MAPPER.getTypeFactory().constructCollectionType(List.class, clazz)); } return build(jsonNode.get("status").intValue(), jsonNode.get("msg").asText(), obj); } catch (Exception e) { return null; } } public String getOk() { return ok; } public void setOk(String ok) { this.ok = ok; }}

    推荐阅读