解決Spring Boot異常返回頁面中文亂碼問題


Spring boot版本:2.1.3

異常返回頁面中文有亂碼,可以看到后台返回的字符編號是ISO-8859-1

 

但是后台輸出正常。

解決辦法一:

在application.properties增加以下配置

# 解決返回頁面中文亂碼問題
spring.http.encoding.force=true
spring.http.encoding.charset=UTF-8(默認就是UTF-8,可以不設置)

解決辦法二:

自定義HandlerExceptionResolver,然后手工設置編碼

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

@Component
public class MyExceptionHandler implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        response.setCharacterEncoding("utf-8");
        return null;
    }
}

第一種方法的原理:

配置HttpProperties默認的spring.http.encoding.charset=UTF-8,CharacterEncodingFilter注冊時會設置,然后在doFilterInternal方法中,如果配置spring.http.encoding.force設為true,則把請求和響應的編碼設置為spring.http.encoding.charset的值,源碼如下

HttpProperties.java,位置:org.springframework.boot.autoconfigure.http.HttpProperties

    /**
     * Configuration properties for http encoding.
     */
    public static class Encoding {

        public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;

HttpEncodingAutoConfiguration.java,位置:org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration

@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {

    private final HttpProperties.Encoding properties;

    public HttpEncodingAutoConfiguration(HttpProperties properties) {
        this.properties = properties.getEncoding();
    }

    @Bean
    @ConditionalOnMissingBean
    public CharacterEncodingFilter characterEncodingFilter() {
        CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
        filter.setEncoding(this.properties.getCharset().name());
        filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST));
        filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE));
        return filter;
    }

org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal()方法源碼

@Override
    protected void doFilterInternal(
            HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        
        // 取字符編碼
        String encoding = getEncoding();
        if (encoding != null) {
            // 如果spring.http.encoding.force設為true且請求編碼為空,則設置請求編碼
            if (isForceRequestEncoding() || request.getCharacterEncoding() == null) {
                request.setCharacterEncoding(encoding);
            }

            // 如果spring.http.encoding.force設為true,則設置響應編碼
            if (isForceResponseEncoding()) {
                response.setCharacterEncoding(encoding);
            }
        }
        filterChain.doFilter(request, response);
    }

如果上面的spring.http.encoding.force不為true,則下面會使用默認的ISO_8859_1,springboot在映射視圖時org.springframework.web.servlet.View.render()調用了org.springframework.web.util.HtmlUtils.htmlEscape(String)轉換錯誤信息的時候使用的是默認編碼ISO_8859_1,如下源碼

WebUtils.java

    /**
     * Default character encoding to use when {@code request.getCharacterEncoding}
     * returns {@code null}, according to the Servlet spec.
     * @see ServletRequest#getCharacterEncoding
     */
    public static final String DEFAULT_CHARACTER_ENCODING = "ISO-8859-1";

HtmlUtils.java

    /**
     * Turn special characters into HTML character references.
     * Handles complete character set defined in HTML 4.01 recommendation.
     * <p>Escapes all special characters to their corresponding
     * entity reference (e.g. {@code &lt;}).
     * <p>Reference:
     * <a href="http://www.w3.org/TR/html4/sgml/entities.html">
     * http://www.w3.org/TR/html4/sgml/entities.html
     * </a>
     * @param input the (unescaped) input string
     * @return the escaped string
     */
    public static String htmlEscape(String input) {
        return htmlEscape(input, WebUtils.DEFAULT_CHARACTER_ENCODING);
    }

