国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專(zhuān)欄INFORMATION COLUMN

讓Controller支持對(duì)平鋪參數(shù)執(zhí)行@Valid數(shù)據(jù)校驗(yàn)

printempw / 802人閱讀

摘要:方案一借助對(duì)方法級(jí)別數(shù)據(jù)校驗(yàn)的能力首先必須明確一點(diǎn)此能力屬于框架的,而部分框架。

每篇一句
在金字塔塔尖的是實(shí)踐,學(xué)而不思則罔,思而不學(xué)則殆(現(xiàn)在很多編程框架都只是教你碎片化的實(shí)踐)
相關(guān)閱讀

【小家Java】深入了解數(shù)據(jù)校驗(yàn):Java Bean Validation 2.0(JSR303、JSR349、JSR380)Hibernate-Validation 6.x使用案例
【小家Spring】@Validated和@Valid的區(qū)別?教你使用它完成Controller參數(shù)校驗(yàn)(含級(jí)聯(lián)屬性校驗(yàn))以及原理分析
【小家Spring】Spring方法級(jí)別數(shù)據(jù)校驗(yàn):@Validated + MethodValidationPostProcessor優(yōu)雅的完成數(shù)據(jù)校驗(yàn)動(dòng)作

對(duì)Spring感興趣可掃碼加入wx群:Java高工、架構(gòu)師3群(文末有二維碼)

前言

我們知道Spring MVC層是默認(rèn)可以支持Bean Validation的,但是我在實(shí)際使用起來(lái)有很多不便之處(相信我的使用痛點(diǎn)也是小伙伴的痛點(diǎn)),就感覺(jué)它是個(gè)半拉子:只支持對(duì)JavaBean的驗(yàn)證,而并不支持對(duì)Controller處理方法的平鋪參數(shù)的校驗(yàn)。

上篇文章一起了解了Spring MVC中對(duì)Controller處理器入?yún)⑿r?yàn)的問(wèn)題,但也僅局限于對(duì)JavaBean的驗(yàn)證。不可否認(rèn)對(duì)JavaBean的校驗(yàn)是我們實(shí)際項(xiàng)目使用中較為常見(jiàn)、使用頻繁的case,關(guān)于此部分詳細(xì)內(nèi)容可參見(jiàn):【小家Spring】@Validated和@Valid的區(qū)別?教你使用它完成Controller參數(shù)校驗(yàn)(含級(jí)聯(lián)屬性校驗(yàn))以及原理分析

在上文我也提出了使用痛點(diǎn):我們Controller控制器方法中入?yún)ⅲ鋵?shí)大部分情況下都是平鋪參數(shù)而非JavaBean的。然而對(duì)于平鋪參數(shù)我們并不能使用@Validated像校驗(yàn)JavaBean一樣去做,并且Spring MVC也并沒(méi)有提供源生的解決方案(其實(shí)提供了,哈哈)。
那怎么辦?難道真的只能自己書(shū)寫(xiě)重復(fù)的if else去完成嗎?當(dāng)然不是,那么本文將對(duì)此常見(jiàn)的痛點(diǎn)問(wèn)題(現(xiàn)象)提供兩種思路,供給使用者參考~

Controller層平鋪參數(shù)的校驗(yàn)

因?yàn)?b>Spring MVC并不天然支持對(duì)控制器方法平鋪參數(shù)的數(shù)據(jù)校驗(yàn),但是這種case的卻有非常的常見(jiàn),因此針對(duì)這種常見(jiàn)現(xiàn)象提供一些可靠的解決方案,對(duì)你的項(xiàng)目的收益是非常高的。

方案一:借助Spring對(duì)方法級(jí)別數(shù)據(jù)校驗(yàn)的能力

