在 Spring MVC 3.1 控制器的处理程序方法中直接流式传输到响应输出流

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15283347/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 05:47:46  来源:igfitidea点击:

Stream directly to response output stream in handler method of Spring MVC 3.1 controller

springspring-mvchttpresponse

提问by John S

I have a controller method that handles ajax calls and returns JSON. I am using the JSON library from json.org to create the JSON.

我有一个处理 ajax 调用并返回 JSON 的控制器方法。我使用来自 json.org 的 JSON 库来创建 JSON。

I could do the following:

我可以执行以下操作:

@RequestMapping(method = RequestMethod.POST)
@ResponseBody
public String getJson()
{
    JSONObject rootJson = new JSONObject();

    // Populate JSON

    return rootJson.toString();
}

But it is inefficient to put together the JSON string, only to have Spring write it to the response's output stream.

但是将 JSON 字符串放在一起,只让 Spring 将其写入响应的输出流是低效的。

Instead, I can write it directly to the response output stream like this:

相反,我可以像这样直接将其写入响应输出流:

@RequestMapping(method = RequestMethod.POST)
public void getJson(HttpServletResponse response)
{
    JSONObject rootJson = new JSONObject();

    // Populate JSON

    rootJson.write(response.getWriter());
}

But it seems like there would be a better way to do this than having to resort to passing the HttpServletResponseinto the handler method.

但似乎有更好的方法来做到这一点,而不是将 传递HttpServletResponse给处理程序方法。

Is there another class or interface that can be returned from the handler method that I can use, along with the @ResponseBodyannotation?

是否有另一个类或接口可以从我可以使用的处理程序方法返回,以及@ResponseBody注释?

回答by Ralph

You can have the Output Stream or the Writer as an parameter of your controller method.

您可以将输出流或编写器作为控制器方法的参数。

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

@see Spring Reference Documentation 3.1 Chapter 16.3.3.1 Supported method argument types

@see Spring Reference Documentation 3.1 Chapter 16.3.3.1 支持的方法参数类型

p.s. I feel that using OutputStreamor Writeras an parameter is still much more easier to use in tests than a HttpServletResponse- and thanks for paying attention to what I have written;-)

ps 我觉得在测试中使用OutputStreamWriter作为参数仍然比 a 更容易使用HttpServletResponse- 感谢您关注我所写的内容;-)

回答by John S

In the end, I wrote an HttpMessageConverterfor this. With it, I can do the following:

最后,我为此写了一篇HttpMessageConverter。有了它,我可以执行以下操作:

@RequestMapping(method = RequestMethod.POST)
@ResponseBody
public JSONObject getJson()
    throws JSONException
{
    JSONObject rootJson = new JSONObject();

    // Populate JSON

    return rootJson;
}

Here is my HttpMessageConverterclass:

这是我的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;
    }
}

The HttpMessageConvertermust be registered with Spring. This can be done in the dispatcher-servlet.xmlfile like this:

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.MappingHymansonHttpMessageConverter">
                <property name="objectMapper" ref="HymansonObjectMapper" />
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>

    ...

</beans>

As you can see, I have other HttpMessageConverterobjects registered too. The order does matter.

如您所见,我也HttpMessageConverter注册了其他对象。顺序很重要。

回答by Hadrien

Note that if you use the OutputStream or Writer it requires you to write the headers yourself.

请注意,如果您使用 OutputStream 或 Writer,则需要您自己编写标头。

One workaround is to use InputStreamResource/ResourceHttpMessageConverter

一种解决方法是使用 InputStreamResource/ResourceHttpMessageConverter