如何在Ruby On Rails中重定向到上一页?

198

我有一个页面,列出了所有具有可排序标题和分页的项目。

path:
/projects?order=asc&page=3&sort=code

我选择编辑其中一个项目

path:
projects/436/edit
当我在那个页面上点击保存时,它会调用项目控制器/更新方法。当我更新代码后,我想重定向到我在编辑特定项目之前所在的路径。换句话说,我希望在相同的页面上具有相同的排序方式。
我看到了link_to(:back),并认为:back可能在redirect_to(:back)中起作用,但这是行不通的。
puts YAML::dump(:back) 
yields the following:
:back 

我该如何让这个起作用?

9个回答

342
在您的编辑操作中,请将请求URL存储在会话哈希中,该哈希在多个请求之间可用:
session[:return_to] ||= request.referer

成功保存后,在更新操作中将其重定向:

redirect_to session.delete(:return_to)

21
开多个标签页会不会影响这个逻辑? - jones
13
你可以直接使用 redirect_to request.referer 吗? - Elle Mundy
1
@DanMundy 不是的,为了让它工作应该是 request.referer.referer,如果可能的话。@Jaime Bellmyer 为什么要用 ||= - x-yuri
@jones 是的。如果您切换到另一个模型编辑器,也会有混淆。 我也很想知道 ||= 的原因。 - Mauro
1
使用 ||= 没有任何意义,因为您确实希望覆盖变量,否则您将无法获得最新访问的页面。 - pmrotule
它在 Rails 7 中对我可行。我将它放在我的 application_controller 的授权方法中。 - Matteo

106

为什么redirect_to(:back)对您无效,为什么不行?

redirect_to(:back) 对我来说很有效。它只是一个快捷方式,用于 redirect_to(request.env['HTTP_REFERER'])

http://apidock.com/rails/ActionController/Base/redirect_to (Rails 3 之前) 或 http://apidock.com/rails/ActionController/Redirecting/redirect_to (Rails 3)

请注意,在 Rails 5 中,redirect_to(:back) 已被弃用。您可以使用

redirect_back(fallback_location: 'something') 替代 (参见 http://blog.bigbinary.com/2016/02/29/rails-5-improves-redirect_to_back-with-redirect-back.html)


12
redirect_to :back 对我来说也不太有效,比如你访问了/posts/new,那么这个URL将会被设置为下一个请求的引用页面,所以在表单成功提交后它会再次显示表单,即 /posts/new。但是对于其他目的,它确实很有效。 - Kris
也就是说,OP想要进行两次重定向。我敢打赌他只需要使用redirect_to projects_path即可。 - x-yuri
是的,redirect_back 在 Rails 5 中也无法正常工作。 - strizzwald
@strizzwald,“不工作良好”是什么意思?有详细信息吗? - Pascal
@pascalbetz,我的意思是如果HTTP_REFERER没有设置,你可能会得到ActionController::RedirectBackError,在这种情况下,你需要从这个异常中进行救援。据我所知,使用redirect_back不需要任何异常处理,因为你必须提供fallback_location。也许“不起作用”不是正确的说法。 - strizzwald

46

我喜欢Jaime的方法,但有一个例外,对我来说更好的是每次重新存储referer:

def edit
    session[:return_to] = request.referer
...
原因是,如果您编辑多个对象,则始终会重定向回使用Jaime方法存储在会话中的第一个URL。例如,假设我有对象Apple和Orange。我编辑Apple并设置了session[:return_to]到该操作的引用者。当我使用相同的代码编辑Oranges时,session[:return_to]将不会被设置,因为它已经定义过了。所以当我更新Orange时,我将被发送到先前的Apple#edit操作的引用者。

是的,但如果意外地将相同的URL存储为您所在的位置,则可以建议应该怎么做?您现在在苹果处,而您又从苹果进入,您想要回到以前的位置。 - Uko

35

这是我们应用程序的做法

def store_location
  session[:return_to] = request.fullpath if request.get? and controller_name != "user_sessions" and controller_name != "sessions"
end

def redirect_back_or_default(default)
  redirect_to(session[:return_to] || default)
end

这种方法只在:return_to会话参数中存储最后一个GET请求,因此所有表单(即使多次发布)也可以使用:return_to


3
不错,这很好。我只建议在if语句中一般不要使用andor,而是使用&&||。详情请参见此处 - Achilles

25

按照Rails Guides中的指示,在Rails 5中,您可以使用:

redirect_back(fallback_location: root_path)

'后退'位置来源于HTTP_REFERER头,浏览器不保证已设置。这就是为什么您应该提供一个“fallback_location”的原因。


这个功能出现在Rails 5中。 - Chambeur
@pSkarl 我该如何在 redirect_back 语句中传递一个 notice 对象,以便通过闪存消息告诉用户出现了错误? - alexventuraio
2
我可以通过以下方式解决这个问题:**redirect_back(fallback_location: root_path, notice: "Something went wrong!")**。希望这里的链接能在某种程度上帮到你。 - alexventuraio

18

request.referer 是由 Rack 设置的,设置方式如下:

def referer
  @env['HTTP_REFERER'] || '/'
end

只需执行redirect_to request.referer,它将始终重定向到真实的引用页面或根路径('/')。在传递测试时,这非常重要,当控制器抛出redirect_to :back以直接导航到特定页面的情况下,测试会失败。


不确定您查看的是哪个文件,但在机架源中,referer2011年3月28日 的定义方式,就是它在今天的定义方式。也就是说,|| '/' 不是定义的一部分。 - maček

2
link_to 'get me back', :back

符号:back就像你的瑞士军刀一样实用。

1

对于那些感兴趣的人,这是我对MBO原始答案的扩展实现(针对rails 4.2.4、ruby 2.1.5编写)。

class ApplicationController < ActionController::Base
  after_filter :set_return_to_location

  REDIRECT_CONTROLLER_BLACKLIST = %w(
    sessions
    user_sessions
    ...
    etc.
  )

  ...

  def set_return_to_location
    return unless request.get?
    return unless request.format.html?
    return unless %w(show index edit).include?(params[:action])
    return if REDIRECT_CONTROLLER_BLACKLIST.include?(controller_name)
    session[:return_to] = request.fullpath
  end

  def redirect_back_or_default(default_path = root_path)
    redirect_to(
      session[:return_to].present? && session[:return_to] != request.fullpath ?
        session[:return_to] : default_path
    )
  end
end

当您同时编辑两个或更多项目时,这些是否不会将您带回到不同的路径? - gekong

0

我想知道这是否有效

  def edit
    if request.referer != request.original_url
        @return_here =  request.referer
    end

  end

在提交表单中使用@return_here作为隐藏值。

当然,重新加载会导致其失效,因此根据需要返回默认的备用方案。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接