Обработка данных JSON в контроллере Rails (сопоставление тела запроса JSON с хэшем параметров)

#ruby-on-rails #json

#ruby-on-rails #json

Вопрос:

Я пытаюсь обработать данные JSON, отправленные на контроллер Rails (3.1.0), путем доступа к данным через хэш параметров. К сожалению, Rails, похоже, не обрабатывает данные автоматически (как следует из документации и других вопросов по StackOverflow).

Контроллер выглядит как:

 class Api::EventsController < ApplicationController
  skip_before_filter :verify_authenticity_token

  def create
    unless params['event']['timestamp'].nil?
      ...
    end
    ...

    render :json => { :status => :success }
  end
end
  

HTTP-запрос выглядит следующим образом (записывается с помощью tcpdump):

 POST /api/locations/1/events HTTP/1.1
Date: Mon, 10 Oct 2011 21:57:22 GMT 00:00
Content-Type: application/json; charset=UTF-8
Accept: application/json
Host: 10.0.2.2:3000
User-Agent: Restlet-Framework/2.1rc1
Authorization: Basic ...
Transfer-Encoding: chunked
Connection: Keep-Alive

4e
{"event":{"data_source":"ANDROID","timestamp":1318283841768,"type":"LEAVING"}}
0
  

В приведенном выше примере params['event'] всегда nil . Использование ActiveSupport::JSON.decode(request.body) для анализа данных JSON работает, но я хотел бы автоматически сопоставить тело запроса с параметрами.

Редактировать: request.params содержит только информацию о маршруте и request.request_parameters полностью пуста.

Чего мне не хватает?

Ответ №1:

Вы уверены, что отправляете правильный запрос? Ссылаясь на RFC 2616, ваш последний фрагмент должен быть пустым, чтобы сообщить получателю об окончании передачи. Просто примите ваш запрос таким образом:

 ...
4e CRLF
{"event":{"data_source":"ANDROID","timestamp":1318283841768,"type":"LEAVING"}} CRLF
0 CRLF
CRLF
  

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

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

Content-Length просто указывает длину данных, которые вы будете отправлять, чтобы получатель знал, когда передача достигла конца.


На всякий случай, если вы просто забыли вставить этот последний фрагмент:

Переопределено?

Вы случайно переопределили некоторые параметры, parameters, request_parameters, request? Почему бы не попробовать

  • request.params для того же значения или

  • request.request_parameters для фактического хэша содержимого {:event => …} без дополнительной отправки

Комментарии:

1. Вы правы насчет завершающего пустого фрагмента. Он фактически отправлен (и завершается дополнительной последовательностью CRLF, поэтому запрос, похоже, в порядке), я просто изначально пропустил его, потому что он передается по проводам в отдельном пакете. Немного сложно изменить запрос, поскольку все это выполняется библиотекой Restlet, поэтому у меня нет контроля над фактическим форматом wire. Я проверю ваши замечания относительно случайно переопределенных методов позже, но я совершенно уверен, что ни один из них не применим (поскольку я вижу базовый хэш параметров, который содержит locations_id , в отладчике).

2. Я также пробовал request.params и request.request_parameters, как вы предложили, но они просто содержат информацию о маршруте или полностью пусты соответственно.

3. Что ж, заглядывая в код синтаксического анализа параметров в params_parser.rb, следующая строка наводит на размышления: return false if request.content_length.zero? Похоже, что фрагментированное кодирование не поддерживается…

4. rack запустил поддержку блоков в 1.0.0 в 2009 году, и похоже, что ActionDispatch не нужно заботиться об этом, потому что промежуточное программное обеспечение запускается при загрузке всего запроса (поправьте меня, если я ошибаюсь). Приведенный вами код, похоже, не связан с тем, что простые запросы GET также не имеют тела запроса (и, следовательно, content_length нуль. Я проверил это)

5. Да, запросы GET не имеют тела, но сообщения имеют. Я наблюдал, как цитируемый код выполняется в отладчике, так что это определенно актуально. Однако код взят из ruby 1.9.2, так что, возможно, JRuby или 1.9.3 устранят этот пробел. Я протестирую и сообщу…