springMVC引入Validation的具體步驟詳解
本文簡單介紹如何引入validation的步驟,如何通過自定義validation減少代碼量,提高生產(chǎn)力。特別提及:非基本類型屬性的valid,GET方法的處理,validation錯誤信息的統(tǒng)一resolve。
本文中validation的實(shí)際實(shí)現(xiàn)委托給Hibernate validation處理
基本配置
pom引入maven依賴
<!-- validation begin --> <dependency> <groupId>javax.validation</groupId> <artifactId>validation-api</artifactId> <version>1.1.0.Final</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>5.4.0.Final</version> </dependency> <!-- validation end -->
增加validation配置
在spring-mvc-servlet.xml中增加如下配置:
<mvc:annotation-driven validator="validator"> <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"> <property name="providerClass" value="org.hibernate.validator.HibernateValidator" /> <property name="validationMessageSource" ref="messageSource"/> </bean> //messageSource 為i18n資源管理bean,見applicationContext.xml配置
自定義exceptionHandler
個性化處理validation錯誤信息,返回給調(diào)用方的信息更加友好, 在applicationContext.xml中增加如下配置:
<!-- 加載i18n消息資源文件 -->
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>errormsg</value>
<value>validation_error</value>
</list>
</property>
</bean>
<bean id="validationExceptionResolver" class="com.*.exception.ValidationExceptionResovler"/>
在項(xiàng)目類路徑上增加:validation_error_zh_CN.properties資源文件:
#the error msg for input validation
#common
field.can.not.be.null={field}不能為空
field.can.not.be.empty={field}不能為空或者空字符串
field.must.be.greater.than.min={field}不能小于{value}
field.must.be.letter.than.max={field}不能大于{value}
ValidationExceptionResovler實(shí)現(xiàn):
ValidationExceptionResovler.java
@Slf4j
public class ValidationExceptionResovler extends AbstractHandlerExceptionResolver {
public ValidationExceptionResovler() {
// 設(shè)置order,在DefaultHandlerExceptionResolver之前執(zhí)行
this.setOrder(0);
}
/**
* Handle the case where an argument annotated with {@code @Valid} such as
* an {@link } or {@link } argument fails validation.
* <p>
* 自定義ValidationException 異常處理器
* 獲取到具體的validation 錯誤信息,并組裝CommonResponse,返回給調(diào)用方。
*
* @param request current HTTP request
* @param response current HTTP response
* @param handler the executed handler
* @return an empty ModelAndView indicating the exception was handled
* @throws IOException potentially thrown from response.sendError()
*/
@ResponseBody
protected ModelAndView handleMethodArgumentNotValidException(BindingResult bindingResult,
HttpServletRequest request,
HttpServletResponse response,
Object handler)
throws IOException {
List<ObjectError> errors = bindingResult.getAllErrors();
StringBuffer errmsgBF = new StringBuffer();
for (ObjectError error : errors) {
String massage = error.getDefaultMessage();
errmsgBF.append(massage);
errmsgBF.append("||");
}
String errmsgString = errmsgBF.toString();
errmsgString = errmsgString.length() > 2 ? errmsgString.substring(0, errmsgString.length() - 2) : errmsgString;
log.error("Validation failed! {} ", errmsgString);
Map<String, Object> map = new TreeMap<String, Object>();
map.put("success", false);
map.put("errorCode", "9999");
map.put("errorMsg", errmsgString);
ModelAndView mav = new ModelAndView();
MappingJackson2JsonView view = new MappingJackson2JsonView();
view.setAttributesMap(map);
mav.setView(view);
return mav;
}
@Override
protected ModelAndView doResolveException(HttpServletRequest request,
HttpServletResponse response, Object handler,
Exception ex) {
BindingResult bindingResult = null;
if (ex instanceof MethodArgumentNotValidException) {
bindingResult = ((MethodArgumentNotValidException) ex).getBindingResult();
} else if(ex instanceof BindException) {
bindingResult = ((BindException) ex).getBindingResult();
} else {
//other exception , ignore
}
if(bindingResult != null) {
try {
return handleMethodArgumentNotValidException(bindingResult, request, response, handler);
} catch (IOException e) {
log.error("doResolveException: ", e);
}
}
return null;
}
}
在controller中增加@Valid
@RequestMapping("/buy")
@ResponseBody
public BaseResponse buy(@RequestBody @Valid BuyFlowerRequest request) throws Exception {
//......
}
在request bean上為需要validation的屬性增加validation注解
@Setter
@Getter
public class BuyFlowerRequest {
@NotEmpty(message = "{name.can.not.be.null}")
private String name;
}
二級對象的validation
上面的寫法,只能對BuyFlowerRequest在基本類型屬性上做校驗(yàn),但是沒有辦法對對象屬性的屬性進(jìn)行validation,如果需要對二級對象的屬性進(jìn)行validation,則需要在二級對象及二級對象屬性上同時添加@Valid 和 具體的validation注解.
如下寫法:
@Setter
@Getter
public class BuyFlowerRequest {
@NotEmpty(field = "花名")
private String name;
@Min(field = "價格", value = 1)
private int price;
@NotNull
private List<PayType> payTypeList;
}
@Setter
@Getter
public class PayType {
@Valid
@Min(value = 1)
private int payType;
@Valid
@Min(value = 1)
private int payAmount;
}
進(jìn)一步減少編碼量
為了減少編碼工作量,通過自定義Validation注解,嘗試將validation作用的filed名稱傳遞到 錯誤信息的資源文件中,從而避免為每個域編寫不同的message模版.
下面以重寫的@NotNull為例講解:
1、定義Validation注解,注意相比原生注解增加了field(),用于傳遞被validated的filed名字
NotNull.java
@Target( { ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER })
@Constraint(validatedBy = { NotNullValidator.class })
@Retention(RetentionPolicy.RUNTIME)
public @interface NotNull {
String field() default "";
String message() default "{field.can.not.be.null}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
2、定義Validator,所有的Validator均實(shí)現(xiàn)ConstraintValidator接口:
NotNullValidator.java
public class NotNullValidator implements ConstraintValidator<NotNull, Object> {
@Override
public void initialize(NotNull annotation) {
}
@Override
public boolean isValid(Object str, ConstraintValidatorContext constraintValidatorContext) {
return str != null;
}
}
3、在filed上加入Validation注解,注意指定filed值,message如果沒有個性化需求,可以不用指明,validation組件會自行填充default message。
BuyFlowerRequest.java
@Setter
@Getter
public class BuyFlowerRequest {
@NotEmpty(field = "花名")
private String name;
@Min(field = "價格", value = 1)
private int price;
}
注:@NotNull注解已經(jīng)支持對list的特殊校驗(yàn),對于List類型節(jié)點(diǎn),如果list==null || list.size() == 0都會返回false,validation失敗。目前已按照此思路自定義實(shí)現(xiàn)了@NotNull、@NotEmpty、@Min、@Max注解,在goods工程中可以找到.
支持GET請求
上面的示例都是POST請求,@RequestBody可以 resolve POST請求,但是不支持GET請求,閱讀spring的文檔和源碼,發(fā)現(xiàn)@ModelAttribute可以將GET請求resolve成Bean,且支持Validation。具體可以翻閱spring源碼:ModelAttributeMethodProcessor.resolveArgument()方法。
使用示例:
@RequestMapping(value = "/buy", method = RequestMethod.GET)
@ResponseBody
public BaseResponse detail(@Valid @ModelAttribute DetailFlowerRequest request) throws Exception {
DetailFlowerResponse response = new DetailFlowerResponse();
response.setName(request.getName());
return ResultFactory.success(response, BaseResponse.class);
}
TODO
1、根據(jù)業(yè)務(wù)場景擴(kuò)展validation,如:日期格式、金額等
2、支持多個field關(guān)系校驗(yàn)的validation
附:spring validation實(shí)現(xiàn)關(guān)鍵代碼
@RequestBody
實(shí)現(xiàn)類:RequestResponseBodyMethodProcessor.java
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
Object arg = this.readWithMessageConverters(webRequest, parameter, parameter.getGenericParameterType());
String name = Conventions.getVariableNameForParameter(parameter);
WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
if (arg != null) {
this.validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors() && this.isBindExceptionRequired(binder, parameter)) {
throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
}
}
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
return arg;
}
@ModelAttibute
實(shí)現(xiàn)類:ModelAttributeMethodProcessor.java
public final Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
String name = ModelFactory.getNameForParameter(parameter);
Object attribute = mavContainer.containsAttribute(name) ? mavContainer.getModel().get(name) : this.createAttribute(name, parameter, binderFactory, webRequest);
if (!mavContainer.isBindingDisabled(name)) {
ModelAttribute ann = (ModelAttribute)parameter.getParameterAnnotation(ModelAttribute.class);
if (ann != null && !ann.binding()) {
mavContainer.setBindingDisabled(name);
}
}
WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
if (binder.getTarget() != null) {
if (!mavContainer.isBindingDisabled(name)) {
this.bindRequestParameters(binder, webRequest);
}
this.validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors() && this.isBindExceptionRequired(binder, parameter)) {
throw new BindException(binder.getBindingResult());
}
}
Map<String, Object> bindingResultModel = binder.getBindingResult().getModel();
mavContainer.removeAttributes(bindingResultModel);
mavContainer.addAllAttributes(bindingResultModel);
return binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
}
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- 使用SpringMVC的@Validated注解驗(yàn)證的實(shí)現(xiàn)
- Spring MVC+FastJson+hibernate-validator整合的完整實(shí)例教程
- JSP中springmvc配置validator的注意事項(xiàng)
- springmvc的validator數(shù)據(jù)校驗(yàn)的實(shí)現(xiàn)示例代碼
- SpringMVC 使用JSR-303進(jìn)行校驗(yàn) @Valid示例
- 詳解SpringMVC驗(yàn)證框架Validation特殊用法
- SpringMVC使用@Valid注解進(jìn)行數(shù)據(jù)驗(yàn)證的方法
- SpringMVC Validator驗(yàn)證示例
- SpringMvc @Valid如何拋出攔截異常
相關(guān)文章
Springboot動態(tài)配置AOP切點(diǎn)詳解
這篇文章主要介紹了Springboot動態(tài)配置AOP切點(diǎn)詳解,Springboot 可以定義注解切點(diǎn)去攔截注解修飾的類方法以及execution(xxxx)切點(diǎn)去攔截具體的類方法,默認(rèn)情況下我們都會使用注解@PointCut去定義切點(diǎn),然后定義切面攔截切點(diǎn),需要的朋友可以參考下2023-09-09
IDEA配置MAVEN本地倉庫的實(shí)現(xiàn)步驟
本文主要介紹了IDEA配置MAVEN本地倉庫的實(shí)現(xiàn)步驟,將詳細(xì)介紹如何配置Maven環(huán)境變量,Maven配置文件,可以輕松地設(shè)置和配置MAVEN本地倉庫,以便在IDEA中享受更高效的開發(fā)體驗(yàn)2023-08-08
Spring Boot 實(shí)現(xiàn)敏感詞及特殊字符過濾處理
這篇文章主要介紹了SpringBoot實(shí)現(xiàn)敏感詞及特殊字符過濾處理方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-06-06
spring boot實(shí)戰(zhàn)之內(nèi)嵌容器tomcat配置
本篇文章主要介紹了Spring Boot 使用內(nèi)嵌的tomcat容器配置,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-01-01
SpringMVC實(shí)現(xiàn)自定義類型轉(zhuǎn)換器
本篇文章主要介紹了SpringMVC實(shí)現(xiàn)自定義類型轉(zhuǎn)換器 ,詳細(xì)的介紹了自定義類型轉(zhuǎn)換器的用法和好處,有興趣的可以了解一下。2017-04-04
SpringBoot配置使Mybatis打印SQL執(zhí)行時的實(shí)際參數(shù)值操作
這篇文章主要介紹了SpringBoot配置使Mybatis打印SQL執(zhí)行時的實(shí)際參數(shù)值操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-12-12
關(guān)于Socket的解析以及雙方即時通訊的java實(shí)現(xiàn)方法
本篇文章主要介紹了關(guān)于Socket的解析以及雙方通訊的java實(shí)現(xiàn)方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下。2017-03-03

