Проверка отсутствия перекрытия маршрутов при создании новых ресурсов в Ruby on Rails
-
13-09-2019 - |
Вопрос
У меня есть RESTful настройка маршрутов в приложении Rails с использованием текстовых постоянных ссылок в качестве идентификатора ресурсов.
Кроме того, существует также несколько специальных именованных маршрутов, которые перекрываются с именованным ресурсом, например:
# bunch of special URLs for one off views to be exposed, not RESTful
map.connect '/products/specials', :controller => 'products', :action => 'specials'
map.connect '/products/new-in-stock', :controller => 'products', :action => 'new_in_stock'
# the real resource where the products are exposed at
map.resources :products
В Product
модель использует постоянная ссылка_fu чтобы сгенерировать постоянные ссылки на основе имени, и ProductsController
выполняет поиск по полю постоянной ссылки при доступе.Все это прекрасно работает.
Однако при создании нового Product
записи в базе данных, я хочу проверить, что сгенерированная постоянная ссылка выполняет не перекрывается специальным URL-адресом.
Если пользователь пытается создать продукт с именем specials
или new-in-stock
или даже обычный метод Rails RESTful resource, такой как new
или edit
, Я хочу, чтобы контроллер просматривал конфигурацию маршрутизации, устанавливал ошибки для объекта модели, не выполнял проверку для новой записи и не сохранял ее.
Я мог бы жестко запрограммировать список известных нелегальных имен постоянных ссылок, но мне кажется, что делать это таким образом неаккуратно.Я бы предпочел подключиться к маршрутизации, чтобы сделать это автоматически.
(названия контроллера и модели изменены, чтобы защитить невинных и упростить ответ, фактическая настройка сложнее, чем в этом примере)
Решение
Что ж, это работает, но я не уверен, насколько это красиво.Основная проблема заключается в смешивании логики контроллера / маршрутизации с моделью.В принципе, вы можете добавить пользовательскую проверку к модели, чтобы проверить ее.При этом используются недокументированные методы маршрутизации, поэтому я не уверен, насколько стабильно это будет происходить в дальнейшем.У кого-нибудь есть идеи получше?
class Product < ActiveRecord::Base
#... other logic and stuff here...
validate :generated_permalink_is_not_reserved
def generated_permalink_is_not_reserved
create_unique_permalink # permalink_fu method to set up permalink
#TODO feels really ugly having controller/routing logic in the model. Maybe extract this out and inject it somehow so the model doesn't depend on routing
unless ActionController::Routing::Routes.recognize_path("/products/#{permalink}", :method => :get) == {:controller => 'products', :id => permalink, :action => 'show'}
errors.add(:name, "is reserved")
end
end
end
Другие советы
Вы можете использовать маршрут, который в противном случае не существовал бы.Таким образом, не будет иметь никакого значения, выберет кто-то зарезервированное слово для заголовка или нет.
map.product_view '/product_view/:permalink', :controller => 'products', :action => 'view'
И в ваших взглядах:
product_view_path(:permalink => @product.permalink)
По причинам, подобным этой, лучше управлять URI явно самостоятельно и избегать случайного раскрытия маршрутов, которые вы не хотите.