Who sets response content-type in Spring MVC (@ResponseBody)

JavaWeb ApplicationsSpring MvcCharacter Encoding

Java Problem Overview


I'm having in my Annotation driven Spring MVC Java web application runned on jetty web server (currently in maven jetty plugin).

I'm trying to do some AJAX support with one controller method returning just String help text. Resources are in UTF-8 encoding and so is the string, but my response from server comes with

content-encoding: text/plain;charset=ISO-8859-1 

even when my browser sends

Accept-Charset	windows-1250,utf-8;q=0.7,*;q=0.7

I'm using somehow default configuration of spring

I have found a hint to add this bean to the configuration, but I think it's just not used, because it says it does not support the encoding and a default one is used instead.

<bean class="org.springframework.http.converter.StringHttpMessageConverter">
	<property name="supportedMediaTypes" value="text/plain;charset=UTF-8" />
</bean>

My controller code is (note that this change of response type is not working for me):

@RequestMapping(value = "ajax/gethelp")
public @ResponseBody String handleGetHelp(Locale loc, String code, HttpServletResponse response) {
	log.debug("Getting help for code: " + code);
	response.setContentType("text/plain;charset=UTF-8");
	String help = messageSource.getMessage(code, null, loc);
	log.debug("Help is: " + help);
	return help;
}

Java Solutions


Solution 1 - Java

I found solution for Spring 3.1. with using @ResponseBody annotation. Here is example of controller using Json output:

@RequestMapping(value = "/getDealers", method = RequestMethod.GET, 
produces = "application/json; charset=utf-8")
@ResponseBody
public String sendMobileData() {

}

Solution 2 - Java

Simple declaration of the StringHttpMessageConverter bean is not enough, you need to inject it into AnnotationMethodHandlerAdapter:

<bean class = "org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
    <property name="messageConverters">
        <array>
            <bean class = "org.springframework.http.converter.StringHttpMessageConverter">
                <property name="supportedMediaTypes" value = "text/plain;charset=UTF-8" />
            </bean>
        </array>
    </property>
</bean>

However, using this method you have to redefine all HttpMessageConverters, and also it doesn't work with <mvc:annotation-driven />.

So, perhaps the most convenient but ugly method is to intercept instantiation of the AnnotationMethodHandlerAdapter with BeanPostProcessor:

public class EncodingPostProcessor implements BeanPostProcessor {
    public Object postProcessBeforeInitialization(Object bean, String name)
            throws BeansException {
        if (bean instanceof AnnotationMethodHandlerAdapter) {
            HttpMessageConverter<?>[] convs = ((AnnotationMethodHandlerAdapter) bean).getMessageConverters();
            for (HttpMessageConverter<?> conv: convs) {
                if (conv instanceof StringHttpMessageConverter) {
                    ((StringHttpMessageConverter) conv).setSupportedMediaTypes(
                        Arrays.asList(new MediaType("text", "html", 
                            Charset.forName("UTF-8"))));
                }
            }
        }
        return bean;
    }

    public Object postProcessAfterInitialization(Object bean, String name)
            throws BeansException {
        return bean;
    }
}

-

<bean class = "EncodingPostProcessor " />

Solution 3 - Java

Note that in Spring MVC 3.1 you can use the MVC namespace to configure message converters:

<mvc:annotation-driven>
  <mvc:message-converters register-defaults="true">
    <bean class="org.springframework.http.converter.StringHttpMessageConverter">
      <property name="supportedMediaTypes" value = "text/plain;charset=UTF-8" />
    </bean>
  </mvc:message-converters>
</mvc:annotation-driven>

Or code-based configuration:

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {

  private static final Charset UTF8 = Charset.forName("UTF-8");

  @Override
  public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    StringHttpMessageConverter stringConverter = new StringHttpMessageConverter();
    stringConverter.setSupportedMediaTypes(Arrays.asList(new MediaType("text", "plain", UTF8)));
    converters.add(stringConverter);
    
    // Add other converters ...
  }
}

Solution 4 - Java

Just in case you can also set encoding by the following way:

@RequestMapping(value = "ajax/gethelp")
public ResponseEntity<String> handleGetHelp(Locale loc, String code, HttpServletResponse response) {
    HttpHeaders responseHeaders = new HttpHeaders();
    responseHeaders.add("Content-Type", "text/html; charset=utf-8");

    log.debug("Getting help for code: " + code);
    String help = messageSource.getMessage(code, null, loc);
    log.debug("Help is: " + help);

    return new ResponseEntity<String>("returning: " + help, responseHeaders, HttpStatus.CREATED);
}

I think using StringHttpMessageConverter is better than this.

Solution 5 - Java

