How to overwrite StringHttpMessageConverter DEFAULT_CHARSET to use UTF8 in spring 4

20,528

Solution 1

Your Network tab seems to be showing

text/html;charset=UTF-8

which isn't what you configured it to be

converter.setSupportedMediaTypes(Arrays.asList(new MediaType("text", "plain", Charset.forName("UTF-8"))));

It seems your custom HttpMessageConverter bean isn't getting registered. Add this to your WebAppConfig class

@Override
protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    super.configureMessageConverters(converters);
    converters.add(responseBodyConverter());
}

You should see your response contain

Content-Type:"text/plain;charset=UTF-8"

I can't explain why your browser's network tab would show UTF-8 but wouldn't be able to parse or render it properly. It works fine for me.

Solution 2

The problem is that StringHttpMessageConverter defaults to ISO-8859-1. You need either set the default to UTF-8 as shown here:

    @Bean
    public HttpMessageConverter<String> responseBodyConverter() {
        StringHttpMessageConverter converter = new StringHttpMessageConverter(StandardCharsets.UTF_8);
        converter.setSupportedMediaTypes(Arrays.asList(new MediaType("text", "plain", Charset.forName("UTF-8"))));
        return converter;
    }

Or get the client to specify exactly what they want with the Accept header, ie: Accept: text/plain;charset="UTF-8".

Solution 3

The problem as others have stated is that StringHttpMessageConverter defaults to ISO-8859-1.

I just replaced the default charset of existing StringHttpMessageConverter to UTF-8

@Configuration
@EnableWebMvc
public class SampleWebConfiguration implements WebMvcConfigurer {
    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.stream()
            .filter(converter -> converter instanceof StringHttpMessageConverter)
            .forEach(converter -> ((StringHttpMessageConverter) converter).setDefaultCharset(StandardCharsets.UTF_8));
    }
}
Share:
20,528
masterdany88
Author by

masterdany88

Full-stack Java/Gwt Web Developer. Interested in software quality and software architecture. Always looking for new opportunities to learn something new and usefull. Main career goal: Software Architect position. Methodologies: Clean Code Design Patterns Java EE Architecture Agile JAVA: Design and implementation of Java EE based applications, using: PlayFramework2, Spring, GWT, JPA/ Hibernate, REST, SBT, Maven, Ant. WEB: Bootstrap, HTML5, JQuery. DB: MSSQL, MySql, H2DB. LINUX

Updated on July 21, 2022

