Как перенаправить на предыдущую страницу в Ruby On Rails?

186

У меня есть страница, на которой перечислены все проекты с сортируемыми заголовками и нумерацией страниц.

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 

Любые идеи о том, как я мог бы заставить это работать. Это похоже на проблему, которую легко решить, но я новичок в RoR.

сервитут
источник

Ответы:

323

В действии редактирования сохраните запрашивающий URL в хеше сеанса, который доступен для нескольких запросов:

session[:return_to] ||= request.referer

Затем перенаправьте на него в действии обновления после успешного сохранения:

redirect_to session.delete(:return_to)
Хайме Беллмайер
источник
68
Я бы предложил redirect_to session.delete(:return_to)в действии обновления. Это очищает значение от сеанса, так как оно больше не нужно.
Стиги
19
не несколько открытых вкладок не путают эту логику?
Джонс
12
Не могли бы вы просто redirect_to request.referer?
Эль Манди,
1
@ DanMundy Нет, чтобы это работало, это должно быть request.referer.referer, если это возможно. @ Хайме Беллмайер Почему ||=?
x-yuri
@ Джонс да. Существует также путаница, если вы переключаетесь на другую модель редактирования. Мне также интересно, почему || =
Mauro
99

Почему у вас 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)

Обратите внимание, что redirect_to(:back)это устарело в Rails 5. Вы можете использовать

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, это устанавливается в качестве реферера для следующего запроса, поэтому после того , как форма успешно отправлена она показывает форму снова, то есть /posts/new. Однако он хорошо работает для других целей.
Крис
То есть OP хочет перенаправить назад дважды. Могу поспорить, он мог просто redirect_to projects_path.
x-yuri
Да. redirect_back не работает так же хорошо в Rails 5
strizzwald
@strizzwald, что значит "плохо работает"? Какие-нибудь детали?
Паскаль
@pascalbetz, я имел в виду, что если HTTP_REFERERне установлено, вы можете получить ActionController::RedirectBackError, вам придется спасаться от этого исключения в этом случае. Насколько я понимаю, использование redirect_backне требует обработки исключений, так как вы вынуждены предоставлять fallback_location. Может быть, «не работает хорошо» - это неправильный способ выразить это.
Strizzwald
45

Мне нравится метод Хайме с одним исключением, для меня лучше работать каждый раз, когда я заново сохраняю реферер:

def edit
    session[:return_to] = request.referer
...

Причина в том, что если вы редактируете несколько объектов, вы всегда будете перенаправлены обратно на первый URL, который вы сохранили в сеансе с помощью метода Jaime. Например, допустим, у меня есть объекты Apple и Orange. Я редактирую Apple и получаю session[:return_to]разрешение от этого действия. Когда я иду, чтобы редактировать апельсины, используя тот же код, session[:return_to]не будет установлен, потому что он уже определен. Поэтому, когда я обновлю Orange, меня отправят рефереру предыдущего действия Apple # edit.

Тони
источник
да, но можете ли вы предложить, что делать, если случайно был сохранен тот же URL-адрес, что и у вас? Вы в яблоке, и вы пришли из яблока. И вы хотите предыдущее местоположение
Uko
33

Вот как мы делаем это в нашем приложении

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

Таким образом, вы сохраняете только последний GET-запрос в параметре :return_toсеанса, поэтому все формы, даже если многократно POSTed будет работать :return_to.

MBO
источник
3
request.request_uriбольше недоступен, поэтому я предлагаю использовать request.fullpathвместо этого
анка
@anka Обновлено. Спасибо за комментарий
MBO
2
да это довольно хорошо. Я бы только предложил вообще не использовать andи orв ifвысказываниях. Используйте &&и ||вместо. Подробности здесь .
Ахиллес
19

В рельсах 5, согласно инструкциям в Rails Guides, вы можете использовать:

redirect_back(fallback_location: root_path)

Местоположение 'back' берется из заголовка HTTP_REFERER, который не гарантируется браузером. Вот почему вы должны предоставить 'fallback_location'.

pSkarl
источник
Эта функция появляется в рельсах 5.
Chambeur
@pSkarl Как я могу передать noticeобъект с redirect_backоператором, чтобы сказать пользователю, что что-то пошло не так с флэш-сообщением?
alexventuraio
2
Ну, я мог бы решить эту проблему, выполнив: redirect_back(fallback_location: root_path, notice: "Something went wrong!"). Надеюсь, это может как-то помочь.
alexventuraio
18

request.referer устанавливается Rack и устанавливается следующим образом:

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

Просто сделайте a, redirect_to request.refererи он всегда будет перенаправлен на истинную ссылающуюся страницу, или root_path ('/'). Это важно при прохождении тестов, которые терпят неудачу в случаях прямой навигации к определенной странице, на которой контроллер выдает redirect_to: back

Стив Типтон
источник
Не уверен, какой файл вы смотрели, но у источника стойки, вот как referer было определено по состоянию на 28 марта 2011 года , и это то, как он определен на сегодняшний день . То есть || '/'не является частью определения.
Мачек
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
aschyiel
источник