谁在 Spring MVC (@ResponseBody) 中设置响应内容类型

Posted

技术标签:

【中文标题】谁在 Spring MVC (@ResponseBody) 中设置响应内容类型【英文标题】:Who sets response content-type in Spring MVC (@ResponseBody) 【发布时间】:2011-04-06 16:37:19 【问题描述】:

我的 Annotation 驱动的 Spring MVC Java web 应用程序运行在 jetty web 服务器上(目前在 maven jetty 插件中)。

我正在尝试使用一种仅返回字符串帮助文本的控制器方法来支持 AJAX。资源采用 UTF-8 编码,字符串也是如此,但我来自服务器的响应带有

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

即使我的浏览器发送了

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

我正在使用 spring 的默认配置

我找到了将这个 bean 添加到配置中的提示,但我认为它只是没有使用,因为它说它不支持编码,而是使用默认的编码。

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

我的控制器代码是(请注意,这种响应类型的更改对我不起作用):

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

【问题讨论】:

【参考方案1】:

我找到了 Spring 3.1 的解决方案。使用 @ResponseBody 注释。 以下是使用 Json 输出的控制器示例:

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


【讨论】:

+1。这也为我解决了这个问题,但只有在我切换到在 applicationContext 中使用 &lt;mvc:annotation-driven/&gt; 之后。 (而不是 &lt;bean class=" [...] DefaultAnnotationHandlerMapping"/&gt;,它在 Spring 3.2 中已被弃用......) 如果以这种方式注释会生成应用程序/xml? @Hurda:显然,您可以通过更改produces 属性的值来指定您希望的任何内容类型。 还有一个 MediaType.APPLICATION_JSON_VALUE,用于“application/json”。 对于 UTF-8,请参阅 MediaType.APPLICATION_JSON_UTF8_VALUE【参考方案2】:

StringHttpMessageConverter bean 的简单声明是不够的,你需要将它注入到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>

但是,使用这种方法你必须重新定义所有HttpMessageConverters,而且它不适用于&lt;mvc:annotation-driven /&gt;

所以,也许最方便但丑陋的方法是用BeanPostProcessor 拦截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", "html", 
                            Charset.forName("UTF-8"))));
                
            
        
        return bean;
    

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

-

<bean class = "EncodingPostProcessor " />

【讨论】:

这似乎是一个肮脏的黑客。我不喜欢它,但要使用它。 Spring 框架开发人员应该在这种情况下工作! 去哪儿了? @zod:在DispatcherServlet的配置中(...-servlet.xml 谢谢。它似乎被忽略了。我们正在使用 mvc(我认为)并且我们有一个带有 @Controller 属性的类,这似乎是入口点。该类在其他任何地方都没有提到(它有一个类似名称的接口),但它被实例化并正确调用。使用@RequestMapping 属性映射路径。我们无法控制响应的内容类型(我们需要 xml)。正如您可能知道的那样,我不知道自己在做什么,而创建它的开发人员已经离开了我的公司。谢谢。 正如@digz6666 所说,这是一个肮脏的黑客行为。 Spring 应该看看 JAX-RS 是如何做到的。【参考方案3】:

请注意,在 Spring MVC 3.1 中,您可以使用 MVC 命名空间来配置消息转换器:

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

或者基于代码的配置:

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

【讨论】:

某种作品,除了 1) 它使用可能列出所有已知字符编码的 Accept-Charset 标头污染响应,以及 2) when the request has an Accept header the supportedMediaTypes property of the converter is not used,例如当我发出请求时直接在浏览器中的 URL 响应具有 Content-Type: text/html 标头。 你可以简化为“text/plain”是默认的:&lt;bean class="org.springframework.http.converter.StringHttpMessageConverter"&gt;&lt;constructor-arg value="UTF-8" /&gt;&lt;/bean&gt; 这个答案应该被接受为正确答案。此外,@IgorMukhin 定义 StringHttpMessageConverter bean 的方式有效。此答案用于设置所有 servlet 的响应内容类型。如果您只需要为特定控制器方法设置响应内容类型,请改用 Warrior 的答案(在 @RequestMapping 中使用产生参数) @GiulioPiancastelli 您的第一个问题可以通过将 添加到 bean 来解决【参考方案4】:

以防万一您也可以通过以下方式设置编码:

@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);

我认为使用 StringHttpMessageConverter 比这更好。

【讨论】:

