前言:
而今你们对“controller接收参数为null”大概比较讲究,各位老铁们都需要学习一些“controller接收参数为null”的相关内容。那么小编同时在网上收集了一些对于“controller接收参数为null””的相关文章,希望朋友们能喜欢,同学们快快来学习一下吧!借助AOP与注解拦截Controller,实现入参统一校验处理
平时在提供接口时,需要有参数的校验,最近干活的时候,需要有大量的类似校验工作,索性用注解实现统一Controller层入参校验,统一异常处理,统一返回值规范。
1. 实现自定义注解
两个注解,实现多字段校验
我们在Controller方法上的注解,就叫@RequestValidate
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RequestValidate {
// 具体 校验 字段
ValidateField[] validateFields();
}
具体校验的字段注解
/**
* 这些属性都是一些可以自己定义的配置项
* 例如: inTable- 所传参数是否在表中有记录,若没有,说明该入参的值不合规,可以抛出异常
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ValidateField {
/**
* 参数名字
*/
String fieldName() default "";
/**
* 是否允许为null
* 默认可以为null
* not null and ""
*/
boolean notBlank() default true;
/**
* 该属性与ifInTable配合使用,判断为哪个表对象
*/
Class inBdClazz();
/**
* 是否在表中存在记录
*/
boolean ifInTable() default true;
}
注解Handler,核心处理校验逻辑!
逻辑简述: 反射获取所有的属性,遍历所有,并将传入的入参与Controller方法上的注解配置对比,是否非空,是否在数据库中有值。(其中是否在数据库中有值,涉及到项目中,实现将所有User信息缓存在map中,class作为键值,User的List作为值的前提,实现上也很好完成)
@Slf4j
@Aspect
@Component
public class RequestValidateHandler {
/**
* 这边切面的意识是 所有使用了@RequestValidate该注解的位置,都是被切下
* @annotation(requestValidate) 这个就可以作为后面handler中的入参
* SERVER_UNKNOWN_ERROR等等: 枚举配置
*/
@Before(value = "@annotation(com.zit.annotation.RequestValidate) && @annotation(requestValidate)")
public void validateBefore(JoinPoint joinPoint, RequestValidate requestValidate) throws IllegalAccessException, InvocationTargetException {
Object[] args = joinPoint.getArgs();
if (args == null || args.length == 0) {
throw new BuRuntimeException(SERVER_UNKNOWN_ERROR);
}
RequestObj request = (RequestObj) args[0];
ValidateField[] fields = requestValidate.validateFields();
validate(request, fields);
}
// 校验指定字段是否满足
private void validate(RequestObj request, ValidateField[] fields) throws IllegalAccessException, InvocationTargetException {
Class requestClass = request.getClass();
Field[] declaredFields = uaRequesrequestClass.getDeclaredFields();
Field.setAccessible(declaredFields, true);
for (ValidateField field : fields) {
String fieldName = field.fieldName();
Object obj1 = null;
for (Field declaredF : declaredFields) {
if (fieldName.equals(declaredF.getName())) {
obj1 = declaredF.get(request);
}
}
if (obj1 == null) {
throw new BuRuntimeException(MISSING_PARAMS, ": [" + fieldName + "]");
}
boolean notBlank = field.notBlank();
boolean inTable = field.inTable();
Class fieldBelongClass = field.inBdClazz();
String paramV = (String) obj1;
if (notBlank) { // 校验非空
if (StringUtils.isBlank(obj1.toString())) {
throw new UaRuntimeException(MISSING_PARAMS, ": [" + fieldName + "]");
} else {
if (ifInTable) { // 校验是否在db中
// 校验是否在db中,这里需要利用泛型
Method[] classMethods = fieldBelongClass.getMethods();
Method theMethod = null;
for (Method method : classMethods) { // 找到属性对应的get方法
if (("get" + fieldName).toLowerCase().equals(method.getName().toLowerCase())) {
theMethod = method;
}
}
if (theMethod == null) {
log.error("校验参数注解配置错误:属性与类名配置不匹配!");
throw new BuRuntimeException(SERVER_UNKNOWN_ERROR);
}
// BuCache为缓存了所有User,Operator等对象集合的Map,键值为User,Operator的Class值
List tempList = BuCache.get(fieldBelongClass);
boolean has = false;
for (Object obj : tempList) {
String st = (String) theMethod.invoke(obj);
if (st.equals(paramV)) {
has = true;
}
}
if (!has) {
throw new BuRuntimeException(INVALID_PARAMS_IN_DB, ": [" + fieldName + " = " + paramV + "]");
}
}
}
}
}
}
}
具体Controller方法上的应用
应用前提: 我们提供的借口入参,都是统一的对象来封装,所以面对大多数类似入参的借口在必填,非空等校验中,可以采用本文的方法
// RequestObj.java
/**
* 统一的入参封装对象
* lombok 省略set/get方法
*/
public class RequestObj {
private String operator;
private String userId;
}
// ResponseObj.java
/**
* 统一的返回值封装对象
* lombok 省略set/get方法
*/
public class ResponseObj {
private String msg;
private String code;
private Object result;
}
栗子如下:
/**
* 注:
* 1. JSONObject为Alibaba的fastJson中的对象
* 2. BuRuntimeException 为自定义的 runTimeException ,为后面统一的异常AOP提供入口
* 3. INVALID_PARAMS 为常量枚举配置,字面意思,不合法的参数
*/
@RequestValidate(validateFields = { // 配置需要校验的参数
@ValidateField(fieldName = "operator", notBlank = false), // 可以为null
@ValidateField(fieldName = "userId", inBdClazz = User.class, inTable = true)
})
@RequestMapping(value = "queryUserByUserUid", method = RequestMethod.POST)
public Object getUser(@RequestBody RequestObj request) {
log.info("getUser: request={}", JSONObject.toJSONString(request));
if (request.getOperator() == null || request.getUserUid() == null) {
throw new BuRuntimeException(INVALID_PARAMS);
}
final ResponseObj result = new ResponseObj();
result.setResult(null);
result.putReturnStatus(OK);
log.info("getUser: result={}", JSONObject.toJSONString(result));
return result;
}
ControllerAdvice controller异常拦截Aop
重点是一个注解:@ControllerAdvice
见名知意,拦截Controller抛出的异常
注解@ExceptionHandler 放在处理异常的方法上
/**
* @Slf4j 为lombok的注解,可以省了 private static final Logger log = LoggerFactory.getLogger(BuExceptionHandler.class);
*/
@ControllerAdvice
@Slf4j
public class BuExceptionHandler {
@ExceptionHandler(value = Exception.class)
public ResponseObj exceptionHandler(Exception e){
final ResponseObj response = new ResponseObj();
log.error("处理请求出现异常", e);
response.setMessage(e.getMessage());
response.setCode("50000"); // 5000 这里可以与上面的message自定义一些枚举,用来统一接口调用与提供方的code
return response;
}
}
至此,就可以在Controller上添加注解,共用一个aop校验入参的一些合法性了,以及其他一些自定义的入参逻辑。
放在结尾:为什么这个代码的格式,这么,这么,这么难看????!!!
乡亲们,怎么解决??欢迎留言告知!难道是要贴图??? 欢迎大家点一波关注,互相交流~ 哇哈哈~
标签: #controller接收参数为null