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


126

我在注释驱动的Spring MVC Java Web应用程序中运行在Jetty Web服务器上运行(当前在Maven Jetty插件中)。

我正在尝试使用一种仅返回String帮助文本的控制器方法来提供一些AJAX支持。资源采用UTF-8编码,字符串也采用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;
}

Answers:


59

简单声明StringHttpMessageConverterbean是不够的,您需要将其注入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>

但是,使用此方法必须重新定义all HttpMessageConverter,并且它也不适用于<mvc:annotation-driven />

因此,也许最方便但最丑陋的方法是拦截AnnotationMethodHandlerAdapterwith的实例化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 " />

10
似乎是肮脏的骇客。我不喜欢它,但是要使用。Spring框架开发人员应在这种情况下工作!
digz6666

<bean class =“ EncodingPostProcessor” />行去哪了?
zod 2011年

1
@zod:在DispatcherServlet的配置(...-servlet.xml
axtavt

谢谢。它似乎被忽略了。我们正在使用mvc(我认为),并且我们有一个带有@Controller属性的类,它似乎是入口点。该类在其他任何地方都没有提及(它的接口具有相似的名称),但已实例化并正确调用了该类。路径使用@RequestMapping属性映射。我们无法控制响应的内容类型(我们需要xml)。您可能会说,我不知道我在做什么,而创建它的开发人员已经离开了我的公司。谢谢。
zod 2011年

3
正如@ digz6666所说,这是一个肮脏的hack。Spring应该看看JAX-RS是如何做到的。
亚当·根特

166

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

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

}

7
+1。这也为我解决了这个问题,但是只有在我切换到<mvc:annotation-driven/>在applicationContext中使用它之后。(而不是<bean class=" [...] DefaultAnnotationHandlerMapping"/>,它无论如何在Spring 3.2中已弃用...)
Jonik

如果这样注释,这会产生application / xml吗?
赫尔达

2
@Hurda:显然,您可以通过更改produces属性的值来指定所需的任何内容类型。
约尼克,

1
还有一个MediaType.APPLICATION_JSON_VALUE,也适用于“ application / json”。
dev 2014年

2
有关UTF-8,请参见MediaType.APPLICATION_JSON_UTF8_VALUE
calvinf '16

51

请注意,在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)当请求具有Accept标头时,supportedMediaTypes不使用转换器的属性,例如当我键入请求时直接在浏览器中的URL响应中具有Content-Type: text/html标题。
朱利奥·皮安卡斯特利

3
您可以简化为“文本/纯文本”作为默认设置: <bean class="org.springframework.http.converter.StringHttpMessageConverter"><constructor-arg value="UTF-8" /></bean>
Igor Mukhin,2015年

该答案应被视为正确答案。同样,@IgorMukhin的定义StringHttpMessageConverter bean的方法也可以使用。该答案用于为所有servlet设置响应内容类型。如果您只需要为特定的控制器方法设置响应内容类型,请改用Warrior的答案(在@RequestMapping中使用
Produces

3
@GiulioPiancastelli您的第一个问题可以通过在豆中添加<property name =“ writeAcceptCharset” value =“ false” />来解决
PickBoy

44

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

@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比这更好。


如果您the manifest may not be valid or the file could not be opened.在IE 11中遇到错误,这也是解决方案。谢谢digz!
阿伦·克里斯托弗

21

您可以将Produces =“ 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);
}

请参阅此博客以获取更多详细信息


2
该代码无法编译;您从void方法返回了一些东西。
Andrew Swan

2
抱歉,严重的错误,现已修复
Charlie Wu

3
这是一个错误的答案。根据spring docs:映射请求的可生产媒体类型,从而缩小了主要映射。格式是一系列媒体类型(“文本/纯文本”,“应用程序/ *”),仅当Accept匹配这些媒体类型之一时才映射请求。可以使用“!”运算符来取反表达式,如“!text / plain”,将所有请求与“ text / plain”以外的其他Accept匹配
Oleksandr_DJ 2013年

@CharlieWu链接存在问题
Matt

10

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

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

因此,就像所有注释指示的那样/应该那样,JAX-RS一样容易。


值得移植到Spring 3.1!
young.fu.panda 2012年

5
@dbyoung似乎不太正确,该Javadoc produces表示:“ ...仅在Content-Type与这些媒体类型之一匹配时才映射请求。” 这意味着AFAIK produces与该方法是否匹配请求有关,而与响应应具有的内容类型无关。
Ittai 2012年

@ittai正确!“生产”确定方法是否与请求匹配,但不确定响应中的内容类型。在确定要设置哪种内容类型时,其他东西必须在看“产生”
anton1980

6

您可以使用产生指示从控制器发送的响应的类型。这个“产生”关键字将在ajax请求中最有用,并且对我的项目非常有帮助

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

public @ResponseBody String getMobileData() {

}

4

谢谢digz6666,您的解决方案对我来说略有变化,因为我使用的是json:

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

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

if(conv instanceof StringHttpMessageConverter){                   
                    (((StringHttpMessageConverter)转换).setSupportedMediaTypes(
                        Arrays.asList(
                                新的MediaType(“ text”,“ html”,Charset.forName(“ UTF-8”)),
                                new MediaType(“ application”,“ json”,Charset.forName(“ UTF-8”)))));
                }

4

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

3

我正在使用在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>

1
这只是过滤请求中的字符,而不是响应中的字符-我已经准备好使用它了
Hurda 2010年

@Hurda:使用forceEncoding=true它也可以过滤响应,但是在这种情况下它无济于事。
axtavt 2010年

到目前为止最好和最快的答案。我也已经声明并使用了此过滤器,但是使用了forceEncoding=false。我只是将其设置为,false并且“ charset = UTF-8”已成功添加到Content-Type标题中。
Saad Benbouzid

2

如果以上方法都不适合您尝试在“ POST”而不是“ GET”上发出ajax请求,那么对我来说效果很好...以上方法均不起作用。我也有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");
    }
}

在为这个问题尝试了很多解决方法之后。我认为了这一点,并且效果很好。


2

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

不需要重写或实现任何东西。


2

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

<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标签。否则,该配置可能无效。


1

根据链接 “如果未指定字符编码,则Servlet规范要求使用ISO-8859-1的编码”。如果您使用的是Spring 3.1或更高版本,请使用休闲配置将charset = UTF-8设置为响应正文
@RequestMapping(value =“您的映射网址”,产生=“ 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;
        }
    }
}

样本配置:

    <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>
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.