140

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

16 Answers 16

179

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() {

}
8
  • 7
    +1. This solved it for me too, but only after I switched to using <mvc:annotation-driven/> in applicationContext. (Instead of <bean class=" [...] DefaultAnnotationHandlerMapping"/>, which is deprecated in Spring 3.2 anyway...)
    – Jonik
    Apr 16, 2013 at 11:07
  • ca this produce application/xml if annotated this way?
    – Hurda
    May 27, 2013 at 15:28
  • 2
    @Hurda: Obviously you can specify any content type that you wish by changing the value of the produces attribute.
    – Jonik
    May 28, 2013 at 8:58
  • 1
    There is a MediaType.APPLICATION_JSON_VALUE, for "application/json" as well.
    – dev
    Jun 11, 2014 at 21:45
  • 2
    For UTF-8, see MediaType.APPLICATION_JSON_UTF8_VALUE.
    – calvinf
    Jul 6, 2016 at 23:20
60

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 " />
6
  • 10
    It seem like a dirty hack. I don't like it but to use. Spring framework developers should work on this case!
    – digz6666
    Mar 11, 2011 at 1:55
  • Where does the line <bean class = "EncodingPostProcessor " /> go?
    – zod
    Mar 30, 2011 at 11:50
  • 1
    @zod: In DispatcherServlet's config (...-servlet.xml)
    – axtavt
    Mar 30, 2011 at 11:57
  • Thanks. It appears to be ignored. We are using mvc (I think) and we have a class with a @Controller attribute, that appears to be the entry point. The class is not mentioned anywhere else (it has an interface with a similar name) yet it is instantiated and called correctly. Paths are mapped with a @RequestMapping attribute. We are unable to control the content type of the response (we need xml). As you can probably tell, I have no idea what I am doing, and the developer who created this has left my company. Thanks.
    – zod
    Mar 30, 2011 at 12:32
  • 3
    As @digz6666 says this is a dirty hack. Spring should see how JAX-RS does it.
    – Adam Gent
    Apr 18, 2011 at 19:59
52

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 ...
  }
}
4
  • Sort of works, except that 1) it pollutes the response with an Accept-Charset header that probably lists every known character encoding, and 2) when the request has an Accept header the supportedMediaTypes property of the converter is not used, so for example when I make the request typing directly the URL in a browser the response has a Content-Type: text/html header instead. Feb 26, 2015 at 19:19
  • 3
    You can simplify as "text/plain" is default anyway: <bean class="org.springframework.http.converter.StringHttpMessageConverter"><constructor-arg value="UTF-8" /></bean> May 1, 2015 at 17:52
  • This answer should be accepted as the right answer. Also, @IgorMukhin 's way to define StringHttpMessageConverter bean works. This answer is used to set response content-types for all servlets. If you just need to set reponse content type for a particular controller method, use Warrior 's answer instead( use produces argument in @RequestMapping)
    – PickBoy
    Mar 23, 2016 at 8:34
  • 3
    @GiulioPiancastelli your first question can be solved by add <property name="writeAcceptCharset" value="false" /> to the bean
    – PickBoy
    Mar 23, 2016 at 11:33
46

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.

1
  • This is also the solution if you get the error the manifest may not be valid or the file could not be opened. in IE 11. Thanks digz! Sep 4, 2015 at 21:44
20

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);
}
1
  • 3
    It is a incorrect answer. As per spring docs: The producible media types of the mapped request, narrowing the primary mapping. The format is a sequence of media types ("text/plain", "application/*), with a request only mapped if the Accept matches one of these media types. Expressions can be negated by using the "!" operator, as in "!text/plain", which matches all requests with a Accept other than "text/plain". Jun 28, 2013 at 9:18
11

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.

3
  • Worth porting to Spring 3.1 for! Jul 16, 2012 at 21:56
  • 5
    @dbyoung That doesn't seem right, the javadoc for the produces says: "...request only mapped if the Content-Type matches one of these media types." which means AFAIK that the produces is relevant to whether the method matches a request and not how which content-type the response should have.
    – Ittai
    Jul 22, 2012 at 11:56
  • @Ittai correct! "produces" determines if the method matches the request, but NOT what content type is in the response. something else must be looking at "produces" when determining what content type to set
    – anton1980
    Jul 12, 2013 at 17:10
6

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() {

}
4

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")) ));
                }
4

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

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>
3
  • 1
    This just filters character in request, not in response - I'm allready using this one
    – Hurda
    Sep 1, 2010 at 10:09
  • @Hurda: With forceEncoding=true it filters the response too, but it wouldn't help in this case.
    – axtavt
    Sep 1, 2010 at 11:44
  • Best and quicker answer so far. I also was already declaring and using this filter, but with forceEncoding=false. I just set it to false and "charset=UTF-8" is successfully added to Content-Type header. Oct 25, 2016 at 15:58
2

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.

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

2

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.

2

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.

1

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

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.