BackBone-клиент с удаленным Rails-сервером

Фон:

Я использую "rails g scaffold hotel name stars:integer", чтобы начать быстро (и вставить в базу данных несколько записей), и пишу Backbone-клиент вне приложения rails.

Я открываю клиент Backbone локально с помощью файла Safari:///Users/lg/Workspace/www/index.html для тестирования клиента, потому что моя идея состоит в том, чтобы установить сервер 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'
});

Но когда я выбираю отели с магистралью, рельсы отвечают форматом.html, а не форматом.json, который может проанализировать Backbone.

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

это может подделать, но не может сделать другие 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 и измените 0.0.0.0:3000/hotels.json на / hotels, то это работает просто фантастически!! Но если я отделяю клиента от сервера и открываю его с помощью Safari, он требует.json в конце URL-адреса. это очень странно

я открываю вопрос о рельсах на github по адресу https://github.com/rails/rails/issues/5005

3 ответа

Решение

Важно понимать, что в Rails не рекомендуется использовать заголовки Accept. Здесь есть хорошая статья, которая объясняет почему в мельчайших деталях. Резюме - браузерная реализация заголовков HTTP Accept не работает. Лучшей практикой Rails является установка параметра:format в запросе. Однако, если вам нравятся заголовки accept, они поддерживаются, но правила относительно того, что является допустимым заголовком accept в rails, могут быть хитрыми. Если ваш заголовок accept соответствует следующему регулярному выражению:

BROWSER_LIKE_ACCEPTS = /,\s*\*\/\*|\*\/\*\s*,/

затем rails выбрасывает его и по умолчанию использует тип mime text / html. Я точно знаю? Ваш заголовок соответствует этому. Причина, по которой у большинства людей нет этой проблемы, заключается в том, что rails "исправляет" поведение jquery по умолчанию в rails jquery-ujs. Они устанавливают обратный вызов ajaxSetup beforeSend в JQuery, который помещает */* в начале заголовка, это то, что хочет видеть регулярное выражение магических рельсов, хотя я не могу точно сказать, почему, кроме того, что они знают, что неизмененный запрос браузера всегда помещает его туда. Вот как вы можете исправить свой заголовок принятия в JQuery.

$(function() {
  $.ajaxSetup({
    'beforeSend': function(xhr) {
    xhr.setRequestHeader("accept", "application/json");
    }
  });
});

Вы настроили свой код рельсов так, что он требует вызова /hotels.json для того, чтобы вернуть JSON, но ваш базовый код вызывает /hotels только.

Самый простой способ исправить это - иметь отдельный API для данных JSON, чем для HTML-страниц. например: /hotels возвращает HTML и /api/hotels вернуть JSON. см. Railscasts Райана Бэйта для примера этого (платного) http://railscasts.com/episodes/323-backbone-on-rails-part-1

Другой вариант - изменить ваши модели / коллекции Backbone, чтобы они добавляли ".json" в конец ваших URL. например:


Backbone.Model.extend({
  url: function(){
    var url;
    if (this.isNew()){
      url = "/hotels/" + this.id + ".json";
    } else {
      url = "/hotels.json";
    }
    return url;
  }
});

Вероятно, есть и другие варианты. Это только два, которые выпали из моей головы.

Когда ваше приложение javascript работает в другом домене, порте или протоколе, чем ваш сервер, происходят странные вещи. Это называется "Политика единого происхождения". Просто загрузите JavaScript с вашего сервера rails, и все будет хорошо.

Другие вопросы по тегам