SpringMVC中的字符编码问题

Posted 简单就是幸福

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringMVC中的字符编码问题相关的知识,希望对你有一定的参考价值。

字符编码问题

一、背景

最近项目中在使用feign接口调用中产生了乱码问题,所以总结下这里产生乱码的原因。

如下所示:

Feign远程调用过程中出现中文乱码问题

正确的文件名称应该是:

二、排查思路

2.1、查看idea默认编码方式

发现都是UTF-8编码方式,继续向下来进行排查。

2.2、查看接口代码

2.3、查看linux编码

发现也是中文,所以可能存在问题的地方是第二个。

三、解决思路

因为从idea中编码中没有找到问题,所以只有在接口中存在问题,尝试来进行修复。

3.1、修改远程调用编码

指定响应的编码方式

如下所示:

四、SpringMVC对字符编码的配置

4.1、字符编码自动配置类HttpEncodingAutoConfiguration

直接来看自动配置类对字符编码的配置。org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration

源码如下所示:

@Configuration(proxyBeanMethods = false)
// 配置server中的配置
@EnableConfigurationProperties(ServerProperties.class)
// servlet环境
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
// 字符编码类
@ConditionalOnClass(CharacterEncodingFilter.class)
// 字符编码的配置是在这里来进行配置的。默认也是开启的
@ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration 

	private final Encoding properties;

	public HttpEncodingAutoConfiguration(ServerProperties properties) 
		this.properties = properties.getServlet().getEncoding();
	
    
    // 配置过滤器
	@Bean
	@ConditionalOnMissingBean
	public CharacterEncodingFilter characterEncodingFilter() 
		CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
         // 设置字符编码
		filter.setEncoding(this.properties.getCharset().name());
         // 强制request编码
		filter.setForceRequestEncoding(this.properties.shouldForce(Encoding.Type.REQUEST));
         // 强制response编码
		filter.setForceResponseEncoding(this.properties.shouldForce(Encoding.Type.RESPONSE));
		return filter;
	

	@Bean
	public LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() 
		return new LocaleCharsetMappingsCustomizer(this.properties);
	

	static class LocaleCharsetMappingsCustomizer
			implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered 

		private final Encoding properties;

		LocaleCharsetMappingsCustomizer(Encoding properties) 
			this.properties = properties;
		
		// 配置映射路径
		@Override
		public void customize(ConfigurableServletWebServerFactory factory) 
			if (this.properties.getMapping() != null) 
				factory.setLocaleCharsetMappings(this.properties.getMapping());
			
		

		@Override
		public int getOrder() 
			return 0;
		

	


通过上面的代码,可以看出springmvc所有request的配置是通过

spring.http.encoding

来进行配置的。所以就有必要来对这个配置类的属性来做了解。

4.2、配置类中属性说明

直接参考:org.springframework.boot.web.servlet.server.Encoding

public class Encoding 

	/**
	 * Default HTTP encoding for Servlet applications.
	 */
     // 采用的模仿编码方式
	public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;

	/**
	 * Charset of HTTP requests and responses. Added to the "Content-Type" header if not
	 * set explicitly.
	 */
    // 针对request和response默认设置的字符编码
	private Charset charset = DEFAULT_CHARSET;

	/**
	 * Whether to force the encoding to the configured charset on HTTP requests and
	 * responses.
	 */
    // 是否强制对HTTP请求和响应上配置的字符集进行编码
	private Boolean force;

	/**
	 * Whether to force the encoding to the configured charset on HTTP requests. Defaults
	 * to true when "force" has not been specified.
	 */
    // 是否强制对HTTP请求上配置的字符集进行编码。未指定“force”时默认为true
	private Boolean forceRequest;

	/**
	 * Whether to force the encoding to the configured charset on HTTP responses.
	 */
    // 是否强制对HTTP响应上配置的字符集进行编码
	private Boolean forceResponse;

	/**
	 * Locale in which to encode mapping.
	 */
    // 要在其中编码映射的区域设置
	private Map<Locale, Charset> mapping;
   
    // ......

即配置文件如下所示:

spring.http.encoding.charset=UTF-8
spring.http.encoding.force=true 

参考官方文档中的配置

https://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html

4.3、过滤器中设置源码

然后进入到org.springframework.web.filter.CharacterEncodingFilter#doFilterInternal方法来

