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
所以通过阅读那些我改变我的应用程序看起来像这样:
应用程序/控制器/ 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