spring boot 2.x 接口参数统一校验

基于自定义拦截器实现

我们可以通过自定义拦截器对接口的参数实现校验。如何实现自定义拦截器请参考spring boot 2.x 简单实现自定义拦截器

public boolean preHandle (HttpServletRequest request, HttpServletResponse response, Object handler )

通过实现preHandle这个方法,我们可以在请求前对各参数进行验证。这个方法有三个参数,通过HttpServletRequest 我们可以获取到所有的请求参数。
请求对象:

@Data
public class RequestUser implements Serializable {
	
	@NotNull (message = "用户ID不能为空")
	private Integer userId;
	
	@NotNull(message = "用户名称不能为空")
	//@Pattern (regexp = "^.{6,12}$",message = "用户名不符合规范")
	@Length(min = 6,max = 12,message = "用户名不符合规范")
	private String userName;
	
	@Max (value = 65, message = "年龄不在规定范围内")
	@Min (value = 18,message = "年龄不在规定范围内")
	@NotNull( message = "年龄不能为空")
	private Integer age;
	
	@NotNull (message = "交易金额不能为空")
	@Pattern (regexp = "^([1-9][0-9]*)$", message = "无效的交易金额")
	private String tradeAmount;
}

用的是 validation-api-2.0.1.Final.jar 中的注解。
@Length 只能用于String 类型。
拦截器实现方法

public boolean preHandle (
		HttpServletRequest request, HttpServletResponse response, Object handler ) throws Exception {
		
		logger.info ("请求前拦截 :{}",request.getRequestURL () );
		
		Map map =  request.getParameterMap ();
		
		HandlerMethod method = ( HandlerMethod ) handler;
		MethodParameter[] params = method.getMethodParameters ();
		if (params ==  null || params.length == 0 ) {
			return  true;
		}
		MethodParameter parameter = params[0];
		Class reqClass = parameter.getParameterType ();
		
		//判断是那一个请求对象
		if (reqClass.newInstance () instanceof  RequestUser) {
			//Map 转化成 bean
			RequestUser ru =  vail.mapToBean (map, RequestUser.class);
			//验证
			String msg = vail.validata (ru);
			if (StringUtils.isNotEmpty(msg)) {
				logger.error ("验证结果:{}",msg);
				//抛出自定义异常结果
				throw  new VailException (msg);
			}
		}
		return  true;
	}

验证的工具类:

public class VailUtils {
	
	
	private static ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
	
	public static  List validateAll( T o) {
		List errorList = new ArrayList ();
		Set> violations = factory.getValidator().validate(o);
		if (!CollectionUtils.isEmpty (violations)) {
			violations.forEach (tConstraintViolation -> {
				errorList.add(tConstraintViolation.getMessage());
			});
			
		}
		return errorList;
	}
	
	
	public String validata(Object param) throws RuntimeException {
		String errorMessage = null;
		try {
			List errorList = validateAll(param);
			Assert.isTrue(CollectionUtils.isEmpty(errorList), errorList.toString());
		} catch (Exception e) {
			errorMessage = e.getMessage();
		}
		return errorMessage;
	}
	
	public  T mapToBean( Map map, Class cla) throws Exception {
		T  bean = null;
		try {
			bean = cla.newInstance ();
			ConvertUtils.register(new DoubleConverter (null), Double.class);
			ConvertUtils.register(new IntegerConverter (null), Integer.class);
			ConvertUtils.register(new DateConverter (null), java.util.Date.class);
			//此方法会自动给 Integer  Double 为 null 的字段赋值,因此需要ConvertUtils.register处理
			BeanUtils.populate(bean, map);
		} catch ( Exception e ){
			e.printStackTrace ();
		}
		
		return bean;
	}
}

异常处理:

@ControllerAdvice
public class ExecptionHandler {
	
	private Logger logger = LoggerFactory.getLogger (ExecptionHandler.class);

	@ExceptionHandler(value = Exception.class)
	@ResponseBody
	String defaultErrorHandler( HttpServletRequest req, Exception e) throws Exception {
		
		HashMap map = new HashMap <> (2);
		if (e instanceof VailException ) {
			VailException  vex = (VailException) e;
			logger.error("{}", vex.getMessage ());
			
			map.put ("code","vailError");
			map.put ("msg",vex.getMessage ());
		}
		//TODO 其他的异常处理
		
		return JSON.toJSONString (map);
	}
}

controller 控制层

@RequestMapping ("/api")
@RestController
public class APIController {

	@GetMapping (value = "/users")
	String  test (RequestUser requestUser){
		return JSON.toJSONString (requestUser);
	}
	
}

访问 http://localhost:881/api/users?userId=1&age=18&userName=%27test%27&tradeAmount=
可以看到提示 :{“msg”:"[无效的交易金额]",“code”:“vailError”}
从请求对象的注解上可以看到,要求是tradeAmount参数不能为空,并且参数值是有限制的。

在方法参数上验证 @Valid + BindingResult

如果只是单一接口的参数需要验证,那么可以不用去实现拦截器,在controller方法里就可以实现验证
例如:在APIController 中我们增加一个方法
使用 @Valid + BindingResult 来完成参数验证,注意@Valid + BindingResult是成对出现的。

@GetMapping (value = "/qry")
	String  test ( @Valid RequestDto  requestDto, BindingResult err) {
		
		//是否存在异常
		if ( err.hasErrors () ) {
			//将每一个异常存入到map中
			HashMap errMap = new HashMap <> ();
			err.getAllErrors ().stream ().forEach (
				er ->{
					FieldError fieldError = (FieldError)er;
					errMap.put (fieldError.getField (),fieldError.getDefaultMessage ());
				}
			);
			return JSON.toJSONString (errMap);
		}
	
		return JSON.toJSONString (requestDto);
	}

RequestDto 对象:

@Data
public class RequestDto implements Serializable {
	
	private static final long serialVersionUID = 8049172738251461758L;
	
	@NotNull(message = "手机号不能为空")
	@Length(min = 11,max = 11,message = "手机号格式错误")
	private String telnum;
	
	@NotNull(message = "归属地不能为空")
	@Length(min = 3,max = 3,message = "归属地数据格式错误")
	private String region;
	
	private String billType;
	
	private String stauts;
	
}

直接访问 http://localhost:881/api/qry 可以看到结果是 {“region”:“归属地不能为空”,“telnum”:“手机号不能为空”}
demo下载

你可能感兴趣的:(spring,boot)