redirect_to和render是否可以交换?

时间:2011-09-21 01:59:50

标签: ruby-on-rails

对于下面的代码,如果用render或vise verse替换redirect_to会发生什么?

def create
  @product = Product.new(params[:product])

  respond_to do |format|
    if @product.save
      format.html { redirect_to(@product, :notice => 'Product was successfully created.') }

    else
      format.html { render :action => "new" }
    end
  end
end

似乎可以在上面的代码中替换另一个。是否只有redirect_to或render必须使用的地方?渲染除了渲染视图外什么都不做。 Redirect_to向服务器发送302请求,重定向后当前参数丢失。

感谢。

3 个答案:

答案 0 :(得分:110)

如果您正在使用render,当用户刷新页面时,它将再次提交之前的POST请求。这可能会导致重复购买等不良后果。

enter image description here

但是如果您使用redirect_to,当用户刷新页面时,它只会再次请求相同的页面。这也称为Post/Redirect/Get (PRG)模式。

enter image description here

因此,应该使用redirect_to的地方是您正在执行HTTP POST请求并且您不希望用户在完成请求时重新提交请求(这可能会导致重复项目和其他问题)

在Rails中,当模型无法保存时,render用于重新显示具有先前填充的相同条目的表单。这更简单,因为如果使用重定向,则必须使用参数或会话传递表单条目。副作用是,如果刷新浏览器,它将尝试重新提交以前的表单条目。这是可以接受的,因为它可能会以同样的方式失败,或者如果它现在成功,那么无论如何它都是用户应该期待的。

有关renderredirect的更深入说明,请阅读此article

答案 1 :(得分:9)

重定向时,您将生成一个命中控制器方法的新请求,渲染只会渲染关联的视图。您在create中使用render,因为如果保存失败,您希望保持模型对象的状态,以便您可以呈现有关其错误的信息。如果您尝试重定向到new_product路径,则会创建一个新的模型对象,并且会丢失用户输入的所有表单数据以及任何错误等等。

编辑(有更多信息):

您必须使用redirect_to的情况的示例是,如果您的视图模板使用未在您重定向的控制器方法中初始化的实例变量。因此,您可能无法在create方法中调用render {:action => 'index'},因为索引模板可能会使用@products变量,但您只能初始化@product,因此会导致异常

答案 2 :(得分:1)

这是我遵循的两种方法的完整列表:

1)默认情况下redirect_to将发出HTTP 302状态代码。 302重定向是一个临时更改,它会在有限的时间内将用户和搜索引擎重定向到所需的页面,直到将其删除。您可以选择将301状态代码指定为redirect_to。当任何页面已永久移动到另一个位置时,将使用301状态代码。现在,用户将看到新页面,因为它已经替换了旧页面。当页面显示在搜索引擎结果中时,它将更改页面的URL。

2)redirect_to将发出新的HTTP请求,因为它被重定向到其他控制器操作或URL。除非确实需要,否则不应该使浏览器不需要重新调用,因此请始终询问何时使用redirect_to以及它是否正确,否则渲染会更好。 -redirect_to将导致跳过当前操作的任何自动模板呈现。

3)默认情况下,渲染将发出HTTP 200状态代码(但是对于无效的ActiveRecord对象,您可能希望将其更改为422无法处理的实体)。 HTTP 200 OK成功状态响应代码指示请求已成功。状态代码422(不可处理实体)表示服务器了解请求实体的内容类型,并且请求实体的语法正确,但是无法处理所包含的指令。

4)渲染将渲染模板,并且控制器动作中定义的任何实例变量都将在模板中可用。当然,如果redirect_to调用后续动作,则实例变量将不可用。重要要点:重定向命中控制器,而渲染不命中,因此,如果渲染其他模板,它将不会命中与该模板关联的操作,因此这些实例变量将不可用!

5)对于渲染,请使用flash.now而不是常规Flash。

flash.now[:error] = "There was a problem"  
# not 
flash[:error] = "There was a problem"

6)如果不这样做,则闪现消息可能不会显示在渲染的页面上,并且会显示在访问的下一页上。

7)渲染不会导致当前动作停止执行! redirect_to不会导致当前动作停止执行!如果您需要在操作中跳过代码的进一步执行,则需要调用“返回”!在下面的代码中,在底部有一个显式渲染,因此您必须返回以免发生重定向错误,并且同时出现这两种渲染:

def update
  @record = Record.new(record_params)
  if @record.save
    flash[:success] = "record was successfully saved"
    redirect_to records_path
    return
  end
  flash.now[:error] = "please fix the problems in the record"
  render :edit
end

另一个选择:

def update
  @record = Record.new(record_params)
  if @record.save
    flash[:success] = "record was successfully saved"
    redirect_to records_path
  else
    flash.now[:error] = "please fix the problems in the record"
    render :edit
  end  
end

8)刷新消息提供了一种在动作之间传递临时原始类型(字符串,数组,哈希)的方法。您放置在闪光灯中的任何东西都将暴露于下一个动作,然后清除。这是发出通知和警报的好方法:

class PostsController < ActionController::Base
  def create
    # save post
    flash[:notice] = "Post successfully created"
    redirect_to @post
  end

  def show
    # doesn't need to assign the flash notice to the template, that's done automatically
  end
end

show.html.erb
  <% if flash[:notice] %>
    <div class="notice"><%= flash[:notice] %></div>
  <% end %>

由于可以同时显示通知和警报,因此可以通过以下方式同时显示通知和警报:

<% flash.each do |key, value| %>
  <%= content_tag :div, value, class: "flash #{key}" %>
<% end %>