基于注解实现请求参数校验
1、定义参数注解,作用于提交对象属性上,指定该属性描述,是否非空,长度等
import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** * @ClassName: FieldDesc * @Description:用于提交参数校验 * @author: Ccl * @date: 2022年4月29日 上午11:51:54 * @Copyright: */ @Retention(RetentionPolicy.RUNTIME) public @interface FieldDesc { String desc(); boolean isNotNull(); int length(); }
2、定义方法注解
import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @ClassName: FieldInterceptor * @Description:作用于方法上,标识对该方法参数进行校验 * @author: Ccl * @date: 2022年4月29日 上午11:51:54 * @Copyright: */ @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited public @interface FieldInterceptor { boolean isCheck() default true; }
3、定义注解切面实现
import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Type; import java.text.ParseException; import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; import org.apache.commons.lang3.time.DateUtils; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import com.alibaba.fastjson.parser.ParserConfig; import com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer; import com.ccl.order.conf.exception.OperationDeniedException; import com.ccl.order.util.DataUtils; import com.ccl.order.util.MapUtils; /** * @ClassName: FieldInterceptorAspect * @Description:方法请求参数的属性,是否非空,长度进行校验,方法入口校验的参数名必须为param * @author: Ccl * @date: 2022年5月1日 下午6:32:08 * @Copyright: */ @Aspect @Order(value = 3) @Component public class FieldInterceptorAspect { private static final Logger log = LoggerFactory.getLogger(FieldInterceptorAspect.class); //切入点 @Pointcut(value = "@annotation(com.ccl.order.aop.FieldInterceptor)") private void pointcut() { } @Around(value="pointcut() && @annotation(fieldInterceptor)") public Object around(ProceedingJoinPoint joinPoint,FieldInterceptor fieldInterceptor){ Map<String,Object> map = changMap(joinPoint); boolean is_update = fieldInterceptor.isCheck();//是否校验参数 if(is_update) { Object param = map.get("param");//获取请求参数对象 check_obj_value(MapUtils.objectToMap(param),param.getClass());//开始校验参数 } try { return joinPoint.proceed();//校验正常,继续往下执行 } catch (Throwable e) { //自定义异常按照正常格式返回 e.printStackTrace(); throw new OperationDeniedException(e.getMessage()); } } /** * * @Title: check_obj_value * @Description: 对象参数校验 * @param: @param param * @param: @param clazz * @return: void * @throws */ public void check_obj_value(Map<String,Object> param,Class clazz) { Field[] fields = DataUtils.getFields(clazz);//获取类所有字段 for(Field field : fields) { field.setAccessible(true); String typeName = field.getType().getSimpleName();//获取类名 if(typeName.contains("List")) {//如果为列表,则需判断是否为列表对象,如果为一般数据类型,则不用循环判断每个对象 List list = (List) param.get(field.getName());//获取列表对象 if(list == null || list.isEmpty()) continue; Object first_obj = list.get(0); if(isJavaBeanForClass(first_obj.getClass())) {//判断列表元素是否为javabean对象 for(int i = 0;i < list.size(); i++) {//循环判断每一个对象参数 Object sub_obj = list.get(i); check_obj_value(MapUtils.objectToMap(sub_obj),sub_obj.getClass()); } }else { checkSubmitParamIsCurrent(field,get_emp_str_if_null(param.get(field.getName())));//判断属性值是否满足要求 } }else { checkSubmitParamIsCurrent(field,get_emp_str_if_null(param.get(field.getName())));//判断属性值是否满足要求 } } } /** * @Title: isJavaBean * @Description: 判断类型是非为java bean * @param: @param type * @param: @return * @return: boolean * @throws */ public static final boolean isJavaBean(Type type){ if(null == type ) return false; return ParserConfig.global.getDeserializer(type) instanceof JavaBeanDeserializer; } /** * @Title: isJavaBeanForClass * @Description: 判断类是否为java bean * @param: @param clazz * @param: @return * @return: boolean * @throws */ public static final boolean isJavaBeanForClass(Class clazz){ if(null == clazz ) return false; return isJavaBean(((Type)clazz)); } /** * 从切点中中获取已经映射好的参数,因为不确定request的类型,所以只能够通过映射封装好的参数来获取参数值,参数一定要用方法@RequestParam接收 * @Title: changMap * @Description: 获取切入点方法请求参数 * @param: @param pjp * @param: @return * @return: Map<String,Object> * @throws */ private static Map<String, Object> changMap(ProceedingJoinPoint pjp) { Signature signature = pjp.getSignature(); MethodSignature methodSignature = (MethodSignature) signature; String[] strings = methodSignature.getParameterNames();//获取该方法所有参数 Object[] args = pjp.getArgs(); List<String> list = Arrays.asList(strings); Map<String, Object> map = new HashMap<String, Object>(); for (int i = 0; i < args.length; i++) { if(!(args[i] instanceof HttpServletRequest) && !(args[i] instanceof HttpServletResponse)){ //有request参数时忽略 map.put(list.get(i), args[i]); } } return map; } public String get_emp_str_if_null(Object obj) { if(obj == null) return ""; return obj.toString(); } //校验提交参数是否正确 public void checkSubmitParamIsCurrent(Field field,String value){ String fieldDesc = ""; boolean isNotNull = false; int length = -1; Annotation[] annotation = field.getAnnotations(); for (Annotation tag : annotation) { if (tag instanceof FieldDesc) { isNotNull = ((FieldDesc) tag).isNotNull(); fieldDesc = ((FieldDesc) tag).desc(); length = ((FieldDesc) tag).length(); break; } } if(StringUtils.isBlank(fieldDesc)) return; if(!isCanConvert(field,value)){ throw new OperationDeniedException("提交参数["+fieldDesc+"],包含值["+value+"]类型错误,请提交正确值!"); }else if(isNotNull && StringUtils.isBlank(value)){ throw new OperationDeniedException("提交参数["+fieldDesc+"],不可为空,请提交正确值!"); }else if(length != -1 && StringUtils.isNotBlank(value) && value.length() > length){ throw new OperationDeniedException("提交参数["+fieldDesc+"]字数为["+value.length()+"],限制字数为["+length+"],请提交正确值!"); } } public boolean isCanConvert(Field field,String valueStr){ if(StringUtils.isBlank(valueStr) || "null".equalsIgnoreCase(valueStr)) return true; return isCanConvert(field.getType(),valueStr); } public boolean isCanConvert(Type type,String valueStr){ if(StringUtils.isBlank(valueStr) || "null".equalsIgnoreCase(valueStr)) return true; String typeName = ((Class)type).getName(); String[] types = {"java.lang.Long","java.lang.Integer","java.lang.Float","java.lang.Double","java.math.BigDecimal","java.lang.Byte","java.lang.Short"}; if(Arrays.asList(types).contains(typeName)){ valueStr = valueStr.trim(); if(!NumberUtils.isDigits(valueStr)){ return false; } }else if(typeName.contains("Date")){//时间格式至少为 年月日格式 Date date = null; try { date = DateUtils.parseDate(valueStr, new String[] {"yyyy-MM-dd HH:mm:ss","yyyy-MM-dd HH:mm","yyyy-MM-dd HH","yyyy-MM-dd"}); } catch (ParseException e) { } return date != null; } return true; } }
4、应用,将参数注解加入到实体对象里面
import java.io.Serializable; import java.math.BigDecimal; import java.util.Date; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.ccl.order.aop.FieldDesc; import com.ccl.order.api.base.ExtendDataDTO; import com.ccl.order.excel.annotation.Excel; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; /** * @ClassName: OrderEntity * @Description: 订单实体类 * @author: Ccl * @date: 2022年5月3日 下午3:25:27 * @Copyright: */ @Data @AllArgsConstructor @NoArgsConstructor @Builder @EqualsAndHashCode(callSuper = false) @TableName("sc_order") public class OrderEntity extends ExtendDataDTO implements Serializable { private static final long serialVersionUID = 1L; /** * 订单id */ @TableId private String id; /** * 公司id */ @Excel(name = "公司id") private String companyId; /** * 订单号 */ @FieldDesc(desc = "订单号",isNotNull = true,length = 50) @Excel(name = "订单号") private String orderNumber; /** * 订单名称 */ @FieldDesc(desc = "订单名称",isNotNull = true,length = 10) @Excel(name = "订单名称") private String orderName; /** * 订单类型 */ @FieldDesc(desc = "订单类型",isNotNull = false,length = -1) @Excel(name = "订单类型") private String orderType; /** * 订单价格 */ @FieldDesc(desc = "订单价格",isNotNull = true,length = 10) @Excel(name = "订单价格") private BigDecimal orderPrice; /** * 发货地址 */ @FieldDesc(desc = "发货地址",isNotNull = true,length = 255) @Excel(name = "发货地址") private String orderSendAddr; /** * 收货地址 */ @FieldDesc(desc = "收货地址",isNotNull = true,length = 255) @Excel(name = "收货地址") private String orderReceiAddr; /** * 下单时间 */ @FieldDesc(desc = "下单时间",isNotNull = true,length = -1) @Excel(name = "下单时间") private Date orderTime; /** * 创建人 */ @Excel(name = "创建人") private String createUserId; /** * 创建时间 */ @Excel(name = "创建时间") private Date createTime; /** * 修改人 */ @Excel(name = "修改人") private String updateUserId; /** * 修改时间 */ @Excel(name = "修改时间") private Date updateTime; }
5、请求入口方法处加上方法注解,标识对该方法进行参数校验
@RestController @RequestMapping("order") @Validated public class OrderController extends BaseController<OrderEntity,OrderService>{ @Autowired private OrderService orderService; @Auth @FieldInterceptor @PostMapping("/saveOrUpdate") public ApiResponse saveOrUpdate(ScuserTicket ticket,@RequestBody OrderEntityDTO param){ orderService.saveOrUpdateOrder(ticket,param); return ApiResponse.buildSuccess(); } }
6、效果


浙公网安备 33010602011771号