Поток непосредственно в выходной поток ответа в методе обработчика контроллера Spring MVC 3.1
У меня есть метод контроллера, который обрабатывает вызовы ajax и возвращает JSON. Я использую JSON-библиотеку от json.org для создания 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
?
Ответы
Ответ 1
Вы можете иметь выходной поток или Writer в качестве параметра вашего метода контроллера.
@RequestMapping(method = RequestMethod.POST)
public void getJson(Writer responseWriter) {
JSONObject rootJson = new JSONObject();
rootJson.write(responseWriter);
}
@see Spring Справочная документация 3.1 Глава 16.3.3.1 Поддерживаемые типы аргументов метода
p.s. Я считаю, что использование 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
. Приказ имеет значение.
Ответ 3
Обратите внимание, что если вы используете OutputStream или Writer, вам необходимо написать заголовки самостоятельно.
Одним из способов решения проблемы является использование InputStreamResource/ResourceHttpMessageConverter