【问题标题】:Rails API versioning - Invoke a controller action if it exists?Rails API 版本控制 - 如果存在则调用控制器操作?
【发布时间】:2016-12-16 00:53:58
【问题描述】:

现在我有两个 API 命名空间,Api::V1 和 Api::V2

我计划随着时间的推移慢慢实施 V2,而不是一次性完成。但是,由于它的一部分存在,我希望客户端可以将所有 HTTP 请求发送到 V2 URL,如果该特定端点尚未实现,则让服务器处理它。

如果 V2 控制器没有操作,有没有办法将 V2 请求路由到 V1 控制器?

简单示例:

路线:

namespace :api do
    namespace :v1 do
        resources :items, only: [:index]
    end
    namespace :v2 do
        resources :items, only: :create
    end
end

将产生以下端点:

  1. GET /api/v1/items
  2. POST /api/v2/items

目标是发送一个 GET 请求到 /api/v2/items 并让它调用 Api::V1:ItemsController#index,因为 V2 控制器还没有这个方法。

【问题讨论】:

    标签: ruby-on-rails api api-versioning


    【解决方案1】:

    我还有一个版本化的 API。我还没有碰到下一个版本。但我想我会分享我计划要做的事情。这可能有点毛骨悚然。我有一种感觉,这对我(思考我的计划)比对你更有用。所以,很抱歉。但是,这里...

    在开始之前,我应该说我对控制器操作采取了不同的方法。在我的应用程序中,我喜欢将我的控制器操作委托给我称之为“经理”的普通旧 ruby​​ 对象。每个控制器都有一个“manager_base”。所以,在我的控制器中,我有这样的东西:

    class ApplicationController < ActionController::Base
    
      private
    
      def do_action(action=nil)
        action ||= caller[0][/`.*'/][1..-2]
        manager.send("manage_#{action}", self, cookies, request)
      end
    
      def manager
        base = self.class.name.split('::')
        base.pop
        base << "#{controller_name.camelize}Managers::ManagerBase"
        base.join('::').constantize
      end
    
    end 
    
    class Api::V1::FooController < ApplicationController
    
      def index
        do_action
        render_result
      end
    
    end
    

    然后我也有:

    class ManagerBase
      class << self
    
        def manage_index(controller, cookies, request)
          sub_out("Index", controller, cookies, request)             
        end
    
        def manage(controller, cookies, request)
          new(controller, cookies, request).manage
        end
    
      private
    
        def sub_out(method, controller, cookies, request)
          sub_manager(method).manage(controller, cookies, request)   
        end
    
      end # Class Methods
    
      def initialize(controller, cookies, request)
        @controller = controller
        @cookies = cookies
        @request = request
      end
    
    end
    
    class Api::V1::FooManagers::ManagerBase < ManagerBase
      class << self
    
        private
    
        def sub_manager(method)
          "Api::V1::FooManagers::#{method}Manager".constantize
        end  
    
      end # Class Methods
    end
    
    class Api::V1::FooManagers::IndexManager < Api::V1::FooManagers::ManagerBase
      def manage
        ... do stuff
      end
    end
    

    如果您跟随弹跳球,以下是我的应用程序流程:

    • indexApi::V1::FooController 调用
    • index 调用do_action(继承自ApplicationController),后者又调用manager(也继承自ApplicationController
    • manager 返回 Api::V1::FooManagers::ManagerBase
    • do_action 然后在 Api::V1::FooManagers::ManagerBase 上调用 manage_index
    • manage_index 调用 sub_out 反过来又调用 sub_manager
    • sub_manager 返回 Api::V1::FooManagers::IndexManager
    • sub_out 然后在 Api::V1::FooManagers::IndexManager 上调用 manage
    • manage(类方法 - 从 ManagerBase 继承)创建 Api::V1::FooManagers::IndexManager 的新实例,然后在新实例上调用 manage(实例方法)。

    可能很明显,也可能不太明显,当我迁移到 Api::V2 时,我有两次机会“挂钩”回我的经理的 Api::V1 版本(这相当于使用我的 V1 控制器方法 -你原来的问题)。

    首先,如果我还没有实现Api::V2::FooManagers::ManagerBase,我可以让ApplicationController.manager 回退到最后一个实现的ManagerBase 版本(即Api::V1::FooManagers::ManagerBase)。在这种情况下,我将使用所有 Api::V1::FooManager 子管理器(如 IndexManager)。

    其次,如果我已经实现了Api::V2::FooManagers::ManagerBase,但还没有实现Api::V2::FooManagers::IndexManager,那么我可以让Api::V2::FooManagers#sub_manager回退到Api::V1::FooManagers::IndexManager

    好的,我现在要停下来。感谢您有机会大声思考这个问题。抱歉,如果它完全没用,很热。

    【讨论】:

    • 哇!这是一个地狱般的计划!事实证明,在对 api 模块进行命名空间时,我可以通过使用 :path 选项来解决我的问题,现在我只需在控制器操作完成时将它们从 v1 移动到 v2
    • 好的,谢谢。我猜。我希望它不会给我带来麻烦。您如何以及在何处使用 :path 选项?
    • 评论太多了,我把它发布为答案检查一下
    【解决方案2】:
    namespace :api do
    
        namespace :v1, path: "v2" do
            # All endpoints that are on v1 of API
        end
    
        namespace :v2 do
            # All endpoints that are on v2 of API
        end
    
    end
    

    如果您在此处运行 rake 路由,您会看到它们都与路由 "api/v2/____#_____" 匹配,但顶部块中的调用 Api::V1 操作,底部块调用 Api::V2操作,因此您必须在实现这些端点时将它们从顶部移动到底部

    【讨论】:

      猜你喜欢
      • 2016-05-22
      • 2016-03-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-10
      • 1970-01-01
      • 1970-01-01
      • 2011-05-23
      相关资源
      最近更新 更多