Как вы можете заставить Sinatra правильно обрабатывать JSON-запрос, содержащий массив? - PullRequest
0 голосов
/ 25 сентября 2018

Я POST'ing в Синатре с javascript следующим образом:

var payload = {
  cart: [ 
    {qty: 1, product: { name: 'baseball' },
    {qty: 3, product: { name: 'soccer' } 
  ]
}

$.post("/endpoint", payload, submit)

Из Синатры, params выдает следующее:

{"cart" => {
  "0"=>{"qty"=>"1", "product"=> {"name"=>"baseball"} },
  "1"=>{"qty"=>"3", "product"=> {"name"=>"soccer"} }
}}

Как я могу сделать, чтобы параметры превращалисьвместо этого вместо этого?

{ 
  :cart => [
    { :qty => 1, :product => { :name => "baseball" } },
    { :qty => 3, :product => { :name => "soccer" } },
  ]
} 

Ответы [ 3 ]

0 голосов
/ 25 сентября 2018
cart_params = {"cart" => {
  "0"=>{"qty"=>"1", "product"=> {"name"=>"baseball"} },
  "1"=>{"qty"=>"3", "product"=> {"name"=>"soccer"} }
}}

> hash = JSON.parse(cart_params.to_json, symbolize_names: true)
> hash[:cart] = hash[:cart].values
> hash
#=> {
#    :cart=>[
#            {:qty=>"1", :product=>{:name=>"baseball"}}, 
#            {:qty=>"3", :product=>{:name=>"soccer"}}
#          ]
#   }
0 голосов
/ 26 сентября 2018

Первое, что вам нужно понять, чтобы решить эту проблему, это то, что вы в настоящее время не отправляете JSON.JQuery сериализует данные как application/x-www-form-urlencoded, используя функцию param() , и отправляет (фактически отправляет экранированную версию этого):

cart[0][qty]=1&cart[0][product][name]=baseball&cart[1][qty]=3&cart[1][product][name]=soccer

Документация дляparam() имеет следующую пару примечаний:

Примечание. Поскольку некоторые платформы имеют ограниченную возможность разбора сериализованных массивов, разработчики должны соблюдать осторожность при передаче аргумента obj, содержащего объекты или массивы, вложенные в другой массив..

и

Примечание. Поскольку не существует универсально согласованной спецификации для строк параметров, невозможно кодировать сложные структуры данных с использованием этого метода таким образом,это идеально работает на всех языках, поддерживающих такой ввод.Вместо этого используйте формат JSON в качестве альтернативы для кодирования сложных данных.

Это проблема, с которой вы столкнулись.Sinatra пытается проанализировать данные, закодированные в форме, в структуру данных Ruby, но ожидает другой формат.Он использует Rack::Utils.parse_nested_query, который выдает параметры, которые вы видите в этой строке запроса.

Как и предполагается в документации jQuery, решение состоит в том, чтобы отправить данные в формате JSON.Это гарантирует, что структура данных не будет искажена между браузером и сервером.

Первый шаг - заставить браузер отправлять JSON.Вы можете сделать это с другой формой функции post(), указав тип содержимого и преобразовав данные в строку JSON:

$.post({url: "/endpoint", data: JSON.stringify(payload), contentType: "application/json", success: submit});

По умолчанию Sinatra будет игнорировать любой запрос с типом содержимого JSONи предоставьте вам обработку, поэтому другая половина решения заключается в том, чтобы Sinatra проанализировала JSON и, при необходимости, заполнила хэш параметров.

Один из способов сделать это - использовать модуль Rack contrib PostBodyContentTypeParser.Просто добавьте в свое приложение следующее (сначала вам нужно будет установить гем rack-contrib):

require 'rack/contrib/post_body_content_type_parser'

use Rack::PostBodyContentTypeParser

Теперь любой запрос POST или PUT, имеющий тип содержимого JSON, будет проанализирован, и содержимое добавлено вхэш параметров.Однако в текущей выпущенной версии нет способа конвертировать ключи в символы (в мастере есть изменение, позволяющее это сделать, но оно еще не выпущено), поэтому будет производиться что-то вроде:

{
  "cart"=>[
    {"qty"=>1, "product"=>{"name"=>"baseball"}},
    {"qty"=>3, "product"=>{"name"=>"soccer"}}
  ]
}

Если вы хотите получить символизированные ключи, вам придется делать это «вручную».В вашем маршруте вы можете добавить:

request.body.rewind # Just in case some middleware has already read it
data = JSON.parse(request.body.read, symbolize_names: true)

(Вы можете добавить это к фильтру before, если хотите, просто добавьте проверку, что тип контента на самом деле - JSON).

Это не добавляет данные в хэш params, но дает данные в форме, которую вы ищете.

0 голосов
/ 25 сентября 2018

У меня была такая же проблема некоторое время назад, и я создал gem, делающий это (любые другие вещи, упрощающие итерацию / отображение глубоко вложенных структур).Установите флажок Iteraptor.

json = {"cart" => {
  "0"=>{"qty"=>"1", "product"=> {"name"=>"baseball"} },  
  "1"=>{"qty"=>"3", "product"=> {"name"=>"soccer"} }  
}}
json.aplanar.recoger(symbolize_keys: true)
#⇒ {:cart=>[
#    {:qty=>"1", :product=>{:name=>"baseball"}},
#    {:qty=>"3", :product=>{:name=>"soccer"}}]}

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...