<转载>springmvc(30):springmvc的字符编码过滤器CharacterEncodingFilter相关的几篇博文

2018年2月22日

(blog 1 

--springmvc字符编码过滤器CharacterEncodingFilter浅析)

一、在web.xml中的配置
    <!-- characterEncodingFilter字符编码过滤器 -->
    <filter>
        <filter-name>characterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>characterEncodingFilter</filter-name>
        <!--这里的url根据你项目需求自己改-->
        <url-pattern>/</url-pattern>
    </filter-mapping>
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

二、CharacterEncodingFilter过滤器类浅析

打开该类源码,可以看到该类有三个类属性

private String encoding;

private boolean forceRequestEncoding = false;

private boolean forceResponseEncoding = false;
 
 
  • 1
  • 2
  • 3
  • 4
  • 5

主要方法只有一个,也就是下面这个

    @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);
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

可以看到这个作用器的作用就是在request和response没有设置字符编码方式的时候设置一个编码方式。注意

if (isForceResponseEncoding()) {
    response.setCharacterEncoding(encoding);
}
 
 
  • 1
  • 2
  • 3

是在

filterChain.doFilter(request, response);
 
 
  • 1

之前执行的,也就是说设置的是response的默认编码方式,可以自己在代码里修改为其他的编码方式,网上有些文档说的是设置的是最终的编码方式,这是错的。


(blog 2 
--详解Spring中的CharacterEncodingFilter--forceEncoding为true在java代码中设置失效--html设置编码无效)

在项目中有很多让人头疼的问题,其中,编码问题位列其一,那么在Spring框架中是如何解决从页面传来的字符串的编码问题的呢?下面我们来看看Spring框架给我们提供过滤器CharacterEncodingFilter

1.看清结构:

&**

看到其继承GenericFilterBean和OncePerRequestFilter,也就是说,这个过滤器就是针对于每次浏览器请求进行过滤的,然后再其之上添加了父类没有的功能即处理字符编码。

 

2.官方解释:

Servlet 2.3/2.4 Filter that allows one to specify a character encoding for requests. This is useful because current browsers typically do not set a character encoding even if specified in the HTML page or form. (这句话就说你在html页面或表单中设置编码是没有用的)

This filter can either apply its encoding if the request does not already specify an encoding, or enforce this filter's encoding in any case ("forceEncoding"="true").(只要你设置了foreEncoding=true,则在代码中设置编码格式没用,)In the latter case, the encoding will also be applied as default response encoding on Servlet 2.4+ containers (although this will usually be overridden by a full content type set in the view).

 

3.如何使用

下面来看看如何在web.xml中配置:

 <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>

 其中encoding用来设置编码格式,forceEncoding用来设置是否理会 request.getCharacterEncoding()方法,设置为true则强制覆盖之前的编码格式。

 

4.源码赏析

&**

Servlet容器启动的时候,会读取web.xml中对于过滤器的配置信息, 读取到<init-param>中的子标签<param-name>encoding和forceEncoding所对应的<param-value>的值,再通过调用该类setEncoding(String encoding)和setForceEncoding(boolean forceEncoding) 将值注入到这连个字段中。

&**

这里就能看到为什么设置foreEncoding为true会覆盖掉request.getCharacterEncoding()中的方法了吧,呵呵,还是那句话,源码之前了无秘密,只有深入到源代码之中才能看清本质。


(blog 3 

--Spring框架中CharacterEncodingFilter的作用真的没那么大)

之前看介绍Spring框架的时候,提到了Spring中内置了几种过滤器,其中有一种是CharacterEncodingFilter的过滤器,是将数据以某种格式编码的方式输出的。这个特性让我很兴奋,因为在WEB系统下,字符编码的格式是一定要面对的。所以我尝试了一下,结果很失望。

在web.xml中添加了如下内容

    <!-- 配置请求过滤器,编码格式设为UTF-8,避免中文乱码-->
    <filter>
       <filter-name>springUtf8Encoding</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>springUtf8Encoding</filter-name>
       <url-pattern>/*</url-pattern>
   </filter-mapping>
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

于是在浏览器中打开网页,在chrome中显示乱码,在opera中显示正常编码,查看了一下返回数据,发现HTTP报头中并没有指明编码类型,也就是说这时候浏览器并不知道消息体中是何种编码,于是它会按照自己默认编码去解析。这是不可忍受的。

于是我查看了CharacterEncodingFilter的源码,感谢博客

/*
 * Copyright 2002-2007 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.web.filter;

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

/**
 * Servlet 2.3/2.4 Filter that allows one to specify a character encoding for
 * requests. This is useful because current browsers typically do not set a
 * character encoding even if specified in the HTML page or form.
 *
 * <p>This filter can either apply its encoding if the request does not
 * already specify an encoding, or enforce this filter's encoding in any case
 * ("forceEncoding"="true"). In the latter case, the encoding will also be
 * applied as default response encoding on Servlet 2.4+ containers (although
 * this will usually be overridden by a full content type set in the view).
 *
 * @author Juergen Hoeller
 * @since 15.03.2004
 * @see #setEncoding
 * @see #setForceEncoding
 * @see javax.servlet.http.HttpServletRequest#setCharacterEncoding
 * @see javax.servlet.http.HttpServletResponse#setCharacterEncoding
 */