首先必須明確一點(diǎn):此能力屬于Spring框架的,而部分web框架Spring MVC。
Spring對(duì)方法級(jí)別數(shù)據(jù)校驗(yàn)的能力非常重要(它能對(duì)Service層、Dao層的校驗(yàn)等),前面也重點(diǎn)分析過(guò),具體使用方式參考本文:【小家Spring】Spring方法級(jí)別數(shù)據(jù)校驗(yàn):@Validated + MethodValidationPostProcessor優(yōu)雅的完成數(shù)據(jù)校驗(yàn)動(dòng)作

使用此種方案來(lái)解決問(wèn)題的步驟比較簡(jiǎn)單,使用起來(lái)也非常方便。下面我寫(xiě)個(gè)簡(jiǎn)單示例作為參考:

@Configuration
@EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurerAdapter {
    @Bean
    public MethodValidationPostProcessor mvcMethodValidationPostProcessor() {
        return new MethodValidationPostProcessor();
    }
}

Controller類(lèi) 上使用@Validated標(biāo)注,然后方法上正常使用約束注解標(biāo)注平鋪的屬性:

@RestController
@RequestMapping
@Validated
public class HelloController {
    @PutMapping("/hello/id/{id}/status/{status}")
    public Object helloGet(@Max(5) @PathVariable Integer id, @Min(5) @PathVariable Integer status) {
        return "hello world";
    }
}

請(qǐng)求:/hello/id/6/status/4 可看見(jiàn)拋異常:

注意一下:這里arg0 arg1并沒(méi)有按照順序來(lái),字段可別對(duì)應(yīng)錯(cuò)了~~~

由此可見(jiàn),校驗(yàn)生效了。拋出了javax.validation.ConstraintViolationException異常,這樣我們?cè)俳Y(jié)合一個(gè)全局異常的處理程序,也就能達(dá)到我們預(yù)定的效果了~

這種方案一樣有一個(gè)非常值得注意但是很多人都會(huì)忽略的地方:因?yàn)槲覀兿M軌虼?b>Controller這個(gè)Bean,所以?xún)H僅只在父容器中配置MethodValidationPostProcessor是無(wú)效的,必須在子容器(web容器)的配置文件中再配置一個(gè)MethodValidationPostProcessor,請(qǐng)務(wù)必注意~

有小伙伴問(wèn)我了,為什么它的項(xiàng)目里只配置了一個(gè)MethodValidationPostProcessor也生效了呢? 我的回答是:檢查一下你是否是用的SpringBoot。

其實(shí)關(guān)于配置一個(gè)還是多個(gè)MethodValidationPostProcessor的case,其實(shí)是個(gè)Bean覆蓋有很大關(guān)系的,這方面內(nèi)容可參考:【小家Spring】聊聊Spring的bean覆蓋(存在同名name/id問(wèn)題),介紹Spring名稱(chēng)生成策略接口BeanNameGenerator

方案二:自己實(shí)現(xiàn),借助HandlerInterceptor做攔截處理(輕量)

方案一的使用已經(jīng)很簡(jiǎn)單了,但我個(gè)人總還覺(jué)得怪怪的,因?yàn)槲乙恢辈幌矚gController層被代理(可能是潔癖吧)。因此針對(duì)這個(gè)現(xiàn)象,我自己接下來(lái)提供一個(gè)自定義攔截器HandlerInterceptor的處理方案來(lái)實(shí)現(xiàn),大家不一定要使用,也是供以參考嘛~
設(shè)計(jì)思路:Controller攔截器 + @Validated注解 + 自定義校驗(yàn)器(當(dāng)然這里面涉及到不少細(xì)節(jié)的:比如入?yún)⒔馕觥⒔壎ǖ鹊葍?nèi)置的API)

1、準(zhǔn)備一個(gè)攔截器ValidationInterceptor用于處理校驗(yàn)邏輯:

// 注意:此處只支持@RequesrMapping方式~~~~
public class ValidationInterceptor implements HandlerInterceptor, InitializingBean {

    @Autowired
    private LocalValidatorFactoryBean validatorFactoryBean;
    @Autowired
    private RequestMappingHandlerAdapter adapter;
    private List argumentResolvers;

