Configuring Response Content-Type and Character Encoding with @ResponseBody in Spring MVC

Nov 23, 2025 · Programming · 10 views · 7.8

Keywords: Spring MVC | @ResponseBody | Character Encoding

Abstract: This article delves into the configuration of content type and character encoding when returning strings with the @ResponseBody annotation in Spring MVC. By analyzing common issue scenarios, it provides detailed methods for configuring StringHttpMessageConverter, intercepting AnnotationMethodHandlerAdapter via BeanPostProcessor, and utilizing namespace and code-based configurations in Spring 3.1+. With concrete code examples, it offers comprehensive solutions from basic setup to advanced optimizations.

Problem Background and Core Challenges

In annotation-driven Spring MVC Java web applications, returning strings with @ResponseBody often leads to unexpected response content types and character encodings. A typical scenario involves resources encoded in UTF-8, but the server returns a Content-Type header such as text/plain;charset=ISO-8859-1, despite the browser supporting UTF-8 in Accept-Charset. This usually stems from the character encoding settings of the default StringHttpMessageConverter in Spring.

Limitations and Improvements of Basic Configuration

Merely declaring a StringHttpMessageConverter Bean is often insufficient, as the converter is not properly injected into the processing chain. For example, configuring only:

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

may not take effect, requiring explicit injection via 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, this method necessitates redefining all HttpMessageConverters and is incompatible with <mvc:annotation-driven />.

Advanced Solution: Interception via BeanPostProcessor

To balance convenience and compatibility, implement a BeanPostProcessor to intercept the instantiation of AnnotationMethodHandlerAdapter:

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", "plain", Charset.forName("UTF-8"))));
                }
            }
        }
        return bean;
    }

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

In configuration, simply add:

<bean class="EncodingPostProcessor" />

This approach dynamically modifies existing converters without full redefinition.

Modern Configuration in Spring 3.1+

For Spring 3.1 and later, prefer MVC namespace or code-based configuration. Namespace approach:

<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 via Java configuration class:

@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 if needed
    }
}

Alternative Approaches and Comparisons

Beyond converter configuration, ResponseEntity can be used to set response headers directly:

@RequestMapping(value = "ajax/gethelp")
public ResponseEntity<String> handleGetHelp(Locale loc, String code) {
    HttpHeaders responseHeaders = new HttpHeaders();
    responseHeaders.add("Content-Type", "text/plain; charset=utf-8");
    String help = messageSource.getMessage(code, null, loc);
    return new ResponseEntity<String>(help, responseHeaders, HttpStatus.OK);
}

However, this is less unified and elegant than configuring StringHttpMessageConverter.

Summary and Best Practices

The key to resolving character encoding issues with @ResponseBody lies in proper configuration of StringHttpMessageConverter. For older Spring versions, BeanPostProcessor interception is effective; for newer ones, prioritize namespace or code-based configuration. Ensure configurations are applied at application startup and verify that the Content-Type and charset in response headers meet expectations to prevent character garbling.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.