Поток директно към изходния поток на отговор в метода на манипулатора на 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?


person John S    schedule 07.03.2013    source източник
comment
Какво не е наред с предаването на HttpServletResponse в метода на манипулатора?   -  person arahant    schedule 08.03.2013
comment
@arahant - Това е справедлив въпрос. Предполагам, че съм останал с впечатлението, че въпреки че е възможно, това не е правилният начин да се правят нещата. Има отговор от @Ralph в този въпрос, който гласи, че прави тестването по-трудно. Преди да напиша много методи за обработка, които правят това, исках да разбера дали има друг начин. Засега изглежда, че ще трябва да напиша персонализиран HttpMessageConverter.   -  person John S    schedule 08.03.2013


Отговори (3)


Можете да имате Output Stream или Writer като параметър на вашия метод на контролер.

@RequestMapping(method = RequestMethod.POST)
public void getJson(Writer responseWriter) {
    JSONObject rootJson = new JSONObject();
    rootJson.write(responseWriter);
}

@вижте Пролет Справочна документация 3.1 Глава 16.3.3.1 Поддържани типове аргументи на метод

p.s. Смятам, че използването на OutputStream или Writer като параметър все още е много по-лесно за използване в тестове, отколкото HttpServletResponse - и благодаря, че обърнахте внимание на какво Писал съм ;-)

person Ralph    schedule 08.03.2013
comment
Благодаря за отговора. Предаването на OutputStream или Writer е за предпочитане пред предаването на HttpServletResponse. - person John S; 11.03.2013

В крайна сметка написах 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 обекти. Редът има значение.

person John S    schedule 04.08.2014
comment
Благодарим ви, че отделихте време да отговорите на собствената си публикация с актуализиран отговор, който IMHO е най-добрият. - person Steve Marion; 08.08.2016
comment
@SteveMARION - Няма за какво. Изглеждаше, че аз съм единственият, който би си направил труда да направи това. Реших, че този отговор няма да събере толкова гласове, колкото приетия, но реших, че ще бъде по-близо. - person John S; 09.08.2016

Имайте предвид, че ако използвате OutputStream или Writer, това изисква сами да напишете заглавките.

Едно заобиколно решение е да използвате InputStreamResource/ResourceHttpMessageConverter

person Hadrien    schedule 21.10.2016