handling - Transmitir directamente al flujo de salida de respuesta en el método del controlador del controlador Spring MVC 3.1
spring response exception handler (3)
Tengo un método de controlador que maneja las llamadas ajax y devuelve JSON. Estoy utilizando la biblioteca JSON de json.org para crear el JSON.
Yo podría hacer lo siguiente:
@RequestMapping(method = RequestMethod.POST)
@ResponseBody
public String getJson()
{
JSONObject rootJson = new JSONObject();
// Populate JSON
return rootJson.toString();
}
Pero es ineficiente armar la cadena JSON, solo para que Spring la escriba en el flujo de salida de la respuesta.
En su lugar, puedo escribirlo directamente en la secuencia de salida de respuesta de esta manera:
@RequestMapping(method = RequestMethod.POST)
public void getJson(HttpServletResponse response)
{
JSONObject rootJson = new JSONObject();
// Populate JSON
rootJson.write(response.getWriter());
}
Pero parece que habría una mejor manera de hacerlo que tener que recurrir a pasar HttpServletResponse
en el método del controlador.
¿Hay otra clase o interfaz que pueda devolverse desde el método del controlador que puedo usar, junto con la anotación @ResponseBody
?
Al final, escribí un HttpMessageConverter
para esto. Con ello, puedo hacer lo siguiente:
@RequestMapping(method = RequestMethod.POST)
@ResponseBody
public JSONObject getJson()
throws JSONException
{
JSONObject rootJson = new JSONObject();
// Populate JSON
return rootJson;
}
Aquí está mi clase 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;
}
}
El HttpMessageConverter
debe estar registrado con Spring. Esto se puede hacer en el archivo dispatcher-servlet.xml
esta manera:
<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>
Como puede ver, también tengo otros objetos HttpMessageConverter
registrados. El orden sí importa.
Puede tener el flujo de salida o el escritor como un parámetro de su método de controlador.
@RequestMapping(method = RequestMethod.POST)
public void getJson(Writer responseWriter) {
JSONObject rootJson = new JSONObject();
rootJson.write(responseWriter);
}
@see Spring Reference Documentation 3.1 Capítulo 16.3.3.1 Tipos de argumentos de métodos compatibles
ps. Siento que usar OutputStream
o Writer
como parámetro es mucho más fácil de usar en las pruebas que en HttpServletResponse
, y gracias por prestar atención a lo que he escrito ;-)
Tenga en cuenta que si utiliza OutputStream o Writer, deberá escribir los encabezados usted mismo.
Una solución es usar InputStreamResource / ResourceHttpMessageConverter