    @Override
    public void afterPropertiesSet() throws Exception {
        argumentResolvers = adapter.getArgumentResolvers();
    }

    // 緩存
    private final Map argumentResolverCache = new ConcurrentHashMap<>(256);
    private final Map, Set> initBinderCache = new ConcurrentHashMap<>(64);

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 只處理HandlerMethod方式
        if (handler instanceof HandlerMethod) {
            HandlerMethod method = (HandlerMethod) handler;
            Validated valid = method.getMethodAnnotation(Validated.class); //
            if (valid != null) {
                // 根據(jù)工廠,拿到一個(gè)校驗(yàn)器
                ValidatorImpl validatorImpl = (ValidatorImpl) validatorFactoryBean.getValidator();

                // 拿到該方法所有的參數(shù)們~~~  org.springframework.core.MethodParameter
                MethodParameter[] parameters = method.getMethodParameters();
                Object[] parameterValues = new Object[parameters.length];

                //遍歷所有的入?yún)ⅲ航o每個(gè)參數(shù)做賦值和數(shù)據(jù)綁定
                for (int i = 0; i < parameters.length; i++) {
                    MethodParameter parameter = parameters[i];
                    // 找到適合解析這個(gè)參數(shù)的處理器~
                    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, new ServletWebRequest(request, response), webDataBinderFactory);
                    parameterValues[i] = value; // 賦值
                }

                // 對(duì)入?yún)⑦M(jìn)行統(tǒng)一校驗(yàn)
                Set> violations = validatorImpl.validateParameters(method.getBean(), method.getMethod(), parameterValues, valid.value());
                // 若存在錯(cuò)誤消息,此處也做拋出異常處理 javax.validation.ConstraintViolationException
                if (!violations.isEmpty()) {
                    System.err.println("方法入?yún)⑿r?yàn)失敗~~~~~~~");
                    throw new ConstraintViolationException(violations);
                }
            }

        }

        return true;
    }

    private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) {
        Class handlerType = handlerMethod.getBeanType();
        Set methods = this.initBinderCache.get(handlerType);
        if (methods == null) {
            // 支持到@InitBinder注解
            methods = MethodIntrospector.selectMethods(handlerType, RequestMappingHandlerAdapter.INIT_BINDER_METHODS);
            this.initBinderCache.put(handlerType, methods);
        }
        List initBinderMethods = new ArrayList<>();
        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 (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 {
    }
    
}

2、配置攔截器到Web容器里(攔截所有請(qǐng)求),并且自己配置一個(gè)LocalValidatorFactoryBean

@Configuration
@EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurerAdapter {

    // 自己配置校驗(yàn)器的工廠  自己隨意定制化哦~
    @Bean
    public LocalValidatorFactoryBean localValidatorFactoryBean() {
        return new LocalValidatorFactoryBean();
    }

    // 配置用于校驗(yàn)的攔截器
    @Bean
    public ValidationInterceptor validationInterceptor() {
        return new ValidationInterceptor();
    }
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(validationInterceptor()).addPathPatterns("/**");
    }
}

3、Controller方法(只需要在方法上標(biāo)注即可)上標(biāo)注@Validated注解:

    @Validated // 只需要方法處標(biāo)注注解即可 非常簡(jiǎn)便
    @GetMapping("/hello/id/{id}/status/{status}")
    public Object helloGet(@Max(5) @PathVariable("id") Integer id, @Min(5) @PathVariable("status") Integer status) {
        return "hello world";
    }

訪問(wèn)/hello/id/6/status/4 能看到如下異常:

同樣的完美完成了我們的校驗(yàn)需求。針對(duì)我自己書(shū)寫(xiě)的這一套,這里繼續(xù)有必要再說(shuō)說(shuō)兩個(gè)小細(xì)節(jié):

