龙空技术网

关于Controller实现入参注解可配置校验

火线ITech 1670

前言:

而今你们对“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;

}

栗子如下:

annotation- 这里是为了放图而放图

/**

* 注:

* 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

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