`
xiangshouxiyang
  • 浏览: 48193 次
  • 性别: Icon_minigender_1
  • 来自: 厦门
社区版块
存档分类
最新评论

springboot使用undertow作为web容器而引发的中文乱码问题

阅读更多

 先说一下出现这种情况的场景。该场景在使用jetty,tomcat作为容器时是可以正常运行的。首先是表单提交。

<form id="submitForm" action="${base}/test/aa.html" method="post">
            <input type="hidden" name="userSource" value="admin"/>
            <input type="hidden" id="hid_modulus" name="modulus" value="$!{modulus}"/>
 </form> 

 springmvc控制器代码如下:

@RequestMapping("test")
@Controller
public class TestController {

    @RequestMapping("aa.html")
    public String aa(Model model, RedirectAttributes redirectAttributes) {
        redirectAttributes.addAttribute("a","李四");
        return  "redirect:bb.html";
    }

    @RequestMapping("bb.html")
    public String bb(Model model, HttpServletRequest request) {
        String a=request.getParameter("a");
        return "login";
    }
}

 aa方法做了一次重定向,重定向时传参是中文,到进入bb方法时,获取到的string a则为乱码。

查看项目已使用了spring提供的字符集设置过滤器CharacterEncodingFilter。强制设置字符集为utf-8。却依然出现乱码问题。

     没办法,只能查看springmvc的源码。跟踪源码后发现,在RedirectView类的createTargetUrl方法里,会去为重定向拼装url字符串:

protected final String createTargetUrl(Map<String, Object> model, HttpServletRequest request)
			throws UnsupportedEncodingException {

		// Prepare target URL.
		StringBuilder targetUrl = new StringBuilder();
		if (this.contextRelative && getUrl().startsWith("/")) {
			// Do not apply context path to relative URLs.
			targetUrl.append(request.getContextPath());
		}
		targetUrl.append(getUrl());

		String enc = this.encodingScheme;
		if (enc == null) {
			enc = request.getCharacterEncoding();
		}
		if (enc == null) {
			enc = WebUtils.DEFAULT_CHARACTER_ENCODING;
		}

		if (this.expandUriTemplateVariables && StringUtils.hasText(targetUrl)) {
			Map<String, String> variables = getCurrentRequestUriVariables(request);
			targetUrl = replaceUriTemplateVariables(targetUrl.toString(), model, variables, enc);
		}
		if (isPropagateQueryProperties()) {
		 	appendCurrentQueryParams(targetUrl, request);
		}
		if (this.exposeModelAttributes) {
			appendQueryProperties(targetUrl, model, enc);
		}

		return targetUrl.toString();
	}

 debug发现,在使用post做form表单提交时,request.getCharacterEncoding()获得的是null,故导致执行到了enc = WebUtils.DEFAULT_CHARACTER_ENCODING这行代码

public static final String DEFAULT_CHARACTER_ENCODING = "ISO-8859-1";

 使用了ISO-8859-1,故导致重定向的url字符串里没有使用utf-8字符集,导致了乱码。

      那为什么request.getCharacterEncoding()获得的是null呢。在CharacterEncodingFilter过滤器里,

@Override
	protected void doFilterInternal(
			HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {

		String encoding = getEncoding();
		if (encoding != null) {
			if (isForceRequestEncoding() || request.getCharacterEncoding() == null) {
				request.setCharacterEncoding(encoding);
			}
			if (isForceResponseEncoding()) {
				response.setCharacterEncoding(encoding);
			}
		}
		filterChain.doFilter(request, response);
	}

 该过滤器会强制使用utf-8。在debug之后,发现request.setCharacterEncoding(encoding)这行代码确实有执行到。再看request.setCharacterEncoding方法里的实现,io.undertow.servlet.spec.HttpServletRequestImpl:

@Override
    public void setCharacterEncoding(final String env) throws UnsupportedEncodingException {
        if (readStarted) {
            return;
        }
        try {
            characterEncoding = Charset.forName(env);

            final ManagedServlet originalServlet = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY).getOriginalServletPathMatch().getServletChain().getManagedServlet();
            final FormDataParser parser = originalServlet.getFormParserFactory().createParser(exchange);
            if (parser != null) {
                parser.setCharacterEncoding(env);
            }
        } catch (UnsupportedCharsetException e) {
            throw new UnsupportedEncodingException();
        }
    }

 debug发现,readStarted为true。故CharacterEncodingFilter过滤器设置request.setCharacterEncoding无效。

注意,HttpServletRequestImpl是undertow容器的实现,springboot集成了undertow作为web服务器。

     在HttpServletRequestImpl的private FormData parseFormData()方法上设置断点,发现在进入CharacterEncodingFilter过滤器前,undertow容器会先进入parseFormData方法,调用堆栈如下:

at io.undertow.servlet.spec.HttpServletRequestImpl.parseFormData(HttpServletRequestImpl.java:750)
	  at io.undertow.servlet.spec.HttpServletRequestImpl.getParameter(HttpServletRequestImpl.java:636)
	  at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:70)
	  at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	  at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
	  at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
	  at io.undertow.servlet.handlers.FilterHandler.handleRequest(FilterHandler.java:84)
	  at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62)
	  at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)
	  at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:131)
	  at io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57)
	  at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
	  at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46)
	  at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64)
	  at io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:60)
	  at io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:77)
	  at io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43)
	  at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
	  at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
	  at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:285)
	  at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:264)
	  at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:81)
	  at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:175)
	  at io.undertow.server.Connectors.executeRootHandler(Connectors.java:207)
	  at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:802)
	  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
	  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
	  at java.lang.Thread.run(Thread.java:744)

 parseFormData()如下:

private FormData parseFormData() {
        if (parsedFormData == null) {
            if (readStarted) {
                return null;
            }
            final ManagedServlet originalServlet = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY).getCurrentServlet().getManagedServlet();
            final FormDataParser parser = originalServlet.getFormParserFactory().createParser(exchange);
            if (parser == null) {
                return null;
            }
            readStarted = true;
            try {
                return parsedFormData = parser.parseBlocking();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return parsedFormData;
    }

 注意,该方法把readStarted设置为true,所以在进入CharacterEncodingFilter过滤器后,调用request.setCharacterEncoding时,其实什么都没有处理,则导致springmvc在拼装重定向url字符串时,没有使用utf-8字符集,导致出现了乱码。至此出现乱码的原因分析完毕。至于为什么容器会去调用 parseFormData(),大家可以查看调用堆栈里每一步的实现,看看是怎么一步步调用到parseFormData(),我觉得这是undertow容器的一个bug。

        分析完原因,给出解决方案,这样才完整。我这里的解决方案是添加一个web过滤器,利用装饰器模式,改写request.getCharacterEncoding()方法,当该方法获得为空时,默认返回utf-8。

Utf8EncodingFilter:

package com.onlyou.superp2b;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.filter.GenericFilterBean;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
 * 默认使用utf-8字符集
 * Created by cd_huang on 2017/7/25.
 */
public class Utf8EncodingFilter extends GenericFilterBean {
	private static Logger logger = LoggerFactory.getLogger(Utf8EncodingFilter.class);
	@Override
	public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
		Utf8EncodingRequest request =new Utf8EncodingRequest((HttpServletRequest)servletRequest);
		filterChain.doFilter(request,servletResponse);
	}

	protected void initFilterBean() throws ServletException {
		logger.info("---加载Utf8EncodingFilter!---");
	}
}

 Utf8EncodingRequest:

package com.onlyou.superp2b;

import org.apache.commons.lang3.StringUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

/**
 * 默认使用utf-8字符集
 * Created by cd_huang on 2017/7/25.
 */
public class Utf8EncodingRequest extends HttpServletRequestWrapper {

	public Utf8EncodingRequest(HttpServletRequest request) {
		super(request);
	}
	@Override
	public String getCharacterEncoding() {
		String characterEncoding =this.getRequest().getCharacterEncoding();
		return StringUtils.isBlank(characterEncoding)?"utf-8":characterEncoding;
	}
}

 最后,测试了下,确实解决了使用undertow作为web容器带来的中文乱码的问题。

分享到:
评论
2 楼 xiangshouxiyang 2017-11-09  
zhanghs886 写道
如果集成的springboot里面没有web.xml文件的配置,那么这个乱码问题要怎么解决呢

springboot有一套完整的体系来替代web.xml。添加过滤器可以类似这样:
@Bean
    public FilterRegistrationBean filterUserContextFilter() {
        FilterRegistrationBean registration = new FilterRegistrationBean(new UserContextFilter());
        registration.addUrlPatterns("*.htm","*.html","*.json");
        return registration;
    }
1 楼 zhanghs886 2017-11-06  
如果集成的springboot里面没有web.xml文件的配置,那么这个乱码问题要怎么解决呢

相关推荐

    Java开发案例-springboot-55-undertow替换默认tomcat容器-源代码+文档.rar

    Java开发案例-springboot-55-undertow替换默认tomcat容器-源代码+文档.rar Java开发案例-springboot-55-undertow替换默认tomcat容器-源代码+文档.rar Java开发案例-springboot-55-undertow替换默认tomcat容器-源代码...

    SpringBoot深入理解之内置web容器及配置的总结

    Undertow是最新的web容器,SpringBoot也支持使用Undertow作为内置容器。Undertow容器提供了许多独特的功能,如异步处理和高性能支持。 内置web容器的配置 SpringBoot中可以通过配置文件或Java代码来配置内置web...

    springboot 使用基础入门

    SpringBoot可以内嵌Tomcat、Jetty或Undertow等Web服务器,这样我们无需额外的部署步骤,可以直接通过`java -jar`命令运行SpringBoot应用。 三、起步依赖(Starter POMs) SpringBoot提供了许多起步依赖,这些依赖...

    使用 Spring Boot 内嵌容器 Undertow创建服务器的方法

    Undertow是一个非常轻量并高性能的web server,它来自 JBoss。支持blocking和non-blocking两种NIO API。接下来通过本文给大家介绍使用Spring Boot 内嵌容器 Undertow创建服务器的方法,感兴趣的朋友一起看看吧

    springboot基本使用和内部原理

    4. **内嵌式Web服务器**:SpringBoot支持内嵌Tomcat、Jetty或Undertow等Web服务器,这样你无需额外部署到外部服务器,只需运行主类即可启动应用。 5. **健康检查与Actuator**:Actuator是SpringBoot的一个模块,...

    SpringBoot新手学习手册

    将Servlet容器变成Undertow 44 SpringBoot JVM参数调优 44 十一、 2.0版本新特性 45 以Java 8 为基准 45 内嵌容器包结构调整 45 Servlet-specific 的server properties调整 45 Actuator 默认映射 46 Spring ...

    Spring Boot实现Undertow服务器同时支持HTTP2、HTTPS的方法

    首先,我们需要了解什么是HTTP2和HTTPS,以及它们的优点和缺点,然后我们将学习如何使用Spring Boot实现Undertow服务器同时支持HTTP2和HTTPS。 什么是HTTP2? HTTP2是HTTP协议自1999年HTTP1.1发布后的首个更新,...

    基于SpringBoot的web项目模板

    2. **内嵌Web服务器**:SpringBoot可以内嵌Tomcat、Jetty或Undertow等Web服务器,这样无需额外部署,就可以直接运行打包后的`.jar`文件作为服务。 3. **starter POMs**:SpringBoot提供了一系列的starter POMs,...

    springboot网上书店.rar

    2. 启动类(通常是@SpringBootApplication注解的类):作为Spring Boot应用的入口,加载配置并启动Spring容器。 3. 控制器层:处理HTTP请求,通常使用@RestController或@Controller注解定义。 4. 服务层:实现业务...

    使用springboot集成jseesite

    使用 springboot 改造 jeesite,只保留最简单的系统配置 。 介绍 1、运行主类,登录 admin/admin ...2、砍掉了所有的冗余的东西,只保留...6、由于 boot 对 jsp 的集成问题,只能用tomcat 和jetty 启动,undertow 有问题

    springboot列子

    3. **内嵌容器(Embedded Containers)**:SpringBoot 可以内嵌Tomcat、Jetty或Undertow等Servlet容器,这意味着你可以直接运行jar文件启动Web应用,无需额外安装服务器。 4. **Actuator**:SpringBoot 提供了 ...

    官网springboot项目

    3. **内嵌Web服务器(Embedded Web Servers)**:SpringBoot支持内嵌Tomcat、Jetty或Undertow等Web服务器,无需额外设置,可以直接运行SpringBoot应用。 4. **Spring Boot Starter Parent**:许多SpringBoot项目...

    SpringBoot启动方式

    Spring Boot 的 Web 模块 defaults to 使用嵌入式 tomcat 作为 web 容器对外提供 HTTP 服务,默认将使用 8080 端口对外监听和提供服务: 1. 假设我们不想使用默认的嵌入式 tomcat(spring-boot-starter-tomcat 自动...

    springboot

    3. **内嵌式 Web 服务器**:SpringBoot 可以内嵌 Tomcat、Jetty 或 Undertow 等 Web 服务器,无需单独打包或部署。这使得开发和测试过程更为便捷,`spring-boot-starter-web` 模块中包含了这些内嵌服务器的配置。 4...

    springboot.rar

    SpringBoot支持内嵌Tomcat、Jetty或Undertow等Web服务器,可以直接打包成可执行的jar文件,无需外部Web容器,简化了部署流程。 4. **Actuator(监控与健康检查)** `spring-boot-starter-actuator`模块提供了多种...

    springboot案例

    3. **内嵌式Web服务器**:SpringBoot支持内嵌Tomcat、Jetty或Undertow等Web服务器,这样我们无需单独打包部署,只需运行主类即可启动应用。 4. **Actuator**:这是一个强大的监控和管理工具,可以提供健康检查、...

    springboot的简单例子

    SpringBoot支持内嵌Tomcat、Jetty或Undertow等Web服务器,无需额外配置即可运行。在"demo"项目中,我们可能看到`application.properties`或`application.yml`配置文件中关于服务器端口的设置。 3. **自动配置...

    springboot中文官方文档

    Spring Boot支持内嵌Tomcat、Jetty或Undertow等Web服务器,这样开发者无需额外部署,可以直接运行打包后的jar文件启动应用。 4. **Actuator(监控与健康检查)**: Actuator提供了多种端点,用于监控和管理应用...

    用java springboot编写的web前端(科技改变生活)

    标题中的“用java springboot编写的web前端”指的是利用Java Spring Boot框架开发的Web应用程序的前端部分。Spring Boot是Spring框架的一个子项目,它旨在简化Spring应用的初始搭建以及开发过程,尤其对于创建独立的...

    undertow-2.2.18.Final.zip

    Undertow是一个高性能、轻量级的Java web服务器和Servlet容器,由Red Hat开发并维护。它是JBoss AS和WildFly应用服务器的核心组件之一。在2.2.18.Final版本中,Undertow提供了最新的功能更新和性能优化。 Undertow...

Global site tag (gtag.js) - Google Analytics