2012-08-26 23 views
24

Tengo una aplicación Sinatra y en la mayoría de mis controladores aparece json y se recoge automáticamente en el objeto params. Sin embargo, tengo una acción de publicación que no obtiene los params a menos que haga un truco con un método anterior para extraer los parámetros request.body, los analiza como JSON y los combina en el hash de params.El método params del controlador Sinatra entra en vacío en la solicitud posterior a JSON

Aquí es el controlador, junto con el método de filtro:

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 

La salida veo es básicamente que los parámetros en la acción del controlador en realidad están en allí correctamente. Sin embargo, si comento la llamada anterior, los parámetros están vacíos.

Lo anterior se siente como un hack. Esperaría que esos parámetros entren sin importar qué ... Debo estar haciendo algo mal allí, pero no sé qué es.

Cualquier ayuda sería muy apreciada ...

Respuesta

31

Para responder a esta pregunta, se intenta por primera vez va a tener que mirar a algunas peticiones HTTP (que no son más que simples mensajes telnet '', lo puede ser fácilmente recreado a mano). Primero, ¿qué sucede cuando envía un HTML normal <form>? La solicitud POST tendrá un aspecto muy similar a este (probablemente con algunos parámetros adicionales, pero no tiene que preocuparse de que en este momento):

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

name=JohnDoe 

Escribiendo ese carácter por carácter (en sustitución del /sample-form con lo la URL es para cualquier acción de formulario, y Host con su IP o nombre de host) será lo mismo que su navegador enviaría. Lo importante para aprender de esto es la sintaxis del parámetro: formname=formvalue. Sinatra interpreta el cuerpo de la solicitud POST en el hash params usando esta sintaxis. Por lo tanto, las solicitudes JSON, que son sustancialmente incompatibles con esto, no aparecerán en el hash params debido a esto.

Sin embargo, lo que está haciendo en su bloque before muestra la solución adecuada. Mientras params de lo anterior sería {'name' => 'JohnDoe'}, request.body.read devolverá el cuerpo original, name=JohnDoe.

En sabiendo esto, uno puede llegar a entender por qué su solución 'hacky' funciona: el cuerpo original de la solicitud POST se interpreta JSON.parse, a continuación, se inserta en el vacío params hash. La razón por la que parece hacky es porque params es un intermediario innecesario en este ejemplo. A continuación se debe hacer el trabajo:

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

Sin embargo, el ejercicio de una solución mejor práctica sería o bien sólo responderá si se proporciona contenido JSON, o responder de manera diferente si se envía un formulario estándar. Como se ve en el ejemplo anterior de la solicitud HTTP POST, se identifica un formulario HTML con el tipo MIME application/x-www-form-urlencoded, mientras que JSON se identifica con application/json. Si desea información específica sobre cómo verificar el tipo MIME de la solicitud POST, consulte this question with some great answers para saber cómo hacer esto con Sinatra.

+0

Estoy teniendo un problema similar, pero su solución no funciona: http://stackoverflow.com/posts/43336445/edit –

7

tenido un problema similar: Posting JSON params from java to sinatra service

he encontrado una mejor solución para tratar con él, mediante la adición de un middleware para hacer lo mismo para mí. Utilicé la gema rack-contrib. A continuación se presentan los cambios que hice en mi código:

EDIT: uso git para obtener la versión específica, donde se corrige el problema cuando el tipo de contenido es application/json;charset=UTF-8

Gemfile:

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

config.ru:

use Rack::PostBodyContentTypeParser 

source: http://jaywiggins.com/2010/03/using-rack-middleware-to-parse-json/

+0

Gracias, gran consejo! –

Cuestiones relacionadas