Spring Boot 进阶 Web

表单验证

 在领域模型girl中(也就是有getset的类中)声明如下:
 @Min(value = 18, message = "未成年少女禁止入门")
 private Integer age;
 在控制器的验证方法中加入:
 @PostMapping(value = "/girls")
    public Result girlAdd(@Valid Girl girl, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            return ResultUtil.error(1, bindingResult.getFieldError().getDefaultMessage());
        }
 
        girl.setCupSize(girl.getCupSize());
        girl.setAge(girl.getAge());
 
        return ResultUtil.success(girlRepository.save(girl));
    }

@valid用于验证参数的正确性,BindingResult对象用来显示错误信息!

使用AOP处理请求

 AOP是一种编程范式
与语言无关,是一种程序设计思想。
面向切面(AOP)Aspect Oriented Programming
面向对象(OOP)Object Oriented Programming
面向过程(POP)Procedure Oriented Programming
面向过程到面向对象  换个角度看世界,换个姿势处理问题  将通用逻辑从业务逻辑中分离出来

使用方法:
引入依赖 starter-aop
pom.xml

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
创建aspect包(放处理文件,包名随意,自己明白就好)
然后在包内新建java类 在类前加上@Aspect @Component注解
然后在类中方法前加上以下代码
@Before("execution(public * com.hx.girl.GirlController.*(..))")   
拦截GirlController中的所有方法,在方法运行前运行被Before注释的代码
execution()是最常用的切点函数,其语法如下所示:
 整个表达式可以分为五个部分:
 1、execution(): 表达式主体。
 2、第一个*号:表示返回类型,*号表示所有的类型。
 3、包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,com.sample.service.impl包、子孙包下所有类的方法。
 4、第二个*号:表示类名,*号表示所有的类。
 5、*(..):最后这个星号表示方法名,*号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数。

 

@Pointcut 注解中填写的内容与 @Before @After 两注解是一样的
为了避免代码重复书写,定义一个公用方法,@Pointcut注解声明切入点
@Before @After 两注解直接复用该方法切入点

@Aspect
@Component
public class HttpAspect {
    private final static Logger logger= LoggerFactory.getLogger(HttpAspect.class); //Logger引用org.slf4j 是Spring自带的日志框架

    @Pointcut("execution(public * com.example.project.web.CustomerContorller.*(..))")
    public void log(){
    }
	
    @Before("log()")
    public void doBefore(){
        logger.info("run before");
    }
	
    @After("log()")
    public void doAfter(){
        logger.info("run after");
    }

}

用于记录http请求的信息的代码:

// 请求参数
ServletRequestAttributes attributes = 
(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
// 请求对象
javax.servlet.http.HttpServletRequest request = attributes.getRequest();
// url
request.getRequestURL();
// method
request.getMethod();
// ip
request.getRemoteAddr();
// doBefore(JoinPoint joinPoint)
joinPoint.getSignature().getDeclaringTypeName() // 类名
joinPoint.getSignature().getName() // 类方法名
// 参数
joinPoint.getArgs()
//获取返回的内容
@AfterReturning(returning = "object", pointcut = "log()") 
public void doAfterReturning(Object object){
	logger.info("response={}",object.toString());	//添加toString()方法用于详细打印
}
写代码时发现重复时,即时优化。
写一个工具类,ResultUtil,成功时,传入data返回;失败时传入失败码和错误信息,返回
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;
	}

}
写ExceptionHandle类来捕获异常,同时使用ResultUtil的方法使数据格式统一。
@ControllerAdvice
public class ExceptionHandle{
	
	@ExceptionHandle(value = Exception.class)
	@ResponseBody
	public Result handle(Exception e){
		return ResultUtil.error(100,e.getMessage());
	}
	
}	
当需要对不同错误设置不同error code时,Exception就无法满足,于是就需要自定义一个Exception
spring只对RuntimeException才会进行事务回滚。
public class GirlException extends RuntimeException{

	private Integer code;
	
	public GirlException(Integer code, String message){
		super(message);
		this.code = code;
	}
	
	...getset方法
}
@ControllerAdvice
public class ExceptionHandle{
	
	@ExceptionHandle(value = Exception.class)
	@ResponseBody
	public Result handle(Exception e){
		if(e instanceof GirlException){
			GirlException ge = (GirlException) e;
			return ResultUtil.error(ge.getCode(),ge.getMessage());
		}else{
			return ResultUtil.error(-1,"未知错误");
		}
	}
}

当抛的异常属于未知错误时,就会不知道具体错误信息,这时候就可以用日志将其打印记录下来。

@ControllerAdvice
public class ExceptionHandle{
	
	private final static Logger logger= LoggerFactory.getLogger(ExceptionHandle.class);
	
	@ExceptionHandle(value = Exception.class)
	@ResponseBody
	public Result handle(Exception e){
		if(e instanceof GirlException){
			GirlException ge = (GirlException) e;
			return ResultUtil.error(ge.getCode(),ge.getMessage());
		}else{
			logger.error("[系统异常]{}",e);
			return ResultUtil.error(-1,"未知错误");
		}
	}
}

ResultEnum 统一管理异常码和异常信息

public enum ResultEnum{
	UNKOWN_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;
	}
	
	...get方法
	
}
GirlException(resultEnum) 构造方法传枚举就可以了
易读,便于管理

总结一下:
1、对外统一的Result  一些公司的工作前端做前端,后端做后端,所以后端需要提供统一格式的输出,才好让前端调用
2、抛出异常,统一处理,包装成Result  这样即便有异常也不会影响前端工程师的使用
3、因为原始的异常只有message,我们还需要异常码,所以要有一个自定义的异常类,方便个性化设置
4、统一处理时,从自己定义的异常中取出异常码和信息,用ResultUtil进行处理,得到Result


单元测试

测试GirlService 在test包中创建测试类 如果是用Idea编辑可以用右键GO TO ->Test 然后选择要测试的方法进行快速创建测试类
@RunWith(SpringRunner.class)
@SpringBootTest
public class GrilServiceTest {

	@Autowired
	private GrilService grilService;
	
	@Test
	public void findOneTest(){
		Girl girl = girlService.findOneTest(3);
		Assert.assertEquals(new Integer(13),gril.getAge());
	}
}
可以右键代码空白区域,选择Run "GrilServiceTest"测试整个类
或者右键要测试的方法,选择Run "findOneTest()"测试这个方法

测试controller 如果还用上面的那个方式测试就无法测试url的请求

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class GrilController {
	@Autowired
	private MockMvc mvc;
	@Test
	public void girlList() throws Exception {
		mvc.perform(
				MockMvcRequestBuilders.get("/girls"))	//请求的url 如果请求是post,就改为.post("/girls"))
		.andExpect(MockMvcResultMatchers.status().isOk())	//期望返回状态码是200
		.andExpect(MockMvcResultMatchers.content().string("hi"));	//期望返回内容是hi
	}
}
在使用项目打包时,就会进行单元测试
mvn clean package
打包项目时跳过单元测试:
mvn clean package -Dmaven.test.skip=true


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值