具有智能重定向的StackOverflow样式路由

时间:2013-05-20 17:12:45

标签: ruby-on-rails routes friendly-id

StackOverflow似乎有这种问题的路线:

/questions/:id/*slug

在路线和to_param中都很容易实现。

但是,当只传递一个ID时,StackOverflow似乎也会重定向到该路径。

示例:

stackoverflow.com/questions/6841333 

重定向到:

stackoverflow.com/questions/6841333/why-is-subtracting-these-two-times-in-1927-giving-a-strange-result/

与slug的任何变化相同

stackoverflow.com/questions/6841333/some-random-stuff

仍然会重定向到相同的网址。

我的问题是:这种类型的重定向通常是在控制器中处理的(将请求与路由进行比较)还是有办法在routes.rb中执行此操作?

我不认为routes.rb文件中存在这种情况的原因是,通常情况下,您无法访问该对象(因此您无法根据ID获取slug,对吧?)

对于任何感兴趣的人,Rails 3.2.13并使用FriendlyID

1 个答案:

答案 0 :(得分:3)

好的,所以我想我已经有了这个。

我正在考虑使用中间件做一些事情,但后来决定可能不适合这种类型的功能(因为我们需要访问ActiveRecord)。

所以我最终建立了一个服务对象,称为PathCheck。该服务如下所示:

class PathCheck
  def initialize(model, request)
    @model = model
    @request = request
  end 

  # Says if we are already where we need to be
  # /:id/*slug
  def at_proper_path?
    @request.fullpath == proper_path
  end

  # Returns what the proper path is
  def proper_path
    Rails.application.routes.url_helpers.send(path_name, @model) 
  end

private
  def path_name
    return "edit_#{model_lowercase_name}_path" if @request.filtered_parameters["action"] == "edit"
    "#{model_lowercase_name}_path"
  end

  def model_lowercase_name
    @model.class.name.underscore
  end
end

这很容易实现到我的控制器中:

def show
  @post = Post.find params[:post_id] || params[:id]
  check_path
end

private
  def check_path
    path_check = PathCheck.new @post, request
    redirect_to path_check.proper_path if !path_check.at_proper_path?
  end

我的||方法中的find是因为为了保持足智多谋的路线,我做了类似......

resources :posts do
  get '*id' => 'posts#show'
end

这将在/posts/:post_id/*id

之上创建类似:/posts/:id的路线

这样,数字ID主要用于查找记录(如果可用)。这样,我们就可以将/posts/12345/not-the-right-slug松散地匹配,以重定向到/posts/12345/the-right-slug

该服务以通用方式编写,因此我可以在任何资源丰富的控制器中使用它。我还没有办法打破它,但我愿意纠正。

资源

  Ryan Bates的

Railscast #398: Service Objects

     Jared Fine

This Helpful Tweet