Comments

  • masterdany88
    masterdany88 almost 2 years

    I am trying to make spring @ResponseBody return always utf-8. But I can't do it for so long. Problem comes when I am returning simple text answer:

    @RequestMapping(value="/test", method=RequestMethod.PUT)
    @ResponseBody
    public String ajaxTest() {
        return "Characters test: ęółąśżźćń";
    }
    

    Each polish chars (ęółąśżźćń) goes to ?

    And in web page I am getting this string: Characters test: ?�??????? instead of Characters test: ęółąśżźćń

    I don't know what I am missing.

    I've added custome bean to public class WebAppConfig extends WebMvcConfigurerAdapter {} Which goes as follows:

    @Bean
    public HttpMessageConverter<String> responseBodyConverter() {
        StringHttpMessageConverter converter = new StringHttpMessageConverter();
        converter.setSupportedMediaTypes(Arrays.asList(new MediaType("text", "plain", Charset.forName("UTF-8"))));
        return converter;
    }
    

    Instruction taken from https://jira.spring.io/browse/SPR-9099

    But it doesn't work. I can see in firefox and chrome that returned value is in utf-8: enter image description here


    Spring version: 4.1.1.RELEASE

    Web app config class:

    package com.derp.common.init;
    
    import java.nio.charset.Charset;
    import java.util.Arrays;
    import java.util.List;
    import java.util.Properties;
    
    import javax.annotation.Resource;
    import javax.sql.DataSource;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.PropertySource;
    import org.springframework.core.env.Environment;
    import org.springframework.http.MediaType;
    import org.springframework.http.converter.HttpMessageConverter;
    import org.springframework.http.converter.StringHttpMessageConverter;
    import org.springframework.jdbc.datasource.DriverManagerDataSource;
    import org.springframework.orm.hibernate4.HibernateTransactionManager;
    import org.springframework.orm.hibernate4.LocalSessionFactoryBean;
    import org.springframework.transaction.annotation.EnableTransactionManagement;
    import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
    import org.springframework.web.servlet.config.annotation.EnableWebMvc;
    import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
    
    //import com.derp.common.wicketView.HomePage;
    
    @Configuration
    @ComponentScan("com.derp")
    @EnableWebMvc
    @EnableTransactionManagement
    @PropertySource("classpath:application.properties")
    public class WebAppConfig extends WebMvcConfigurerAdapter {
    
        private static final String PROPERTY_NAME_DATABASE_DRIVER = "db.driver";
        private static final String PROPERTY_NAME_DATABASE_PASSWORD = "db.password";
        private static final String PROPERTY_NAME_DATABASE_URL = "db.url";
        private static final String PROPERTY_NAME_DATABASE_USERNAME = "db.username";
    
        private static final String PROPERTY_NAME_HIBERNATE_DIALECT = "hibernate.dialect";
        private static final String PROPERTY_NAME_HIBERNATE_SHOW_SQL = "hibernate.show_sql";
        private static final String PROPERTY_NAME_HIBERNATE_HBM2DDL_AUTO = "hibernate.hbm2ddl.auto";
        private static final String PROPERTY_NAME_HIBERNATE_CONNECTION_CHARSET = "hibernate.connection.CharSet";
        private static final String PROPERTY_NAME_HIBERNATE_CONNECTION_CHARACTERENCODING = "hibernate.connection.characterEncoding";
        private static final String PROPERTY_NAME_HIBERNATE_CONNECTION_USEUNICODE = "hibernate.connection.useUnicode";
        private static final String PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN_SERVICES = "services.entitymanager.packages.to.scan";
        private static final String PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN_COMMON = "common.entitymanager.packages.to.scan";
        private static final String PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN_CMS = "cms.entitymanager.packages.to.scan";
        private static final String PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN_PROCEDURE = "procedure.entitymanager.packages.to.scan";
    
        @Resource
        private Environment env;
    
        @Bean
        public DataSource dataSource() {
            DriverManagerDataSource dataSource = new DriverManagerDataSource();
            dataSource.setDriverClassName(env.getRequiredProperty(PROPERTY_NAME_DATABASE_DRIVER));
            dataSource.setUrl(env.getRequiredProperty(PROPERTY_NAME_DATABASE_URL));
            dataSource.setUsername(env.getRequiredProperty(PROPERTY_NAME_DATABASE_USERNAME));
            dataSource.setPassword(env.getRequiredProperty(PROPERTY_NAME_DATABASE_PASSWORD));
            return dataSource;
        }
    
        @Bean
        public LocalSessionFactoryBean sessionFactory() {
            LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();
            sessionFactoryBean.setDataSource(dataSource());
            //sessionFactoryBean.setPackagesToScan(env.getRequiredProperty(PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN));
            sessionFactoryBean.setPackagesToScan(new String[] {
                    env.getRequiredProperty(PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN_SERVICES),
                    env.getRequiredProperty(PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN_COMMON),
                    env.getRequiredProperty(PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN_CMS),
                    env.getRequiredProperty(PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN_PROCEDURE)
                    });
            sessionFactoryBean.setHibernateProperties(hibProperties());
            return sessionFactoryBean;
        }
    
        private Properties hibProperties() {
            Properties properties = new Properties();
            properties.put(PROPERTY_NAME_HIBERNATE_DIALECT, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_DIALECT));
            properties.put(PROPERTY_NAME_HIBERNATE_SHOW_SQL, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_SHOW_SQL));
            properties.put(PROPERTY_NAME_HIBERNATE_HBM2DDL_AUTO, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_HBM2DDL_AUTO));
            properties.put(PROPERTY_NAME_HIBERNATE_CONNECTION_CHARSET, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_CONNECTION_CHARSET));
            properties.put(PROPERTY_NAME_HIBERNATE_CONNECTION_CHARACTERENCODING, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_CONNECTION_CHARACTERENCODING));
            properties.put(PROPERTY_NAME_HIBERNATE_CONNECTION_USEUNICODE, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_CONNECTION_USEUNICODE));
            properties.put("jadira.usertype.autoRegisterUserTypes", "true");
            return properties;  
        }
    
        @Bean
        public HibernateTransactionManager transactionManager() {
            HibernateTransactionManager transactionManager = new HibernateTransactionManager();
            transactionManager.setSessionFactory(sessionFactory().getObject());
            return transactionManager;
        }
    
    
        @Override
        public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
            // Simple strategy: only path extension is taken into account
            configurer.favorPathExtension(true).
                ignoreAcceptHeader(true).
                useJaf(false).
                defaultContentType(MediaType.TEXT_HTML).
                mediaType("html", MediaType.TEXT_HTML).
                mediaType("xml", MediaType.APPLICATION_XML).
                mediaType("json", MediaType.APPLICATION_JSON);
        }
        @Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
            registry.addResourceHandler("/img/**").addResourceLocations("/WEB-INF/img/*");
            registry.addResourceHandler("/css/**").addResourceLocations("/WEB-INF/css/*");
            registry.addResourceHandler("/js/**").addResourceLocations("/WEB-INF/js/*");
            registry.addResourceHandler("/lib/**").addResourceLocations("/WEB-INF/lib/*");
        }
    
    
    
        @Override
        public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
            super.configureMessageConverters(converters);
            converters.add(responseBodyConverter());
        }
        @Bean
        public HttpMessageConverter<String> responseBodyConverter() {
            StringHttpMessageConverter converter = new StringHttpMessageConverter();
            converter.setSupportedMediaTypes(Arrays.asList(new MediaType("text", "plain", Charset.forName("UTF-8"))));
            return converter;
        }
    }
    

    Application initializer:

    package com.derp.common.init;
    
    import java.nio.charset.Charset;
    import java.util.Arrays;
    import java.util.LinkedList;
    import java.util.List;
    
    import javax.servlet.ServletContext;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRegistration.Dynamic;
    
    import org.springframework.http.MediaType;
    import org.springframework.http.converter.StringHttpMessageConverter;
    import org.springframework.web.WebApplicationInitializer;
    import org.springframework.web.context.ContextLoaderListener;
    import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
    import org.springframework.web.filter.CharacterEncodingFilter;
    import org.springframework.web.filter.HiddenHttpMethodFilter;
    import org.springframework.web.servlet.DispatcherServlet;
    
    public class Initializer implements WebApplicationInitializer {
    
    
        @Override
        public void onStartup(ServletContext servletContext) throws ServletException {
            AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
            ctx.register(WebAppConfig.class);
            ctx.register(ThymeleafConfig.class);
            servletContext.addListener(new ContextLoaderListener(ctx));
    
            ctx.setServletContext(servletContext);
    
            Dynamic servlet = servletContext.addServlet("dispatcher", new DispatcherServlet(ctx));
            servlet.addMapping("/");
            servlet.setAsyncSupported(true);     
            servlet.setLoadOnStartup(1);
            // Allow to use Put and Delete method for REST architecture
            registerCharachterEncodingFilter(servletContext);
            registerHiddenFieldFilter(servletContext);
        }
    
    
        private void registerCharachterEncodingFilter(ServletContext aContext) {
            CharacterEncodingFilter cef = new CharacterEncodingFilter();
            cef.setForceEncoding(true);
            cef.setEncoding("UTF-8");
            aContext.addFilter("charachterEncodingFilter", cef).addMappingForUrlPatterns(null ,true, "/*");
        }
        private void registerHiddenFieldFilter(ServletContext aContext) {
            aContext.addFilter("hiddenHttpMethodFilter", new HiddenHttpMethodFilter()).addMappingForUrlPatterns(null ,true, "/*"); 
        }
    
    }
    

    Java script/Jquery ajax call

      $('h1').click(function() {
          $.ajax({
              type: "PUT",
              url: "/derp/procedury/test",
              data: "none",
              success: function (response, status, xhr) {
                  showNotifications(status, xhr.responseText);
              },
              error: function (response, status, xhr) {
                  showNotifications('error', JSON.stringify(response));
                  showNotifications('error', status);
                  showNotifications('error', xhr);
              }
          });
      });
    

    Please help.