本例的@PathVariable("id")是指定的value值的,因?yàn)樵谔幚?b>@PathVariable過(guò)程中我并沒(méi)有去分析字節(jié)碼來(lái)得到形參名,所以為了簡(jiǎn)便此處寫(xiě)上value值,當(dāng)然這里是可以?xún)?yōu)化的,有興趣的小伙伴可自行定制

因?yàn)橹贫?b>value值,錯(cuò)誤信息中也能正確識(shí)別出字段名了~

Spring MVC的自動(dòng)數(shù)據(jù)封裝體系中,value值不是必須的,只要字段名對(duì)應(yīng)上了也是ok的(這里面運(yùn)用了字節(jié)碼技術(shù),后文有講解)。但是在數(shù)據(jù)校驗(yàn)中,它可并沒(méi)有用到字節(jié)碼結(jié)束,請(qǐng)注意做出區(qū)分~~~

總結(jié)

本文介紹了兩種方案來(lái)處理我們平時(shí)遇到Controller中對(duì)處理方法平鋪類(lèi)型的數(shù)據(jù)校驗(yàn)問(wèn)題,至于具體你選擇哪種方案當(dāng)然是仁者見(jiàn)仁了。(方案一簡(jiǎn)便,方案二需要你對(duì)Spring MVC的處理流程API很熟練,可炫技)

數(shù)據(jù)校驗(yàn)相關(guān)知識(shí)介紹至此,不管是Java上的數(shù)據(jù)校驗(yàn),還是Spring上的數(shù)據(jù)校驗(yàn),都可以統(tǒng)一使用優(yōu)雅的Bean Validation來(lái)完成了。希望這么長(zhǎng)時(shí)間來(lái)講的內(nèi)容能對(duì)你的項(xiàng)目有實(shí)地的作用,真的能讓你的工程變得更加的簡(jiǎn)介,甚至高能。畢竟真正做技術(shù)的人都是追求一定的極致性,甚至是存在代碼潔癖,甚至是偏執(zhí)的~

此種潔癖據(jù)我了解表現(xiàn)在多個(gè)方面:比如沒(méi)使用的變量一定要?jiǎng)h除、代碼格式不好看一定要格式化、看到重復(fù)代碼一定要提取公因子等等~
知識(shí)交流
若文章格式混亂,可點(diǎn)擊:原文鏈接-原文鏈接-原文鏈接-原文鏈接-原文鏈接

==The last:如果覺(jué)得本文對(duì)你有幫助,不妨點(diǎn)個(gè)贊唄。當(dāng)然分享到你的朋友圈讓更多小伙伴看到也是被作者本人許可的~==

**若對(duì)技術(shù)內(nèi)容感興趣可以加入wx群交流:Java高工、架構(gòu)師3群
若群二維碼失效,請(qǐng)加wx號(hào):fsx641385712(或者掃描下方wx二維碼)。并且備注:"java入群" 字樣,會(huì)手動(dòng)邀請(qǐng)入群**

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/75695.html

