Rails 4,Ajax 导致 404 和 500 错误

Posted

技术标签:

【中文标题】Rails 4,Ajax 导致 404 和 500 错误【英文标题】:Rails 4, Ajax causing a 404 and 500 error 【发布时间】:2016-02-22 05:19:54 【问题描述】:

我正在关注 Michael Hartl 的 Rails 教程,Section 12.2.5,在完全按照他的教程进行操作后,我遇到了一个奇怪的 404/500 错误。

我对这个特定错误知之甚少,不知道如何修复它。

尝试从标准的关注和取消关注按钮切换到启用 Ajax 的关注/取消关注按钮时会发生错误。

关注按钮只会产生重复的 500 错误。 (见图)

单击“关注”按钮后,服务器控制台如下所示:

Started POST "/relationships" for ::1 at 2015-11-21 10:42:41 -0500
Processing by RelationshipsController#create as JS
  Parameters: "utf8"=>"✓", "authenticity_token"=>"nUhePXUdztraIcRVmQ6ewMjzLBPLbCJKjvGUExoQu7XH+zTJYj9J/+wopP7kAI1ycAa5t0bG7fEuApgAQqcDDw==", "followed_id"=>"4", "commit"=>"Follow"
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1  [["id", 1]]
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1  [["id", 4]]
   (0.1ms)  begin transaction
  SQL (0.5ms)  INSERT INTO "relationships" ("followed_id", "follower_id", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["followed_id", 4], ["follower_id", 1], ["created_at", "2015-11-21 15:42:41.359403"], ["updated_at", "2015-11-21 15:42:41.359403"]]
   (3.4ms)  commit transaction
  Rendered users/_unfollow.html.erb (7.6ms)
  Rendered relationships/create.js.erb (10.4ms)
Completed 500 Internal Server Error in 35ms (ActiveRecord: 4.6ms)