you can add produces = "text/plain;charset=UTF-8" to RequestMapping

@RequestMapping(value = "/rest/create/document", produces = "text/plain;charset=UTF-8")
@ResponseBody
public String create(Document document, HttpServletRespone respone) throws UnsupportedEncodingException {
	
	Document newDocument = DocumentService.create(Document);
	
	return jsonSerializer.serialize(newDocument);
}

see this blog for more detail

Solution 6 - Java

I was fighting this issue recently and found a much better answer available in Spring 3.1:

@RequestMapping(value = "ajax/gethelp", produces = "text/plain")

So, as easy as JAX-RS just like all the comments indicated it could/should be.

Solution 7 - Java

You can use produces to indicate the type of the response you are sending from the controller. This "produces" keyword will be most useful in ajax request and was very helpful in my project

@RequestMapping(value = "/aURLMapping.htm", method = RequestMethod.GET, produces = "text/html; charset=utf-8") 
 
public @ResponseBody String getMobileData() {

}

Solution 8 - Java

Thanks digz6666, your solution works for me with a slight changes because I'm using json:

responseHeaders.add("Content-Type", "application/json; charset=utf-8");

The answer given by axtavt (whch you've recommended) wont work for me. Even if I've added the correct media type:

if (conv instanceof StringHttpMessageConverter) {
((StringHttpMessageConverter) conv).setSupportedMediaTypes(
Arrays.asList(
new MediaType("text", "html", Charset.forName("UTF-8")),
new MediaType("application", "json", Charset.forName("UTF-8")) ));
}

Solution 9 - Java

I set the content-type in the MarshallingView in the ContentNegotiatingViewResolver bean. It works easily, clean and smoothly:

<property name="defaultViews">
  <list>
    <bean class="org.springframework.web.servlet.view.xml.MarshallingView">
      <constructor-arg>
        <bean class="org.springframework.oxm.xstream.XStreamMarshaller" />     
      </constructor-arg>
      <property name="contentType" value="application/xml;charset=UTF-8" />
    </bean>
  </list>
</property>

Solution 10 - Java

I'm using the CharacterEncodingFilter, configured in web.xml. Maybe that helps.

    <filter>
    <filter-name>characterEncodingFilter</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>

Solution 11 - Java

if none of the above worked for you try to make ajax requests on "POST" not "GET" , that worked for me nicely ... none of the above did. I also have the characterEncodingFilter.

Solution 12 - Java

package com.your.package.spring.fix;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;

/**
 * @author Szilard_Jakab (JaKi)
 * Workaround for Spring 3 @ResponseBody issue - get incorrectly 
   encoded parameters     from the URL (in example @ JSON response)
 * Tested @ Spring 3.0.4
 */
public class RepairWrongUrlParamEncoding {
    private static String restoredParamToOriginal;

    /**
    * @param wrongUrlParam
    * @return Repaired url param (UTF-8 encoded)
    * @throws UnsupportedEncodingException
    */
    public static String repair(String wrongUrlParam) throws 
                                            UnsupportedEncodingException {
    /* First step: encode the incorrectly converted UTF-8 strings back to 
                  the original URL format
    */
    restoredParamToOriginal = URLEncoder.encode(wrongUrlParam, "ISO-8859-1");
    
    /* Second step: decode to UTF-8 again from the original one
    */
    return URLDecoder.decode(restoredParamToOriginal, "UTF-8");
    }
}

After I have tried lot of workaround for this issue.. I thought this out and it works fine.

Solution 13 - Java

The simple way to solve this problem in Spring 3.1.1 is that: add following configuration codes in servlet-context.xml

    <annotation-driven>
    <message-converters register-defaults="true">
    <beans:bean class="org.springframework.http.converter.StringHttpMessageConverter">
    <beans:property	name="supportedMediaTypes">    
    <beans:value>text/plain;charset=UTF-8</beans:value>
    </beans:property>
    </beans:bean>
    </message-converters>
    </annotation-driven>

Don't need to override or implement anything.

Solution 14 - Java

if you decide to fix this problem through the following configuration:

<mvc:annotation-driven>
  <mvc:message-converters register-defaults="true">
    <bean class="org.springframework.http.converter.StringHttpMessageConverter">
      <property name="supportedMediaTypes" value = "text/plain;charset=UTF-8" />
    </bean>
  </mvc:message-converters>
</mvc:annotation-driven>

you should confirm that there should only one mvc:annotation-driven tag in all your *.xml file. otherwise, the configuration may not be effective.

Solution 15 - Java

According to the link " If a character encoding is not specified, the Servlet specification requires that an encoding of ISO-8859-1 is used ".If you are using spring 3.1 or later use the fallowing configuration to set charset=UTF-8 to response body
@RequestMapping(value = "your mapping url", produces = "text/plain;charset=UTF-8")

Solution 16 - Java

public final class ConfigurableStringHttpMessageConverter extends AbstractHttpMessageConverter<String> {

    private Charset defaultCharset;

    public Charset getDefaultCharset() {
        return defaultCharset;
    }

	private final List<Charset> availableCharsets;

	private boolean writeAcceptCharset = true;

	public ConfigurableStringHttpMessageConverter() {
		super(new MediaType("text", "plain", StringHttpMessageConverter.DEFAULT_CHARSET), MediaType.ALL);
        defaultCharset = StringHttpMessageConverter.DEFAULT_CHARSET;
		this.availableCharsets = new ArrayList<Charset>(Charset.availableCharsets().values());
	}

    public ConfigurableStringHttpMessageConverter(String charsetName) {
        super(new MediaType("text", "plain", Charset.forName(charsetName)), MediaType.ALL);
        defaultCharset = Charset.forName(charsetName);
		this.availableCharsets = new ArrayList<Charset>(Charset.availableCharsets().values());
    }

	/**
	 * Indicates whether the {@code Accept-Charset} should be written to any outgoing request.
	 * <p>Default is {@code true}.
	 */
	public void setWriteAcceptCharset(boolean writeAcceptCharset) {
		this.writeAcceptCharset = writeAcceptCharset;
	}

	@Override
	public boolean supports(Class<?> clazz) {
		return String.class.equals(clazz);
	}

	@Override
	protected String readInternal(Class clazz, HttpInputMessage inputMessage) throws IOException {
		Charset charset = getContentTypeCharset(inputMessage.getHeaders().getContentType());
		return FileCopyUtils.copyToString(new InputStreamReader(inputMessage.getBody(), charset));
	}

	@Override
	protected Long getContentLength(String s, MediaType contentType) {
		Charset charset = getContentTypeCharset(contentType);
		try {
			return (long) s.getBytes(charset.name()).length;
		}
		catch (UnsupportedEncodingException ex) {
			// should not occur
			throw new InternalError(ex.getMessage());
		}
	}

	@Override
	protected void writeInternal(String s, HttpOutputMessage outputMessage) throws IOException {
		if (writeAcceptCharset) {
			outputMessage.getHeaders().setAcceptCharset(getAcceptedCharsets());
		}
		Charset charset = getContentTypeCharset(outputMessage.getHeaders().getContentType());
		FileCopyUtils.copy(s, new OutputStreamWriter(outputMessage.getBody(), charset));
	}

	/**
	 * Return the list of supported {@link Charset}.
	 *
	 * <p>By default, returns {@link Charset#availableCharsets()}. Can be overridden in subclasses.
	 *
	 * @return the list of accepted charsets
	 */
	protected List<Charset> getAcceptedCharsets() {
		return this.availableCharsets;
	}

	private Charset getContentTypeCharset(MediaType contentType) {
		if (contentType != null && contentType.getCharSet() != null) {
			return contentType.getCharSet();
		}
		else {
			return defaultCharset;
		}
	}
}

Sample configuration :

    <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
        <property name="messageConverters">
            <util:list>
                <bean class="ru.dz.mvk.util.ConfigurableStringHttpMessageConverter">
                    <constructor-arg index="0" value="UTF-8"/>
                </bean>
            </util:list>
        </property>
    </bean>

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionHurdaView Question on Stackoverflow
Solution 1 - JavaWarriorView Answer on Stackoverflow
Solution 2 - JavaaxtavtView Answer on Stackoverflow
Solution 3 - JavaRossen StoyanchevView Answer on Stackoverflow
Solution 4 - Javadigz6666View Answer on Stackoverflow
Solution 5 - JavaCharlie WuView Answer on Stackoverflow
Solution 6 - JavadbyoungView Answer on Stackoverflow
Solution 7 - JavaBalasubramanian JayaramanView Answer on Stackoverflow
Solution 8 - JavaredochkaView Answer on Stackoverflow
Solution 9 - JavaReto-sanView Answer on Stackoverflow
Solution 10 - JavaTheresia Sofia SnowView Answer on Stackoverflow
Solution 11 - JavaMariusView Answer on Stackoverflow
Solution 12 - JavaSzilard JakabView Answer on Stackoverflow
Solution 13 - JavaAdaroMuView Answer on Stackoverflow
Solution 14 - JavaLichView Answer on Stackoverflow
Solution 15 - JavaRamesh PapagantiView Answer on Stackoverflow
Solution 16 - JavaIgor KostominView Answer on Stackoverflow