版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
《SSM開發(fā)實(shí)戰(zhàn)》第6章:SpringMVC攔截器開發(fā)案例案例實(shí)現(xiàn)技術(shù)架構(gòu)正確的請求與響應(yīng)在動態(tài)WEB應(yīng)用程序開發(fā)時(shí),請求處理與響應(yīng)結(jié)果是應(yīng)用開發(fā)的核心主題,只有在客戶端發(fā)送了正確的請求后,服務(wù)端才可以進(jìn)行正確的業(yè)務(wù)處理,以及有效的數(shù)據(jù)響應(yīng)過濾保護(hù)所有的WEB應(yīng)用都是運(yùn)行在公網(wǎng)上,并且可以使用的資源路徑也都暴露在外部,這樣用戶在進(jìn)行請求訪問的時(shí)候,如果沒有按照WEB應(yīng)用設(shè)計(jì)的標(biāo)準(zhǔn)進(jìn)行數(shù)據(jù)的傳輸,那么最終就會導(dǎo)致數(shù)據(jù)轉(zhuǎn)換處理產(chǎn)生異常,或者更嚴(yán)重的情況下,傳輸了一些惡意的數(shù)據(jù),導(dǎo)致項(xiàng)目的核心數(shù)據(jù)被泄漏。所以在這樣的處理環(huán)境下就需要對應(yīng)用程序進(jìn)行保護(hù),而在標(biāo)準(zhǔn)JakartaEE設(shè)計(jì)中可以通過過濾器來實(shí)現(xiàn)請求數(shù)據(jù)驗(yàn)證邏輯的定義基于攔截器實(shí)現(xiàn)數(shù)據(jù)驗(yàn)證由于JakartaEE僅僅提供了技術(shù)標(biāo)準(zhǔn),所以現(xiàn)代的Java項(xiàng)目開發(fā)中都會基于Spring開發(fā)框架,以方便的實(shí)現(xiàn)Bean管理與動態(tài)代理設(shè)計(jì)的支持,針對于WEB開發(fā)也提供了SpringMVC框架的支持,但是SpringMVC開發(fā)處理之中并不建議使用過濾器的方式來進(jìn)行攔截,主要的原因在于:SpringMVC是基于DispatcherServlet類實(shí)現(xiàn)所有請求的分發(fā)處理操作,而過濾器是與DispatcherServlet同級別的WEB應(yīng)用組件,所以如果直接編寫過濾器進(jìn)行處理,那么將無法使用Spring中的Bean管理機(jī)制,更無法準(zhǔn)確的找到控制層路徑對應(yīng)的處理類的信息。所以要想在SpringMVC之中實(shí)現(xiàn)這樣的請求數(shù)據(jù)驗(yàn)證過濾處理,就只能夠通過內(nèi)置攔截器組件來實(shí)現(xiàn)攔截器驗(yàn)證處理邏輯在一個(gè)項(xiàng)目之中會存在有大量的控制器,所以也會提供有大量需要進(jìn)行請求數(shù)據(jù)驗(yàn)證的訪問路徑存在,那么此時(shí)就需要基于可重用的設(shè)計(jì)思想,通過合理的面向?qū)ο笤O(shè)計(jì),基于攔截器處理機(jī)制,實(shí)現(xiàn)請求數(shù)據(jù)驗(yàn)證資源文件實(shí)現(xiàn)規(guī)則配置為了更加靈活的實(shí)現(xiàn)請求數(shù)據(jù)的驗(yàn)證處理,開發(fā)者可以通過perties資源文件來定義指定控制層處理方法的驗(yàn)證規(guī)則,而驗(yàn)證規(guī)則是在系統(tǒng)中由開發(fā)者自行定義的,同時(shí)在設(shè)計(jì)時(shí)也需要保留程序的可擴(kuò)展性。因?yàn)椴煌膽?yīng)用環(huán)境會有不同的應(yīng)用規(guī)則存在,所以在進(jìn)行設(shè)計(jì)時(shí)就要充分的考慮到各Spring提供了MessageSource接口,可以在Spring容器啟動時(shí)進(jìn)行所有配置規(guī)則的加載,而在每次進(jìn)行請求攔截時(shí),基于HandlerMethod對象實(shí)例進(jìn)行規(guī)則KEY的拼湊,并通過MessageSource實(shí)例進(jìn)行規(guī)則查找,最后使用專屬的驗(yàn)證類進(jìn)行規(guī)則的判斷,如果用戶發(fā)送的請求數(shù)據(jù)滿足驗(yàn)證規(guī)則的需要,則將請求轉(zhuǎn)發(fā)到目標(biāo)Action,如果不滿足數(shù)據(jù)驗(yàn)證規(guī)則,則直接跳轉(zhuǎn)到錯(cuò)誤頁進(jìn)行顯示。種應(yīng)用場景以及操作簡化性的問題。維護(hù)驗(yàn)證規(guī)則配置此已經(jīng)得到了關(guān)于數(shù)據(jù)驗(yàn)證處理的初步實(shí)現(xiàn)方案,但是在這個(gè)實(shí)現(xiàn)方案的背后會存在有另外一個(gè)現(xiàn)實(shí)的問題,那么就是代碼維護(hù)。試想一下在一個(gè)完整的項(xiàng)目應(yīng)用之中,會存在大量的Action程序類,每一個(gè)Action程序類中都可能包含有若干個(gè)控制層處理方法,而如果將每一個(gè)控制層方法的驗(yàn)證規(guī)則都寫在一個(gè)perties文件之中,則配置項(xiàng)一定會非常的多,并且在維護(hù)時(shí)也非常的繁瑣,一個(gè)基礎(chǔ)的CRUD控制器所應(yīng)有的驗(yàn)證項(xiàng)配置。案例項(xiàng)目與子模塊在項(xiàng)目開發(fā)中一般都會提供有一些專屬的工具模塊,以供不同的子模塊去使用,考慮到數(shù)據(jù)驗(yàn)證為一個(gè)常用的功能,所以在進(jìn)行項(xiàng)目構(gòu)建時(shí)可以采用公共子模塊的形式定義項(xiàng)目依賴管理標(biāo)準(zhǔn)實(shí)現(xiàn)架構(gòu)在本次的案例講解中,所有的案例都將保存在“ssm-case”項(xiàng)目之中,讀者可以根據(jù)子模塊的名稱找到對應(yīng)的實(shí)現(xiàn)源代碼,由于不同開發(fā)環(huán)境的需要,所以只會在ssm-case項(xiàng)目之中配置Spring的核心依賴,而關(guān)于SpringMVC有關(guān)的依賴配置會在不同的子模塊中進(jìn)行定義。如果現(xiàn)在開發(fā)者希望在項(xiàng)目中使用不同的項(xiàng)目進(jìn)行代碼的管理,那么就需要引入Nexus本地私服進(jìn)行管理,不同的項(xiàng)目直接打包發(fā)布到Nexus私服之中,而后再通過Nexus私服進(jìn)行依賴庫的引入,在本系列的《Java項(xiàng)目構(gòu)建與代碼管理開發(fā)實(shí)戰(zhàn)》中已經(jīng)詳細(xì)講解了此操作的實(shí)現(xiàn),如果未掌握的讀者請自行參考,也請有需要的讀者自行搭建相關(guān)服務(wù)環(huán)境。perties配置project_group=com.yootk
project_version=1.0.0
project_jdk=17validate-case子模塊project(":validate-case"){ //子模塊配置
dependencies{ //根據(jù)需要進(jìn)行依賴配置
implementation('org.springframework:spring-web:6.0.0-M3')
implementation('org.springframework:spring-webmvc:6.0.0-M3')
implementation('jakarta.servlet.jsp.jstl:jakarta.servlet.jsp.jstl-api:2.0.0')
implementation('org.mortbay.jasper:taglibs-standard:10.0.2')
implementation('com.fasterxml.jackson.core:jackson-core:2.13.3')
implementation('com.fasterxml.jackson.core:jackson-databind:2.13.3')
implementation('com.fasterxml.jackson.core:jackson-annotations:2.13.3')
compileOnly('jakarta.servlet.jsp:jakarta.servlet.jsp-api:3.1.0')
compileOnly('jakarta.servlet:jakarta.servlet-api:5.0.0')
implementation(project(':common')) //引入公共子模塊
}
}
project(":common"){ //子模塊配置
dependencies{ //根據(jù)需要進(jìn)行依賴配置
implementation('org.springframework:spring-web:6.0.0-M3')
implementation('org.springframework:spring-webmvc:6.0.0-M3')
implementation('com.fasterxml.jackson.core:jackson-core:2.13.3')
implementation('com.fasterxml.jackson.core:jackson-databind:2.13.3')
implementation('com.fasterxml.jackson.core:jackson-annotations:2.13.3')
compileOnly('jakarta.servlet:jakarta.servlet-api:5.0.0')
}
}AbstractAction公共控制層抽象父類packagemon.web.action.abs; //程序包名稱
publicabstractclassAbstractAction{ //控制層父類
privatestaticfinalDateTimeFormatterLOCAL_DATE_FORMAT=
DateTimeFormatter.ofPattern("yyyy-MM-dd"); //日期轉(zhuǎn)換格式
@InitBinder
publicvoidinitBinder(WebDataBinderbinder){ //綁定轉(zhuǎn)換處理
binder.registerCustomEditor(java.util.Date.class,newPropertyEditorSupport(){
@Override
publicvoidsetAsText(Stringtext)throwsIllegalArgumentException{
LocalDatelocalDate=LocalDate.parse(text,
LOCAL_DATE_FORMAT); //設(shè)置本地日期實(shí)例
Instantinstant=localDate.atStartOfDay()
.atZone(ZoneId.systemDefault()).toInstant();//創(chuàng)建處理實(shí)例
super.setValue(java.util.Date.from(instant)); //字符串與日期轉(zhuǎn)換
}
}); //綁定編輯器
}
}YootkServletpackagemon.web.servlet;
publicclassYootkDispatcherServletextendsDispatcherServlet{ //自定義Servlet
privatebooleanrestSwitch=false; //是否使用REST方式顯示
privateObjectMappermapper=newObjectMapper(); //創(chuàng)建Jackson數(shù)據(jù)映射類
publicYootkDispatcherServlet(WebApplicationContextwebApplicationContext){
super(webApplicationContext); //調(diào)用父類構(gòu)造
}
publicvoidsetRestSwitch(booleanrestSwitch){ //設(shè)置REST處理標(biāo)記
this.restSwitch=restSwitch;
}
@Override
protectedvoidnoHandlerFound(HttpServletRequestrequest,
HttpServletResponseresponse)throwsException{
if(this.restSwitch){ //使用REST風(fēng)格回應(yīng)
response.setStatus(HttpStatus.NOT_FOUND.value()); //響應(yīng)狀態(tài)
response.setContentType(MediaType.APPLICATION_JSON_VALUE);//響應(yīng)MIME狀態(tài)
response.setCharacterEncoding("UTF-8"); //響應(yīng)編碼
Map<String,Object>result=newHashMap<>(); //保存響應(yīng)結(jié)果
result.put("message","請求路徑未發(fā)現(xiàn),無法處理請求!");//錯(cuò)誤信息
result.put("status",HttpStatus.NOT_FOUND); //錯(cuò)誤碼
response.getWriter().print(mapper.writeValueAsString(result));//數(shù)據(jù)響應(yīng)
}else{ //采用普通跳轉(zhuǎn)模式
response.sendRedirect("/notfound"); //自定義路徑
}
}
}視圖資源配置packagemon.web.config;
@Configuration
publicclassResourceViewConfig{ //視圖資源配置類
@Bean
publicInternalResourceViewResolverresourceViewResolver(){//視圖解析
InternalResourceViewResolverresolver=
newInternalResourceViewResolver(); //視圖解析
resolver.setPrefix("/WEB-INF/pages"); //定義路徑前綴
resolver.setSuffix(".jsp"); //定義資源路徑后綴
returnresolver;
}
}SpringApplicationContextConfig配置類packagecom.yootk.validate.context.config;
@Configuration
@ComponentScan("mon.web.config") //Spring掃描包
publicclassSpringApplicationContextConfig{} //Spring上下文配置類ObjectMapper子類packagecom.yootk.validate.mapper;
publicclassCustomObjectMapperextendsObjectMapper{//自定義配置類
publicstaticfinalStringDEFAULT_DATE_FORMAT="yyyy-MM-dd";//日期格式
publicCustomObjectMapper(){
super.setDateFormat(newSimpleDateFormat(DEFAULT_DATE_FORMAT));//日期格式化
super.configure(SerializationFeature.INDENT_OUTPUT,true);//格式化輸出
super.setSerializationInclusion(JsonInclude.Include.NON_NULL);//NULL不參與序列化
super.setTimeZone(TimeZone.getTimeZone("GMT+8:00"));//配置時(shí)區(qū)
}
}SpringWEBContextConfig配置類packagecom.yootk.validate.context.config;
@Configuration//配置類
@EnableWebMvc//啟用MVC配置
@ComponentScan("com.yootk.validate.action")//SpringWEB掃描包
publicclassSpringWEBContextConfigimplementsWebMvcConfigurer{//SpringWEB配置
@Override
publicvoidaddResourceHandlers(ResourceHandlerRegistryregistry){
registry.addResourceHandler("/yootk-js/**")
.addResourceLocations("/WEB-INF/static/js/");//資源映射
registry.addResourceHandler("/yootk-css/**")
.addResourceLocations("/WEB-INF/static/css/");//資源映射
registry.addResourceHandler("/yootk-images/**")
.addResourceLocations("/WEB-INF/static/images/");//資源映射
registry.addResourceHandler("/yootk-upload/**")
.addResourceLocations("/WEB-INF/upload/");//上傳資源映射
}
@Override
publicvoidconfigureMessageConverters(List<HttpMessageConverter<?>>converters){
MappingJackson2HttpMessageConverterconverter=
newMappingJackson2HttpMessageConverter();//Jackson消息轉(zhuǎn)換器
CustomObjectMapperobjectMapper=newCustomObjectMapper();//對象的映射轉(zhuǎn)換處理配置
converter.setObjectMapper(objectMapper);
converter.setSupportedMediaTypes(List.of(
MediaType.APPLICATION_JSON));//MIME類型
converters.add(converter);//追加轉(zhuǎn)換器
}
}SpringMVC啟動配置類packagecom.yootk.validate.web.config;
publicclassStartWEBApplication
extendsAbstractAnnotationConfigDispatcherServletInitializer{
@Override
protectedFrameworkServletcreateDispatcherServlet(
WebApplicationContextservletAppContext){
returnnewYootkDispatcherServlet(servletAppContext);//自定義Servlet處理類
}
@Override
protectedClass<?>[]getRootConfigClasses(){ //Spring配置類
returnnewClass[]{SpringApplicationContextConfig.class};
}
@Override
protectedClass<?>[]getServletConfigClasses(){ //SpringWEB配置類
returnnewClass[]{SpringWEBContextConfig.class};
}
@Override
protectedString[]getServletMappings(){ //DispatcherServlet映射路徑
returnnewString[]{"/"};
}
@Override
protectedFilter[]getServletFilters(){ //過濾器
CharacterEncodingFiltercharacterEncodingFilter=
newCharacterEncodingFilter(); //編碼過濾
characterEncodingFilter.setEncoding("UTF-8"); //編碼設(shè)置
characterEncodingFilter.setForceEncoding(true); //強(qiáng)制編碼
returnnewFilter[]{characterEncodingFilter};
}
@Override
protectedvoidcustomizeRegistration(ServletRegistration.Dynamicregistration){
longmaxFileSize=2097152; //單個(gè)文件最大長度(2M)
longmaxRequestSize=5242880; //整體請求文件最大長度(5M)
intfileSizeThreshold=1048576; //文件寫入磁盤大小的閾值(1M)
MultipartConfigElementelement=newMultipartConfigElement(
"/tmp",maxFileSize,maxRequestSize,fileSizeThreshold);
registration.setMultipartConfig(element);
}
}配置WEB模塊目錄數(shù)據(jù)接收問題在WEB開發(fā)中,最為常用的參數(shù)傳遞模式,是通過地址重寫或者是表單提交的方式進(jìn)行傳輸,此時(shí)就可以直接依靠HttpServletRequest接口提供的getParameterValue()以及getParameterValues()方法實(shí)現(xiàn)請求數(shù)據(jù)的接收,但是如果此時(shí)用戶傳遞的是一個(gè)JSON數(shù)據(jù)內(nèi)容,就需要考慮到數(shù)據(jù)接收問題了請求接收如果此時(shí)要通過攔截器進(jìn)行請求數(shù)據(jù)的驗(yàn)證,則必然要使用HttpServletRequest接口提供的getInputStream()方法進(jìn)行請求主體數(shù)據(jù)的接收,這樣才可以進(jìn)行數(shù)據(jù)驗(yàn)證的處理,然而該數(shù)據(jù)流在整個(gè)的一次請求之中只允許讀取一次。這樣當(dāng)數(shù)據(jù)驗(yàn)證通過后,SpringMVC的內(nèi)部會使用Jackson實(shí)現(xiàn)JSON請求數(shù)據(jù)與對象實(shí)例(方法參數(shù)上使用@RequestBody注解)進(jìn)行轉(zhuǎn)換,所以此時(shí)會再次調(diào)用getInputStream()方法,所以最終導(dǎo)致的問題就是Jackson處理時(shí)無法接收到所需的數(shù)據(jù),也就自然無法實(shí)現(xiàn)JSON對象的轉(zhuǎn)換處理。自定義請求封裝類JakartaEE為了解決這一設(shè)計(jì)問題,提供了HttpServletRequestWrapper包裝類,開發(fā)者可以通過該類擴(kuò)展一個(gè)自定義的請求處理類,并將用戶所發(fā)送的主體數(shù)據(jù)直接保存在該類中,這樣只要在后續(xù)的請求中傳遞自定義請求類的對象,就可以實(shí)現(xiàn)getInputStream()方法的重復(fù)調(diào)用輸入流數(shù)據(jù)讀取的工具類packagemon.http.util;
publicclassReadRequestBodyData{ //讀取主體數(shù)據(jù)
privateReadRequestBodyData(){}
publicstaticbyte[]getRequestBodyData(HttpServletRequestrequest)
throwsIOException{ //輸入流數(shù)據(jù)讀取
HttpServletRequestWrapperrequestWrapper=newHttpServletRequestWrapper(request);
intcontentLength=requestWrapper.getContentLength();//獲取數(shù)據(jù)長度
if(contentLength<0){ //長度小于0
returnnull; //返回空數(shù)據(jù)
}
bytebuffer[]=newbyte[contentLength]; //開辟數(shù)組
intlen=0; //讀取長度
requestWrapper.getInputStream().read(buffer); //數(shù)據(jù)讀取
returnbuffer;
}
}HttpRequestWraper子類packagemon.http;
publicclassYootkRequestWrapperextendsHttpServletRequestWrapper{//請求包裝類
privatebytebody[]; //用戶請求數(shù)據(jù)
publicYootkRequestWrapper(HttpServletRequestrequest){//request包裝
super(request);
try{
this.body=ReadRequestBodyData.getRequestBodyData(request);//數(shù)據(jù)讀取
}catch(IOExceptione){}
}
@Override
publicServletInputStreamgetInputStream()throwsIOException{//返回輸入流
finalByteArrayInputStreaminputStream=newByteArrayInputStream(body);
returnnewServletInputStream(){
privateinttemp=0;
@Override
publicintread()throwsIOException{ //數(shù)據(jù)讀取
temp=inputStream.read(); //數(shù)據(jù)讀取
returntemp; //數(shù)據(jù)返回
}
@Override
publicbooleanisFinished(){ //完成判斷
returntemp!=1;
}
@Override
publicbooleanisReady(){ //是否準(zhǔn)備完畢
returntrue;
}
@Override
publicvoidsetReadListener(ReadListenerreadListener){}
};
}
}YootkStreamFilter過濾器packagemon.http.filter;
publicclassYootkStreamFilterimplementsFilter{ //請求包裝過濾
@Override
publicvoiddoFilter(ServletRequestrequest,ServletResponseresponse,
FilterChainchain)throwsIOException,ServletException{
HttpServletRequesthttpServletRequest=(HttpServletRequest)request;
if(httpServletRequest.getContentType()!=null){ //判斷請求類型
if(httpServletRequest.getContentType()
.startsWith("application/json")){ //MIME類型判斷
ServletRequestrequestWrapper=
newYootkRequestWrapper(httpServletRequest);//請求包裝
chain.doFilter(requestWrapper,response); //請求轉(zhuǎn)發(fā)
}else{
chain.doFilter(request,response); //請求轉(zhuǎn)發(fā)
}
}else{
chain.doFilter(request,response); //請求轉(zhuǎn)發(fā)
}
}
}StartWEBApplication配置@Override
protectedFilter[]getServletFilters(){ //過濾器
CharacterEncodingFiltercharacterEncodingFilter=
newCharacterEncodingFilter(); //編碼過濾
characterEncodingFilter.setEncoding("UTF-8"); //編碼設(shè)置
characterEncodingFilter.setForceEncoding(true); //強(qiáng)制編碼
YootkStreamFilterstreamFilter=newYootkStreamFilter();//請求包裝過濾
returnnewFilter[]{characterEncodingFilter,streamFilter};
}攔截規(guī)則配置數(shù)據(jù)驗(yàn)證規(guī)則是實(shí)現(xiàn)攔截器驗(yàn)證處理的核心關(guān)鍵,在大部分的項(xiàng)目中都可能會包含有數(shù)字、日期、日期時(shí)間、布爾、數(shù)組等相關(guān)的驗(yàn)證需要,同時(shí)還需要考慮不同的應(yīng)用場景所帶來的一些特殊的驗(yàn)證規(guī)則配置,為了解決該類的設(shè)計(jì)問題,可以在項(xiàng)目中定義一個(gè)IValidateRule驗(yàn)證規(guī)則接口,而后不同的數(shù)據(jù)驗(yàn)證處理只需要實(shí)現(xiàn)該接口即可數(shù)據(jù)驗(yàn)證規(guī)則No.規(guī)則標(biāo)記規(guī)則處理類描述01stringmon.validate.rule.StringValidateRule請求數(shù)據(jù)不允許為空02intmon.validate.rule.IntValidateRule請求數(shù)據(jù)必須為整型數(shù)據(jù)03longmon.validate.rule.LongValidateRule請求數(shù)據(jù)必須為長整型數(shù)據(jù)04doublemon.validate.rule.DoubleValidateRule請求數(shù)據(jù)必須為浮點(diǎn)型數(shù)據(jù)05booleanmon.validate.rule.BooleanValidateRule請求數(shù)據(jù)必須為布爾型數(shù)據(jù)06datemon.validate.rule.DateValidateRule請求數(shù)據(jù)必須為日期格式數(shù)據(jù)07datetimemon.validate.rule.DatetimeValidateRule請求數(shù)據(jù)必須為日期時(shí)間格式數(shù)據(jù)08randmon.validate.rule.RandValidateRule請求數(shù)據(jù)需要與生成的驗(yàn)證碼相匹配09int[]mon.validate.rule.IntsValidateRule請求數(shù)組中的每一項(xiàng)必須為整型數(shù)據(jù)10long[]mon.validate.rule.LongsValidateRule請求數(shù)組中的每一項(xiàng)必須為長整型數(shù)據(jù)11string[]mon.validate.rule.StringsValidateRule請求數(shù)組不允許為空IValidateRule數(shù)據(jù)驗(yàn)證接口packagemon.validate;
publicinterfaceIValidateRule{ //定義數(shù)據(jù)驗(yàn)證接口
publicbooleanvalidate(Objectstr); //數(shù)據(jù)驗(yàn)證方法
publicStringerrorMessage(); //返回錯(cuò)誤信息
publicdefaultStringgetParameterValue(Objectparam){ //接收參數(shù)
if(StringUtils.hasLength(request().getContentType())){
if(request().getContentType().startsWith(MediaType.APPLICATION_JSON_VALUE)){
if(paraminstanceofList){ //是否為List集合
List<String>all=(List)param; //對象轉(zhuǎn)型
returnall.get(0); //返回原始數(shù)據(jù)
}
returnparam.toString(); //返回原始數(shù)據(jù)
}
}
returnrequest().getParameter(param.toString()); //接收請求參數(shù)
}
publicdefaultString[]getParameterValues(Objectparam){//接收數(shù)組
if(StringUtils.hasLength(request().getContentType())){
if(request().getContentType().startsWith(MediaType.APPLICATION_JSON_VALUE)){
if(paraminstanceofList){ //是否為List集合
List<String>all=(List)param; //對象轉(zhuǎn)型
returnall.toArray(newString[]{}); //返回?cái)?shù)組
}
}
}
returnrequest().getParameterValues(param.toString());//接收請求參數(shù)
}
publicdefaultHttpServletRequestrequest(){
HttpServletRequestrequest=((ServletRequestAttributes)RequestContextHolder
.getRequestAttributes()).getRequest();
returnrequest;
}
}AbstractValidateRule抽象類packagemon.validate.rule.abs;
publicabstractclassAbstractValidateRuleimplementsIValidateRule{
@Override
publicStringerrorMessage(){ //配置公共錯(cuò)誤數(shù)據(jù)
return"請求數(shù)據(jù)錯(cuò)誤,無法通過驗(yàn)證,請確認(rèn)數(shù)據(jù)內(nèi)容是否正確!";
}
}創(chuàng)建字符串?dāng)?shù)據(jù)驗(yàn)證子類packagemon.validate.rule;
publicclassStringValidateRuleextendsAbstractValidateRuleimplementsIValidateRule{
@Override
publicbooleanvalidate(Objectparam){
Stringvalue=getParameterValue(param); //獲取請求參數(shù)
//StringUtils為Spring內(nèi)置的字符串處理工具類,直接通過該類方法實(shí)現(xiàn)字符串是否為空的判斷
returnStringUtils.hasLength(value); //判斷數(shù)據(jù)是否為空
}
@Override
publicStringerrorMessage(){
return"請求數(shù)據(jù)不允許為空!";
}
}布爾型數(shù)據(jù)驗(yàn)證子類packagemon.validate.rule;
publicclassBooleanValidateRuleextendsAbstractValidateRuleimplementsIValidateRule{
@Override
publicbooleanvalidate(Objectparam){
Stringvalue=getParameterValue(param); //獲取請求參數(shù)
if(!StringUtils.hasLength(value)){ //字符串為空
returnfalse; //驗(yàn)證失敗
}
if("on".equalsIgnoreCase(value)||"1".equalsIgnoreCase(value)||
"up".equalsIgnoreCase(value)||"yes".equalsIgnoreCase(value)||
"true".equalsIgnoreCase(value)){ //數(shù)據(jù)判斷
returntrue;
}
returnfalse;
}
@Override
publicStringerrorMessage(){
return"請求數(shù)據(jù)必須是“true”或者是“false”!";
}
}整型數(shù)據(jù)驗(yàn)證子類packagemon.validate.rule;
publicclassIntValidateRuleextendsAbstractValidateRuleimplementsIValidateRule{
@Override
publicbooleanvalidate(Objectparam){
Stringvalue=getParameterValue(param); //獲取請求參數(shù)
if(!StringUtils.hasLength(value)){ //字符串為空
returnfalse; //驗(yàn)證失敗
}
returnvalue.matches("\\d+"); //正則驗(yàn)證
}
@Override
publicStringerrorMessage(){
return"請求數(shù)據(jù)必須是整數(shù)!";
}
}長整型數(shù)據(jù)驗(yàn)證子類packagemon.validate.rule;
publicclassLongValidateRuleextendsIntValidateRuleimplementsIValidateRule{}浮點(diǎn)型數(shù)據(jù)驗(yàn)證子類packagemon.validate.rule;
publicclassDoubleValidateRuleextendsAbstractValidateRuleimplementsIValidateRule{
@Override
publicbooleanvalidate(Objectparam){
Stringvalue=getParameterValue(param); //獲取請求參數(shù)
if(!StringUtils.hasLength(value)){ //字符串為空
returnfalse; //驗(yàn)證失敗
}
returnvalue.matches("\\d+(\\.\\d+)?"); //正則驗(yàn)證
}
@Override
publicStringerrorMessage(){
return"請求數(shù)據(jù)必須是小數(shù)!";
}
}日期數(shù)據(jù)驗(yàn)證子類packagemon.validate.rule;
publicclassDateValidateRuleextendsAbstractValidateRuleimplementsIValidateRule{
@Override
publicbooleanvalidate(Objectparam){
Stringvalue=getParameterValue(param); //獲取請求參數(shù)
if(!StringUtils.hasLength(value)){ //字符串為空
returnfalse; //驗(yàn)證失敗
}
returnvalue.matches("\\d{4}-\\d{2}-\\d{2}"); //正則驗(yàn)證
}
@Override
publicStringerrorMessage(){
return"請求數(shù)據(jù)必須是日期型格式(yyyy-MM-dd)!";
}
}日期時(shí)間數(shù)據(jù)驗(yàn)證子類packagemon.validate.rule;
publicclassDatetimeValidateRuleextendsAbstractValidateRuleimplementsIValidateRule{
@Override
publicbooleanvalidate(Objectparam){
Stringvalue=getParameterValue(param); //獲取請求參數(shù)
if(!StringUtils.hasLength(value)){ //字符串為空
returnfalse; //驗(yàn)證失敗
}
returnvalue.matches("\\d{4}-\\d{2}-\\d{2}\\d{2}:\\d{2}:\\d{2}");//正則驗(yàn)證
}
@Override
publicStringerrorMessage(){
return"請求數(shù)據(jù)必須是日期時(shí)間型格式(yyyy-MM-ddHH:mm:ss)!";
}
}驗(yàn)證碼數(shù)據(jù)驗(yàn)證子類packagemon.validate.rule;
publicclassRandValidateRuleextendsAbstractValidateRuleimplementsIValidateRule{
publicstaticfinalStringRAND_SESSION_NAME="rand";
@Override
publicbooleanvalidate(Objectparam){
Stringvalue=getParameterValue(param); //獲取請求參數(shù)
if(!StringUtils.hasLength(value)){ //數(shù)據(jù)不為空
returnfalse; //為空直接返回false
}
Stringrand=(String)request().getSession().getAttribute(RAND_SESSION_NAME);
returnvalue.equalsIgnoreCase(rand); //驗(yàn)證碼檢查
}
@Override
publicStringerrorMessage(){
return"請求的驗(yàn)證碼不正確!";
}
}字符串?dāng)?shù)組驗(yàn)證子類packagemon.validate.rule;
publicclassStringsValidateRuleextendsAbstractValidateRuleimplementsIValidateRule{
@Override
publicbooleanvalidate(Objectparam){
Stringvalues[]=getParameterValues(param); //獲取請求參數(shù)
returnvalues!=null; //數(shù)組不為空
}
@Override
publicStringerrorMessage(){
return"請求數(shù)據(jù)不允許為空!";
}
}整型數(shù)組驗(yàn)證子類packagemon.validate.rule;
publicclassIntsValidateRuleextendsAbstractValidateRuleimplementsIValidateRule{
@Override
publicbooleanvalidate(Objectparam){
Stringvalues[]=getParameterValues(param); //獲取請求參數(shù)
if(values==null){ //數(shù)據(jù)為空
returnfalse; //驗(yàn)證失敗
}else{ //數(shù)據(jù)不為空,驗(yàn)證內(nèi)部數(shù)據(jù)項(xiàng)
for(intx=0;x<values.length;x++){ //數(shù)組循環(huán)
if(!values[x].matches("\\d+")){ //數(shù)據(jù)錯(cuò)誤
returnfalse; //驗(yàn)證失敗
}
}
returntrue; //驗(yàn)證成功
}
}
@Override
publicStringerrorMessage(){
return"請求內(nèi)容必須是數(shù)字!"; //錯(cuò)誤信息KEY
}
}長整型數(shù)組驗(yàn)證子類packagemon.validate.rule;
publicclassLongsValidateRuleextendsIntsValidateRuleimplementsIValidateRule{}定義驗(yàn)證注解完整的WEB應(yīng)用會提供有大量的控制層處理方法,同時(shí)不同的控制層方法也會接收不同的請求參數(shù),這樣在進(jìn)行控制層驗(yàn)證處理時(shí),就需要開發(fā)者進(jìn)行明確的驗(yàn)證規(guī)則的配置,而為了驗(yàn)證規(guī)則的維護(hù)可以通過自定義注解進(jìn)行配置,在攔截處理時(shí),可以通過HandlerMethod對象獲取注解以實(shí)現(xiàn)請求數(shù)據(jù)驗(yàn)證請求數(shù)據(jù)檢查注解packagemon.annotation;
@Target({ElementType.METHOD}) //方法上使用注解
@Retention(RetentionPolicy.RUNTIME) //運(yùn)行時(shí)生效
public@interfaceRequestDataValidate{
booleanrequired()defaulttrue; //啟用配置
Stringvalue()default""; //驗(yàn)證規(guī)則
}數(shù)據(jù)驗(yàn)證攔截器packageerceptor;
publicclassRequestDataValidateInterceptorimplementsHandlerInterceptor{//攔截器
privatefinalstaticLoggerLOGGER=
LoggerFactory.getLogger(RequestDataValidateInterceptor.class);
privatebooleanrestSwitch=false; //是否使用REST方式顯示
@Override
publicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,
Objecthandler)throwsException{ //處理前攔截
if(handlerinstanceofHandlerMethod){ //類型判斷
HandlerMethodhandlerMethod=(HandlerMethod)handler;//對象轉(zhuǎn)型
//根據(jù)當(dāng)前調(diào)用方法獲取方法上配置的@RequestDataValidate注解實(shí)例
RequestDataValidatevalidate=
handlerMethod.getMethodAnnotation(RequestDataValidate.class);
if(validate==null){ //不提供驗(yàn)證規(guī)則
returntrue; //轉(zhuǎn)發(fā)到目標(biāo)Action
}else{ //驗(yàn)證處理
if(!validate.required()){ //不需要驗(yàn)證
returntrue; //轉(zhuǎn)發(fā)到目標(biāo)Action
}else{ //數(shù)據(jù)驗(yàn)證
Stringrules=validate.value(); //獲取驗(yàn)證規(guī)則
if(StringUtils.hasLength(rules)){ //驗(yàn)證規(guī)則存在
LOGGER.debug("【{}()】{}",
handlerMethod.getMethod().getName(),rules);}
returntrue; //轉(zhuǎn)發(fā)到目標(biāo)Action
}
}
}
returntrue;
}
publicvoidsetRestSwitch(booleanrestSwitch){ //修改攔截器顯式風(fēng)格
this.restSwitch=restSwitch;
}
}SpringWEBContextConfig配置類@Override
publicvoidaddInterceptors(InterceptorRegistryregistry){ //攔截器注冊
RequestDataValidateInterceptorinterceptor=newRequestDataValidateInterceptor();
interceptor.setRestSwitch(false); //攔截器顯式風(fēng)格
registry.addInterceptor(interceptor).addPathPatterns("/pages/**");//攔截路徑
}Emp程序類packagecom.yootk.validate.vo;
publicclassEmp{ //定義VO類
privateLongempno; //雇員編號為長整型數(shù)據(jù)
privateStringename; //雇員姓名為字符串?dāng)?shù)據(jù)
privatejava.util.Datehiredate; //雇傭日期為日期型
privateDoublesal; //基本工資為浮點(diǎn)型數(shù)據(jù)
privateSet<String>roles; //雇員所擁有的角色//Setter、Getter、無參構(gòu)造、多參構(gòu)造方法略...
}EmpAction程序類packagecom.yootk.validate.action;
@Controller //控制器標(biāo)記
@RequestMapping("/pages/emp/") //映射父路徑
publicclassEmpActionextendsAbstractAction{
privatestaticfinalLoggerLOGGER=LoggerFactory.getLogger(EmpAction.class);
//控制層add()方法在數(shù)據(jù)接收時(shí)需要進(jìn)行請求數(shù)據(jù)驗(yàn)證處理
@PostMapping("add") //子路徑
@RequestDataValidate(
"empno:long;ename:string;sal:double;hiredate:date;roles:strings")
publicModelAndViewadd(Empemp){
LOGGER.info("【增加雇員數(shù)據(jù)】雇員編號:{}、姓名:{}、工資:{}、雇傭日期:{}、角色:{}",
emp.getEmpno(),emp.getEname(),emp.getSal(),
emp.getHiredate(),emp.getRoles());
returnnull;
}
@PostMapping("edit") //子路徑
@RequestDataValidate(required=true,
value="empno:long;ename:string;sal:double;hiredate:date;roles:strings")
publicModelAndViewedit(@RequestBodyEmpemp){
LOGGER.info("【更新雇員數(shù)據(jù)】雇員編號:{}、姓名:{}、工資:{}、雇傭日期:{}、角色:{}",
emp.getEmpno(),emp.getEname(),emp.getSal(),
emp.getHiredate(),emp.getRoles());
returnnull;
}
@GetMapping("get") //子路徑
//控制層get()方法在數(shù)據(jù)接收時(shí)雖然定義了驗(yàn)證規(guī)則,但是由于required屬性為false,不會觸發(fā)驗(yàn)證操作
@RequestDataValidate(required=false,value="empno:long")
publicModelAndViewget(longempno){
LOGGER.info("【查詢雇員信息】雇員編號:{}",empno);
returnnull;
}
@DeleteMapping("delete") //子路徑
@RequestDataValidate("ids:longs")
publicModelAndViewdelete(longids[]){
LOGGER.info("【刪除雇員信息】雇員編號:{}",Arrays.toString(ids));
returnnull;
}
}curl路徑測試執(zhí)行雇員數(shù)據(jù)增加操作curl-XPOST-d"empno=7369&ename=smith&sal=2450&hiredate=1979-09-19&roles=news&roles=system&roles=message""http://localhost:8080/pages/emp/add"根據(jù)編號查詢雇員信息curl-XGET"http://localhost:8080/pages/emp/get?empno=7369"執(zhí)行雇員數(shù)據(jù)修改操作curl-XPOST"http://localhost:8080/pages/emp/edit"-H"Content-Type:application/json;charset=utf-8"-d"{\"empno\":\"7369\",\"ename\":\"Smith\",\"hiredate\":\"1969-09-19\",\"sal\":\"800\",\"roles\":[\"news\",\"system\",\"message\"]}"根據(jù)編號刪除雇員數(shù)據(jù)curl-XDELETE"http://localhost:8080/pages/emp/delete?ids=7369&ids=7566&ids=7839"數(shù)據(jù)驗(yàn)證處理在使用@RequestDataValidate注解進(jìn)行請求攔截規(guī)則配置時(shí),所有的規(guī)則采用的都是“參數(shù)名稱:驗(yàn)證類型”的形式傳遞的,而后多個(gè)驗(yàn)證規(guī)則之間使用分號“;”分割,這時(shí)就可以針對于不同的規(guī)則調(diào)用IValidateRule接口的實(shí)現(xiàn)類進(jìn)行驗(yàn)證處理。為了程序設(shè)計(jì)結(jié)構(gòu)的管理,可以在定義一個(gè)ValidateUtils工具類,以封裝驗(yàn)證操作的處理邏輯ValidateUtils工具類packagemon.util;
publicclassValidateUtils{ //數(shù)據(jù)驗(yàn)證處理
privatefinalstaticLoggerLOGGER=
LoggerFactory.getLogger(RequestDataValidateInterceptor.class);
privateValidateUtils(){} //禁止生成實(shí)例
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 現(xiàn)代科技輔助下的空間認(rèn)知教學(xué)
- 科技與健康的結(jié)合孕婦瑜伽的應(yīng)用
- 2024年臨床醫(yī)療管理信息系統(tǒng)項(xiàng)目資金需求報(bào)告代可行性研究報(bào)告
- 讓孩子在探索中學(xué)習(xí)
- 數(shù)學(xué)思維訓(xùn)練提升低年級學(xué)生問題解決能力的方法
- 科技企業(yè)創(chuàng)新型發(fā)展戰(zhàn)略研究
- 二零二五年度健康美食廚師聘用及合作開發(fā)合同3篇
- 2025年北師大版九年級歷史下冊階段測試試卷含答案
- 2025年新科版八年級地理上冊月考試卷
- 2025年華師大新版一年級語文下冊階段測試試卷含答案
- 定額〔2025〕1號文-關(guān)于發(fā)布2018版電力建設(shè)工程概預(yù)算定額2024年度價(jià)格水平調(diào)整的通知
- 2024年城市軌道交通設(shè)備維保及安全檢查合同3篇
- 【教案】+同一直線上二力的合成(教學(xué)設(shè)計(jì))(人教版2024)八年級物理下冊
- 湖北省武漢市青山區(qū)2023-2024學(xué)年七年級上學(xué)期期末質(zhì)量檢測數(shù)學(xué)試卷(含解析)
- 單位往個(gè)人轉(zhuǎn)賬的合同(2篇)
- 電梯操作證及電梯維修人員資格(特種作業(yè))考試題及答案
- 科研倫理審查與違規(guī)處理考核試卷
- GB/T 44101-2024中國式摔跤課程學(xué)生運(yùn)動能力測評規(guī)范
- 鍋爐本體安裝單位工程驗(yàn)收表格
- 高危妊娠的評估和護(hù)理
- 2024年山東鐵投集團(tuán)招聘筆試參考題庫含答案解析
評論
0/150
提交評論