Маршруты в стиле StackOverflow с умными переадресациями

StackOverflow, кажется, имеет такой стиль маршрутов для вопросов:

/questions/:id/*slug

Чего достаточно легко добиться, как в маршрутах, так и в to_param.

Однако StackOverflow, похоже, также перенаправляет на этот путь, когда передается только идентификатор.

Пример:

stackoverflow.com/questions/6841333 

перенаправляет на:

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

То же самое касается любой вариации слизняка.

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

Будет по-прежнему перенаправлять на тот же URL.

Мой вопрос: этот тип перенаправления обычно обрабатывается в контроллере (сравнивая запрос с маршрутом) или есть способ сделать это в routes.rb?

Причина, по которой я не думаю, что это возможно в файле routes.rb, заключается в том, что обычно у вас нет доступа к объекту (поэтому вы не можете получить слаг на основе идентификатора, верно?)

Для всех, кто заинтересован, Rails 3.2.13, а также использование FriendlyID


person Kyle Macey    schedule 20.05.2013    source источник
comment
возможно, они используют регулярное выражение для удаления части идентификатора параметра, чтобы выполнять более быстрый поиск, а затем перенаправлять на основе этого. Я также предполагаю, что они сохраняют идентификатор в URL-адресе, поэтому, если вопросы имеют одинаковый заголовок, URL-адрес уникален. также это похоже на логику контроллера.   -  person Polygon Pusher    schedule 21.05.2013


Ответы (1)


Итак, я думаю, что у меня есть это.

Я хотел сделать что-то с промежуточным ПО, но потом решил, что это, вероятно, не место для такого типа функциональности (поскольку нам нужен доступ к 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

Таким образом, числовой идентификатор в основном используется для поиска записи, если она доступна. Это позволяет нам приблизительно сопоставить /posts/12345/not-the-right-slug для перенаправления на /posts/12345/the-right-slug.

Сервис написан универсальным образом, поэтому я могу использовать его в любом находчивом контроллере. Я еще не нашел способ сломать его, но я открыт для исправления.

Ресурсы

Railscast #398: Service Objects, Райан Бейтс

Этот полезный твит Джареда Файна

person Kyle Macey    schedule 21.05.2013