2011-01-20 9 views
25

Preambolo:rotaie 3 formato di risposta e il controllo delle versioni tramite tipo di fornitore MIME nell'intestazione Accept

ho studiato come versione un'API e trovato diversi modi per farlo. Ho deciso di provare il suggerimento peter williams' e ho creato nuovi tipi di mime del fornitore per specificare la versione e il formato. Non sono riuscito a trovare un resoconto definitivo per fare ciò seguendo "il modo delle rotaie", quindi ho ricostruito le informazioni da più punti. Sono stato in grado di farlo funzionare, ma c'è un po 'goofiness nel modo in cui i renderer gestiscono l'istanza di Widget array vs Widget in respond_with.

passi di base & problema:

ho registrato tipi MIME e renderer per la versione 1 aggiunto in XML e JSON per ApplicationController, i renderer chiamano to_myproj_v1_xml e to_myproj_v1_json metodi nel modello. respond_with(@widget) funziona bene ma respond_with(@widgets) lancia un HTTP/1.1 500 Internal Server Error dicendo che "Il modello è mancante".

Soluzione:

"Template manca" significa che non render è stato chiamato e esiste alcun modello di corrispondenza. per caso, ho scoperto che sta cercando un metodo di classe ... quindi ho trovato il codice sotto il quale funziona, ma non ne sono davvero contento. Il goofiness è per lo più in e relativo a xml = obj.to_myproj_v1_xml(obj) e la duplicazione nel modello.

La mia domanda è: qualcuno ha fatto qualcosa di simile in modo leggermente più pulito?

- = codice aggiornato = -

config/inizializzatori/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/controller/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/inizializzatori/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:

Trovato un'altra soluzione che si sente meglio ma ancora un po 'strana (non sono ancora del tutto a mio agio con le patch della scimmia), probabilmente ok ... in pratica ho spostato i dati di risposta dal metodo di classe to_myproj_v1_json a una patch di scimmia su Array. In questo modo quando c'è una matrice di widget, chiama il metodo di istanza as_myproj_v1_json su ciascun widget e restituisce l'intera matrice come formato desiderato.

Una nota:

  • as_json non ha nulla a che fare con il formato JSON, crea solo un hash. Aggiungi la formattazione personalizzata a as_myproj_v1_json (o una sovrascrittura di as_json se non usi tipi mime personalizzati), quindi to_json cambierà un hash in una stringa json.

Ho aggiornato il seguente codice per essere quello che è attualmente utilizzato, quindi la domanda originale potrebbe non avere senso. se qualcuno vuole la domanda originale e il codice mostrato come era e il codice fisso in una risposta posso farlo.

+0

Hai una domanda? Non capisco ... – Anton

+10

Ho trovato difficile trovare la domanda in questo post, ma alla fine l'ho trovata. Tuttavia, se si è arrivati ​​a una soluzione da soli, si prega di inviare come risposta piuttosto (e contrassegnare come accettato) di aggiornare la domanda. – Kev

+0

@jay, direi che devi essere più attento a modificare i tuoi post in futuro - arrivando alla tua domanda un visitatore è ora salutato con "AGGIORNAMENTO: trovato un'altra soluzione ..." che è molto confuso. – Ben

risposta

0

Per la risposta: vedi la domanda :-)

In breve, esistono diverse soluzioni, di cui uno nella domanda precedente:

  • scimmia-patch Array di attuare un metodo che restituirà il (vecchio) v1 JSON
+0

sì, quello era il mio SO n00bness che usciva. si spera che con l'aiuto delle modifiche sia più chiaro ... – jay

0

Non ho mai visto questo trucco di tipo di contenuto utilizzato in un progetto di Rails prima, quindi questo è nuovo per me. Il modo in cui l'ho visto in genere è definire uno spazio dei nomi di percorso (ad esempio/api/v1 /) che va a un controller (ad esempio, Api :: Version1Controller).

Inoltre, so che si vuole fare le cose nel "modo Rails", e forse questo suona irritabile proveniente da un ragazzo che è stato con Rails dal 1.3, ma l'intera respond_with/respond_to roba è piuttosto magica per me. Non sapevo che respond_to cerca un metodo to_XXX quando serializza oggetti, ad esempio (forse ho bisogno di leggere su quello). Avere patch Array come questa sembra piuttosto sciocco. Inoltre, per un'API, la formattazione dei dati del modello è in realtà il lavoro della vista, non quello del modello. Potrei esaminare qualcosa come rabl in questo caso. C'è una bella recensione su di esso here.

+0

ho esaminato entrambi utilizzando lo spazio dei nomi di percorso e il tipo di contenuto durante l'indagine. l'identificatore del tipo di contenuto sembra più "RESTful" in quanto una risorsa viene sempre definita con un singolo uri come/widget/1 invece di avere 2 uri /widget/1.json e /widget/1.xml. Sono assolutamente d'accordo con il punto che la vista dovrebbe fare la rappresentazione, e anche il rabl sembra molto interessante, grazie per averlo sottolineato! – jay