项目框架
参数校验
why
在日常的接口开发中,为了防止非法参数对业务造成影响,经常需要对接口的参数做校验
what
springboot validator参数校验遵循jsr-303规范(java specification requests)
example
@Data
public class NestClass {
private String name;
@NotNull(message = "参数不能为空")
@Valid
private Company company;
@Min(value = 5,message = "男性每天工作不得低于5小时",groups = Male.class)
@Min(value = 10,message = "女性每天工作不得低于10小时",groups = Female.class)
private Integer workTime;
@Data
public static class Company{
@Min(value = 10,message = "年龄最小不能小于10岁")
private Integer age;
}
public interface Male{}
public interface Female{}
}
@LogAspect
@PostMapping("nested")
@CosmoController
public String selectNested(@RequestBody @Validated(NestClass.Male.class) NestClass nestClass){
log.info("com.example.demo.controller-->getNested::nestClass = [{}]",nestClass);
return "1234";
}
这个例子中包含普通、分组、嵌套校验。
分组校验
使用场景:同一个对象在不同场景的需求不同。
workTime字段根据male和female进行了不同的要求,可以通过male和female类根据场景进行应用。
嵌套校验
当对象中还包含对象时对内部对象进行使用,上述例子中对NestClass对象中的Company对象的age字段进行校验
异常处理
全局异常处理,异常情况被拦截,给予特殊响应封装
@RestControllerAdvice
@Slf4j
@CosmoController
public class GlobalExceptionHandler {
@ExceptionHandler(ConstraintViolationException.class)
public BaseResult getConstraintViolationException(ConstraintViolationException e){
log.info("com.example.demo.exception-->getConstraintViolationException::e = [{}]",e.getMessage());
return new BaseResult(ResultEnum.ARGUMENTS_NOT_VALIDATE,e.getMessage());
}
/**
**@author xuweiqiang
*@time 2023/3/15 16:52
*校验异常进入此方法
**/
@ExceptionHandler(MethodArgumentNotValidException.class)
public BaseResult getMethodArgumentNotValidException(MethodArgumentNotValidException e){
log.info("com.example.demo.exception-->getMethodArgumentNotValidException::e = [{}]",e.getMessage());
BindingResult bindingResult = e.getBindingResult();
List<ObjectError> allErrors = bindingResult.getAllErrors();
log.info("allErrors:{}",allErrors);
StringBuffer stringBuffer = new StringBuffer();
allErrors.forEach(error -> stringBuffer.append(error.getDefaultMessage()));
return new BaseResult(ResultEnum.ARGUMENTS_NOT_VALIDATE,stringBuffer);
}
@ExceptionHandler(BindingException.class)
public BaseResult getBindException(BindingException e){
log.info("com.example.demo.exception-->getBindException::e = [{}]",e);
return new BaseResult(ResultEnum.ARGUMENTS_NOT_VALIDATE,e.getMessage());
}
@ExceptionHandler(BusinessException.class)
public BaseResult getBusinessException(BusinessException e){
log.info("进入BusinessException的异常处理页面,BusinessException:{}",e.getMessage());
return BaseResult.failure(e.getErrorMessage(),e.getErrorCode());
}
@ExceptionHandler(Exception.class)
public BaseResult getException(Exception e){
log.info("进入Exception的异常处理页面,exception:{}",e.getMessage());
return BaseResult.failure(ResultEnum.SERVER_ERROR,e.getMessage());
}
/**
* 后端无法正常解析前端传来的json
* @param e
* @return
*/
@ExceptionHandler(HttpMessageNotReadableException.class)
public BaseResult getHttpMessageNotReadableException(HttpMessageNotReadableException e){
log.info("com.example.demo.exception-->getHttpMessageNotReadableException::e = [{}]",e);
return BaseResult.failure(ResultEnum.HTTP_MESSAGE_NOT_READABLE,e.getMessage());
}
}
响应封装
正常响应的封装,通过support方法可以指定哪类接口要进行封装。
响应封装的逻辑,异常处理页面的方法直接返回,正常页面的响应封装一层。
@RestControllerAdvice
@Slf4j
public class ResponseInterceptorHandler implements ResponseBodyAdvice {
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
log.info("进入supports方法");
boolean annotationPresent = returnType.getMethod().isAnnotationPresent(CosmoController.class) || returnType.getDeclaringClass().isAnnotationPresent(CosmoController.class);
return annotationPresent;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
log.info("进入beforeBodyWrite方法");
log.info("body的内容为:{}",body);
//如果是BaseResult类,应该就是进入异常处理页面,被封装处理过,所以异常处理页面可以直接返回
if(body instanceof BaseResult){
return body;
}
//String类型一定要序列化才能返回,不然报错
if(body instanceof String){
return FastJsonUtils.bean2Json(BaseResult.success((String)body));
}
//正常结果,封装一层返回
return BaseResult.success(body);
}
}
总结
前端数据以json方式进入后端,后端首先对参数进行校验,校验成功进入代码逻辑,最后将结果进行封装响应。中途出现任何异常都会被全局异常处理拦截。