Подтвердить что ты не робот

Метод параметров контроллера Sinatra, который пуст по запросу JSON

У меня есть приложение Sinatra, и в большинстве моих контроллеров json приходит и автоматически выбирается в объекте params. Тем не менее, у меня есть пост-действие, которое вообще не получает параметров, если я не сыграю трюк с помощью метода before, чтобы вытащить параметры request.body, проанализировав их как JSON и объединив их в хэши params.

Вот контроллер вместе с методом фильтра:

before do
  if request.request_method == "POST"
    body_parameters = request.body.read
    params.merge!(JSON.parse(body_parameters))
  end
end


post '/locations/new' do
  content_type :json
  puts "params after post params method = #{params.inspect}"
  ... other code ...
end

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

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

Любая помощь будет глубоко оценена...

4b9b3361

Ответ 1

Чтобы ответить на этот вопрос, нам сначала нужно посмотреть некоторые HTTP-запросы (которые являются не более чем просто telnet 'сообщениями, это можно легко воссоздать вручную). Во-первых, что происходит, когда вы отправляете обычный HTML <form>? Запрос POST будет очень похож на этот (возможно, с некоторыми дополнительными параметрами, но нам не нужно беспокоиться об этом прямо сейчас):

POST /submit-form HTTP/1.1
Host: localhost
Content-Type: application/x-www-form-urlencoded
Content-Length: 12

name=JohnDoe

Ввод этого символа-символа (замена /sample-form на любой URL-адрес для любого действия формы и Host на ваш IP-адрес или имя хоста) будет тем же, что и ваш браузер. Важное значение для этого - синтаксис параметра: formname=formvalue. Sinatra интерпретирует тело запроса POST в хеше params, используя этот синтаксис!. Поэтому запросы JSON, будучи в значительной степени несовместимыми с этим, будут отображаться not из-за этого хеш params.

Однако то, что вы делаете в своем блоке before, показывает правильное решение. В то время как params из приведенного выше будет {'name' => 'JohnDoe'}, request.body.read вернет исходное тело, name=JohnDoe.

Зная это, можно понять, почему ваше "хакерское" решение работает: исходное тело запроса POST интерпретируется JSON.parse, а затем вставляется в пустой хеш params. Причина, по которой это кажется взломанным, состоит в том, что params является ненужным посредником в этом примере. Следующее должно выполнить эту задачу:

post '/locations/new' do
    @json = JSON.parse(request.body.read)
    # @json now contains a hash of the submitted JSON content
end

Однако решение, использующее лучшую практику, будет либо отвечать только в случае предоставления контента JSON, либо отвечать по-другому, если стандартная форма будет отправлена. Как видно из вышеприведенного примера HTTP POST запроса, HTML-форма идентифицируется с типом application/x-www-form-urlencoded MIME, тогда как JSON идентифицируется с помощью application/json. Если вам нужна спецификация проверки типа запроса POST MIME, проверьте этот вопрос с некоторыми замечательными ответами о том, как это сделать с Sinatra!

Ответ 2

Была похожая проблема: Отправка параметров JSON из java в службу sinatra

Я нашел лучшее решение для этого, добавив промежуточное программное обеспечение, чтобы сделать то же самое для меня. Я использовал жемчужину Contribute Gem. Ниже приведены изменения, которые я сделал в своем коде:

ОБНОВЛЕНИЕ: использовать git, чтобы получить конкретную версию, где она устраняет проблему, когда тип контента application/json;charset=UTF-8

Gemfile:

gem 'rack-contrib', git: '[email protected]:rack/rack-contrib', ref: 'b7237381e412852435d87100a37add67b2cfbb63'

config.ru:

use Rack::PostBodyContentTypeParser

источник: http://jaywiggins.com/2010/03/using-rack-middleware-to-parse-json/

Ответ 3

Поздно на вечеринку, но если кому-то еще это нужно -

Чтобы добавить к goyalankit ответ: если вы попытаетесь проверить это (например, с использованием RSpec), оно, вероятно, не будет работать, потому что стандартная настройка теста не использует промежуточное программное обеспечение.

Чтобы также использовать промежуточное ПО в тестах:

# spec_helper.rb

OUTER_APP = Rack::Builder.parse_file("config.ru").first

module RSpecMixin
  include Rack::Test::Methods
  def app
    OUTER_APP # typically this might just be Sinatra::Application
  end
end

RSpec.configure do |config|
  config.include RSpecMixin
end

И пример использования:

it 'is ok' do
  post '/', { key: 'value' }.to_json, { 'CONTENT_TYPE' => 'application/json' }
  expect(last_response).to be_ok
end

И мой config.ru:

require 'rack/contrib'
require './app'

use Rack::PostBodyContentTypeParser
run Sinatra::Application