" />

最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

關(guān)于request.getRequestDispatcher().forward()的妙用及DispatcherType對(duì)Filter配置的影響

 更新時(shí)間:2024年01月23日 10:02:19   作者:shuxiaohua  
這篇文章主要介紹了關(guān)于request.getRequestDispatcher().forward()的妙用及DispatcherType對(duì)Filter配置的影響,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

背景

我們應(yīng)用如上圖所示,Nignx做負(fù)債均衡,微服務(wù)間使用feign進(jìn)行調(diào)用。

為了方便鑒權(quán)Filter配置攔截的url以及nginx配置對(duì)外暴露的url,我們?yōu)樗蟹?wù)設(shè)計(jì)了統(tǒng)一的url規(guī)范

類型用途
v1/xx給前端用的url
v5/xx內(nèi)部接口,服務(wù)間調(diào)用

因此所有服務(wù)都未配置server.servlet.context-path

那么問題來了,現(xiàn)在我們要把服務(wù)從虛擬機(jī)遷移到docker中。

使用公司的docker需要有用于分發(fā)的文根,因?yàn)閐ocker服務(wù)提供了公共域名(減小各個(gè)產(chǎn)品各自去申請(qǐng)域名的工作量),這樣url必須有前綴文根讓公共域名知道請(qǐng)求往哪個(gè)應(yīng)用分發(fā)。

同時(shí)docker的ip是變化的。

配置nignx的upstream時(shí)只能配置域名,不能在像之前配置虛擬機(jī)的ip。

為了降低改動(dòng)量,因此在保留原有的url外,重新提供一套帶文根的url。–這樣既能保證nginx能夠方便的使用域名來配置upstream,也能保證之前feign調(diào)用的url保持不變。

另外為了保證之前配置的Filter對(duì)新url生效,請(qǐng)求進(jìn)來后需要重定向到老url上。

備注:

上述所說的文根并非指servlet中的context-path,context-path只是servlet中的概念,對(duì)于HTTP或者nginx來講,他們是沒有context-path的概念的,他們只是需要利用url中的一小節(jié)進(jìn)行路由分發(fā)。

因此我們?cè)趘1/xx的基礎(chǔ)上增加/a/v1/xx的url即可,至于是通過配置context-path實(shí)現(xiàn),還是通過配置servlet-path實(shí)現(xiàn)都是可以的。

工作一-新增一套帶文根的url

新增一套帶文根的url,同時(shí)又保留老的url,只能給spring的DispatcherServlet新增一個(gè)servlet-path,類似于用原生servlet開發(fā)應(yīng)用時(shí),給一個(gè)servlet配置多個(gè)url。