NoMethodError (undefined method `id' for nil:NilClass):
  app/views/users/_unfollow.html.erb:1:in `_app_views_users__unfollow_html_erb__4478734189885612533_2206022220'
  app/views/relationships/create.js.erb:1:in `_app_views_relationships_create_js_erb___3841425439351308004_2234925000'
  app/controllers/relationships_controller.rb:8:in `create'

取消关注按钮在第一次点击时会产生 500 错误,然后再按一次 404 错误。 (见图)

单击取消关注按钮后,服务器控制台如下所示:

Started DELETE "/relationships/106" for ::1 at 2015-11-21 10:44:16 -0500
Processing by RelationshipsController#destroy as JS
  Parameters: "utf8"=>"✓", "authenticity_token"=>"MQQWiFHmikzre6tgsaVNf25z1r4Ps4yMhiX6BVAHvWFrt3x8RsQNad1yy8vMq17N1oZDGoIZQzcm1vYWCLAF2w==", "commit"=>"Unfollow", "id"=>"106"
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1  [["id", 1]]
  Relationship Load (0.2ms)  SELECT  "relationships".* FROM "relationships" WHERE "relationships"."id" = ? LIMIT 1  [["id", 106]]
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1  [["id", 4]]
  Relationship Load (0.6ms)  SELECT  "relationships".* FROM "relationships" WHERE "relationships"."follower_id" = ? AND "relationships"."followed_id" = ? LIMIT 1  [["follower_id", 1], ["followed_id", 4]]
   (0.4ms)  begin transaction
  SQL (1.3ms)  DELETE FROM "relationships" WHERE "relationships"."id" = ?  [["id", 106]]
   (571.2ms)  commit transaction
  Rendered users/_follow.html.erb (7.3ms)
  Rendered relationships/destroy.js.erb (10.0ms)
Completed 500 Internal Server Error in 608ms (ActiveRecord: 574.0ms)

NoMethodError (undefined method `id' for nil:NilClass):
  app/views/users/_follow.html.erb:2:in `block in _app_views_users__follow_html_erb__4224173111585839618_2218160400'
  app/views/users/_follow.html.erb:1:in `_app_views_users__follow_html_erb__4224173111585839618_2218160400'
  app/views/relationships/destroy.js.erb:1:in `_app_views_relationships_destroy_js_erb___1536758446230344516_2256927580'
  app/controllers/relationships_controller.rb:18:in `destroy'

在 Chrome 开发者工具中,显示触发错误的代码如下:

xhr.send( ( options.hasContent && options.data ) || null );

我将在下面将问题分为两部分...在添加启用 ajax 的按钮之前的工作原理以及开始产生错误的代码添加。


工作正常:标准按钮

这是在切换到 Ajax 之前的工作原理:

relationships_controller.rb

class RelationshipsController < ApplicationController
  before_action :logged_in_user

  def create
    user = User.find(params[:followed_id])
    current_user.follow(user)
    redirect_to user
  end

  def destroy
    user = Relationship.find(params[:id]).followed
    current_user.unfollow(user)
    redirect_to user
  end
end

_follow.html.erb

<%= form_for(current_user.active_relationships.build) do |f| %>
  <div><%= hidden_field_tag :followed_id, @user.id %></div>                        
  <%= f.submit "Follow", class: "btn btn-primary" %>
<% end %>

_unfollow.html.erb

<%= form_for(current_user.active_relationships.find_by(followed_id: @user.id),
             html:  method: :delete ) do |f| %>
  <%= f.submit "Unfollow", class: "btn" %>
<% end %>

所有测试都通过。 following_test.rb

require 'test_helper'

class FollowingTest < ActionDispatch::IntegrationTest

  def setup
    @user  = users(:michael)
    @other = users(:archer)
    log_in_as(@user)
  end

  test "following page" do
    get following_user_path(@user)
    assert_not @user.following.empty?
    assert_match @user.following.count.to_s, response.body
    @user.following.each do |user|
      assert_select "a[href=?]", user_path(user)
    end
  end

  test "followers page" do
    get followers_user_path(@user)
    assert_not @user.followers.empty?
    assert_match @user.followers.count.to_s, response.body
    @user.followers.each do |user|
      assert_select "a[href=?]", user_path(user)
    end
  end

  test "should follow a user the standard way" do
    assert_difference '@user.following.count', 1 do
      post relationships_path, followed_id: @other.id
    end
  end

  test "should follow a user with Ajax" do
    assert_difference '@user.following.count', 1 do
      xhr :post, relationships_path, followed_id: @other.id
    end
  end

  test "should unfollow a user the standard way" do
    @user.follow(@other)
    relationship = @user.active_relationships.find_by(followed_id: @other.id)
    assert_difference '@user.following.count', -1 do
      delete relationship_path(relationship)
    end
  end

  test "should unfollow a user with Ajax" do
    @user.follow(@other)
    relationship = @user.active_relationships.find_by(followed_id: @other.id)
    assert_difference '@user.following.count', -1 do
      xhr :delete, relationship_path(relationship)
    end
  end
end

不工作:AJAX 按钮

然后在教程的 Section 12.2.5 中,我们被告知像这样切换以下文件以启用 Ajax 关注/取消关注按钮:

_follow.html.erb

<%= form_for(current_user.active_relationships.build, remote: true) do |f| %>
  <div><%= hidden_field_tag :followed_id, @user.id %></div>
  <%= f.submit "Follow", class: "btn btn-primary" %>
<% end %>

_unfollow.html.erb

<%= form_for(current_user.active_relationships.find_by(followed_id: @user.id),
             html:  method: :delete ,
             remote: true) do |f| %>
  <%= f.submit "Unfollow", class: "btn" %>
<% end %>

relationships_controller.rb

class RelationshipsController < ApplicationController
  before_action :logged_in_user

  def create
    @user = User.find(params[:followed_id])
    current_user.follow(@user)
    respond_to do |format|
      format.html  redirect_to @user 
      format.js
    end
  end

  def destroy
    @user = Relationship.find(params[:id]).followed
    current_user.unfollow(@user)
    respond_to do |format|
      format.html  redirect_to @user 
      format.js
    end
  end
end

创建以下两个文件:

create.js.erb

$("#follow_form").html("<%= escape_javascript(render('users/unfollow')) %>");
$("#followers").html('<%= @user.followers.count %>');

destroy.js.erb

$("#follow_form").html("<%= escape_javascript(render('users/follow')) %>");
$("#followers").html('<%= @user.followers.count %>');

作者比说:

这样,您应该导航到用户个人资料页面并验证 您无需刷新页面即可关注和取消关注。

没有骰子。

运行bundle exec rake test时出现4个测试错误:

ERROR["test_should_unfollow_a_user_with_Ajax", FollowingTest, 2015-11-13 14:40:53 -0500]
 test_should_unfollow_a_user_with_Ajax#FollowingTest (1447443653.75s)
ActionView::Template::Error:         ActionView::Template::Error: undefined method `id' for nil:NilClass
            app/views/users/_follow.html.erb:2:in `block in _app_views_users__follow_html_erb___290914909715466310_2191384660'
            app/views/users/_follow.html.erb:1:in `_app_views_users__follow_html_erb___290914909715466310_2191384660'
            app/views/relationships/destroy.js.erb:1:in `_app_views_relationships_destroy_js_erb___2680666598962148369_2191775880'
            app/controllers/relationships_controller.rb:18:in `destroy'
            test/integration/following_test.rb:53:in `block (2 levels) in <class:FollowingTest>'
            test/integration/following_test.rb:52:in `block in <class:FollowingTest>'
        app/views/users/_follow.html.erb:2:in `block in _app_views_users__follow_html_erb___290914909715466310_2191384660'
        app/views/users/_follow.html.erb:1:in `_app_views_users__follow_html_erb___290914909715466310_2191384660'
        app/views/relationships/destroy.js.erb:1:in `_app_views_relationships_destroy_js_erb___2680666598962148369_2191775880'
        app/controllers/relationships_controller.rb:18:in `destroy'
        test/integration/following_test.rb:53:in `block (2 levels) in <class:FollowingTest>'
        test/integration/following_test.rb:52:in `block in <class:FollowingTest>'

ERROR["test_should_follow_a_user_with_Ajax", FollowingTest, 2015-11-13 14:40:53 -0500]
 test_should_follow_a_user_with_Ajax#FollowingTest (1447443653.81s)
ActionView::Template::Error:         ActionView::Template::Error: undefined method `id' for nil:NilClass
            app/views/users/_unfollow.html.erb:1:in `_app_views_users__unfollow_html_erb__1207823279820701916_2213161380'
            app/views/relationships/create.js.erb:1:in `_app_views_relationships_create_js_erb__279097861293830543_2213276120'
            app/controllers/relationships_controller.rb:8:in `create'
            test/integration/following_test.rb:37:in `block (2 levels) in <class:FollowingTest>'
            test/integration/following_test.rb:36:in `block in <class:FollowingTest>'
        app/views/users/_unfollow.html.erb:1:in `_app_views_users__unfollow_html_erb__1207823279820701916_2213161380'
        app/views/relationships/create.js.erb:1:in `_app_views_relationships_create_js_erb__279097861293830543_2213276120'
        app/controllers/relationships_controller.rb:8:in `create'
        test/integration/following_test.rb:37:in `block (2 levels) in <class:FollowingTest>'
        test/integration/following_test.rb:36:in `block in <class:FollowingTest>'

ERROR["test_should_follow_a_user_the_standard_way", FollowingTest, 2015-11-13 14:40:53 -0500]
 test_should_follow_a_user_the_standard_way#FollowingTest (1447443653.86s)
ActionController::ActionControllerError:         ActionController::ActionControllerError: Cannot redirect to nil!
            app/controllers/relationships_controller.rb:9:in `block (2 levels) in create'
            app/controllers/relationships_controller.rb:8:in `create'
            test/integration/following_test.rb:31:in `block (2 levels) in <class:FollowingTest>'
            test/integration/following_test.rb:30:in `block in <class:FollowingTest>'
        app/controllers/relationships_controller.rb:9:in `block (2 levels) in create'
        app/controllers/relationships_controller.rb:8:in `create'
        test/integration/following_test.rb:31:in `block (2 levels) in <class:FollowingTest>'
        test/integration/following_test.rb:30:in `block in <class:FollowingTest>'

ERROR["test_should_unfollow_a_user_the_standard_way", FollowingTest, 2015-11-13 14:40:54 -0500]
 test_should_unfollow_a_user_the_standard_way#FollowingTest (1447443654.06s)
ActionController::ActionControllerError:         ActionController::ActionControllerError: Cannot redirect to nil!
            app/controllers/relationships_controller.rb:19:in `block (2 levels) in destroy'
            app/controllers/relationships_controller.rb:18:in `destroy'
            test/integration/following_test.rb:45:in `block (2 levels) in <class:FollowingTest>'
            test/integration/following_test.rb:44:in `block in <class:FollowingTest>'
        app/controllers/relationships_controller.rb:19:in `block (2 levels) in destroy'
        app/controllers/relationships_controller.rb:18:in `destroy'
        test/integration/following_test.rb:45:in `block (2 levels) in <class:FollowingTest>'
        test/integration/following_test.rb:44:in `block in <class:FollowingTest>'

  76/76: [==================================] 100% Time: 00:00:10, Time: 00:00:10

Finished in 10.29205s
76 tests, 312 assertions, 0 failures, 4 errors, 0 skips

官方教程的github在这里:https://github.com/mhartl/sample_app_3rd_edition

【问题讨论】:

【参考方案1】:

我有这个确切的问题。虽然您的 sn-ps 反映了这种变化,但对我来说,问题是 Hartl 没有对他在 relationships_controller.rb 中所做的关键更改之一进行 sn-p 突出显示(他确实在文本中谈到了它,我正在略读教程中的这一点)。

变化是user从常规变量变为实例变量。所以:

user = User.find(params[:followed_id])

变成

@user = User.find(params[:followed_id])

添加该更改后,ActionView::Template::Error: undefined method 'id' for nil:NilClass 错误就解决了。

【讨论】:

以上是关于Rails 4,Ajax 导致 404 和 500 错误的主要内容,如果未能解决你的问题,请参考以下文章

Rails 4:通过ajax检查优惠券

AJAX、CORS、Chrome 和 HTTP 错误代码 (401,403,404,500) 的推荐解决方案

尝试访问损坏的图片 url 时抛出内部服务器错误 500 而不是 404

Rails 500 服务器错误,对 form_for 部分的 Ajax 请求

URL Rewrite 导致404,500错误

设计 Ajax 404 路由错误