Rails:通过表单创建新记录时validation失败后的URL

让我们说我正在使用一个表单和一个标准的Rails restful控制器创建一个新的Foo,它看起来像这样:

class FoosController  'Created a foo.' else render 'new' end end ... end 

所以,如果我使用标准的restful控制器(如上所述),那么当我创建Foo时,我在example.com/foos/new ,如果我提交表单并且它保存正确我在example.com/foos显示索引操作。 但是,如果未正确填充表单,则会再次呈现表单并显示错误消息。 这都是普通的香草。

但是,如果显示错误,将呈现表单页面,但URL将为example.com/foos ,因为CREATE操作会发布到该URL。 但是,人们期望在example.com/foos上找到Foos #index,而不是他们刚刚提交的表单,并添加了错误消息。

这似乎是Rails的标准行为,但它对我来说并没有多大意义。 显然我可以重定向回到new而不是从create动作渲染new,但问题是错误消息等会随着内存中部分完整的Foos而丢失。

这个问题是否有一个干净的解决方案,当他们提交的新Foo表单中存在错误时,可以将人们发送回example.com/foos/new

谢谢!

您可以通过在初始化程序中添加它来挂钩rails路由: https : //gist.github.com/903411

然后将常规资源放在routes.rb中:

 resources :users 

它应该创建您正在寻找的路线和行为。

回答你对另一个答案的评论:

我想知道是否有一种方法,根本没有重写控制器,告诉rails你希望URL匹配渲染的模板,而不是调用它的控制器动作。

我不这么认为; URL直接绑定到路由,路由绑定到控制器和操作对 – 渲染层根本不接触它。

为了回答你原来的问题 ,这里是我回答的另一个类似问题的信息。


正如您所发现的那样,默认情况下,当您指定resources :things用于创建新事物的POST路径位于/things 。 这是rake routes的输出:

  things GET /things(.:format) {:action=>"index", :controller=>"things"} POST /things(.:format) {:action=>"create", :controller=>"things"} new_thing GET /things/new(.:format) {:action=>"new", :controller=>"things"} edit_thing GET /things/:id/edit(.:format) {:action=>"edit", :controller=>"things"} thing GET /things/:id(.:format) {:action=>"show", :controller=>"things"} PUT /things/:id(.:format) {:action=>"update", :controller=>"things"} DELETE /things/:id(.:format) {:action=>"destroy", :controller=>"things"} 

听起来你想要更像这样的东西:

 create_things POST /things/new(.:format) {:action=>"create", :controller=>"things"} things GET /things(.:format) {:action=>"index", :controller=>"things"} new_thing GET /things/new(.:format) {:action=>"new", :controller=>"things"} edit_thing GET /things/:id/edit(.:format) {:action=>"edit", :controller=>"things"} thing GET /things/:id(.:format) {:action=>"show", :controller=>"things"} PUT /things/:id(.:format) {:action=>"update", :controller=>"things"} DELETE /things/:id(.:format) {:action=>"destroy", :controller=>"things"} 

虽然不推荐,但您可以通过以下路径获得此结果:

 resources :things, :except => [ :create ] do post "create" => "things#create", :as => :create, :path => 'new', :on => :collection end 

您还需要修改表单以使其POST到正确的路径。

如果您担心要显示的URL,可以手动设置路由。 对于你想要的,你可以有一个GET/foos/new渲染你的表单,并在同一个URL上创建一个POST

 map.with_options :controller => :foos do |foo| foo.new_foo '/foos/new', :conditions => {:method => :get}, :action => :new foo.create_foo '/foos/new', :conditions => {:method => :post}, :action => :create foo.foos '/foos', :conditions => {:method => :get}, :action => :index end 

这应该可以在不需要对控制器进行任何更改的情况下工作(耶!) – 您的示例中的所有三个操作都将得到处理。 少数免责声明:

  1. 这是基于我对2.3.8应用程序的路由 – 可能需要一些语法(语义?)更改才能使其进入Rails 3路由样式。
  2. 我尝试将这种路由风格与map.resources混合起来的map.resources非常大 – 除非你比我更熟悉这个,或者Rails 3路由更好(两者都很容易),你必须为每一条路径做到这一点。控制器。
  3. 最后,不要忘记将(.:format)等添加到需要它们的路由中(本例中没有,但请参阅#2)。

希望这可以帮助!

编辑:最后一件事 – 您需要在/foos/new.html.erb上的form_for帮助程序中对URL进行硬编码。 只需添加:url => create_foo_path ,所以Rails不会尝试发布到/foos ,它默认情况下(可能有一种方法可以更改模型中的创建URL,但我不知道,如果有一个)。

您可以使用Rack :: Flash在用户会话中存储所需的参数,然后重定向到您的表单URL。

 def create @foo = Foo.new(params[:foo]) if @foo.save redirect_to foos_path, :notice => 'Created a foo.' else flash[:foo] = params[:foo] flash[:errors] = @foo.errors redirect_to new_foo_path #sorry - can't remember the Rails convention for this route end end def new # in your view, output the contents of flash[:foo] @foo = Foo.new(flash[:foo]) end