Spring Boot 控制器内容协商
Posted
技术标签:
【中文标题】Spring Boot 控制器内容协商【英文标题】:Spring boot controller content negotiation 【发布时间】:2016-01-05 17:29:50 【问题描述】:我在 Spring-boot 应用程序中编写了一个简单的 REST 控制器,但我不确定如何实现内容协商以使其根据请求标头中的 Content-Type 参数返回 JSON 或 XML。有人可以向我解释一下,我做错了什么吗?
控制器方法:
@RequestMapping(value = "/message", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE )
public Message getMessageXML(@RequestParam("text") String text) throws Exception
Message message = new Message();
message.setDate(new Date());
message.setName("Test");
message.setAge(99);
message.setMessage(text);
return message;
调用此方法时我总是得到 JSON(即使我将 Content-Type
指定为 application/xml
或 text/xml
)。
当我实现两个方法时,每个方法都有不同的映射和不同的内容类型,我可以从 xml 中获取 XML,但是如果我在一个方法中指定两个 mediaTypes(如提供的示例),它将不起作用。
我想要的是调用\message
端点并接收
感谢任何帮助。
编辑: 我更新了我的控制器以接受所有媒体类型
@RequestMapping(value = "/message", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE , consumes = MediaType.ALL_VALUE)
public Message getMessageXML(@RequestParam("text") String text) throws Exception
Message message = new Message();
message.setDate(new Date());
message.setName("Vladimir");
message.setAge(35);
message.setMessage(text);
return message;
【问题讨论】:
您需要提供Accept
标头,其值为application/xml
或任何受支持的媒体类型。
“Content-Type”标头定义了您发送的内容类型 - 而不是您想要接收的内容(这就是“Accept”标头的用途。所以使用“Content-Type” GET请求的标头没有意义,因为不能有任何(正文)内容。因此,在您的情况下,您应该为您的请求使用“Accept”标头,并在响应中使用“Content-Type”标头来命名实际发送的内容类型。
【参考方案1】:
您可以在博文@RequestMapping with Produces and Consumes 第 6 点找到一些提示。
注意关于 Content-Type 和 Accept headers 的部分:
@RequestMapping 与 Produces 和 Consumes:我们可以使用 header Content-Type 和 Accept 找出请求内容和什么是 它想要响应的 mime 消息。为清楚起见,@RequestMapping 提供生产和消费变量,我们可以在其中指定 请求内容类型将调用哪个方法和响应 内容类型。例如:
@RequestMapping(value="/method6", produces="application/json","application/xml", consumes="text/html") @ResponseBody public String method6() return "method6";
上述方法只能使用 Content-Type 为 text/html 的消息 并且能够生成 application/json 类型的消息和 应用程序/xml。
您也可以尝试this 不同的方法(使用 ResponseEntity 对象),它可以让您找出传入的消息类型并生成相应的消息(也利用 @ResponseBody 注释)
【讨论】:
【参考方案2】:您可以使用ContentNegotiationConfigurer
首先,您应该在配置类中覆盖configureContentNegotiation
方法:
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer)
configurer.favorPathExtension(false).
favorParameter(true).
defaultContentType(MediaType.APPLICATION_JSON).
mediaType("xml", MediaType.APPLICATION_XML);
favorParameter(true)
- 启用优先路径表达式而不是参数或接受标头。
defaultContentType(MediaType.APPLICATION_JSON)
- 设置默认内容类型。这意味着如果您不传递路径表达式,那么 Spring 将生成 JSON 作为响应。
mediaType("xml", MediaType.APPLICATION_XML)
- 设置 XML 的路径表达式键。
现在如果你像这样声明你的控制器:
@Controller
class AccountController
@RequestMapping(value="/accounts", method=RequestMethod.GET)
@ResponseStatus(HttpStatus.OK)
public @ResponseBody List<Account> list(Model model, Principal principal)
return accountManager.getAccounts(principal) );
并将其命名为 localhost:8080/app/accounts.json
,然后 Spring 将生成 JSON 作为响应。因此,如果您致电localhost:8080/app/accounts.xml
,您将收到 XML 响应
您可以找到有关此here 的更多信息。
【讨论】:
只是对 defaultContentType 的一个小脚注:browsers tend to send accept headers that prefer XML。绕过(假设您没有使用它)Accept 标头可以在您的 contentNegotiation 覆盖中完成:configurer.ignoreAcceptHeader(true)
我认为这里有一个小错误 - 您声称控制器工作的方式,您应该将喜爱路径扩展设置为真,而不是喜爱参数。
另一个小脚注:在 Spring Boot 应用程序中,您的 @Configuration
类应该不包含 @EnableWebMvc
注释 (source)。它可能会阻止其他东西工作,例如 springfox-swagger-ui html 页面。
使用 implements WebMvcConfigurer
而不是已弃用的 extends WebMvcConfigurerAdapter
用于当前的 Spring Boot 版本。以上是关于Spring Boot 控制器内容协商的主要内容,如果未能解决你的问题,请参考以下文章
Spring boot 控制器调用不支持内容类型“application/json;charset=UTF-8”
SpringBoot/Kotlin 和通过内容协商进行版本控制:正确的方法?
使用 Spring Boot 到达静态内容时如何添加响应标头?