<servlet-mapping>
    <servlet-name>RedServlet</servlet-name>
    <url-pattern>/red/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>RedServlet</servlet-name>
    <url-pattern>/red/red/*</url-pattern>
</servlet-mapping>

配置方法如下,因?yàn)樾聈rl和老url都要走到同一個(gè)業(yè)務(wù)類中,所以得復(fù)用spring自己自動(dòng)配置的DispatcherServlet。

spring自動(dòng)配置的DispatcherServlet見

org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration.DispatcherServletRegistrationConfiguration#dispatcherServletRegistration

@Bean
    public ServletRegistrationBean dispatcherServletWithNewPrefix(DispatcherServlet dispatcherServlet) {
        ServletRegistrationBean registration = new ServletRegistrationBean(dispatcherServlet,
                "/ceshi/*");
        registration.setName("ceshi");
        return registration;
    }

閉坑指導(dǎo)一

如上代碼配置ServletRegistrationBean時(shí),一定重新配置Name。

因?yàn)閠omcat在配置servlet的時(shí)候會(huì)根據(jù)servletName進(jìn)行去重,如果有同名的servlet,后面的會(huì)注冊(cè)失敗。

詳細(xì)的可以跟蹤org.springframework.boot.web.servlet.ServletRegistrationBean#addRegistration斷點(diǎn)往下看,這里重點(diǎn)關(guān)注對(duì)應(yīng)的邏輯

org.apache.catalina.core.ApplicationContext#addServlet(java.lang.String, java.lang.String, javax.servlet.Servlet, java.util.Map<java.lang.String,java.lang.String>)。

    private ServletRegistration.Dynamic addServlet(String servletName, String servletClass,
            Servlet servlet, Map<String,String> initParams) throws IllegalStateException {
        # 此處參數(shù)校驗(yàn)等不重要的邏輯
        
        # 通過本次要注冊(cè)的servletName獲取之前注冊(cè)過的
        Wrapper wrapper = (Wrapper) context.findChild(servletName);


        if (wrapper == null) {
            wrapper = context.createWrapper();
            wrapper.setName(servletName);
            context.addChild(wrapper);
        } else {
            # wrapper不為空說明之前有同名servlet,此次的servlet不在進(jìn)行注冊(cè)
            if (wrapper.getName() != null &&
                    wrapper.getServletClass() != null) {
                if (wrapper.isOverridable()) {
                    wrapper.setOverridable(false);
                } else {
                    return null;
                }
            }
        }
        ... ...
 }

閉坑指導(dǎo)二

如果按照上述配置代碼,僅僅只是給dispatcherServlet配置新的url及name,會(huì)導(dǎo)致上傳附件功能異常。

 @RequestMapping(value = "/xx/upload", method = RequestMethod.POST)
 public handleFormUpload(@RequestParam("file") MultipartFile file)

上述接口在處理附件時(shí)會(huì)拋出以下異常

o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] threw exception
java.lang.IllegalStateException: Unable to process parts as no multi-part configuration has been provided
    at org.apache.catalina.connector.Request.parseParts(Request.java:2866)
    at org.apache.catalina.connector.Request.getParts(Request.java:2834)
    at org.apache.catalina.connector.RequestFacade.getParts(RequestFacade.java:1098)
    at javax.servlet.http.HttpServletRequestWrapper.getParts(HttpServletRequestWrapper.java:361)
    at javax.servlet.http.HttpServletRequestWrapper.getParts(HttpServletRequestWrapper.java:361)
    at javax.servlet.http.HttpServletRequestWrapper.getParts(HttpServletRequestWrapper.java:361)
    at javax.servlet.http.HttpServletRequestWrapper.getParts(HttpServletRequestWrapper.java:361)
    at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.parseRequest(StandardMultipartHttpServletRequest.java:95)
    at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.<init>(StandardMultipartHttpServletRequest.java:88)
    at org.springframework.web.multipart.support.StandardServletMultipartResolver.resolveMultipart(StandardServletMultipartResolver.java:122)
    at org.springframework.web.servlet.DispatcherServlet.checkMultipart(DispatcherServlet.java:1205)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1039)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
    at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:681)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:764)
    ... ...

跟蹤堆棧,結(jié)合相關(guān)分析可得到如下信息:

1.spring mvc為了簡(jiǎn)化附件上傳,在進(jìn)入業(yè)務(wù)處理前,對(duì)先對(duì)附件進(jìn)解析,即堆棧中的

org.springframework.web.servlet.DispatcherServlet.checkMultipart(DispatcherServlet.java:1205)

解析后將HttpServletRequest轉(zhuǎn)換成MultipartHttpServletRequest,方便上層使用,獲取附件。

2.serlvet3.0開始,HttpServletRequest接口新增了getPart接口,用于方便的處理附件上傳(multipart/form-data類型的請(qǐng)求)。

    public Collection<Part> getParts() throws IOException, ServletException;
    public Part getPart(String name) throws IOException, ServletException;

新版本的spring mvc解析附件時(shí),不在通過commons-fileupload進(jìn)行處理,而是直接使用的servlet原生的api。

追蹤堆棧可以看到如下代碼

org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.parseRequest(StandardMultipartHttpServletRequest.java:95)

	private void parseRequest(HttpServletRequest request) {
		try {
		    # 使用servlet原生的API對(duì)附件進(jìn)行處理
			Collection<Part> parts = request.getParts();
			this.multipartParameterNames = new LinkedHashSet<>(parts.size());
			MultiValueMap<String, MultipartFile> files = new LinkedMultiValueMap<>(parts.size());
			for (Part part : parts) {
				... 
			}
			setMultipartFiles(files);
		}
	}

3.跟蹤堆棧,可以定位到追蹤拋出異常代碼的位置

    private void parseParts(boolean explicit) {
        ... 
        Context context = getContext();
        MultipartConfigElement mce = getWrapper().getMultipartConfigElement();

        if (mce == null) {
            if(context.getAllowCasualMultipartParsing()) {
                mce = new MultipartConfigElement(null, connector.getMaxPostSize(),
                        connector.getMaxPostSize(), connector.getMaxPostSize());
            } else {
                if (explicit) {
                    # 異常在這里拋出
                    partsParseException = new IllegalStateException(
                            sm.getString("coyoteRequest.noMultipartConfig"));
                    return;
                } else {
                    parts = Collections.emptyList();
                    return;
                }
            }
    }

對(duì)比沒改造之前,發(fā)現(xiàn)就是因?yàn)閙ce為空導(dǎo)致。經(jīng)過調(diào)試及查閱相關(guān)信息可知

MultipartConfigElement mce = getWrapper().getMultipartConfigElement(),這一行是獲取servlet上傳附件的配置。

默認(rèn)情況下(allowCasualMultipartParsing=false),如果不配置multipartConfig的情況下使用getPart接口會(huì)拋異常。

如何配置multipartConfig,可見spring boot對(duì)dispatch的自動(dòng)配置。

org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration.DispatcherServletRegistrationConfiguration#dispatcherServletRegistration

		public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
				WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
			DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
					webMvcProperties.getServlet().getPath());
			registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
			registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
			# 對(duì)servlet的MultipartConfig進(jìn)行配置
			multipartConfig.ifAvailable(registration::setMultipartConfig);
			return registration;
		}

當(dāng)然我們也可以配置全局開關(guān)allowCasualMultipartParsing,詳見百度。

工作二-訪問新url時(shí),內(nèi)部重定向到老url上

@WebFilter("/ceshi/*")
public class TestFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String dispatcher = httpServletRequest.getPathInfo();
        request.getRequestDispatcher(dispatcher).forward(request, response);
    }
}

閉坑指導(dǎo)一

Filter里面不能在調(diào)用chain.doFilter(request,response)

chain代表本次請(qǐng)求的執(zhí)行流程,里面包含了需要執(zhí)行的Filter以及servlet,如果執(zhí)行完forward后再調(diào)用chain.doFilter,會(huì)將本該丟棄的流程重新執(zhí)行一遍。

閉坑指導(dǎo)二-Filter的類型

forward后會(huì)調(diào)用那些Filter,是之前流程中還沒調(diào)用的Filter嗎?

要回答這個(gè)問題需要跟蹤forward后的源碼。

具體邏輯見org.apache.catalina.core.ApplicationFilterFactory#createFilterChain

調(diào)用棧如下

    public static ApplicationFilterChain createFilterChain(ServletRequest request,
            Wrapper wrapper, Servlet servlet) {
        # 去掉多余的參數(shù)校驗(yàn),保留主邏輯方便代碼閱讀
        
        ApplicationFilterChain filterChain = new ApplicationFilterChain();
        
        filterChain.setServlet(servlet);
        filterChain.setServletSupportsAsync(wrapper.isAsyncSupported());

        StandardContext context = (StandardContext) wrapper.getParent();
        FilterMap filterMaps[] = context.findFilterMaps();

        # 重定向后DispatcherType為FORWARD
        DispatcherType dispatcher =
                (DispatcherType) request.getAttribute(Globals.DISPATCHER_TYPE_ATTR);

        String requestPath = null;
        Object attribute = request.getAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR);
        if (attribute != null){
            requestPath = attribute.toString();
        }

        String servletName = wrapper.getName();

        // Add the relevant path-mapped filters to this filter chain
        for (FilterMap filterMap : filterMaps) {
            # 判斷Filter的DispatcherType是否匹配
            if (!matchDispatcher(filterMap, dispatcher)) {
                continue;
            }
            # 判斷Filter的url是否匹配
            if (!matchFiltersURL(filterMap, requestPath))
                continue;
            ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                    context.findFilterConfig(filterMap.getFilterName());
            filterChain.addFilter(filterConfig);
        }

        // Add filters that match on servlet name second
        for (FilterMap filterMap : filterMaps) {
            if (!matchDispatcher(filterMap, dispatcher)) {
                continue;
            }
            # 判斷Filter是否匹配該servletName
            if (!matchFiltersServlet(filterMap, servletName))
                continue;
            ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                    context.findFilterConfig(filterMap.getFilterName());
            filterChain.addFilter(filterConfig);
        }
        return filterChain;
    }
    private static boolean matchDispatcher(FilterMap filterMap, DispatcherType type) {
        switch (type) {
            case FORWARD :
                if ((filterMap.getDispatcherMapping() & FilterMap.FORWARD) != 0) {
                    return true;
                }
                break;
            case INCLUDE :
                if ((filterMap.getDispatcherMapping() & FilterMap.INCLUDE) != 0) {
                    return true;
                }
                break;
            case REQUEST :
                if ((filterMap.getDispatcherMapping() & FilterMap.REQUEST) != 0) {
                    return true;
                }
                break;
            case ERROR :
                if ((filterMap.getDispatcherMapping() & FilterMap.ERROR) != 0) {
                    return true;
                }
                break;
            case ASYNC :
                if ((filterMap.getDispatcherMapping() & FilterMap.ASYNC) != 0) {
                    return true;
                }
                break;
        }
        return false;
    }

由上可見,不管是正常的處理,還是從定向后的處理,篩選Filter時(shí)都遵從統(tǒng)一的邏輯,即DispatcherType是否滿足、path是否滿足、servlet是否滿足。

ps:

看來國(guó)外小哥寫代碼也不咋滴啊,上面兩個(gè)循環(huán)命名可以合并成一個(gè)的。

不僅如此,上述代碼還會(huì)導(dǎo)致同時(shí)滿足path和servletName的filter會(huì)重復(fù)添加

for (FilterMap filterMap : filterMaps) {
    if (!matchDispatcher(filterMap, dispatcher)) {
        continue;
    }
    if (!matchFiltersURL(filterMap, requestPath) && !matchFiltersServlet(filterMap, servletName))
        continue;
    ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                    context.findFilterConfig(filterMap.getFilterName());
    filterChain.addFilter(filterConfig);
}

總結(jié)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Mybatis insert方法主鍵回填和自定義操作

    Mybatis insert方法主鍵回填和自定義操作

    這篇文章主要介紹了Mybatis insert方法主鍵回填和自定義操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • springboot+rabbitmq實(shí)現(xiàn)智能家居實(shí)例詳解

    springboot+rabbitmq實(shí)現(xiàn)智能家居實(shí)例詳解

    這篇文章主要為大家介紹了springboot+rabbitmq實(shí)現(xiàn)智能家居的示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-07-07
  • SpringBoot中yml的數(shù)據(jù)綁定示例

    SpringBoot中yml的數(shù)據(jù)綁定示例

    本文主要介紹了SpringBoot中yml的數(shù)據(jù)綁定示例,借助于YAML的簡(jiǎn)潔語法和結(jié)構(gòu)化特性,我們能夠輕松地管理應(yīng)用程序的配置信息,使得配置文件更加清晰易讀,感興趣的可以了解一下
    2023-11-11
  • Java在并發(fā)環(huán)境中SimpleDateFormat多種解決方案

    Java在并發(fā)環(huán)境中SimpleDateFormat多種解決方案

    這篇文章主要介紹了Java在并發(fā)環(huán)境中SimpleDateFormat多種解決方案,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-07-07
  • 使用Java實(shí)現(xiàn)解析Excel公式

    使用Java實(shí)現(xiàn)解析Excel公式

    在日常工作中,我們經(jīng)常需要在Excel中使用公式對(duì)表中數(shù)據(jù)進(jìn)行計(jì)算和分析,所以本文小編主要來和大家介紹一下如何在Java中實(shí)現(xiàn)解析Excel公式,感興趣的可以了解下
    2024-02-02
  • Spring AOP與代理類的執(zhí)行順序級(jí)別淺析

    Spring AOP與代理類的執(zhí)行順序級(jí)別淺析

    這篇文章主要介紹了Spring AOP與代理類的執(zhí)行順序級(jí)別,關(guān)于 Spring AOP和Aspectj的關(guān)系,兩個(gè)都實(shí)現(xiàn)了切面編程,Spring AOP更多地是為了Spring框架本身服務(wù)的,而Aspectj具有更強(qiáng)大、更完善的切面功能
    2023-03-03
  • java解析XML詳解

    java解析XML詳解

    這篇文章主要介紹了XML解析四種方式代碼示例詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2021-07-07
  • java實(shí)現(xiàn)學(xué)籍管理系統(tǒng)

    java實(shí)現(xiàn)學(xué)籍管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)學(xué)籍管理系統(tǒng),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-12-12
  • Java中Integer的valueOf()方法用法及說明

    Java中Integer的valueOf()方法用法及說明

    文章詳細(xì)介紹了`Integer.valueOf()`的三個(gè)重載方法,分別是根據(jù)指定進(jìn)制將字符串轉(zhuǎn)換為十進(jìn)制整數(shù)、將字符串直接轉(zhuǎn)換為十進(jìn)制整數(shù)和將int類型轉(zhuǎn)換為Integer對(duì)象,并給出了具體示例
    2026-04-04
  • SpringBoot?錯(cuò)誤頁(yè)面跳轉(zhuǎn)方式

    SpringBoot?錯(cuò)誤頁(yè)面跳轉(zhuǎn)方式

    這篇文章主要介紹了SpringBoot?錯(cuò)誤頁(yè)面跳轉(zhuǎn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-02-02

最新評(píng)論

民县| 长武县| 苏州市| 大田县| 黄石市| 乾安县| 阿拉尔市| 平邑县| 顺义区| 巢湖市| 康马县| 四子王旗| 丹棱县| 康马县| 神木县| 清涧县| 兰坪| 呼伦贝尔市| 安泽县| 双鸭山市| 孝感市| 射洪县| 汉源县| 岳普湖县| 房山区| 新津县| 迭部县| 富源县| 碌曲县| 萍乡市| 嵊州市| 乌兰浩特市| 扎赉特旗| 东阳市| 怀柔区| 雷州市| 阳西县| 长乐市| 新河县| 澄江县| 洛阳市|