使用SpringMVC的内容协商实现RESTneneneba API版本控制



我正在尝试使用内容协商来实现API版本控制,因为在URI中放入版本不是RESTful。Spring MVC似乎不可能做到这一点,因为在解析控制器方法时,没有考虑@RequestMapping的consumps/products属性,只考虑路径。为了便于讨论,我们假设我有这个控制器

@RestController
@RequestMapping(path = "/foo")
public class FooController {
    @RequestMapping(path = "{id}", method = RequestMethod.GET, produces = { MediaType.APPLICATION_JSON_VALUE })
    @ResponseBody
    public Foo getFoo(@PathVariable Long id) {
        return repository.findOne(id);
    }
    @RequestMapping(path = "{id}", method = RequestMethod.GET, produces = { "application/vnd.com.me.model.v1+json" })
    @ResponseBody
    public FooV1 getFooV1(@PathVariable Long id) {
        return repositoryV1.findOne(id);
    }
    ...
}

REST API是"GET/foo/1"或"GET/foo/2"等,但我希望能够发送任一

Accept: application/json

Accept: application/vnd.com.me.model.v1+json

并使其进入正确的控制器方法。忽略如何最好地表示模型的问题。这只是为了让SpringMVC让我有一个单独的RESTURI来处理不同的内容类型。这显然也适用于其他HTTP方法,PUT、POST等。考虑到上面的控制器定义,当Spring试图解析映射时,您会遇到一个错误,一些变体是"对不起,已经有'/foo/{id}'的映射了。"

我是否错过了一些能让我实现这一目标的东西?

这绝对应该如您所描述的那样工作。我这里有一个工作示例:

@RestController
@RequestMapping(path = "/persons")
public class PersonController {
    private final PersonV1Repository personV1Repository;
    private final PersonV2Repository personV2Repository;
    @Autowired
    public PersonController(PersonV1Repository personV1Repository, PersonV2Repository personV2Repository) {
        this.personV1Repository = personV1Repository;
        this.personV2Repository = personV2Repository;
    }
    @RequestMapping(produces = "application/vnd.com.me.model.v1+json")
    public ResponseEntity<List<PersonV1>> getPersonsV1() {
        return ResponseEntity.ok(personV1Repository.findAll());
    }
    @RequestMapping(path = "/{id}", produces = "application/vnd.com.me.model.v1+json")
    public ResponseEntity<PersonV1> getPersonV1(@PathVariable Long id) {
        return ResponseEntity.ok(personV1Repository.findOne(id));
    }
    @RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<List<PersonV2>> getPersonsV2() {
        return ResponseEntity.ok(personV2Repository.findAll());
    }
    @RequestMapping(path = "/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<PersonV2> getPersonV2(@PathVariable Long id) {
        return ResponseEntity.ok(personV2Repository.findOne(id));
    }
}

我使用Accept标头获得我选择的版本-以下是请求和响应的示例:

http :8080/persons/2 Accept:application/vnd.com.me.model.v1+json -v
GET /persons/2 HTTP/1.1
Accept: application/vnd.com.me.model.v1+json
Accept-Encoding: gzip, deflate
Connection: keep-alive
Host: localhost:8080
User-Agent: HTTPie/0.9.2

HTTP/1.1 200 OK
Content-Type: application/vnd.com.me.model.v1+json;charset=UTF-8
Date: Mon, 07 Dec 2015 20:14:26 GMT
Server: Apache-Coyote/1.1
Transfer-Encoding: chunked
X-Application-Context: application
{
    "firstname": "some",
    "id": 2,
    "lastname": "other"
}

并获得默认版本:

http :8080/persons/2 Accept:application/json -v
GET /persons/2 HTTP/1.1
Accept: application/json
Accept-Encoding: gzip, deflate
Connection: keep-alive
Host: localhost:8080
User-Agent: HTTPie/0.9.2

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Date: Mon, 07 Dec 2015 20:16:20 GMT
Server: Apache-Coyote/1.1
Transfer-Encoding: chunked
X-Application-Context: application
{
    "id": 2,
    "name": "some"
}

控制器中必须有其他一些重复的映射-您可以添加完整的控制器代码以进行进一步调查。

好的,我发现了问题所在。我的坏。问题是我有一个DELETE方法,它只需要一个类型为long的id,所以我没有products/consumps声明,因此由于有两个方法具有相同的请求映射,这确实导致了一个"不明确"的错误。

很高兴知道Spring正在按我的期望工作

最新更新