背景:
我使用“rails gscaffold hotel name Stars:integer”来快速启动(并向数据库中插入一些记录),
并在 Rails 应用程序之外编写一个 Backbone 客户端。
我使用 Safari file:///Users/lg/Workspace/www/index.html 在本地打开 Backbone 客户端来测试客户端,因为我的想法是将 Rails 服务器放在主机(例如 Heroku)上并插入 Backbone 客户端进入 PhoneGap 应用程序。
我的骨干客户端只有几行:
Hotel = Backbone.Model.extend({
initialize: function(){
console.log("initialize Hotel")
}
});
Hotels = Backbone.Collection.extend({
model: Hotel,
url: 'http://0.0.0.0:3000/hotels'
});
但是当我获取具有骨干的酒店时,rails 使用 format.html 进行响应,而不是 Backbone 可以解析的 format.json 。
hotels_controller.rb
# GET /hotels
# GET /hotels.json
def index
@hotels = Hotel.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: @hotels }
end
end
Safari 检查器控制台:
hotels = new Hotels()
Object
hotels.fetch()
Object
hotels.length
0
Request URL:http://0.0.0.0:3000/hotels
Request method:GET
Status code:200 OK
Request Headers
Accept:application/json, text/javascript, */*; q=0.01
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/534.53.11 (KHTML, like Gecko) Version/5.1.3 Safari/534.53.10
Response Headers
Cache-Control:max-age=0, private, must-revalidate
Connection:Keep-Alive
Content-Length:2233
Content-Type:text/html; charset=utf-8
Date:Sat, 11 Feb 2012 14:31:52 GMT
Etag:"606da2b7c21ca96c9d71aabccdd439e9"
Server:WEBrick/1.3.1 (Ruby/1.9.2/2011-07-09)
编辑:
将 url 设置为 url 进行更新:“http://0.0.0.0:3000/hotels.json
它可以 fatch 但不能使其他人 CRUD (示例 PUT)
hotels = new Hotels()
Object
hotels.fetch()
Object
hotels.length
5
hotel = hotels.get(2)
Object
hotel.set({name: "name 2"})
Object
hotel.save()
Object
PUT http://0.0.0.0:3000/hotels.json/2 404 (Not Found)
相反,如果我仅设置 /hotels 它可以工作(但骨干客户端必须驻留在服务器上)
编辑2:
将代码上传到github
https://github.com/RevH/backbonefails
编辑 3:
另一个细节是,如果您将backboneclient目录插入Rails public目录并将 0.0.0.0:3000/hotels.json 更改为 /hotels ,效果非常好!但是如果我将客户端与服务器分开并使用 Safari 打开它,则在 url 末尾需要 .json 。
我在 github 上打开了一个 Rails 问题 https://github.com/rails/rails/issues/5005
Background:
I use "rails g scaffold hotel name stars:integer" to start fast (and insert into the database some records),
and write a Backbone client outside the rails app.
I open the Backbone client locally with Safari file:///Users/lg/Workspace/www/index.html for testing the client because my idea is to put the rails server on a host (ex. Heroku) and insert the Backbone client into a PhoneGap App.
My backbone client is only few lines:
Hotel = Backbone.Model.extend({
initialize: function(){
console.log("initialize Hotel")
}
});
Hotels = Backbone.Collection.extend({
model: Hotel,
url: 'http://0.0.0.0:3000/hotels'
});
But when i fetch hotels with backbone, rails responds with the format.html and not the format.json which Backbone can parse.
hotels_controller.rb
# GET /hotels
# GET /hotels.json
def index
@hotels = Hotel.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: @hotels }
end
end
Safari inspector console:
hotels = new Hotels()
Object
hotels.fetch()
Object
hotels.length
0
Request URL:http://0.0.0.0:3000/hotels
Request method:GET
Status code:200 OK
Request Headers
Accept:application/json, text/javascript, */*; q=0.01
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/534.53.11 (KHTML, like Gecko) Version/5.1.3 Safari/534.53.10
Response Headers
Cache-Control:max-age=0, private, must-revalidate
Connection:Keep-Alive
Content-Length:2233
Content-Type:text/html; charset=utf-8
Date:Sat, 11 Feb 2012 14:31:52 GMT
Etag:"606da2b7c21ca96c9d71aabccdd439e9"
Server:WEBrick/1.3.1 (Ruby/1.9.2/2011-07-09)
EDIT:
Updated with url set to url: "http://0.0.0.0:3000/hotels.json
it can fatch but can't make others CRUD (example PUT)
hotels = new Hotels()
Object
hotels.fetch()
Object
hotels.length
5
hotel = hotels.get(2)
Object
hotel.set({name: "name 2"})
Object
hotel.save()
Object
PUT http://0.0.0.0:3000/hotels.json/2 404 (Not Found)
Instead if i set only /hotels it works (but the backbone client must reside on the server)
EDIT 2:
uploaded the code on github
https://github.com/RevH/backbonefails
EDIT 3:
Another details is if you insert the backboneclient directory into Rails public directory and change 0.0.0.0:3000/hotels.json to /hotels it works fantastic!! But if i separate the client from server and open it with Safari it require .json at the end of the url. this is very strange.
i open a rails issue on github at https://github.com/rails/rails/issues/5005
发布评论
评论(3)
需要理解的重要一点是,Rails 中的最佳实践不是使用 Accept 标头。 这里有一篇很好的文章,详细解释了原因。摘要:HTTP Accept 标头的浏览器实现已损坏。 Rails 最佳实践是在请求中设置 :format 参数。但是,如果您喜欢接受标头,它们是受支持的,但有关 Rails 中有效接受标头的规则可能很棘手。如果您的接受标头与以下正则表达式匹配:
则rails会将其丢弃并默认为text/html mime类型。我知道,对吧?你的标题恰好与此匹配。大多数人没有这个问题的原因是rails“修复”了rails jquery-ujs.他们在 JQuery 中设置了 ajaxSetup beforeSend 回调,将
*/*
放在标头的开头,这就是 magic Rails 正则表达式想要看到的内容,尽管我无法真正说出原因,除了它们知道未经修改的浏览器请求总是会将其放在那里。以下是修复 JQuery 中接受标头的方法。The important thing to understand is that best practice in Rails is NOT to use the Accept headers. There is a nice writeup here that explains why in glorious detail. Summary is browser implementation of HTTP Accept headers is broken. Rails best practice is to set the :format parameter in the request. However, if you like the accept headers they are supported but the rules around what is a valid accept header in rails can be tricky. If your accept header matches the following regex:
then rails throws it away and defaults to text/html mime type. I know, right? Your header happens to match this. The reason most people don't have this problem is that rails "fixes" the default jquery behavior in the rails jquery-ujs. They set a ajaxSetup beforeSend callback in JQuery that puts the
*/*
at the beginning of the header, which is what the magic rails regex wants to see though I can't really tell why, other than they know that an unmodified browser request will always put it there. Here is how you might fix your accept header in JQuery.您已设置 Rails 代码,以便需要调用
/hotels.json
才能返回 json,但您的骨干代码仅调用/hotels
。解决此问题的最简单方法是为 json 数据(而不是 html 页面)提供单独的 api。例如:
/hotels
返回 html,/api/hotels
返回 json。有关示例,请参阅 Ryan Bate 的 Railscasts(付费)http://railscasts .com/episodes/323-backbone-on-rails-part-1另一种选择是更改您的 Backbone 模型/集合,以便将“.json”附加到你的网址结尾。例如:
可能还有其他选择。这只是我突然想到的两个。
you've set up your rails code so that it requires a call to
/hotels.json
in order to return json, but your backbone code is calling/hotels
only.the easiest way to fix this is to have a separate api for json data, than for html pages. for example:
/hotels
returns html and/api/hotels
return json. see Ryan Bate's Railscasts for an example of this (paid) http://railscasts.com/episodes/323-backbone-on-rails-part-1another option would be to change your Backbone models / collections so that they append the ".json" to the end of your urls. for example:
There are likely other options as well. These are just two that came up off the top of my head.
当你的 JavaScript 应用程序运行在与你的服务器不同的域、端口或协议上时,就会发生奇怪的事情。这称为同源策略。只需从 Rails 服务器加载 JavaScript,一切都会好起来的。
When your javascript application runs on a different domain, port or protocol than your server, strange things will happen. This is called Same Origin Policy. Just load the javascript from your rails server and everything will be fine.