with tutorial respond rails parse example ruby-on-rails json

ruby on rails - tutorial - Rails convierte arreglos vacíos en nils en params de la solicitud



rails parse json file (7)

Tengo un modelo Backbone en mi aplicación que no es un objeto plano típico, es un gran objeto anidado y almacenamos las partes anidadas en columnas TEXT en una base de datos MySQL.

Quería manejar la codificación / decodificación JSON en la API de Rails para que desde el exterior parezca que puedes POST / GET este gran objeto JSON anidado incluso si partes de él están almacenadas como texto JSON en cadena.

Sin embargo, me encontré con un problema donde Rails convierte mágicamente matrices vacías a valores nil . Por ejemplo, si PUBLICO esto:

{ name: "foo", surname: "bar", nested_json: { complicated: [] } }

El controlador My Rails ve esto:

{ :name => "foo", :surname => "bar", :nested_json => { :complicated => nil } }

Y entonces mis datos JSON han sido alterados ...

¿Alguien se ha encontrado con este problema antes? ¿Por qué Rails estaría modificando mis datos de POST?

ACTUALIZAR

Aquí es donde lo hacen:

https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/http/request.rb#L288

Y aquí está ~ por qué lo hacen:

https://github.com/rails/rails/pull/8862

Así que ahora la pregunta es, ¿cómo lidiar mejor con esto en mi situación API JSON anidada?


Aquí está (creo) una solución razonable que no implica volver a analizar el cuerpo de solicitud sin procesar. Puede que esto no funcione si su cliente está PUBLICANDO datos de formulario, pero en mi caso estoy PUBLICANDO JSON.

en application_controller.rb :

# replace nil child params with empty list so updates occur correctly def fix_empty_child_params resource, attrs attrs.each do |attr| params[resource][attr] = [] if params[resource].include? attr and params[resource][attr].nil? end end

Entonces en tu controlador ...

before_action :fix_empty_child_params, only: [:update] def fix_empty_child_params super :user, [:child_ids, :foobar_ids] end

Me encontré con esto y en mi situación, si un recurso POST contiene child_ids: [] o child_ids: nil , quiero que esa actualización signifique "eliminar a todos los niños". Si el cliente tiene la intención de no actualizar la lista child_ids entonces no se debe enviar en el cuerpo POST, en cuyo caso params[:resource].include? attr params[:resource].include? attr será false y los parámetros de solicitud no se modificarán.


Aquí hay una forma de evitar este problema.

def fix_nils obj # fixes an issue where rails turns [] into nil in json data passed to params case obj when nil return [] when Array return obj.collect { |x| nils_to_empty_arrays x } when Hash newobj = {} obj.each do |k,v| newobj[k] = nils_to_empty_arrays v end return newobj else return obj end end

Y luego solo hazlo

fixed_params = fix_nils params

que funciona siempre que no tengas nils en tus params a propósito.


Después de mucha búsqueda, descubrí que comenzando en Rails 4.1 puede omitir la "característica" de deep_munge usando completamente

config.action_dispatch.perform_deep_munge = false

No pude encontrar ninguna documentación, pero puede ver la introducción de esta opción aquí: https://github.com/rails/rails/commit/e8572cf2f94872d81e7145da31d55c6e1b074247

Existe un posible riesgo de seguridad al hacerlo, documentado aquí: https://groups.google.com/forum/#!topic/rubyonrails-security/t1WFuuQyavI


Me encontré con un problema similar y descubrí que al pasar una matriz con una cadena vacía, Rails procesó correctamente, como se mencionó anteriormente. Si se encuentra con esto al enviar un formulario, es posible que desee incluir un campo oculto vacío que coincida con el parámetro de matriz:

<input type="hidden" name="model[attribute_ids][]"/>

Cuando el parámetro real está vacío, el controlador siempre verá una matriz con una cadena vacía, manteniendo así el envío sin estado.


Me encontré con un problema similar.

Se corrigió enviando una cadena vacía como parte de la matriz.

Entonces, idealmente tus params deberían gustar

{ name: "foo", surname: "bar", nested_json: { complicated: [""] } }

Entonces, en lugar de enviar matriz vacía, siempre paso ("") en mi solicitud para eludir el proceso de munging profundo.


Parece que se trata de un problema conocido y recientemente introducido: https://github.com/rails/rails/issues/8832

Si sabe dónde estará la matriz vacía, siempre podría params[:...][:...] ||= [] en un filtro anterior.

Alternativamente, puede modificar el modelo de BackBone al método JSON, especificando el valor de JSON.stringify() utilizando JSON.stringify() antes de publicarlo y JSON.parse manualmente de nuevo utilizando JSON.parse en un before_filter.

Feo, pero funcionará.


Puede volver a analizar los parámetros usted mismo, así:

class ApiController before_filter :fix_json_params # Rails 4 or earlier # before_action :fix_json_params # Rails 5 [...] protected def fix_json_params if request.content_type == "application/json" @reparsed_params = JSON.parse(request.body.string).with_indifferent_access end end private def params @reparsed_params || super end end

Esto funciona buscando solicitudes con un tipo de contenido JSON, volviendo a analizar el cuerpo de la solicitud y luego interceptando el método params para devolver los parámetros re-analizados, si es que existen.