相關(guān)文章

  • @Validated和@Valid的區(qū)別?校驗(yàn)級(jí)聯(lián)屬性(內(nèi)部類(lèi))

    摘要:畢竟永遠(yuǎn)相信本文能給你帶來(lái)意想不到的收獲使用示例關(guān)于數(shù)據(jù)校驗(yàn)這一塊在中的使用案例,我相信但凡有點(diǎn)經(jīng)驗(yàn)的程序員應(yīng)該沒(méi)有不會(huì)使用的,并且還不乏熟練的選手。 每篇一句 NBA里有兩大笑話(huà):一是科比沒(méi)天賦,二是詹姆斯沒(méi)技術(shù) 相關(guān)閱讀 【小家Java】深入了解數(shù)據(jù)校驗(yàn):Java Bean Validation 2.0(JSR303、JSR349、JSR380)Hibernate-Validati...

    Winer 評(píng)論0 收藏0
  • 從深處去掌握數(shù)據(jù)校驗(yàn)@Valid的作用(級(jí)聯(lián)校驗(yàn)

    摘要:如果說(shuō)要使用數(shù)據(jù)校驗(yàn),我十分相信小伙伴們都能夠使用,但估計(jì)大都是有個(gè)前提的環(huán)境。具體使用可參考小家讓支持對(duì)平鋪參數(shù)執(zhí)行數(shù)據(jù)校驗(yàn)?zāi)J(rèn)使用只能對(duì)進(jìn)行校驗(yàn)級(jí)聯(lián)校驗(yàn)什么叫級(jí)聯(lián)校驗(yàn),其實(shí)就是帶校驗(yàn)的成員里存在級(jí)聯(lián)對(duì)象時(shí),也要對(duì)它完成校驗(yàn)。 每篇一句 NBA里有兩大笑話(huà):一是科比沒(méi)天賦,二是詹姆斯沒(méi)技術(shù) 相關(guān)閱讀 【小家Java】深入了解數(shù)據(jù)校驗(yàn):Java Bean Validation 2.0(...

    BlackFlagBin 評(píng)論0 收藏0
  • Spring方法級(jí)別數(shù)據(jù)校驗(yàn):@Validated + MethodValidationPostPr

    摘要:就這樣借助相關(guān)約束注解,就非常簡(jiǎn)單明了,語(yǔ)義清晰的優(yōu)雅的完成了方法級(jí)別入?yún)⑿r?yàn)返回值校驗(yàn)的校驗(yàn)。但倘若是返回值校驗(yàn)執(zhí)行了即使是失敗了,方法體也肯定被執(zhí)行了只能哪些類(lèi)型上提出這個(gè)細(xì)節(jié)的目的是約束注解并不是能用在所有類(lèi)型上的。 每篇一句 在《深度工作》中作者提出這么一個(gè)公式:高質(zhì)量產(chǎn)出=時(shí)間*專(zhuān)注度。所以高質(zhì)量的產(chǎn)出不是靠時(shí)間熬出來(lái)的,而是效率為王 相關(guān)閱讀 【小家Java】深入了解數(shù)據(jù)校...

    孫吉亮 評(píng)論0 收藏0
  • Spring 參數(shù)校驗(yàn)最佳實(shí)踐(附完整實(shí)例)

    摘要:否則非法請(qǐng)求參數(shù)小則影響用戶(hù)體驗(yàn)或者產(chǎn)生垃圾數(shù)據(jù),大則會(huì)拖跨整個(gè)系統(tǒng)其次,手工對(duì)所有的參數(shù)進(jìn)行校驗(yàn)相當(dāng)繁瑣,容易出錯(cuò),而且最后,通過(guò)工具來(lái)完成其實(shí)是比較好的方式,但是必須讓工具變得優(yōu)雅一些。 聲明:本文屬原創(chuàng)文章,始發(fā)于公號(hào):程序員自學(xué)之道,同步發(fā)布到 sf,轉(zhuǎn)載請(qǐng)注明出處。 不夠好的方案 在 Web 開(kāi)發(fā)中, 我們經(jīng)常需要校驗(yàn)各種參數(shù),這是一件繁瑣又重要的事情,對(duì)于很多人來(lái)說(shuō),在做參...

    tomlingtm 評(píng)論0 收藏0
  • Learn Spring - Spring MVC

    摘要:例如,將請(qǐng)求信息中的字符串格式參數(shù)轉(zhuǎn)換為對(duì)應(yīng)方法中的類(lèi)類(lèi)型入?yún)⒖赏ㄟ^(guò)的屬性注冊(cè)自定義轉(zhuǎn)換器。 1. 處理流程 請(qǐng)求提交給DispatchServlet 查找HandlerMapping 調(diào)用由HandlerAdapter封裝后的Handler 返回ModelAndView到DispatcherServlet 借由ViewResolver完成邏輯視圖到真實(shí)視圖的轉(zhuǎn)換 返回響應(yīng) 2. ...

    DesGemini 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<