在Rails 3中,当资源创build操作失败并调用render:new时,为什么URL必须更改为资源的索引url?

我有一个名为书籍的资源。 它在我的路由文件中被正确地列为资源。

我有一个新的行动,给新的观点的标准:

@book = Book.new 

在模型上,有一些由存在validation的属性,所以如果保存操作失败,将会产生错误。

在我的控制器中:

 @book = Book.create ... # some logic if @book.save redirect_to(@book) else render :new end 

这是非常标准的; 以及使用render:new的原理是这样的,这个对象被传回到视图,错误可以被报告,重新填写表单等等。

这有效,除了每次我被发回到表单(通过渲染:新),我的错误显示,但我的url是INDEXurl,这是

 /books 

而不是

 /books/new 

我是从哪里开始的。 我看过其他几个关于这个问题的post,但没有答案。 至less,人们会认为它将着陆在/书/创build,我也有一个查看文件(在这种情况下相同的新)。

我可以做这个:

 # if the book isn't saved then flash[:error] = "Errors!" redirect_to new_book_path 

但是,然后@book数据丢失,以及错误信息,这是forms和行动的整个点等。

为什么渲染:新登陆我在/书,我的索引行动,通常情况下,该URL调用INDEX方法,其中列出所有的书籍?

它实际上送你到创buildpath。 它在create操作中,其path是/books ,使用HTTP方法POST。 这看起来与索引path/books ,但索引path使用HTTP方法GET。 在确定要调用哪个动作时,导轨路由代码将该方法考虑在内。 validation失败后,您仍然处于创build操作中,但您正在呈现new视图。 这有点令人困惑,但像render :new一样render :new实际上并没有调用新的动作; 它仍在运行创build操作,它告诉Rails渲染新的视图

我刚开始使用Rails-Tutorial ,遇到同样的问题。 解决scheme很简单:如果您在提交表单(错误)后需要相同的URL,只需在一个操作中合并新build和创build操作。

这是我的代码的一部分,这使得这成为可能(希望它可以帮助别人^^)

routes.rb(为新操作添加后路由):

 ... resources :books post "books/new" ... 

控制器:

 ... def create @book = Book.new(book_params) if @book.save # save was successful print "Book saved!" else # If we have errors render the form again render 'new' end end def new if book_params # If data submitted already by the form we call the create method create return end @book = Book.new render 'new' # call it explicit end private def book_params if params[:book].nil? || params[:book].empty? return false else return params.require(:book).permit(:title, :isbn, :price) end end 

new.html.erb:

 <%= form_for @book, :url => {:action => :new} do |f| %> <%= f.label :title %> <%= f.text_field :title %> <%= f.label :isbn %> <%= f.text_field :isbn %> <%= f.label :price %> <%= f.password_field :price %> <%= f.submit "Save book" %> <% end %> 

刚刚有同样的问题,所以也许这有可能帮助某个人。 你基本上必须做3次调整才能使这个东西起作用,尽pipe我的解决scheme还不够理想。

1)在创build操作中:

 if @book.save redirect_to(@book) else flash[:book] = @book redirect_to new_book_path end 

2)在新的行动:

 @book = flash[:book] ? Book.new(flash[:book]): Book.new 

3)无论你分析闪存哈希,一定要过滤掉闪光[:书]。

– >显示正确的URL,表单数据被保存。 不过,我不知道如何把用户对象放入闪存散列,我不认为这是它的目的。 anyboy是否知道一个更好的地方把它放进去?

由于您正在通过发布到/books/创build资源,因此它不会将您置于/books/ /books/new 。 当你的创build失败时,只是渲染新的动作,而不是将你redirect到新的动作。 正如@MrYoshiji上面所说的,你可以尝试将它redirect到新的动作,但是这是非常低效的,因为你将创build另一个HTTP请求并往返服务器,只是改变url。 在这一点上,如果它很重要,你可以使用javascript改变它。

它可以通过使用相同的url,但不同的方法来修复新的和创build的行动。

在路由文件中可以使用下面的代码。

 resources :books do get :common_path_string, on: :collection, action: :new post :common_path_string, on: :collection, action: :create end 

现在你的新页面将呈现在URL

书籍/ common_path_string

如果在validation之后出现任何错误,那么url也是一样的。

也在表单中改为使用

 books_path 

使用

 url: common_path_string_books_path, method: :post 

select你喜欢的common_path_string

这是我的解决scheme。 不知道它是RESTful,还是Raily和其他任何东西(我仍然是初学者),但至less还在工作:

 # routes.rb post 'posts/new', to: 'posts#create', as: 'post_create' resources :posts # posts_controller.rb def new end def create if Post.save? redirect_to posts_path else render action: 'new' end # new.html.erb <%= form_with model: Post.new, url: :post_create, method: :post, local: true do |f| %> <%= f.submit 'Submit' %> 

更新

尽pipe解决scheme在技术上是有效的,但考虑到create行动的目标是创造(在大多数情况下,这是正面的),我将考虑这一点:

除非你真的需要,否则你不应该让浏览器重新打电话,所以当你使用redirect_to时候,总是提出问题,如果是正确的,或者render会更好。

从这篇文章