使用 Rails 3 进行内部重定向
我正在尝试在 Rails 3 中实现一个通用的虚荣 url 系统。通用是指虚荣 url 不与特定模型绑定。它类似于 Vanities gem,其中我有一个从所有虚荣 URL 中命中的 VanityUrlController
。不同之处在于我不想从 foo.com/username
到 foo.com/users/1
或 foo.com/ 进行外部重定向产品名称
到foo.com/products/1
。我希望虚荣 url 保留下来,并让 VanityUrlContoller 执行模仿相应显示操作的内部重定向。
我知道我想要将内部重定向分派到哪个控制器和操作,但在实际分派它时遇到问题。这就是我现在所处的位置:
TargetController.new.process("show", request.env)
它似乎开始处理新的“请求”,但是缺少关键部分......比如实际的请求对象。
任何想法或指示将不胜感激。
更新:
我遇到了 ActionController 中的调度方法,这似乎让我走得更远。
TargetController.new.dispatch("show", request)
我对此有两个问题,1)它被列为私有 api 方法,因此如果有其他方法可以做到这一点,我宁愿这样做,2)即使它正在渲染 TargetController 的显示模板,它也是抱怨“缺少模板 vanity_urls/show”。
更新
这是我们提出的解决方案的基础知识。我们做了一些其他事情,例如强制编码和检查其他一些特定于应用程序的内容,但这应该是您开始所需的全部内容。
它位于 routes.rb
文件的最底部,因此您的虚荣路由不会破坏其他命名路由。
# Vanity routes.
match ':id', :as => 'vanity', :to => proc { |env|
id = env["action_dispatch.request.path_parameters"][:id]
vain_object = <method to find the object you want to display>
if vain_object.nil?
# render your 404 page
'application#404'
else
model = vain_object.class.model_name
# figure out the controller you want to go to
controller = [model.pluralize.camelize,"Controller"].join.constantize
# reset the :id parameter with the id of the object to be displayed
env["action_dispatch.request.path_parameters"][:id] = vain_object.id
# do your internal redirect
controller.action("show").call(env)
end
}
在创建虚荣路线时您还需要小心,这样它们就不会与您的其他控制器发生冲突。其他一些需要了解的有用信息是:
Rails.application.routes.routes.any? { |r| r.requirements[:controller] == vanity_url }
它告诉您您的 vanity_url
是否与当前控制器具有相同的名称。
Rails.application.routes.recognize_path("/#{vanity_url}", :method => :get)
它告诉你这是否已经映射到任何东西。
当然,一路上有一些技巧,但它的作用就像一个魅力。
I am trying to implement a generic vanity url system in Rails 3. Generic in the sense that the vanity url isn't tied to a specific model. It is similar to the Vanities gem where I have a VanityUrlController
that is hit from all the vanity urls. The difference is that I don't want to do an external redirect from foo.com/username
to foo.com/users/1
or foo.com/product-name
to foo.com/products/1
. I want the vanity url to to stick around and have the the VanityUrlContoller do an internal redirect that mimics the corresponding show action.
I know what controller and action I want to dispatch the internal redirect to, but I am having problems with actually dispatching it. This is where I am at the moment:
TargetController.new.process("show", request.env)
It seems to start processing the new "request," but there are key pieces missing... like the actual request object.
Any thoughts or pointers would be very appreciated.
Update:
I ran across the dispatch method in ActionController which seems to get me a little farther.
TargetController.new.dispatch("show", request)
I have two problems with this, 1) it is listed as a private api method so if there is another way to do this, I'd rather that, and 2) even though it is rendering the show template for the TargetController, it is complaining about "Missing template vanity_urls/show."
UPDATE
Here is the basics of the solution we came up with. We do some other things like forcing encodings and checking some other application specific stuff, but this should be all you need to get going.
This goes at the very bottom of your routes.rb
file so your vanity routes don't clobber your other named routes.
# Vanity routes.
match ':id', :as => 'vanity', :to => proc { |env|
id = env["action_dispatch.request.path_parameters"][:id]
vain_object = <method to find the object you want to display>
if vain_object.nil?
# render your 404 page
'application#404'
else
model = vain_object.class.model_name
# figure out the controller you want to go to
controller = [model.pluralize.camelize,"Controller"].join.constantize
# reset the :id parameter with the id of the object to be displayed
env["action_dispatch.request.path_parameters"][:id] = vain_object.id
# do your internal redirect
controller.action("show").call(env)
end
}
You'll also want to be careful while creating your vanity routes, so they don't collide with your other controllers. Some other useful things to know about are:
Rails.application.routes.routes.any? { |r| r.requirements[:controller] == vanity_url }
Which tells you if your vanity_url
has the same name as a current controller.
Rails.application.routes.recognize_path("/#{vanity_url}", :method => :get)
Which tells you if this maps to anything already.
Sure, there are a couple of hacks along the way, but it works like a charm.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
尝试FriendlyId 插件。它似乎完全按照你想要的做。视频教程:
http://railscasts.com/episodes/314-pretty-urls-with-friendid
Try the FriendlyId plugin. It seems to do exactly what you want. Video tutorial:
http://railscasts.com/episodes/314-pretty-urls-with-friendlyid