2011-01-20 11 views
25

Preámbulo:carriles 3 formato de respuesta y control de versiones utilizando el tipo de proveedor MIME en el encabezado Accept

he investigado cómo la versión de una API y encontré varias maneras de hacerlo. Decidí probar la sugerencia peter williams' y crear nuevos tipos de mime de proveedor para especificar la versión y el formato. No pude encontrar ningún escrito definitivo para hacer esto siguiendo "el camino de los rieles", así que reuní información de varios lugares. Pude ponerlo en funcionamiento, pero hay algunos errores en la forma en que los procesadores manejan la matriz de widgets frente a la instancia de widgets en respond_with.

pasos básicos & de problemas:

I registrados tipos MIME y ha añadido extracción de grasas para la versión 1, tanto en XML y JSON para ApplicationController, los procesadores llaman to_myproj_v1_xml y to_myproj_v1_json métodos en el modelo. respond_with(@widget) funciona bien, pero respond_with(@widgets) arroja un HTTP/1.1 500 Internal Server Error diciendo que la "Plantilla falta".

Solución:

"Plantilla falta" significa que no rinda fue llamado y no exista una plantilla a juego. por accidente, descubrí que está buscando un método de clase ... así que se me ocurrió el siguiente código que funciona, pero no estoy muy contento con él. La tontería está principalmente relacionada con xml = obj.to_myproj_v1_xml(obj) y la duplicación en el modelo.

Mi pregunta es: ¿alguien ha hecho algo similar de una manera ligeramente más limpia?

- = código actualizado = -

config/inicializadores/mime_types.rb:

Mime::Type.register 'application/vnd.com.mydomain.myproj-v1+xml', :myproj_v1_xml 
Mime::Type.register 'application/vnd.com.mydomain.myproj-v1+json', :myproj_v1_json 

app/controllers/application_controller.rb:

class ApplicationController < ActionController::Base 
    protect_from_forgery 
    before_filter :authenticate 

    ActionController.add_renderer :myproj_v1_xml do |obj, options| 
    xml = obj.to_myproj_v1_xml 
    self.content_type ||= Mime::Type.lookup('application/vnd.com.mydomain.myproj-v1+xml') 
    self.response_body = xml 
    end 

    ActionController.add_renderer :myproj_v1_json do |obj, options| 
    json = obj.to_myproj_v1_json 
    self.content_type ||= Mime::Type.lookup('application/vnd.com.mydomain.myproj-v1+json') 
    self.response_body = json 
    end 
end 

app/models/widget.rb:

class Widget < ActiveRecord::Base 
    belongs_to :user 
    V1_FIELDS = [:version, :model, :description, :name, :id] 

    def to_myproj_v1_xml 
    self.to_xml(:only => V1_FIELDS) 
    end 

    def to_myproj_v1_json 
    self.to_json(:only => V1_FIELDS) 
    end 

    def as_myproj_v1_json 
    self.as_json(:only => V1_FIELDS) 
    end 
end 

app/controladores/widgets_controller.rb:

class WidgetsController < ApplicationController 

    respond_to :myproj_v1_xml, :myproj_v1_json 

    def index 
    @widgets = @user.widgets 
    respond_with(@widgets) 
    end 

    def create 
    @widget = @user.widgets.create(params[:widget]) 
    respond_with(@widget) 
    end 

    def destroy 
    @widget = @user.widgets.find(params[:id]) 
    respond_with(@widget.destroy) 
    end 

    def show 
    respond_with(@widget = @user.widgets.find(params[:id])) 
    end 

... 

end 

config/inicializadores/monkey_array.rb

class Array 

    def to_myproj_v1_json(options = {}) 
    a = [] 
    self.each { |obj| a.push obj.as_myproj_v1_json } 
    a.to_json() 
    end 

    def to_myproj_v1_xml(options = {}) 
    a = [] 
    self.each { |obj| a.push obj.as_myproj_v1_json } # yes this is json instead of xml. as_json returns a hash 
    a.to_xml() 
    end 

end 

UPDATE:

Encontré otra solución que se siente mejor pero aún un poco extraña (todavía no estoy del todo cómoda con parches de mono), probablemente bien ... básicamente se movió construyendo los datos de respuesta del método de clase to_myproj_v1_json a un parche de mono en Array. De esta forma, cuando hay una Matriz de widgets, llama al método de instancia as_myproj_v1_json en cada widget y devuelve toda la matriz como el formato deseado.

Una nota:

  • as_json no tiene nada que ver con el formato JSON, sólo crea un hash. Agregue formato personalizado a as_myproj_v1_json (o an_json anule si no está usando tipos de mime personalizados), entonces to_json cambiará un hash a una cadena json.

He actualizado el código siguiente para que sea el que se utiliza actualmente, por lo que la pregunta original puede no tener sentido. si alguien quiere que se muestren la pregunta y el código originales tal como estaban y un código fijo en una respuesta, puedo hacerlo en su lugar.

+0

Tiene una pregunta? No entiendo ... – Anton

+10

Encontré difícil encontrar la pregunta en esta publicación, pero eventualmente la encontré. Sin embargo, si ha llegado a una solución por su cuenta, publíquela como respuesta (y marque como aceptada) antes que actualizar la pregunta. – Kev

+0

@jay, yo diría que debes ser más cuidadoso con la edición de tus publicaciones en el futuro: al llegar a tu pregunta, un visitante es recibido con "ACTUALIZACIÓN: encontró otra solución ..." que es muy confuso. – Ben

Respuesta

0

Para la respuesta: ver la pregunta :-)

En resumen, existen diferentes soluciones, de las cuales una está en la pregunta anterior:

  • matriz de mono-patch para implementar un método que dará el (anterior) v1 JSON nuevo
+0

sí, esa fue mi SO n00bness saliendo. con suerte, con la ayuda de ediciones, es más claro ... – jay

0

No he visto este truco de tipo de contenido utilizado en cualquier lugar en un proyecto de Rails antes, así que esto es nuevo para mí. La forma en que normalmente lo he visto hacer es definir un espacio de nombres de ruta (por ejemplo,/api/v1 /) que va a un controlador (por ejemplo, Api :: Version1Controller).

Además, sé que quieres hacer las cosas al estilo de "Rails", y tal vez esto suene mal hecho por un tipo que ha estado con Rails desde 1.3, pero todo el material de respond_with/respond_to es mágico para mí. No sabía que respond_to busca un método to_XXX cuando serializa objetos, por ejemplo (tal vez necesito leer sobre eso). Tener que parchear a mono Array así parece bastante tonto. Además, para una API, formatear los datos del modelo es realmente el trabajo de la vista, no el modelo. Podría buscar algo como rabl en este caso. Hay una buena reseña al respecto here.

+0

analicé tanto el uso del espacio de nombres de la ruta como el tipo de contenido al investigar esto. el especificador de tipo de contenido parece más "RESTful" en cuanto a que un recurso siempre se define con un solo uri como/widget/1 en lugar de tener 2 uri /widget/1.json y /widget/1.xml. Estoy totalmente de acuerdo con el punto de que la vista debería estar haciendo la representación, y rabl también se ve muy interesante, ¡gracias por señalar eso! – jay

Cuestiones relacionadas