Rails 3:如何在Ajax调用中“ redirect_to”?


85

attempt_login提交登录表单后,使用Ajax调用以下方法。

class AccessController < ApplicationController
  [...]
  def attempt_login
    authorized_user = User.authenticate(params[:username], params[:password])

    if authorized_user
      session[:user_id] = authorized_user.id
      session[:username] = authorized_user.username
      flash[:notice] = "Hello #{authorized_user.name}."
      redirect_to(:controller => 'jobs', :action => 'index')
    else
      [...]
    end
  end
end

问题是那redirect_to行不通。

您将如何解决?

Answers:


102

最后,我刚更换

redirect_to(:controller => 'jobs', :action => 'index')

有了这个:

render :js => "window.location = '/jobs/index'"

而且效果很好!


43
更好的方法是render :js => "window.location = '#{jobs_path}'"
zakelfassi 2014年

3
它可以工作,但是用实际的json成功消息传回重定向位置并在前端进行重定向会更好吗?
justinxreese 2014年

1
是不是jobs_path基本上是刚性的网址是什么?如果URL更改,则路由名称也将更改,除非您格外小心。另一个选择是render js: "window.location = '#{polymorphic_path(@job.class)}'"并使用基于Job模型计算的资源丰富的路由。仅当您的路线资源丰富并且使用与模型一致的标准命名约定时,这才有效。(或者,如果您在模型上指定了model_name,以便它们生成正确的路线名称。)
弄脏

2
太棒了 任何人都知道为什么简单的redirect_to无法正常工作吗?
Tasos Anesiadis '16

1
@Tasos Anesiadis,当表单为“远程” Rails表单时,redirect_to不起作用,因为已告知浏览器将来自控制器的响应解释为Javascript。您可以在Chrome DevTools的“响应”选项卡(通过“网络”面板)中看到redirect_to页面,但需要的是控制器发出的向浏览器发出指令以查找另一个页面。除非您想通过fetch()和JSON手动提交和处理表单数据,否则需要此处提供的window.location解决方案,或将表单更改为常规的“本地”表单。
MSC

67

有一种非常简单的方法可以保留闪存以备下一个请求。在您的控制器中执行类似的操作

flash[:notice] = 'Your work was awesome! A unicorn is born!'
flash.keep(:notice)
render js: "window.location = '#{root_path}'"

flash.keep将确保闪光灯不停地为下一个请求。因此,root_path呈现时,它将显示给定的Flash消息。Rails很棒:)


27

我认为这稍微好一点:

render js: "window.location.pathname='#{jobs_path}'"


12
稍微好一点:render js: "window.location.pathname = #{jobs_path.to_json}"
tokland 2012年

26

在我的一个应用程序中,我使用JSON进行重定向和Flash消息数据。它看起来像这样:

class AccessController < ApplicationController
  ...
  def attempt_login
    ...
    if authorized_user
      if request.xhr?
        render :json => {
          :location => url_for(:controller => 'jobs', :action => 'index'),
          :flash => {:notice => "Hello #{authorized_user.name}."}
        }
      else
        redirect_to(:controller => 'jobs', :action => 'index')
      end
    else
      # Render login screen with 422 error code
      render :login, :status => :unprocessable_entity
    end
  end
end

而简单的jQuery示例将是:

$.ajax({
  ...
  type: 'json',
  success: functon(data) {
    data = $.parseJSON(data);
    if (data.location) {
      window.location.href = data.location;
    }
    if (data.flash && data.flash.notice) {
      // Maybe display flash message, etc.
    }
  },
  error: function() {
    // If login fails, sending 422 error code sends you here.
  }
})

1
这里有很多很好的信息。正确正确地使用了render:location,:status选项和xhr?检查。随着越来越多的Web应用程序采用API来为移动应用程序等提供服务,我希望本文中的内容变得更加标准化。绝对有我的支持。好答案
TheJKFever 2015年

18

结合最佳答案:

...
if request.xhr?
  flash[:notice] = "Hello #{authorized_user.name}."
  flash.keep(:notice) # Keep flash notice around for the redirect.
  render :js => "window.location = #{jobs_path.to_json}"
else
...

感谢您的回答,我用了。但是,现在进行测试时,当我尝试以JS形式请求此操作时,它会引发CORS警告:ActionController :: InvalidCrossOriginRequest。您是否知道如何将其集成到测试中?
V.Déhaye16年

1
def redirect_to(options = {}, response_status = {})
  super(options, response_status)
  if request.xhr?
    # empty to prevent render duplication exception
    self.status = nil
    self.response_body = nil
    path = location
    self.location = nil

    render :js => "window.location = #{path.to_json}"
  end
end

0

我不想修改我的控制器操作,所以想到了这个hack:

class ApplicationController < ActionController::Base
  def redirect_to options = {}, response_status = {}
    super

    if request.xhr?
      self.status        = 200
      self.response_body = "<html><body><script>window.location.replace('#{location}')</script></body></html>"
    end
  end
end
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.