Como redirecionar para a página anterior no Ruby On Rails?

186

Eu tenho uma página que lista todos os projetos que possuem cabeçalhos e paginação classificáveis.

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

Eu escolho editar um dos projetos

path:
projects/436/edit

Quando clico em salvar nessa página, ele chama o método de controlador / atualização de projetos. Depois de atualizar o código, quero redirecionar para o caminho em que estava antes de clicar em editar um projeto específico. Em outras palavras, eu quero estar na mesma página com a mesma classificação.

Eu vi link_to (: back) e pensei que: back pode funcionar em redirect_to (: back), mas isso não é possível.

puts YAML::dump(:back) 
yields the following:
:back 

Alguma idéia de como eu poderia fazer isso funcionar. Parece um problema que seria facilmente resolvido, mas sou novo no RoR.

servidão
fonte

Respostas:

323

Na sua ação de edição, armazene o URL solicitante no hash da sessão, disponível em várias solicitações:

session[:return_to] ||= request.referer

Em seguida, redirecione para ele em sua ação de atualização, após um salvamento bem-sucedido:

redirect_to session.delete(:return_to)
Jaime Bellmyer
fonte
68
Eu sugeriria redirect_to session.delete(:return_to)na ação de atualização. Isso limpa o valor da sessão, pois não é mais necessário.
stigi
19
ter várias guias abertas não confunde essa lógica?
jones
12
Você não poderia apenas redirect_to request.referer?
Elle Mundy
1
@ DanMundy Não, para que funcione, deveria ser request.referer.referer, se isso fosse possível. @Jaime Bellmyer Por quê ||=?
X-yuri 27/05
@ Jones sim. Também há confusão se você alternar para outra edição do modelo. Também estou interessado em saber por que || =
Mauro
99

Por que redirect_to(:back)não funciona para você, por que é impossível?

redirect_to(:back)funciona como um encanto para mim. É apenas um atalho para redirect_to(request.env['HTTP_REFERER'])

http://apidock.com/rails/ActionController/Base/redirect_to (anterior ao Rails 3) ou http://apidock.com/rails/ActionController/Redirecting/redirect_to (Rails 3)

Observe que redirect_to(:back)está sendo descontinuado no Rails 5. Você pode usar

redirect_back(fallback_location: 'something')em vez disso (consulte http://blog.bigbinary.com/2016/02/29/rails-5-improves-redirect_to_back-with-redirect-back.html )

Pascal
fonte
12
redirect_to :backtambém não funciona bem, digamos que você visite /posts/new, isso é definido como referenciador para a próxima solicitação; portanto, após o envio do formulário com êxito, ele mostra o formulário novamente, ou seja /posts/new. No entanto, funciona bem para outros fins.
Kris
Ou seja, o OP deseja redirecionar de volta duas vezes. Aposto que ele poderia apenas redirect_to projects_path.
x-yuri
Sim. redirect_back não funciona tão bem no Rails 5
strizzwald
@strizzwald o que significa "não funciona bem"? Algum detalhe?
Pascal
@pascalbetz, o que eu quis dizer é que, se HTTP_REFERERnão estiver definido, você pode obter ActionController::RedirectBackError, precisaria resgatar dessa exceção nesse caso. Pelo meu entendimento, o uso redirect_backnão requer nenhum tratamento de exceção, pois você é forçado a fornecer fallback_location. Talvez 'não funcione bem' não seja o caminho certo para colocá-lo.
strizzwald
45

Eu gosto do método de Jaime, com uma exceção, funcionou melhor para mim re-armazenar o referenciador toda vez:

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

O motivo é que, se você editar vários objetos, sempre será redirecionado de volta para o primeiro URL que você armazenou na sessão com o método de Jaime. Por exemplo, digamos que eu tenho objetos Apple e Orange. Eu edito a Apple e session[:return_to]me coloco no referenciador dessa ação. Quando for editar laranjas usando o mesmo código, session[:return_to]não será definido porque já está definido. Portanto, quando eu atualizar o Orange, serei enviado ao referenciador da ação de edição anterior da Apple #.

Tony
fonte
sim, mas você pode sugerir o que fazer se acidentalmente o mesmo URL estiver armazenado como você está? Você está na maçã e você veio da maçã. E você deseja localização anterior
Uko
33

É assim que fazemos em nossa aplicação

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

Dessa forma, você armazena apenas a última solicitação GET nos :return_toparâmetros da sessão, para que todos os formulários, mesmo quando várias vezes o POST, funcionassem :return_to.

MBO
fonte
3
request.request_urinão está mais disponível, então eu sugiro usar request.fullpathvez
Anka
@anka Atualizado. Obrigado pelo comentário
MBO
2
Sim, isso é muito bom. Eu apenas sugeriria geralmente não usar ande orem ifdeclarações. Use &&e em ||vez disso. Detalhes aqui .
Achilles
19

No trilhos 5, conforme as instruções nos Guias do Rails, você pode usar:

redirect_back(fallback_location: root_path)

O local 'back' é extraído do cabeçalho HTTP_REFERER, que não é garantido como definido pelo navegador. É por isso que você deve fornecer um 'fallback_location'.

pSkarl
fonte
Esse recurso aparece nos trilhos 5.
Chambeur 4/08/16
@pSkarl Como posso passar um noticeobjeto com a redirect_backinstrução para informar ao usuário que algo deu errado com uma mensagem em flash?
alexventuraio
2
Bem, eu poderia resolver isso fazendo: redirect_back(fallback_location: root_path, notice: "Something went wrong!"). Espero que possa ajudar de alguma forma.
alexventuraio
18

request.referer é definido pelo rack e é da seguinte maneira:

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

Basta fazer um redirect_to request.referere ele sempre será redirecionado para a página de referência verdadeira ou para o root_path ('/'). Isso é essencial ao passar por testes que falham nos casos de navegação direta para uma página específica na qual o controlador lança um redirect_to: back

Steve Tipton
fonte
Não tenho certeza de qual arquivo você estava visualizando, mas na origem do rack, refererfoi assim que foi definido em 28 de março de 2011 e é assim que é definido hoje . Ou seja, || '/'não faz parte da definição.
Maček
1

Para quem estiver interessado, aqui está minha implementação, estendendo a resposta original do MBO (escrita nos trilhos 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
fonte