protected void doFilterInternal(
  HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
  throws ServletException, IOException 
  
  // 获取得到默认编码方式。默认为UTF-8
  String encoding = getEncoding();
  if (encoding != null) 
    // 两个设置条件:1、forceRequestEncoding是否为true;2、利用request获取得到默认编码
    if (isForceRequestEncoding() || request.getCharacterEncoding() == null) 
      request.setCharacterEncoding(encoding);
    
    // 是否设置强制响应编码。默认值是false
    // 默认为false
    if (isForceResponseEncoding()) 
      response.setCharacterEncoding(encoding);
    
  
  filterChain.doFilter(request, response);

终于在这里找到了原因。原因就是这里的响应没有设置为中文编码。

五、Tomcat编码设置

另外web窗口 tomcat可以配置

# Character encoding to use to decode the URI.
server.tomcat.uri-encoding=UTF-8 

六、总结

1、排查问题的时候,就考虑到多种情况的发生,并逐一进行验证。

2、最终的字符编码配置如下所示:

spring:
  http:
    encoding:
      charset: UTF-8
      force: true

Spring MVC UTF-8 编码

【中文标题】Spring MVC UTF-8 编码【英文标题】:Spring MVC UTF-8 Encoding 【发布时间】:2011-08-21 03:30:42 【问题描述】:

目前我正在尝试开始使用 Spring MVC。在尝试时,我遇到了编码问题。

我想在我的 JSP 页面上显示 UTF-8 字符,所以我在我的 ModelAndView 中添加了一个带有 UTF-8 字符的字符串。它看起来像这样:

@Controller
public class HomeController 

    private static final Logger logger = LoggerFactory.getLogger(HomeController.class);

    @RequestMapping(value="/", method=RequestMethod.GET)
    public ModelAndView home() 
        logger.info("Welcome home!");
        return new ModelAndView("home", "utftest", "ölm");
    


在 JSP 页面上,我只想显示带有 UTF-8 字符的字符串,如下所示:

<%@ page language="java" pageEncoding="UTF-8"%>
<%@ page contentType="text/html;charset=UTF-8" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Home</title>
</head>
<body>
    <h1>Hello world!</h1>
    <p><c:out value="ö" /></p>
    <p><c:out value="$utftest"></c:out></p>
</body>
</html>

结果我得到以下信息:

Hello world!

ö

ölm

请注意,以下代码 &lt;c:out value="ö" /&gt; 显示时没有编码错误。 我还在 Springsource Tool Suite 中将默认编码设置为 UTF-8,但我仍然收到错误的字符。

编辑:

也许我应该提到我正在使用装有 OS X 10.6 的 Mac。对于 Spring 开发,我使用 Spring (http://www.springsource.com/developer/sts) 的 Springsource Tool Suite。 希望这有助于找出我的设置有什么问题。

编辑 2:

感谢 McDwell,我刚刚尝试在我的控制器中使用 "\u00f6lm" 而不是 "ölm",并且 JSP 页面上的编码问题已经消失。

这是否意味着我的 .java 文件使用错误的字符集进行编码?在 Eclipse 中哪里可以更改?

谢谢。

【问题讨论】:

Springsource 工具套件,基本上是带有一些插件的 Eclipse 吗?您究竟是如何设置默认编码的? 是的,它只是带有一些用于 Spring 开发的预安装插件的 Eclipse (springsource.com/developer/sts)。我将 Springsource 首选项中的编码(与 Eclipse 中的相同)General > Workspace 和 General > Content Types 更改为 UTF-8 好吧,McDowell 在我删除的答案(基本上回答了您已经完成的内容)中提到,这可能是由于 Java 编译器使用错误的编码读取源文件造成的。这是一个非常合理的原因。但由于我不使用/拥有 Mac,我不知道如何修复它。 @OemerA - 您可以通过将"ölm" 替换为"\u00f6lm" 来缩小问题范围。如果这可行,您就知道问题出在 Java 源文件及其编译为字节码。 @OemerA - 我从来没有安装过 STS,所以我不能说太多。鉴于ö 是UTF-8 中的字节序列C3 B6,我会说编辑器正在做正确的事情(您可以使用外部十六进制编辑器进行确认)。因此,读取文件并将其传递给编译器的任何内容都是使用错误的编码读取它 - 查看项目构建器(通过右键单击项目)并可能访问他们各自的错误数据库。 【参考方案1】:

确保在 web.xml 中注册 Spring 的 CharacterEncodingFilter(必须是该文件中的第一个过滤器)。

<filter>  
    <filter-name>encodingFilter</filter-name>  
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>  
    <init-param>  
       <param-name>encoding</param-name>  
       <param-value>UTF-8</param-value>  
    </init-param>  
    <init-param>  
       <param-name>forceEncoding</param-name>  
       <param-value>true</param-value>  
    </init-param>  
</filter>  
<filter-mapping>  
    <filter-name>encodingFilter</filter-name>  
    <url-pattern>/*</url-pattern>  
</filter-mapping> 

如果您使用的是 Tomcat,您可能没有在您的 server.xml 中设置 URIEncoding。如果您不将其设置为 UTF-8,它将无法正常工作。绝对保留CharacterEncodingFilter。不过,这里有一个简明的checklist。它肯定会指导您完成这项工作。

【讨论】:

过滤器只设置POST请求的请求参数的编码。 URIEncoding 对 GET 请求执行此操作。当只是通过 HTTP 响应显示页面时,他已经遇到了问题,而不是在处理提交的数据时。 对不起,这对我不起作用。我仍然得到和以前一样的错误字符。 作为 Tomcat 用户,这个答案中最重要的部分是关于在 server.xml 文件中设置 URIEncoding 的语句,即在连接器的定义中到您使用的任何端口(例如 8080 ),您必须具有以下分配:URIEncoding="UTF-8" URIEncoding 自 Tomcat 8 (tomcat.apache.org/migration-8.html#URIEncoding) 以来默认设置为 UTF-8,因此对于新部署来说,这是一个少一个问题。过滤器仍然是正确处理 POST 请求所必需的。 稍微解释一下:我们需要CharacterEncodingFilter,因为浏览器在提交post数据时通常不提供编码信息,因此服务器可能使用与传入数据不同的编码。所以我们告诉服务器CharacterEncodingFilter中的预期编码。【参考方案2】:

好的,伙计们,我找到了编码问题的原因。

问题出在我的构建过程中。我没有在我的pom.xml 文件中告诉 Maven 使用 UTF-8 编码构建项目。因此,Maven 只是从我的系统中获取了默认编码,即 MacRoman,并使用 MacRoman 编码构建它。

幸运的是,Maven 在构建项目时会就此向您发出警告(但由于所有其他消息,该警告很有可能很快从您的屏幕上消失)。

这是您需要在pom.xml 文件中设置的属性:

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    ...
</properties>

感谢大家的帮助。如果没有你们,我将无法解决这个问题!

【讨论】:

谢谢!! +1。我没有使用 maven,我刚开始使用 gradle(但我是新手),这个问题开始发生。在gradle中指定java任务的编译编码:compileJava.options.encoding = 'UTF-8'解决问题。 这个问题也可能发生在其他情况下,与您的构建问题无关。【参考方案3】:

除了 Benjamin 的回答(我只是略过)之外,您还需要确保您的文件实际上是使用正确的编码存储的(对于源代码、JSP 等,将是 UTF-8,但请注意Java 属性文件必须按照定义编码为 ISO 8859-1)。

这样做的问题是无法判断使用什么编码来存储文件。您唯一的选择是使用特定编码打开文件,并检查内容是否有意义。您还可以尝试使用 iconv 将文件从假定的编码转换为所需的编码 - 如果产生错误,则说明您的假设不正确。因此,如果您假设 hello.jsp 编码为 UTF-8,请运行“iconv -f UTF-16 -t UTF-8 hello.jsp”并检查错误。

如果您发现您的文件没有正确编码,您需要找出原因。它可能是您用来创建文件的编辑器或 IDE。如果是 Eclipse(和 STS),请确保将文本文件编码(首选项/常规/工作区)设置为 UTF-8(不幸的是,它默认为您系统的平台编码)。

编码问题难以调试的原因在于涉及的组件太多(文本编辑器、浏览器,以及介于两者之间的每个软件组件,在某些情况下还包括数据库),而且每个组件都有可能引入一个错误。

【讨论】:

根据问题的评论,OP已经将编辑器配置为将文件保存为UTF-8。 是的,尽管 OP 的评论是在我回答之前发布的,但当我第一次加载页面并阅读问题时,它并不存在。【参考方案4】:

除了 Benjamin 的回答 - 如果您使用 Spring Security,将 CharacterEncodingFilter 放在 web.xml 中可能并不总是有效。在这种情况下,您需要创建一个自定义过滤器并将其作为第一个过滤器添加到过滤器链中。为确保它是链中的第一个过滤器,您需要在您的 WebSecurityConfigurerAdapter 中使用 addFilterBefore 在 ChannelProcessingFilter 之前添加它:

@Configuration
@EnableWebSecurity 
public class SecurityConfig extends WebSecurityConfigurerAdapter 


    @Override
    protected void configure(HttpSecurity http) throws Exception 

        //add your custom encoding filter as the first filter in the chain
        http.addFilterBefore(new EncodingFilter(), ChannelProcessingFilter.class);

        http.authorizeRequests()
            .and()
            // your code here ...
    

Spring Security 中所有过滤器的排序可在此处获得:HttpSecurityBuilder - addFilter()

您的自定义 UTF-8 编码过滤器可能如下所示:

public class EncodingFilter extends GenericFilterBean 

    @Override
    public void doFilter(
            ServletRequest request, 
            ServletResponse response,
            FilterChain chain) throws IOException, ServletException 

        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");

        chain.doFilter(request, response);
    

不要忘记添加你的jsp文件:

<%@ page pageEncoding="UTF-8" contentType="text/html; charset=UTF-8" %>

如果存在,则从 web.xml 中删除 CharacterEncodingFilter。

【讨论】:

你救了我的命。 我必须在我的 jsp 文件中添加 &lt;%@ page contentType="text/html;charset=UTF-8" %&gt;【参考方案5】:

在 Spring MVC 返回字符串中强制 UTF-8 编码的最简单解决方案:

@RequestMapping 中,使用:

produces = MediaType.APPLICATION_JSON_VALUE + "; charset=utf-8"

【讨论】:

有效!!!谢谢我已经尝试了这里列出的所有解决方案,但只有这样有效:) 我觉得这个更好看MediaType.APPLICATION_JSON_UTF8_VALUE 这也是唯一对我有用的解决方案。 “≤”返回为“?”。添加这个为我解决了这个问题。非常感谢!!【参考方案6】:

右键点击你的controller.java然后属性并检查你的文本文件是否用utf-8编码,如果不是这是你的错误。

【讨论】:

【参考方案7】:

要解决此问题,您需要以下三个步骤:

    将页面编码设置为 UTF-8,如下所示:

    <%@ page language="java" pageEncoding="UTF-8"%>
    <%@ page contentType="text/html;charset=UTF-8" %>
    

    在 web.xml 文件中设置过滤器如下:

    <filter>
        <filter-name>encodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    

    将资源编码设置为 UTF-8,以防您直接在 Java 代码或 JSP 中编写任何 UTF-8 字符。

【讨论】:

【参考方案8】:

根据您呈现视图的方式,您可能还需要:

@Bean
public StringHttpMessageConverter stringHttpMessageConverter() 
    return new StringHttpMessageConverter(Charset.forName("UTF-8"));

【讨论】:

谢谢。我有一个控制器方法,它返回一个编码错误的@ResponseBody String(因为StringHttpMessageConverter 默认在内部使用ISO-8859-1)。顺便说一句,您可以使用 Java 7 的 StandardCharsets.UTF_8,例如new StringHttpMessageConverter(StandardCharsets.UTF_8).【参考方案9】:

像这里一样为我工作Spring MVC Java config

通过添加到网络初始化程序

 @Override
      protected Filter[] getServletFilters() 

        CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
        characterEncodingFilter.setEncoding("UTF-8");
        return new Filter[]  characterEncodingFilter;
      

【讨论】:

【参考方案10】:

从 servlet spec 4.0 开始,您可以在 servlet 上下文中设置请求字符编码,而无需指定过滤器。

在 web.xml 中:

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                      http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
  version="4.0">
  <request-character-encoding>UTF-8</request-character-encoding>
  [...]

或者在 Java 配置中:

servletContext.setRequestCharacterEncoding("UTF-8");

【讨论】:

以上是关于SpringMVC中的字符编码问题的主要内容,如果未能解决你的问题,请参考以下文章

SpringMVC其他说明

Spring mvc jackson json mapper-如何将unicode编码应用于json响应中的特殊字符

tomcat设置utf-8编码,springMVC后台接收参数反而乱码

SpringMVC 返回 json 字符串中文乱码

SpringMVC 返回 json 字符串中文乱码

Spring MVC UTF-8 编码