【问题标题】:REST web service versioning in practice实践中的 REST Web 服务版本控制
【发布时间】:2013-09-26 23:49:14
【问题描述】:

我正在创建一个新的 Web 服务,并且我已经阅读了 APIgee 的一些电子书,其中建议对 Web 服务进行版本控制。我知道在 URL 和标头中保留版本信息之间存在一些“战斗”。根据我的阅读和理解,我想在标题中使用版本控制。

我的问题是;这在实践中看起来如何?我正在使用 Spring MVC 3.2。您是否只是在响应不同版本的同一控制器中创建这样的方法?

版本 1:

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

版本 2:

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

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

【问题讨论】:

    标签: java spring rest jakarta-ee spring-mvc


    【解决方案1】:

    这里的问题不在于版本信息的位置(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);
        }
    }
    

    【讨论】:

    • 有道理。谢谢:)
    猜你喜欢
    • 1970-01-01
    • 2023-01-28
    • 1970-01-01
    • 1970-01-01
    • 2013-01-06
    • 1970-01-01
    • 2013-03-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多