"REST Web服务版本控制实践"

13 浏览
0 Comments

"REST Web服务版本控制实践"

我正在创建一个新的Web服务,我已经阅读了APIgee的一些电子书,其中版本化Web服务是被推荐的。我了解,在URL和头部保留版本信息之间存在一些“争执”。根据我所读及理解的信息,我想在头部中使用版本化。

我的问题是:这在实践中是什么样子的呢?我正在使用Spring MVC 3.2。你只是创建一个响应不同版本的方法,在同一个控制器中就可以了吗?

第一版:

@RequestMapping(method = RequestMethod.GET, produces = "application/vnd.example-v1+json")

第二版:

@RequestMapping(method = RequestMethod.GET, produces = "application/vnd.example-v2+json")

还是说这是错误的?还是说更常见的是创建包含控制器不同版本的不同包?或者还有其他方法吗?

admin 更改状态以发布 2023年5月22日
0
0 Comments

这里的问题不在于版本信息在URI还是标头中,而是在于如何为不同版本组织代码。

我怀疑没有一个单一的标准方法。这取决于版本有多不同。

简单的格式更改。比如说,在V1中你使用XML,在V2中你只是将它移动到了JSON。在这种情况下,你可以使用完全相同的代码,只需全局配置应用程序以输出JSON即可。无需不同的包或控制器。 (例如,你可以使用JAXB注释来驱动XML和由Jackson生成的JSON输出。)

适度的模式更改。 假设V2引入了少量破坏性模式更改。在这种情况下,创建新的包可能没有意义。你可能只需要在控制器中进行简单的条件逻辑,以处理/为版本服务正确的表示。

主要模式更改。 如果你的模式更改是深度和广泛的,你可能需要更多的单独控制器。你甚至可能需要不同的域模型(实体/服务)。在这种情况下,为控制器创建一套平行的包可能是有意义的,这套包一直延伸到实体,仓库,甚至数据库表。

应用这些想法

方法1. 在你的 @RequestMapping 示例中应用这些想法,如果响应在版本之间完全相同,则它们应该委托给一个共享的方法:

@RequestMapping(
    value = "/orders/{id}",
    method = RequestMethod.GET,
    produces = "application/vnd.example-v1")
@ResponseBody
public Order getOrderV1(@PathVariable("id") Long id) {
    return getOrder(id);
}
@RequestMapping(
    value = "/orders/{id}",
    method = RequestMethod.GET,
    produces = "application/vnd.example-v2")
@ResponseBody
public Order getOrderV2(@PathVariable("id") Long id) {
    return getOrder(id);
}
private Order getOrder(Long id) {
    return orderRepo.findOne(id);
}

像这样就能够工作了。如果版本之间的排序不同,那么可以在方法中实现差异。

方法2. 另一件你可能尝试的事情——我自己还没有尝试过——是每种资源类型(例如,订单,产品,客户等)都有自己的基础控制器,具有HTTP方法的方法级别注释(仅定义valuemethod,但没有produces)。然后使用特定于版本的扩展来扩展基础,其中扩展控制器在类级别具有@RequestMapping(value = "/orders", produces = "application/vnd.example-v1")。然后仅覆盖版本与基线之间的差异。我不确定这是否能行,但如果可以的话,这将是一种相当干净的组织控制器的方法。这是我的意思:

// The baseline
public abstract class BaseOrderController {
    @RequestMapping(value = "/{id}", method = RequestMethod.GET)
    @ResponseBody
    public Order getOrder(@PathVariable("id") Long id) { ... }
}    
// V1 controller
@RequestMapping(value = "/orders", produces = "application/vnd.example-v1")
public class OrderControllerV1 extends BaseOrderController {
    ... no difference from baseline, so nothing to implement ...
}
// V2 controller
@RequestMapping(value = "/orders", produces = "application/vnd.example-v2")
public class OrderControllerV2 extends BaseOrderController {
    @RequestMapping(value = "/{id}", method = RequestMethod.GET)
    @ResponseBody
    @Override
    public Order getOrder(@PathVariable("id") Long id) {
        return orderRepoV2.findOne(id);
    }
}

0