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

我正在使用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"} 

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

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

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

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

:todo不在params Hash中,所以params[:todo] nil ,基本上你的Todo实例没有传递给它的属性,因此它为DB中的name列保存nil

你的控制器应该使用参数包装来解决这个问题,这将把所有的params哈希包装成一个params[:todo]子哈希。 所以如果你在你的控制器中有这个:

 class TodosController < ApplicationController wrap_parameters :todo ... end 

在执行操作之前,您的params哈希将转换为以下内容:

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

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

就像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 
Interesting Posts