Подтвердить что ты не робот

Поток непосредственно в выходной поток ответа в методе обработчика контроллера 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?

4b9b3361

Ответ 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