2013-03-07 82 views
19

我有一个处理ajax调用并返回JSON的控制器方法。我正在使用json.org的JSON库来创建JSON。直接流到Spring MVC 3.1控制器的处理程序方法中的响应输出流

我能做到以下几点:

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

    // Populate JSON 

    return rootJson.toString(); 
} 

但它是无效的放在一起JSON字符串,只有有春天它写入响应的输出流。

相反,我可以直接把它写像这样的响应输出流:

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

    // Populate JSON 

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

但似乎会有一个更好的方式来做到这一点不必求助于经过HttpServletResponse进入处理程序方法。

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

+0

什么是错的“传递HttpServletResponse的进入处理方法”? – arahant 2013-03-08 05:04:39

+2

@arahant - 这是一个公平的问题。我想我的印象是,尽管这是可能的,但这不是正确的做事方式。有一个@Ralph在[这个问题]中的答案(http://stackoverflow.com/questions/5673260/downloading-a-file-from-spring-controllers)这就是说它使测试更难。在我编写大量的处理器方法之前,我想知道是否还有其他方法。到目前为止,它看起来像我将不得不编写一个自定义的'HttpMessageConverter'。 – 2013-03-08 05:14:50

回答

29

您可以将Output Stream或Writer作为控制器方法的参数。

@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

附:我觉得利用OutputStreamWriter作为一个参数仍然是更容易在测试中比HttpServletResponse使用 - 和感谢关注what I have written ;-)

+0

感谢您的回答。传递'OutputStream'或'Writer'比传递'HttpServletResponse'更可取。 – 2013-03-10 22:06:28

3

最后,我写了这样的一个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对象。订单确实很重要。

+0

感谢您花时间回复您自己的帖子,更新后的答案是恕我直言,最好的答案。 – 2016-08-08 07:56:16

+0

@SteveMARION - 不客气。看起来我是唯一一个会为此而烦恼的人。我想这个答案不会得到像接受的答案那么多的选票,但我认为它会更接近。 – 2016-08-09 14:37:42

1

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

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

相关问题