我正在尝试使用内容协商来实现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正在按我的期望工作