can.js如何将记录添加到Rails服务器?

时间:2013-04-12 15:27:56

标签: ruby-on-rails canjs

我正在使用Can.js将记录添加到Ruby on Rails服务器上:

var Todo = can.Model({
  findAll : 'GET /todos',
  findOne : 'GET /todos/{id}',
  create  : 'POST /todos',
  update  : 'PUT /todos/{id}',
  destroy : 'DELETE /todos/{id}'
}, {});

Todo.prototype.description = function() {
    return this.name + " and " + this.complete;
};

Todo.findAll({}, function(todos) {
    todos.each(function(todo) {
        console.log(todo.name, todo.complete);
        console.log(todo.description());
    });
});

var todo = new Todo({name: "mow lawn"});

todo.save();

findAll()实际上可以获取所有记录,但save()将插入空白记录。我怀疑它可能是CSRF token,但显示为警告,如果它不想插入记录,它可能根本不会创建任何记录,而不是添加名称为的记录没有? (我也试过var todo = new Todo({name: "mow lawn", complete: true});,结果相同)。

但是Rails服务器在终端上打印出:

Started POST "/todos" for 127.0.0.1 at 2013-04-12 08:16:05 -0700
Processing by TodosController#create as JSON
  Parameters: {"name"=>"mow lawn"}
WARNING: Can't verify CSRF token authenticity
   (0.2ms)  begin transaction
  SQL (0.8ms)  INSERT INTO "todos" ("complete", "created_at", "name", "updated_at") 
VALUES (?, ?, ?, ?)  [["complete", nil], ["created_at", Fri, 12 Apr 2013 15:16:05 UTC 
+00:00], ["name", nil], ["updated_at", Fri, 12 Apr 2013 15:16:05 UTC +00:00]]
   (1.5ms)  commit transaction
Completed 201 Created in 11ms (Views: 1.3ms | ActiveRecord: 2.5ms)

在Chrome开发者工具中,我在网络中看到了:

Request URL:http://localhost:3000/todos
Request Method:POST

part of header:
Content-Type:application/x-www-form-urlencoded; charset=UTF-8

form data:
name:mow lawn

如果我的行动是:

  def create
    p "params[:todo] is", params[:todo]
    @todo = Todo.new(params[:todo])

    respond_to do |format|
      if @todo.save
        format.html { redirect_to @todo, notice: 'Todo was successfully created.' }
        format.json { render json: @todo, status: :created, location: @todo }
      else
        format.html { render action: "new" }
        format.json { render json: @todo.errors, status: :unprocessable_entity }
      end
    end
  end

然后终端将显示:

"params[:todo] is"
nil

即使终端显示标准的Rails日志:

Parameters: {"name"=>"mow lawn"}

2 个答案:

答案 0 :(得分:2)

如Rails控制台输出中所示,您的params哈希如下所示:

Parameters: {"name"=>"mow lawn"}

但您的控制器正在使用以下内容创建一个新的Todo实例:

@todo = Todo.new(params[:todo])

:todo不在params哈希中,因此params[:todo]nil,基本上您的Todo实例没有传递给它的属性,因此它正在保存{{1}数据库中的nil列。

您的控制器应使用parameter wrapping来解决此问题,这会将所有name哈希包装到params子哈希中。所以如果你在你的控制器中有这个:

params[:todo]

在执行操作之前,您的class TodosController < ApplicationController wrap_parameters :todo ... end 哈希将转换为以下内容:

params

然后您现有的操作代码应该有效。

答案 1 :(得分:0)

Stuart M 所说,您需要在控制器中设置wrap_parameters :todo

但是wrap_parameters :todo无法使用默认的Content-Type: application/x-www-form-urlencoded canjs发送模型更新。

以下资源让我解决了这个问题:

https://github.com/bitovi/canjs/issues/111

https://forum.javascriptmvc.com/topic/howto-use-application-json-and-encode-data-automatically-in-can-model

通过阅读那些我改变我的应用程序看起来像这样:

应用/控制器/ todo_controller.rb

class TodosController < ApplicationController
  wrap_parameters :todo

  def create
    Todo.new(params[:todo])
  end

end

应用/资产/ Javascript角/ canjs.models.js

$.ajaxPrefilter(function(options, originalOptions, jqXHR){
    if( options.processData && /^(post|put|patch|delete)$/i.test( options.type ))
    {
        options["contentType"] = "application/json";
        options["data"] = JSON.stringify(originalOptions["data"]);
    }
});

Todo = Model.new({
    ...
});

一切都像魔术一样:)


注意:您可能需要设置CORS才能使用此功能,以下是如何在公共API上进行设置:

<强>配置/ routes.rb中

SomeTodoApp::Application.routes.draw do

  resources :todos, except: [:new, :edit]

  match "*all" => "application#cors_preflight_check", via: :options

end

应用/控制器/ application_controller.rb

class ApplicationController < ActionController::API

# protect_from_forgery is always a good thing if you know your clients can support get it working

  protect_from_forgery

  before_filter :cors_preflight_check
  after_filter :cors_set_access_control_headers

# For all responses in this controller, return the CORS access control headers.

  def cors_set_access_control_headers
    headers['Access-Control-Allow-Origin'] = '*'
    headers['Access-Control-Allow-Methods'] = 'POST, PUT, PATCH, GET, OPTIONS'
    headers['Access-Control-Allow-Headers'] = 'Origin, X-Requested-With, Content-Type, Accept, Authorization'
    headers['Access-Control-Request-Method'] = '*'
    headers['Access-Control-Max-Age'] = "1728000"
  end

# If this is a preflight OPTIONS request, then short-circuit the
# request, return only the necessary headers and return an empty
# text/plain.

  def cors_preflight_check
    if request.method == :options
      headers['Access-Control-Allow-Origin'] = '*'
      headers['Access-Control-Allow-Methods'] = 'POST, PUT, PATCH, GET, OPTIONS'
      headers['Access-Control-Allow-Headers'] = 'Origin, X-Requested-With, Content-Type, Accept, Authorization'
      headers['Access-Control-Request-Method'] = '*'
      headers['Access-Control-Max-Age'] = '1728000'
      render :text => '', :content_type => 'text/plain'
    end
  end

end