[scode type=“share”]今天在使用activeMQ-web的使用数据返回到前端一直中文乱码,本来以为是activeMQ的原因,应该可以设置字符集。看了看它的源码,并没有提供设置字符集的地方。[/scode]

乱码的本质

乱码无非就是前端和后端使用的字符集不一致,解决的方法就是把字符集设置为一致。统一字符集一般设置为utf-8。

查看源码

在使用ActiveMQ-web的时候配置了一个Servlet,这个Servlet类是AjaxServlet,发送的请求都去这里了。AjaxServlet 继承 MessageListenerServlet,在MessageListenerServlet中看到了这一段代码处理消息的响应

StringWriter swriter = new StringWriter();
                PrintWriter writer = new PrintWriter(swriter);
                Map<MessageAvailableConsumer, String> consumerIdMap = client.getIdMap();
                Map<MessageAvailableConsumer, String> consumerDestinationNameMap = client.getDestinationNameMap();
                response.setStatus(200);
                writer.println("<ajax-response>");
                String m;
                if (message != null) {
                    String id = (String)consumerIdMap.get(consumer);
                    m = (String)consumerDestinationNameMap.get(consumer);
                    LOG.debug("sending pre-existing message");
                    this.writeMessageResponse(writer, message, id, m);
                    ++messages;
                }

                LinkedList<UndeliveredAjaxMessage> undeliveredMessages = ((AjaxListener)consumer.getAvailableListener()).getUndeliveredMessages();
                LOG.debug("Send " + undeliveredMessages.size() + " unconsumed messages");
                synchronized(undeliveredMessages) {
                    Iterator it = undeliveredMessages.iterator();

                    while(it.hasNext()) {
                        ++messages;
                        UndeliveredAjaxMessage undelivered = (UndeliveredAjaxMessage)it.next();
                        Message msg = undelivered.getMessage();
                        consumer = (MessageAvailableConsumer)undelivered.getConsumer();
                        String id = (String)consumerIdMap.get(consumer);
                        String destinationName = (String)consumerDestinationNameMap.get(consumer);
                        LOG.debug("sending undelivered/buffered messages");
                        LOG.debug("msg:" + msg + ", id:" + id + ", destinationName:" + destinationName);
                        this.writeMessageResponse(writer, msg, id, destinationName);
                        it.remove();
                        if (messages >= this.maximumMessages) {
                            break;
                        }
                    }
                }

                for(int i = 0; i < consumers.size() && messages < this.maximumMessages; ++i) {
                    consumer = (MessageAvailableConsumer)consumers.get(i);
                    if (consumer.getAvailableListener() != null) {
                        while(messages < this.maximumMessages) {
                            message = consumer.receiveNoWait();
                            if (message == null) {
                                break;
                            }

                            ++messages;
                            String id = (String)consumerIdMap.get(consumer);
                            String destinationName = (String)consumerDestinationNameMap.get(consumer);
                            LOG.debug("sending final available messages");
                            this.writeMessageResponse(writer, message, id, destinationName);
                        }
                    }
                }

                writer.print("</ajax-response>");
                writer.flush();
                m = swriter.toString();
                response.getWriter().println(m);

这里只需要知道它使用了response返回数据,在调试的时候看消息文本并没有乱码,返回到前端就乱码了,初步分析是因为response设置的字符集不是utf-8。

解决方案

response乱码解决有三种方式:

  1. response.setCharacterEncoding("utf-8”);//设置服务器端的编码,默认是ISO-8859-1
  2. response.setContentType("text/html;charset=utf-8”);
  3. response.getWriter().println("”);

不管哪一种其实都要通过设置response对象参数来解决,但是这个response对象在activemq-web的源码里面,我们改动不了。只能通过拦截器来实现设置字符集了,每次请求进过拦截器,设置字符集。

初步解决(踩坑)

在SpringBoot中配置拦截器

@Configuration
public class ServletConfig  {
    //注册AjaxServlet,使用activemq-web所需
    @Bean
    public ServletRegistrationBean activemqRegistration() {
        AjaxServlet ajaxServlet = new AjaxServlet();
        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(ajaxServlet);
        servletRegistrationBean.addUrlMappings("/amq");

        return servletRegistrationBean;
    }
    //解决中文乱码,注册拦截器
    @Bean
    public FilterRegistrationBean charsetRegistration(){
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter("utf-8",true);
        filterRegistrationBean.setFilter(characterEncodingFilter);
        return filterRegistrationBean;
    }
}

配置完了之后发现,明明启用了设置respspon字符集但是拦截的时候forceResponseEncoding这个属性的值还是一直为false。看看这一段代码:

protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String encoding = this.getEncoding();
        if (encoding != null) {
            if (this.isForceRequestEncoding() || request.getCharacterEncoding() == null) {
                request.setCharacterEncoding(encoding);
            }

            if (this.isForceResponseEncoding()) {
                response.setCharacterEncoding(encoding);
            }
        }

        filterChain.doFilter(request, response);
    }

可以看到当forceResponseEncoding为false的时候根本不会设置字符集。

最终解决

springboot自动配置就配置了字符集拦截器,所以覆盖了我们配置的这个。在application.yml配置文件里面可以直接设置字符拦截器的一系列属性。在里面加上

spring:
  http:
    encoding:
      force-response: true

到这里问题就解决了

Q.E.D.