代码之家  ›  专栏  ›  技术社区  ›  Garrett

将路由中的:格式属性从路由的末尾移动到路由的开头

  •  1
  • Garrett  · 技术社区  · 15 年前

    在我的应用程序中,我试图让我的API模仿Github在路由开始时的(.:格式),而不是在结尾附加它。

    下面是我的代码,它“正在工作”,但可以忽略:

    map.namespace :api do |api|
      api.namespace :v1 do |v1|
        v1.resource :company, :path_prefix => "api/v1/:format"
      end
    end
    

    我可以去 /api/v1/xml/company.json IT Rails将提供 json 作为 params[:format] 而不是 xml .

    当我奔跑 rake routes 我得到

    /api/v1/:format/company(.:format)
    

    有没有办法让它返回:

    /api/v1/:format/company
    

    事先谢谢!

    6 回复  |  直到 15 年前
        1
  •  1
  •   Steve Graham    15 年前

    EmFi是对的。我没有回答这个问题,只是表达了我的意见。

    将以下代码放入Rails应用程序中配置初始值设定项目录中的初始值设定项文件中。您命名的文件对框架来说并不重要,因为这个目录中的所有文件都在加载路径中。我建议你叫它 actioncontroller_resource_monkeypatch.rb 为了使意图明确。

    ActionController::Resources.module_eval do
      def map_resource_routes(map, resource, action, route_path, route_name = nil, method = nil, resource_options = {} )
        if resource.has_action?(action)
          action_options = action_options_for(action, resource, method, resource_options)
    
          formatted_route_path = route_path.match(/\/:format\//) ? route_path : "#{route_path}.:format" 
    
          if route_name && @set.named_routes[route_name.to_sym].nil?
            map.named_route(route_name, formatted_route_path, action_options)
          else
            map.connect(formatted_route_path, action_options)
          end
        end
      end
    end
    

    我的答案与EMFI的方法相同,即通过MonkeyPatching ActionController::Resources#map_resource_routes . 我决定把我的帽子扔到戒指里,因为它没有提供一个完整的实现,这留给你自己作为练习。我也觉得三元赋值 formatted_route_path 比if-else/except-else块更简洁。另加一行代码而不是五行!这是我至少能得到200英镑的赏金!

    现在运行 rake routes

    new_api_v1_company GET    /api/v1/:format/company/new  {:action=>"new", :controller=>"api/v1/companies"}
    edit_api_v1_company GET    /api/v1/:format/company/edit {:action=>"edit", :controller=>"api/v1/companies"}
         api_v1_company GET    /api/v1/:format/company      {:action=>"show", :controller=>"api/v1/companies"}
                        PUT    /api/v1/:format/company      {:action=>"update", :controller=>"api/v1/companies"}
                        DELETE /api/v1/:format/company      {:action=>"destroy", :controller=>"api/v1/companies"}
                        POST   /api/v1/:format/company      {:action=>"create", :controller=>"api/v1/companies"}
    

    塔达!

        2
  •  2
  •   Steve Graham    15 年前

    这将需要一些严重的MonkeyPatching。路由也是Rails代码库中最复杂的部分之一,它将传入的HTTP请求连接到代码并生成URL。

    恕我冒昧,但据我所知,你违反Rails惯例的原因是模仿另一家公司。换言之,您愿意无视Rails贡献者的集体智慧,支持少数开发人员的决策。

    我认为你应该问问自己,你为什么要让这件事足够引人注目,与所需的努力相称?

    它越难做轨道以外的事情,人们就越应该质疑他们的决定。重大障碍的存在通常意味着有更好的方法去做一些事情。

        3
  •  0
  •   BJ Clark    15 年前

    我相信(.format)是可选的(这就是括号的意思),所以 /api/v1/:format/company(.:format) == /api/v1/:format/company

    如果你想改变的更多,你将需要黑客/猴子补丁轨道。

        4
  •  0
  •   jerhinesmith    15 年前

    我只是在这里拍照(明天我可以玩代码的时候我会检查和更新),但是你不能避免使用 :format 像这样做?

    路线 :

    map.connect ':controller/:return_type/:action/:id'
    

    控制器 :

    @results = MyObject.all
    
    case :return_type
      when 'xml' render :text => @results.to_xml
      when 'json' render :text => @results.to_json
    end
    

    如果情况过于混乱/丑陋,您可以轻松地创建一个模仿您正在寻找的行为的助手方法。

        5
  •  0
  •   Community Dunja Lalic    7 年前

    Steve Graham says everything 这是必须说的,但是一个200人代表的问题应该得到一个正确的答案。不管多么不明智。此外,这看起来确实有用。

    猴子补丁非常简单。您只需要重写actionController::resource映射resource路由,如下所示。

    def map_resource_routes(map, resource, action, route_path, 
          route_name = nil, method = nil, resource_options = {} )
      if resource.has_action?(action)
        action_options = action_options_for(action, resource, method, resource_options)
    
        unless route_path.match(/\/:format\//)       # new line of code
          formatted_route_path = "#{route_path}.:format"
        else                                         # new line of code
          formatted_route_path = route_path          # new line of code
        end                                          # new line of code   
    
        if route_name && @set.named_routes[route_name.to_sym].nil?
          map.named_route(route_name, formatted_route_path, action_options)
        else
          map.connect(formatted_route_path, action_options)
        end
      end
    end
    

    将这个补丁合并到代码中涉及到要么修补Rails gem,要么制作一个插件,这两者都非常简单,留给读者作为练习。

    如果路径已经包含一个名为format的参数,代码将跳过向所有路由添加选项格式的行。

    编辑:链接到该解决方案所指的Steve Graham答案。最初发帖的时候只有一个。

        6
  •  -1
  •   Ryan Bigg Andrés Bonilla    15 年前

    我用了这个代码:

    ActionController::Routing::Routes.draw do |map|
      map.namespace(:v1, :path_prefix => "api/v1/:format") do |v1|
        v1.resources :repositories
      end
    end
    

    URL变成 api/v1/[json/xml/whatever]/<restful url位于此处>

    我也喜欢名称间距的概念(比如在你的问题中):

    class V1::RepositoriesController < V1::ApplicationController
    
    end
    

    虽然此方法允许您将格式放在URL中两次: 这不是由你来确保用户不会将格式放在URL中两次,而是由你的用户来确保他们不会将格式放在URL中两次。

    不要花时间解决Pebkacs。