然后在org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration.StaticView.render方法最后一句獲取輸出流時還有一個默認編碼ISO-8859-1

        public void render(Map<String, ?> model, HttpServletRequest request,
                HttpServletResponse response) throws Exception {
            if (response.isCommitted()) {
                String message = getMessage(model);
                logger.error(message);
                return;
            }
            StringBuilder builder = new StringBuilder();
            Date timestamp = (Date) model.get("timestamp");
            Object message = model.get("message");
            Object trace = model.get("trace");
            if (response.getContentType() == null) {
                response.setContentType(getContentType());
            }
            builder.append("<html><body><h1>Whitelabel Error Page</h1>").append(
                    "<p>This application has no explicit mapping for /error, so you are seeing this as a fallback.</p>")
                    .append("<div id='created'>").append(timestamp).append("</div>")
                    .append("<div>There was an unexpected error (type=")
                    .append(htmlEscape(model.get("error"))).append(", status=")
                    .append(htmlEscape(model.get("status"))).append(").</div>");
            if (message != null) {
                builder.append("<div>").append(htmlEscape(message)).append("</div>");
            }
            if (trace != null) {
                builder.append("<div style='white-space:pre-wrap;'>")
                        .append(htmlEscape(trace)).append("</div>");
            }
            builder.append("</body></html>");

            // 取Writer時還有一個默認編碼ISO-8859-1
            response.getWriter().append(builder.toString());
        }

response.getWriter()方法的源碼,可以看到是調用response.getWriter()取的

    // 位置:org.apache.catalina.connector.ResponseFacade.getWriter()
    @Override
    public PrintWriter getWriter()
        throws IOException {

        //        if (isFinished())
        //            throw new IllegalStateException
        //                (/*sm.getString("responseFacade.finished")*/);

        PrintWriter writer = response.getWriter();
        if (isFinished()) {
            response.setSuspended(true);
        }
        return writer;

    }

response.getWriter()的源碼,ENFORCE_ENCODING_IN_GET_WRITER默認為true

// 位置:org.apache.catalina.connector.Response.getWriter()
    private static final boolean ENFORCE_ENCODING_IN_GET_WRITER;

    static {
        ENFORCE_ENCODING_IN_GET_WRITER = Boolean.parseBoolean(
                System.getProperty("org.apache.catalina.connector.Response.ENFORCE_ENCODING_IN_GET_WRITER",
                        "true"));
    }
    
@Override
    public PrintWriter getWriter()
        throws IOException {

        if (usingOutputStream) {
            throw new IllegalStateException
                (sm.getString("coyoteResponse.getWriter.ise"));
        }

        if (ENFORCE_ENCODING_IN_GET_WRITER) {
            /*
             * If the response's character encoding has not been specified as
             * described in <code>getCharacterEncoding</code> (i.e., the method
             * just returns the default value <code>ISO-8859-1</code>),
             * <code>getWriter</code> updates it to <code>ISO-8859-1</code>
             * (with the effect that a subsequent call to getContentType() will
             * include a charset=ISO-8859-1 component which will also be
             * reflected in the Content-Type response header, thereby satisfying
             * the Servlet spec requirement that containers must communicate the
             * character encoding used for the servlet response's writer to the
             * client).
             */
            setCharacterEncoding(getCharacterEncoding());
        }

        usingWriter = true;
        outputBuffer.checkConverter();
        if (writer == null) {
            writer = new CoyoteWriter(outputBuffer);
        }
        return writer;
    }

    /**
     * @return the character encoding used for this Response.
     */
    @Override
    public String getCharacterEncoding() {
        String charset = getCoyoteResponse().getCharacterEncoding();
        if (charset != null) {
            return charset;
        }

        Context context = getContext();
        String result = null;
        if (context != null) {
            result =  context.getResponseCharacterEncoding();
        }

        // 默認編碼為ISO-8859-1
        if (result == null) {
            result = org.apache.coyote.Constants.DEFAULT_BODY_CHARSET.name();
        }

        return result;
    }

第二種方法的原理:會在org.springframework.web.servlet.DispatcherServlet.processHandlerException()中調用自定義的HandlerExceptionResolver設置編碼,源碼如下

    // 部分源碼
    protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
            @Nullable Object handler, Exception ex) throws Exception {

        // Success and error responses may use different content types
        request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);

        // Check registered HandlerExceptionResolvers...
        ModelAndView exMv = null;
        if (this.handlerExceptionResolvers != null) {
            for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
                exMv = resolver.resolveException(request, response, handler, ex);
                if (exMv != null) {
                    break;
                }
            }
        }

 

轉自  https://blog.csdn.net/q283614346/article/details/102947320

 

 

 

 
        

 

 

 
        

 

 

 
        
 
        

 

 
        

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM