在 Spring MVC 中,如何在使用 @ResponseBody 时设置 mime 类型标头

Posted

技术标签:

【中文标题】在 Spring MVC 中,如何在使用 @ResponseBody 时设置 mime 类型标头【英文标题】:In Spring MVC, how can I set the mime type header when using @ResponseBody 【发布时间】:2011-05-27 04:11:26 【问题描述】:

我有一个返回 JSON 字符串的 Spring MVC 控制器,我想将 mimetype 设置为 application/json。我该怎么做?

@RequestMapping(method=RequestMethod.GET, value="foo/bar")
@ResponseBody
public String fooBar()
    return myService.getJson();

业务对象已作为 JSON 字符串提供,因此使用 MappingJacksonJsonView 不是我的解决方案。 @ResponseBody 很完美,但是如何设置 mimetype 呢?

【问题讨论】:

使用 spring 3.2 和它的新测试功能......没有使用 ResponseEntity 就没有解决方案吗? 使用 HttpHeaders.setContentType - 这里有一些 examples of how to use it @dorw 是在这个问题之后几年推出的 :-) 【参考方案1】:

我的现实版本。加载 html 文件并流式传输到浏览器。

@Controller
@RequestMapping("/")
public class UIController 

    @RequestMapping(value="index", method=RequestMethod.GET, produces = "text/html")
    public @ResponseBody String GetBootupFile() throws IOException  
        Resource resource = new ClassPathResource("MainPage.html");
        String fileContents = FileUtils.readFileToString(resource.getFile());
        return fileContents;
    

【讨论】:

你知道Spring MVC可以serve static resources out of the box吗? 这不仅比需要的工作多很多,而且你破坏了 Spring 可以自动为你处理和管理的许多其他事情,以及打开安全问题和其他故障点。【参考方案2】:

注册org.springframework.http.converter.json.MappingJacksonHttpMessageConverter为消息转换器,直接从方法中返回对象。

<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
  <property name="webBindingInitializer">
    <bean class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer"/>
  </property>
  <property name="messageConverters">
    <list>
      <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"/>
    </list>
  </property>
</bean>

和控制器:

@RequestMapping(method=RequestMethod.GET, value="foo/bar")
public @ResponseBody Object fooBar()
    return myService.getActualObject();

这需要依赖org.springframework:spring-webmvc

【讨论】:

是的,这可能是一个最佳实践,但正如我所写,我的对象已经是 JSON 字符串,我只想用正确的 mime 类型将它们写出来。 您引用的 bean 的 maven 依赖项是什么?【参考方案3】:

使用ResponseEntity 而不是ResponseBody。这样您就可以访问响应标头,并且可以设置适当的内容类型。根据Spring docs:

HttpEntity 类似于 @RequestBody@ResponseBody。 除了访问请求 和响应正文,HttpEntity(以及 响应特定子类 ResponseEntity) 还允许访问 请求和响应标头

代码如下:

@RequestMapping(method=RequestMethod.GET, value="/fooBar")
public ResponseEntity<String> fooBar2() 
    String json = "jsonResponse";
    HttpHeaders responseHeaders = new HttpHeaders();
    responseHeaders.setContentType(MediaType.APPLICATION_JSON);
    return new ResponseEntity<String>(json, responseHeaders, HttpStatus.CREATED);

【讨论】:

嗨,我想返回一个序列化对象,但是使用你的方法我有一个问题,它没有编译,因为它说:HttpHeaders is abstract can't be instantiated....你能解释一下吗以及你将如何做到这一点必须返回一个对象的序列化?现在,如果不使用 ResponseEntity,它可以正常工作 @Lince81 org.springframework.http.HttpHeaders 不是抽象类 (static.springsource.org/spring/docs/3.0.x/javadoc-api/org/…)。查看您的导入是否正确以及您的库是否已更新。 @Lince81 示例的重点是将已经序列化的对象作为字符串返回,同时设置不同的 Content-Type。如果您希望 Spring 序列化对象(如 XML、JSON 等),请使用 @ResponseBody 并配置适当的 MessageConverters(请参阅答案中的链接) 只是想指出 Spring MVC 3.1 允许您在 RequestMapping 中为“produces”指定一个值。所以你仍然可以使用@ResponseBody,但是你需要@RequestMapping(method=RequestMethod.GET, value="/fooBar",produces="application/json")。 使用这种方法,您可以让相同的方法返回不同的内容类型,这是 Spring 的“生产”无法做到的。此外,这是侵入性最小的,即您不必创建/实例化您自己的 MessageConverters。【参考方案4】:

我会考虑重构服务以返回您的域对象而不是 JSON 字符串,并让 Spring 处理序列化(在您编写时通过 MappingJacksonHttpMessageConverter)。从 Spring 3.1 开始,实现看起来相当简洁:

@RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE, 
    method = RequestMethod.GET
    value = "/foo/bar")
@ResponseBody
public Bar fooBar()
    return myService.getBar();

评论:

首先,&lt;mvc:annotation-driven /&gt;@EnableWebMvc 在您的应用程序配置中必须是 added。

接下来,@RequestMapping 注解的produces 属性用于指定响应的内容类型。因此,它应该设置为MediaType.APPLICATION_JSON_VALUE(或"application/json")。

最后,必须添加 Jackson,以便 Spring 自动处理 Java 和 JSON 之间的任何序列化和反序列化(Spring 检测到 Jackson 依赖项,MappingJacksonHttpMessageConverter 将在引擎盖下)。

【讨论】:

这个答案应该是第一位的,让 Spring 为您完成工作!! 根据javadoc,属性produces的目的是匹配请求的Accept头。这解释了为什么 produces 的值是一个值列表。因此,produces 不是添加任何响应标头的可靠方法。根据javadoc,它与响应头无关。【参考方案5】:

您可能无法使用@ResponseBody 来做到这一点,但这样的事情应该可以工作:

package xxx;

import java.io.ByteArrayOutputStream;
import java.io.IOException;

import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class FooBar 
  @RequestMapping(value="foo/bar", method = RequestMethod.GET)
  public void fooBar(HttpServletResponse response) throws IOException 
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    out.write(myService.getJson().getBytes());
    response.setContentType("application/json");
    response.setContentLength(out.size());
    response.getOutputStream().write(out.toByteArray());
    response.getOutputStream().flush();
  

【讨论】:

他需要写入响应,还是只设置标题就可以了? 很好的答案。如果 mime 类型是动态的,那么字节数组肯定也是动态的,因此只编写字节数组而不使用 @ResponseBody 的魔力是有意义的。唯一的修改是直接写入 OutputStream,而不是将其加载到内存中 (out.toByteArray)。 @JamesWatkins 您需要将 JSON 加载到内存中才能计算内容长度。如果您的 JSON 对象相当小(通常应该如此!),则无需担心。 我认为问题的真正本质是:“如何设置 mime 类型并同时发送非 HTML 响应?”我可能有一个非常大的文件要流式传输到客户端。此外,内容长度是可选的,还有其他方法可以派生它(从数据库等)。【参考方案6】:

我不认为你可以,除了response.setContentType(..)

【讨论】:

这意味着我必须定义一个 HttpServletResponse 类型的参数? 试过了,但没用(mime 类型仍然是 text/html) @Sean Patrick Floyd 很奇怪。您的请求的“接受”标头是什么?【参考方案7】:

我认为这是不可能的。似乎有一个开放的 Jira:

SPR-6702: Explicitly set response Content-Type in @ResponseBody

【讨论】:

它已在 Spring 3.1 中修复 - 使用 @RequestMapping(method = RequestMethod.GET, value = "foo/bar", produces = "application/json"),请参阅 SPR-7353

以上是关于在 Spring MVC 中,如何在使用 @ResponseBody 时设置 mime 类型标头的主要内容,如果未能解决你的问题,请参考以下文章

springmvc返回json数据 如何在filter中获取的返回参数?

如何在带有注解配置的spring mvc中使用spring数据

我们如何在 Spring MVC 项目中使用 Spring Cloud Sleuth?

如何在spring-mvc中根据域过滤请求

如何在spring mvc中使用带有freemarker的消息?

如何在 Spring MVC 的控制器中使用 DAO?