public class CharacterEncodingFilter extends OncePerRequestFilter {

    private String encoding;

    private boolean forceEncoding = false;


    /**
     * Set the encoding to use for requests. This encoding will be passed into a
     * {@link javax.servlet.http.HttpServletRequest#setCharacterEncoding} call.
     * <p>Whether this encoding will override existing request encodings
     * (and whether it will be applied as default response encoding as well)
     * depends on the {@link #setForceEncoding "forceEncoding"} flag.
     */
    public void setEncoding(String encoding) {
        this.encoding = encoding;
    }

    /**
     * Set whether the configured {@link #setEncoding encoding} of this filter
     * is supposed to override existing request and response encodings.
     * <p>Default is "false", i.e. do not modify the encoding if
     * {@link javax.servlet.http.HttpServletRequest#getCharacterEncoding()}
     * returns a non-null value. Switch this to "true" to enforce the specified
     * encoding in any case, applying it as default response encoding as well.
     * <p>Note that the response encoding will only be set on Servlet 2.4+
     * containers, since Servlet 2.3 did not provide a facility for setting
     * a default response encoding.
     */
    public void setForceEncoding(boolean forceEncoding) {
        this.forceEncoding = forceEncoding;
    }


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

        if (this.encoding != null && (this.forceEncoding || request.getCharacterEncoding() == null)) {
            request.setCharacterEncoding(this.encoding);
            if (this.forceEncoding) {
                response.setCharacterEncoding(this.encoding);
            }
        }
        filterChain.doFilter(request, response);
    }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91

这个filter可能与我们自己写的Filter不太相同,它继承的是OncePerRequestFilter,看看OncePerRequestFilter的源码,可以发现,OncePerRequestFilter的过滤器会负责调用doFilterInternal()方法,因此,我们只关注这个方法即可。 
这个方法很简单,就是看看是否设置了encoding,如果设置了encoding,且请求中没有设置characterEncoding的话,它会负责设置characterEncoding,并且如果forceEncoding设置为true的话,在responce中也会设置characterEncoding。

如何设置encoding和forceEncoding呢?这就是我们在配置文件中写的里面的数据,spring会负责将这些数据注入。我们可以不用管了。现在我们把这个方法简化一下,就是两个请求。

request.setCharacterEncoding(encoding);
response.setCharacterEncoding(encoding);
 
 
  • 1
  • 2

OK,现在我们来看一下setCharacterEncoding()干了点啥?我没找到setCharacterEncoding()的源码,只看看它的文档说明吧,

public void setCharacterEncoding(java.lang.String env)
                          throws java.io.UnsupportedEncodingException
Overrides the name of the character encoding used in the body of this request. This method must be called prior to reading request parameters or reading input using getReader().
Parameters:
env - a String containing the name of the character encoding.
Throws:
java.io.UnsupportedEncodingException - if this is not a valid encoding
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

看到这个说明的时候,我觉得这个作用就是更改HTTP报头中charset的内容,仅此而已,不会更改HTTPbody中的编码格式。所以我想到使用setContentType()来显式的设置输出的报文信息

setContentType("text/json";charst=utf-8);
 
 
  • 1

这样就OK了,在Chrome和Opera中已经可以正常显示了。

我们在看看setContentType的说明

public void setContentType(java.lang.String type)

Sets the content type of the response being sent to the client, if the response has not been committed yet. The given content type may include a character encoding specification, for example, text/html;charset=UTF-8. **The response's character encoding is only set from the given content type** if this method is called before getWriter is called.
This method may be called repeatedly to change content type and character encoding. This method has no effect if called after the response has been committed. It does not set the response's character encoding if it is called after getWriter has been called or after the response has been committed.

Containers must communicate the content type and the character encoding used for the servlet response's writer to the client if the protocol provides a way for doing so. In the case of HTTP, the Content-Type header is used.

Parameters:
type - a String specifying the MIME type of the content
See Also:
setLocale(java.util.Locale), setCharacterEncoding(java.lang.String), getOutputStream(), getWriter()
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

这里面很明显的说明了:输出的字符编码只有在给定的content type下面才有效。也即必须在有setContentType的前提下setCharacterEncoding()才有效,但是问题是setContentType()完全可以同时设置charset,那何必在setCharacterEncoding呢?

评论:

过滤器只负责向后台提交数据时编码,而不是页面上显示的编码。你的问题中的乱码是由于浏览器不知道后台是哪种编码只能用默认的编码显示才造成的乱码。一般都在html文件开头加上<meta http-equiv="content-type" content="text/html; charset=utf-8">类似的字样表示脚本编码。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值