本文簡單介紹如何引入validation的步驟,如何通過自定義validation減少代碼量,提高生產力。特別提及:非基本類型屬性的valid,get方法的處理,validation錯誤信息的統一resolve。
本文中validation的實際實現委托給hibernate validation處理
基本配置
pom引入maven依賴
1
2
3
4
5
6
7
8
9
10
11
12
|
<!-- 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中增加如下配置:
1
2
3
4
5
6
7
|
<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錯誤信息,返回給調用方的信息更加友好, 在applicationcontext.xml中增加如下配置:
1
2
3
4
5
6
7
8
9
10
11
|
<!-- 加載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" /> |
在項目類路徑上增加:validation_error_zh_cn.properties資源文件:
1
2
3
4
5
6
|
#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實現:
validationexceptionresovler.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
@slf4j public class validationexceptionresovler extends abstracthandlerexceptionresolver { public validationexceptionresovler() { // 設置order,在defaulthandlerexceptionresolver之前執行 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,返回給調用方。 * * @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
1
2
3
4
5
|
@requestmapping ( "/buy" ) @responsebody public baseresponse buy( @requestbody @valid buyflowerrequest request) throws exception { //...... } |
在request bean上為需要validation的屬性增加validation注解
1
2
3
4
5
6
7
|
@setter @getter public class buyflowerrequest { @notempty (message = "{name.can.not.be.null}" ) private string name; } |
二級對象的validation
上面的寫法,只能對buyflowerrequest在基本類型屬性上做校驗,但是沒有辦法對對象屬性的屬性進行validation,如果需要對二級對象的屬性進行validation,則需要在二級對象及二級對象屬性上同時添加@valid 和 具體的validation注解.
如下寫法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
@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; } |
進一步減少編碼量
為了減少編碼工作量,通過自定義validation注解,嘗試將validation作用的filed名稱傳遞到 錯誤信息的資源文件中,從而避免為每個域編寫不同的message模版.
下面以重寫的@notnull為例講解:
1、定義validation注解,注意相比原生注解增加了field(),用于傳遞被validated的filed名字
notnull.java
1
2
3
4
5
6
7
8
9
|
@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均實現constraintvalidator接口:
notnullvalidator.java
1
2
3
4
5
6
7
8
9
10
11
12
|
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
1
2
3
4
5
6
7
8
9
10
|
@setter @getter public class buyflowerrequest { @notempty (field = "花名" ) private string name; @min (field = "價格" , value = 1 ) private int price; } |
注:@notnull注解已經支持對list的特殊校驗,對于list類型節點,如果list==null || list.size() == 0都會返回false,validation失敗。目前已按照此思路自定義實現了@notnull、@notempty、@min、@max注解,在goods工程中可以找到.
支持get請求
上面的示例都是post請求,@requestbody可以 resolve post請求,但是不支持get請求,閱讀spring的文檔和源碼,發現@modelattribute可以將get請求resolve成bean,且支持validation。具體可以翻閱spring源碼:modelattributemethodprocessor.resolveargument()方法。
使用示例:
1
2
3
4
5
6
7
8
9
|
@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、根據業務場景擴展validation,如:日期格式、金額等
2、支持多個field關系校驗的validation
附:spring validation實現關鍵代碼
@requestbody
實現類:requestresponsebodymethodprocessor.java
1
2
3
4
5
6
7
8
9
10
11
12
13
|
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
實現類:modelattributemethodprocessor.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
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); } |
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。
原文鏈接:http://www.cnblogs.com/daoqidelv/p/9061862.html