Каждый раз, когда я хочу POST массива объектов JSON с jQuery в Rails, у меня есть эта проблема.
Если я строю массив, я вижу, что jQuery делает свою работу правильно:
"shared_items"=>"[{\"entity_id\":\"253\",\"position\":1},{\"entity_id\":\"823\",\"position\":2}]"
Но если я просто отправлю массив как данные вызова AJAX, я получаю:
"shared_items"=>{"0"=>{"entity_id"=>"253", "position"=>"1"}, "1"=>{"entity_id"=>"823", "position"=>"2"}}
Если я просто отправлю простой массив, он работает:
"shared_items"=>["entity_253"]
Почему Rails меняет массив на этот странный хеш? Единственная причина, которая приходит на ум, состоит в том, что Rails не может правильно понять содержимое, потому что здесь нет типа (есть ли способ установить его в вызове jQuery?):
Processing by SharedListsController#create as
Спасибо!
Update:
Я отправляю данные как массив, а не строку, и массив создается динамически с помощью функции .push()
. Пробовал $.post
и $.ajax
, тот же результат.
Ответ 1
В случае, если кто-то наткнется на это и хочет получить лучшее решение, вы можете указать опцию "contentType:" application/json "в вызове .ajax и иметь Rails правильно проанализировать объект JSON без искажения его в хэшах с целыми ключами со всеми строковыми значениями.
Итак, чтобы подвести итог, моя проблема заключалась в следующем:
$.ajax({
type : "POST",
url : 'http://localhost:3001/plugin/bulk_import/',
dataType: 'json',
data : {"shared_items": [{"entity_id":"253","position":1}, {"entity_id":"823","position":2}]}
});
привело к тому, что Rails разобрал вещи как:
Parameters: {"shared_items"=>{"0"=>{"entity_id"=>"253", "position"=>"1"}, "1"=>{"entity_id"=>"823", "position"=>"2"}}}
тогда как это (ПРИМЕЧАНИЕ: мы теперь стягиваем объект javascript и указываем тип содержимого, поэтому рельсы будут знать, как разбирать нашу строку):
$.ajax({
type : "POST",
url : 'http://localhost:3001/plugin/bulk_import/',
dataType: 'json',
contentType: 'application/json',
data : JSON.stringify({"shared_items": [{"entity_id":"253","position":1}, {"entity_id":"823","position":2}]})
});
приводит к хорошему объекту в Rails:
Parameters: {"shared_items"=>[{"entity_id"=>"253", "position"=>1}, {"entity_id"=>"823", "position"=>2}]}
Это работает для меня в Rails 3, на Ruby 1.9.3.
Ответ 2
Немного старый вопрос, но я воевал с этим сам сегодня, и вот ответ, который я придумал: я считаю, что это слегка ошибка jQuery, но что он делает только то, что естественно для него. Однако у меня есть обходное решение.
Учитывая следующий вызов jQuery ajax:
$.ajax({
type : "POST",
url : 'http://localhost:3001/plugin/bulk_import/',
dataType: 'json',
data : {"shared_items": [{"entity_id":"253","position":1},{"entity_id":"823","position":2}]}
});
Значения, которые jQuery опубликует, будут выглядеть примерно так (если вы посмотрите на запрос в своем Firebug-of-choice), вы получите данные формы, которые выглядят следующим образом:
shared_items%5B0%5D%5Bentity_id%5D:1
shared_items%5B0%5D%5Bposition%5D:1
Если вы CGI.unencode, вы получите
shared_items[0][entity_id]:1
shared_items[0][position]:1
Я считаю, что это связано с тем, что jQuery считает, что эти ключи в вашем JSON являются именами элементов формы и что они должны обрабатывать их так, как если бы у вас было поле с именем "user [name]".
Итак, они входят в ваше приложение Rails, Rails видит скобки и строит хеш для хранения самого внутреннего ключа имени поля ( "1", который "jQuery" добавил ").
В любом случае, я обошел это поведение, построив мой вызов ajax следующим образом:
$.ajax({
type : "POST",
url : 'http://localhost:3001/plugin/bulk_import/',
dataType: 'json',
data : {"data": JSON.stringify({"shared_items": [{"entity_id":"253","position":1},{"entity_id":"823","position":2}])},
}
});
Что заставляет jQuery думать, что этот JSON - это значение, которое вы хотите передать полностью, а не объект Javascript, который он должен принять, и превратить все ключи в имена полей формы.
Однако это означает, что на стороне Rails немного отличается, потому что вам нужно явно декодировать JSON в параметрах [: data].
Но это нормально:
ActiveSupport::JSON.decode( params[:data] )
TL; DR: Итак, решение: в параметре данных для вашего вызова jQuery.ajax(), явно {"data": JSON.stringify(my_object) }
, вместо того, чтобы подавать массив JSON в jQuery (где он неправильно предполагает, что вы хотите с этим сделать.
Ответ 3
Я просто столкнулся с этой проблемой с Rails 4. Чтобы явным образом ответить на ваш вопрос ( "Почему Rails меняет массив на этот странный хеш?" ), см. раздел 4.1 Руководство по Rails для контроллеров действий:
Чтобы отправить массив значений, добавьте пустую пару квадратных скобок "[]" к имени ключа.
Проблема заключается в том, что jQuery форматирует запрос с явными индексами массива, а не с пустыми квадратными скобками. Так, например, вместо отправки shared_items[]=1&shared_items[]=2
он отправляет shared_items[0]=1&shared_items[1]=2
. Rails видит индексы массива и интерпретирует их как хеш-ключи, а не индексы массивов, превращая запрос в странный хэш-код Ruby: { shared_items: { '0' => '1', '1' => '2' } }
.
Если у вас нет контроля над клиентом, вы можете исправить эту проблему на стороне сервера, преобразовая хэш в массив. Вот как я это сделал:
shared_items = []
params[:shared_items].each { |k, v|
shared_items << v
}
Ответ 4
следующий метод может быть полезен, если вы используете сильные параметры
def safe_params
values = params.require(:shared_items)
values = items.values if items.keys.first == '0'
ActionController::Parameters.new(shared_items: values).permit(shared_items: [:entity_id, :position]).require(:shared_items)
end
Ответ 5
Рассматривали ли вы выполнение parsed_json = ActiveSupport::JSON.decode(your_json_string)
? Если вы отправляете материал другим способом, вы можете использовать .to_json
для сериализации данных.
Ответ 6
Вы просто пытаетесь получить строку JSON в действие контроллера Rails?
Я не уверен, что Rails делает с хешем, но вы можете обойти эту проблему и получить больше удачи, создав объект Javascript/JSON (в отличие от строки JSON) и отправив это как параметр данных для вашего вызова Ajax.
myData = {
"shared_items":
[
{
"entity_id": "253",
"position": 1
},
{
"entity_id": "823",
"position": 2
}
]
};
Если вы хотите отправить это через ajax, вы сделаете что-то вроде этого:
$.ajax({
type: "POST",
url: "my_url", // be sure to set this in your routes.rb
data: myData,
success: function(data) {
console.log("success. data:");
console.log(data);
}
});
Обратите внимание на приведенный выше фрагмент ajax, jQuery сделает интеллектуальное предположение о типе dataType, хотя обычно полезно указать его явно.
В любом случае, в действии вашего контроллера вы можете получить объект JSON, который вы передали с помощью хэша params, т.е.
params[:shared_items]
например. это действие вернет вам ваш объект json:
def reply_in_json
@shared = params[:shared_items]
render :json => @shared
end
Ответ 7
Используйте rack-jquery-params gem (отказ от ответственности: я автор). Он исправляет вашу проблему с массивами, становящимися хэшами с целыми ключами.