A Custom Media Type for a Spring REST API – 为Spring REST API定制的媒体类型

最后修改: 2016年 12月 30日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this tutorial, we’re going to take a look at defining custom media types and producing them by Spring REST controller.

在本教程中,我们将看看如何定义自定义媒体类型,并通过Spring REST控制器生产它们。

A good use case for using custom media type is versioning an API.

使用自定义媒体类型的一个很好的用例是对一个API进行版本管理。

2. API – Version 1

2.API – 版本1

Let’s start with a simple example – an API exposing a single Resource by id.

让我们从一个简单的例子开始–一个通过id暴露单一资源的API。

We’re going to start with a Version 1 of the Resource we’re exposing to the client. In order to do that, we’re going to use a custom HTTP header – “application/vnd.baeldung.api.v1+json”.

我们将从我们要暴露给客户端的资源的第一版开始。为了做到这一点,我们将使用一个自定义的HTTP头–“application/vnd.baeldung.api.1+json”

The client will ask for this custom media type via the Accept header.

客户端将通过Accept 头询问这个自定义媒体类型。

Here’s our simple endpoint:

这是我们的简单端点。

@RequestMapping(
  method = RequestMethod.GET, 
  value = "/public/api/items/{id}", 
  produces = "application/vnd.baeldung.api.v1+json"
)
@ResponseBody
public BaeldungItem getItem( @PathVariable("id") String id ) {
    return new BaeldungItem("itemId1");
}

Notice the produces parameter here – specifying the custom media type that this API is able to handle.

注意这里的produces参数–指定该API能够处理的自定义媒体类型。

Now, the BaeldungItem Resource – which has a single field – itemId:

现在,BaeldungItem资源–它有一个字段–itemId

public class BaeldungItem {
    private String itemId;
    
    // standard getters and setters
}

Last but not least let’s write an integration test for endpoint:

最后但并非最不重要的是,让我们为端点写一个集成测试。

@Test
public void givenServiceEndpoint_whenGetRequestFirstAPIVersion_then200() {
    given()
      .accept("application/vnd.baeldung.api.v1+json")
    .when()
      .get(URL_PREFIX + "/public/api/items/1")
    .then()
      .contentType(ContentType.JSON).and().statusCode(200);
}

3. API – Version 2

3.API – 第二版

Now let’s assume that we need to change the details that we’re exposing to the client with our Resource.

现在,让我们假设我们需要改变我们用资源暴露给客户的细节。

We used to expose a raw id – let’s say that now we need to hide that and expose a name instead, to get a bit more flexibility.

我们过去暴露了一个原始的id–比方说,现在我们需要隐藏这个id,而暴露一个名字,以获得更大的灵活性。

It’s important to understand that this change is not backwards compatible; basically – it’s a breaking change.

重要的是要理解这一变化并不向后兼容;基本上–这是一个突破性的变化。

Here’s our new Resource definition:

这里是我们新的资源定义。

public class BaeldungItemV2 {
    private String itemName;

    // standard getters and setters
}

And so, what we’ll need to do here is – migrate our API to a second version.

因此,我们在这里需要做的是–将我们的API迁移到第二个版本。

We’re going to do that by creating the next version of our custom media type and defining a new endpoint:

我们将通过创建我们的自定义媒体类型的下一个版本并定义一个新的端点来做到这一点。

@RequestMapping(
  method = RequestMethod.GET, 
  value = "/public/api/items/{id}", 
  produces = "application/vnd.baeldung.api.v2+json"
)
@ResponseBody
public BaeldungItemV2 getItemSecondAPIVersion(@PathVariable("id") String id) {
    return new BaeldungItemV2("itemName");
}

And so we now have the exact same endpoint, but capable of handling the new V2 operation.

因此,我们现在有完全相同的端点,但能够处理新的V2操作。

When the client will ask for “application/vnd.baeldung.api.v1+json” – Spring will delegate to the old operation and the client will receive a BaeldungItem with a itemId field (V1).

当客户端将要求“application/vnd.baeldung.api.v1+json” – Spring将委托给旧操作,客户端将收到一个带有itemId字段(V1)的BaeldungItem

But when the client now sets the Accept header to “application/vnd.baeldung.api.v2+json” – they’ll correctly hit the new operation and get back the Resource with the itemName field (V2):

但是,当客户端现在将Accept头设置为“application/vnd.baeldung.api.v2+json” – 他们会正确地点击新的操作,并获得带有itemName字段的资源(V2)。

@Test
public void givenServiceEndpoint_whenGetRequestSecondAPIVersion_then200() {
    given()
      .accept("application/vnd.baeldung.api.v2+json")
    .when()
      .get(URL_PREFIX + "/public/api/items/2")
    .then()
      .contentType(ContentType.JSON).and().statusCode(200);
}

Note how the test is similar but is using the different Accept header.

注意这个测试是如何类似的,但使用的是不同的Accept头。

4. Custom Media Type on Class Level

4.在班级层面的自定义媒体类型

Finally, let’s talk about a class-wide definition of the media type – that’s possible as well:

最后,让我们来谈谈媒体类型的全类定义–这也是可能的。

@RestController
@RequestMapping(
  value = "/", 
  produces = "application/vnd.baeldung.api.v1+json"
)
public class CustomMediaTypeController

As expected, the @RequestMapping annotation easily works on class level and allows us to specify the value, produces and consumes parameters.

正如预期的那样, @RequestMapping 注解很容易在类级别上工作,并允许我们指定valueproducesconsumes参数。

5. Conclusion

5.总结

This articles illustrated examples when defining Custom Media Types could be useful in versioning public API.

这篇文章举例说明了定义自定义媒体类型在公共API的版本管理中的作用。

The implementation of all these examples and code snippets can be found in the GitHub project.

所有这些例子和代码片断的实现都可以在GitHub项目中找到。