如果您在 IE 11 中收到错误 the manifest may not be valid or the file could not be opened.,这也是解决方案。感谢 digz!【参考方案5】:

您可以将produce = "text/plain;charset=UTF-8" 添加到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

【讨论】:

该代码无法编译;你正在从一个 void 方法返回一些东西。 抱歉,错误已修复 这是一个错误的答案。根据 spring 文档:映射请求的可生产媒体类型,缩小主映射。格式是一系列媒体类型(“text/plain”、“application/*”),只有在 Accept 匹配这些媒体类型之一时才映射请求。表达式可以使用“!”运算符取反,如"!text/plain",它匹配除 "text/plain" 之外的所有带有 Accept 的请求。 @CharlieWu 链接有问题【参考方案6】:

我最近正在解决这个问题,并在 Spring 3.1 中找到了更好的答案:

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

因此,就像所有 cmets 表明它可以/应该那样简单,就像 JAX-RS 一样简单。

【讨论】:

值得移植到 Spring 3.1 ! @dbyoung 这似乎不对,produces 的 javadoc 说:“...仅在 Content-Type 与这些媒体类型之一匹配时才映射请求。”这意味着 AFAIK 认为 produces 与方法是否匹配请求相关,而不是响应应该具有哪种内容类型。 @Ittai 正确! “produces”确定方法是否匹配请求,但不确定响应中的内容类型。在确定要设置的内容类型时,必须考虑其他东西“产生”【参考方案7】:

您可以使用 producer 来指示您从控制器发送的响应的类型。这个“produces”关键字在 ajax 请求中最有用,对我的项目很有帮助

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

public @ResponseBody String getMobileData() 


【讨论】:

【参考方案8】:

感谢 digz6666,您的解决方案对我有用,因为我使用的是 json:

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

axtavt(您推荐的)给出的答案对我不起作用。即使我添加了正确的媒体类型:

if (conv instanceof StringHttpMessageConverter) ((StringHttpMessageConverter) 转换).setSupportedMediaTypes( 数组.asList( 新的 MediaType("text", "html", Charset.forName("UTF-8")), new MediaType("application", "json", Charset.forName("UTF-8")) ));

【讨论】:

【参考方案9】:

我在 ContentNegotiatingViewResolver bean 的 MarshallingView 中设置内容类型。它工作简单、干净、流畅:

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

【讨论】:

【参考方案10】:

我正在使用在 web.xml 中配置的 CharacterEncodingFilter。也许这有帮助。

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

【讨论】:

这只是过滤请求中的字符,而不是响应 - 我已经在使用这个了 @Hurda:forceEncoding=true 也会过滤响应,但在这种情况下无济于事。 迄今为止最好和更快的答案。我也已经声明并使用了这个过滤器,但是使用了forceEncoding=false。我只是将其设置为false,并且“charset=UTF-8”已成功添加到Content-Type 标头中。【参考方案11】:

如果上述方法都不适合您尝试在 "POST" 而不是 "GET" 上发出 ajax 请求,那对我来说效果很好......以上都没有。我也有characterEncodingFilter。

【讨论】:

【参考方案12】:
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");
    

在我为这个问题尝试了很多解决方法之后。我想出了这个问题,它工作正常。

【讨论】:

【参考方案13】:

在Spring 3.1.1中解决这个问题的简单方法是:在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>

不需要覆盖或实现任何东西。

【讨论】:

【参考方案14】:

如果您决定通过以下配置解决此问题:

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

您应该确认在您的所有 *.xml 文件中应该只有一个 mvc:annotation-driven 标签。 否则配置可能无效。

【讨论】:

【参考方案15】:

根据link“如果未指定字符编码,则Servlet规范要求使用ISO-8859-1的编码”。如果您使用的是spring 3.1或更高版本,请使用fallowing配置设置charset =UTF-8 响应正文@RequestMapping(value = "your mapping url",produces = "text/plain;charset=UTF-8")

【讨论】:

【参考方案16】:
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;
        
    

示例配置:

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

【讨论】:

以上是关于谁在 Spring MVC (@ResponseBody) 中设置响应内容类型的主要内容,如果未能解决你的问题,请参考以下文章

Spring常用注解

谁在调用错误视图,为啥?

如何显示关注用户的状态更新? (MVC)

Spring MVC学习笔记---Spring MVC 的HelloWorld

Spring MVC学习笔记---Spring MVC 的HelloWorld

Spring MVC学习—MVC的介绍以及Spring MVC的入门案例