咱們知道Spring MVC層是默承認以支持Bean Validation的,嘗試使用了一下感受很不方便,只支持對Bean的驗證,還須要在Bean後面加一個BindingResult做爲參數。而在咱們的工程中有不少接口都是基本數據類型做爲參數的。下面分享怎麼來實現MVC層對基本數據類型提供Bean Validation的支持。前端
Spring Mvc 層默認的Bean Validation支持配置能夠參考這位朋友的博文:http://jinnianshilongnian.iteye.com/blog/1990081git
Spring中對Bean Validation能夠支持方法級別的驗證的,這塊只支持Service層的,能夠參考http://jinnianshilongnian.iteye.com/blog/1495594 。咱們的實現中的不少代碼也是參考了Spring這塊的實現。web
咱們經過mvc的攔截器配合註解來實現這樣的一個擴展。咱們攔截Controller方法上帶有@Valid註解的方法(這個註解來自Bean Validation規範),而後經過Spring的RequestMappingHandlerAdapter來獲取每個參數的值,而後使用org.hibernate.validator.internal.engine.ValidatorImpl (Hibernate對Bean Validation規範的實現,它額外提供了對方法參數的驗證,本來的規範中的api是沒有的)來根據每一個參數上面的驗證註解來驗證這些參數是否合法。驗證失敗的話拋出ConstraintViolationException。ajax
這樣的一個驗證過程,下面是這個攔截器的代碼:spring
/** * 添加Spring Controller 方法級別的Bean Validation的支持 * @author JiangFeng * */ public class ValidationInterceptor implements HandlerInterceptor{ protected final Log logger = LogFactory.getLog(getClass()); private List<HandlerMethodArgumentResolver> argumentResolvers; @Autowired private Validator validator; @Autowired private RequestMappingHandlerAdapter adapter; private final Map<MethodParameter, HandlerMethodArgumentResolver> argumentResolverCache = new ConcurrentHashMap<MethodParameter, HandlerMethodArgumentResolver>(256); private final Map<Class<?>, Set<Method>> initBinderCache = new ConcurrentHashMap<Class<?>, Set<Method>>(64); @Autowired public ValidationInterceptor(RequestMappingHandlerAdapter requestMappingHandlerAdapter){ argumentResolvers = requestMappingHandlerAdapter.getArgumentResolvers(); } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { LocalValidatorFactoryBean validatorFactoryBean = (LocalValidatorFactoryBean)validator; ValidatorImpl validatorImpl = (ValidatorImpl) validatorFactoryBean.getValidator(); ServletWebRequest webRequest = new ServletWebRequest(request, response); HandlerMethod method = (HandlerMethod)handler; Valid valid = method.getMethodAnnotation(Valid.class); if(valid!=null){ Class<?>[] groups = new Class<?>[0]; MethodParameter[] parameters = method.getMethodParameters(); Object[] parameterValues = new Object[parameters.length]; for (int i = 0; i < parameters.length; i++) { MethodParameter parameter = parameters[i]; HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter); Assert.notNull(resolver, "Unknown parameter type [" + parameter.getParameterType().getName() + "]"); ModelAndViewContainer mavContainer = new ModelAndViewContainer(); mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request)); WebDataBinderFactory webDataBinderFactory = getDataBinderFactory(method); Object value = resolver.resolveArgument(parameter, mavContainer, webRequest, webDataBinderFactory); parameterValues[i] = value; } Set<ConstraintViolation<Object>> violations = validatorImpl.validateParameters(method.getBean(), method.getMethod(), parameterValues, groups); if (!violations.isEmpty()) { throw new ConstraintViolationException(violations); } } return true; } private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception { Class<?> handlerType = handlerMethod.getBeanType(); Set<Method> methods = this.initBinderCache.get(handlerType); if (methods == null) { methods = HandlerMethodSelector.selectMethods(handlerType, RequestMappingHandlerAdapter.INIT_BINDER_METHODS); this.initBinderCache.put(handlerType, methods); } List<InvocableHandlerMethod> initBinderMethods = new ArrayList<InvocableHandlerMethod>(); for (Method method : methods) { Object bean = handlerMethod.getBean(); initBinderMethods.add(new InvocableHandlerMethod(bean, method)); } return new ServletRequestDataBinderFactory(initBinderMethods, adapter.getWebBindingInitializer()); } private HandlerMethodArgumentResolver getArgumentResolver( MethodParameter parameter) { HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter); if (result == null) { for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) { if (logger.isTraceEnabled()) { logger.trace("Testing if argument resolver [" + methodArgumentResolver + "] supports [" + parameter.getGenericParameterType() + "]"); } if (methodArgumentResolver.supportsParameter(parameter)) { result = methodArgumentResolver; this.argumentResolverCache.put(parameter, result); break; } } } return result; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { } }
咱們還須要在spring mvc的配置文件中啓用Bean Validationjson
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"> <property name="providerClass" value="org.hibernate.validator.HibernateValidator" /> <!-- 若是不加默認到 使用classpath下的 ValidationMessages.properties --> <property name="validationMessageSource" ref="messageSource" /> </bean> <!-- 國際化的消息資源文件(本系統中主要用於顯示/錯誤消息定製) --> <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource"> <property name="useCodeAsDefaultMessage" value="false" /> <property name="defaultEncoding" value="UTF-8" /> <property name="cacheSeconds" value="60" /> </bean>
Bean Validation配合上統一異常處理,而後再加上前端js裏面對json的統一解析。整個開發過程當中已經不須要對驗證作任何處理了。api
個人框架中的應用場景的代碼mvc
@RequestMapping("/handle/addFacilityAdd.action") @ResponseBody @Valid @Workflow public Result addFacilityAdd( @NotEmpty(message = "設備類型不能爲空") @RequestParam(value = "facilityId", required = false) String facilityId, @Digits(fraction = 6, integer = 3) @RequestParam(value = "longitude", required = false) Double longitude, @Digits(fraction = 6, integer = 3) @RequestParam(value = "latitude", required = false) Double latitude, @NotEmpty(message = "地址不能爲空") @RequestParam(value = "address", required = false) String address, @NotEmpty(message = "內容不能爲空") @RequestParam(value = "content", required = false) String content) throws Exception
演示失敗返回app
{"success":false,"returnTime":"2014-04-18 10:51:50","error":{"errorCode":1002,"errorMessage":"錯誤的參數","fieldErrors":[{"field":"content","message":"內容不能爲空"},{"field":"address","message":"地址不能爲空"},{"field":"facilityId","message":"設備類型不能爲空"}]},"result":{}}
統一異常攔截後的前端驗證js方法框架
var Valid = {}; Valid.valid = function (d) { if(typeof d !='object'){ d = eval('(' + d + ')'); } if (!d.success) { if(d.error.fieldErrors.length>0){ for (var error in d.error.fieldErrors) { Alert.warning(d.error.fieldErrors[error].message); } }else{ Alert.warning(d.error.errorMessage); } try { console.error(d); } catch (e) { } return false; } else { return true; } }
在ajax的回調方法中先調用一驗證,就能夠直接提示出Bean Validation驗證失敗的錯誤信息了。
攔截器代碼 http://git.oschina.net/for-1988/gbh91frxjy6msalqp28vi.code.git