谁在 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 中使用<mvc:annotation-driven/>
之后。 (而不是 <bean class=" [...] DefaultAnnotationHandlerMapping"/>
,它在 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>
但是,使用这种方法你必须重新定义所有HttpMessageConverter
s,而且它不适用于<mvc:annotation-driven />
。
所以,也许最方便但丑陋的方法是用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 框架开发人员应该在这种情况下工作! 行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”是默认的:<bean class="org.springframework.http.converter.StringHttpMessageConverter"><constructor-arg value="UTF-8" /></bean>
这个答案应该被接受为正确答案。此外,@IgorMukhin 定义 StringHttpMessageConverter bean 的方式有效。此答案用于设置所有 servlet 的响应内容类型。如果您只需要为特定控制器方法设置响应内容类型,请改用 Warrior 的答案(在 @RequestMapping 中使用产生参数)
@GiulioPiancastelli 您的第一个问题可以通过将 以防万一您也可以通过以下方式设置编码:
@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 的HelloWorld