【问题标题】:Are redirect_to and render exchangeable?redirect_to 和 render 是可交换的吗?
【发布时间】:2011-09-21 01:59:49
【问题描述】:

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

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请求,重定向后当前参数丢失。

谢谢。

【问题讨论】:

    标签: ruby-on-rails


    【解决方案1】:

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

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

    所以应该使用redirect_to 的地方是当你正在做一个HTTP POST 请求并且你不希望用户在完成后重新提交请求(这可能会导致重复的项目和其他问题)。

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

    有关renderredirect 的更深入解释,您应该阅读此article

    【讨论】:

    • 我认为我没有看到更完美的解释,尤其是最后以实际用例差异为何重要。
    • 注意:刷新与在 URL 栏中按 Enter 相同。在 URL 栏中按 Enter 只会向该 URL 发送一个 GET 请求。刷新会重新提交最后一个请求(浏览器会跟踪该请求)。
    • 关注点分离还有一个好处。 IE。您不必重写渲染所涉及的逻辑,您可以让用户向处理渲染的路由提交请求。
    【解决方案2】:

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

    编辑(包含更多信息):

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

    【讨论】:

    • 看来 render 在更多地方使用是安全的。有什么地方必须使用 redirect_to 吗?谢谢。
    【解决方案3】:

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

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

    2) redirect_to 将发出一个新的 HTTP 请求,因为它被重定向到不同的控制器操作或 URL。你不应该让浏览器需要重新调用,除非你真的必须这样做,所以当你使用 redirect_to 时总是质疑它是否是正确的,或者渲染会更好。 - redirect_to 将导致跳过当前操作的任何自动模板渲染。

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

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

    5) 对于渲染,使用 flash.now,而不是普通的 flash。

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

    6) 如果您不这样做,则 Flash 消息可能不会显示在呈现的页面上,而是会显示在访问的下一页上。

    7) 渲染不会导致当前动作停止执行! redirect_to 不会导致当前动作停止执行!如果您需要绕过操作中代码的进一步执行,则需要调用“return”!在下面的代码中,底部有一个显式渲染,因此您必须执行 return 以避免重定向错误和渲染两者都存在:

    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) flash 消息提供了一种在动作之间传递临时原始类型(字符串、数组、哈希)的方法。你放在闪光灯里的任何东西都会暴露在下一个动作中,然后被清除。这是发出通知和警报的好方法:

    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 中同时显示通知和警报,因此您可以这样显示通知和警报:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-01
      • 2017-08-26
      • 1970-01-01
      相关资源
      最近更新 更多