在 Spring MVC 3.1 控制器的处理程序方法中直接流到响应输出流
Posted
技术标签:
【中文标题】在 Spring MVC 3.1 控制器的处理程序方法中直接流到响应输出流【英文标题】:Stream directly to response output stream in handler method of Spring MVC 3.1 controller 【发布时间】:2013-03-07 22:50:34 【问题描述】:我有一个控制器方法来处理 ajax 调用并返回 JSON。我正在使用来自 json.org 的 JSON 库来创建 JSON。
我可以做到以下几点:
@RequestMapping(method = RequestMethod.POST)
@ResponseBody
public String getJson()
JSONObject rootJson = new JSONObject();
// Populate JSON
return rootJson.toString();
但是将 JSON 字符串放在一起是低效的,只是让 Spring 将其写入响应的输出流。
相反,我可以像这样直接将其写入响应输出流:
@RequestMapping(method = RequestMethod.POST)
public void getJson(HttpServletResponse response)
JSONObject rootJson = new JSONObject();
// Populate JSON
rootJson.write(response.getWriter());
但似乎有比将HttpServletResponse
传递到处理程序方法更好的方法。
除了@ResponseBody
注解,还有其他可以从处理程序方法返回的类或接口吗?
【问题讨论】:
“将 HttpServletResponse 传递给处理程序方法”有什么问题? @arahant - 这是一个公平的问题。我想我的印象是,尽管有可能,但这不是正确的做事方式。 @Ralph 在this question 中有一个答案,说它使测试变得更加困难。在我编写很多执行此操作的处理程序方法之前,我想知道是否还有其他方法。到目前为止,我似乎必须编写一个自定义HttpMessageConverter
。
【参考方案1】:
您可以将输出流或写入器作为控制器方法的参数。
@RequestMapping(method = RequestMethod.POST)
public void getJson(Writer responseWriter)
JSONObject rootJson = new JSONObject();
rootJson.write(responseWriter);
@见Spring Reference Documentation 3.1 Chapter 16.3.3.1 Supported method argument types
附言我觉得使用OutputStream
或Writer
作为参数在测试中仍然比HttpServletResponse
更容易使用——感谢关注what I have written ;-)
【讨论】:
感谢您的回答。传递OutputStream
或Writer
优于传递HttpServletResponse
。【参考方案2】:
最后,我为此写了HttpMessageConverter
。有了它,我可以做到以下几点:
@RequestMapping(method = RequestMethod.POST)
@ResponseBody
public JSONObject getJson()
throws JSONException
JSONObject rootJson = new JSONObject();
// Populate JSON
return rootJson;
这是我的HttpMessageConverter
课程:
package com.example;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.nio.charset.Charset;
import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
public class JsonObjectHttpMessageConverter
extends AbstractHttpMessageConverter<JSONObject>
private static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
public JsonObjectHttpMessageConverter()
super(new MediaType("application", "json"), new MediaType("text", "javascript"));
@Override
protected boolean supports(Class<?> clazz)
return JSONObject.class.equals(clazz);
@Override
protected JSONObject readInternal(Class<? extends JSONObject> clazz,
HttpInputMessage inputMessage)
throws IOException,
HttpMessageNotReadableException
throw new UnsupportedOperationException();
@Override
protected void writeInternal(JSONObject jsonObject,
HttpOutputMessage outputMessage)
throws IOException,
HttpMessageNotWritableException
PrintWriter writer = new PrintWriter(new OutputStreamWriter(outputMessage.getBody(),
getContentTypeCharset(outputMessage)));
try
jsonObject.write(writer);
writer.flush();
catch (JSONException e)
throw new HttpMessageNotWritableException(e.getMessage(), e);
private Charset getContentTypeCharset(HttpMessage message)
MediaType contentType = message.getHeaders().getContentType();
Charset charset = (contentType != null) ? contentType.getCharSet() : null;
return (charset != null) ? charset : DEFAULT_CHARSET;
HttpMessageConverter
必须在 Spring 中注册。这可以在dispatcher-servlet.xml
文件中完成,如下所示:
<beans ...>
...
<mvc:annotation-driven conversion-service="conversionService" validator="validator">
<mvc:argument-resolvers>
...
</mvc:argument-resolvers>
<mvc:message-converters>
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/plain;charset=UTF-8</value>
<value>application/json;charset=UTF-8</value>
<value>*/*</value>
</list>
</property>
<property name="writeAcceptCharset" value="false" />
</bean>
<bean class="com.example.JsonObjectHttpMessageConverter" />
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="objectMapper" ref="jacksonObjectMapper" />
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
...
</beans>
如您所见,我还注册了其他 HttpMessageConverter
对象。顺序很重要。
【讨论】:
感谢您抽出宝贵时间用更新的答案回复您自己的帖子,恕我直言,这是最好的答案。 @SteveMARION - 不客气。看起来我是唯一一个愿意这样做的人。我认为这个答案不会获得与接受的答案一样多的选票,但我认为它会更接近。【参考方案3】:请注意,如果您使用 OutputStream 或 Writer,则需要您自己编写标头。
一种解决方法是使用 InputStreamResource/ResourceHttpMessageConverter
【讨论】:
以上是关于在 Spring MVC 3.1 控制器的处理程序方法中直接流到响应输出流的主要内容,如果未能解决你的问题,请参考以下文章
Spring MVC-处理程序映射(Handler Mapping)-控制器类名称处理程序映射(Controller Class Name Handler Mapping)示例(转载实践)