RESTful Rails

Geplaatst door Matthijs Langenberg do, 02 nov 2006 07:18:00 GMT

In de komende release van Rails gaat REST een belangrijke rol spelen, maar wat is REST nu eigenlijk?

REST staat voor REpresentational State Transfer, dit betreft het opvragen en wijzigen van resources waarbij gebruik wordt gemaakt van de vier standaard HTTP request methoden (namelijk: GET, POST, PUT en DELETE). REST is gebaseerd op het feit dat elke HTTP methode een te ondernemen actie op een resource (een ‘model’) vertegenwoordigt. Dit maakt het mogelijk om dezelfde URL voor meerdere acties te gebruiken. Stel dat ik het eerste artikel van een blog zou willen opvragen:
 GET /articles/1 
En nu wil ik hetzelfde artikel verwijderen:
 DELETE /articles/1 
Zoals je ziet blijft de URL hetzelfde en gebruik ik alleen een ander HTTP request. Aangezien requests binnenkomen bij een controller houd dit dus in dat er een mapping plaatsvind van HTTP methode naar controller actie en wel als volgt:
HTTP verb Controller Action CRUD Action
GET show READ
POST create CREATE
PUT update UPDATE
DELETE destroy DESTROY


Zoals je ziet zijn we weer bij de CRUD acties uitgekomen. REST en CRUD sluiten bij elkaar aan, REST is de manier waarop CRUD acties over HTTP aangeroepen worden, daarom is het belangrijk dat controllers alleen de nodige CRUD acties bevatten, anders zijn de acties niet via REST aan te roepen. Waarschijnlijk roept dit enkele vragen bij je op, wat doe je bijvoorbeeld met een ‘close’ actie in je TasksController? Er is immers geen HTTP methode voor een ‘close’ actie wat dus zou betekenen dat deze actie nooit middels REST aangeroepen kan worden. Het juiste is om een nieuwe controller aan te maken welke deze taak middels een van de CRUD acties voor je oplost, om dit even duidelijker toe te lichten, de onderstaande controller is niet RESTful:
    class TasksController < ApplicationController
        def index
            @tasks = Taks.find(:all)
        end

        def show
            @task = Task.find(params[:id])
        end

        def close
            @task = Task.find(params[:id])
            @task.close!
        end
    end
Deze controller is niet RESTful aangezien de ‘close’ actie niet aangeroepen kan worden. Hiervoor roepen we een nieuwe controller in het leven genaamd de ClosuresController
    class ClosuresController < ApplicationController
        def create
            @task = Task.find(params[:task_id])
            @task.close!
        end
    end
Deze actie kan nu perfect benaderd worden door een POST tasks/1/closures request uit te voeren. Het is dus belangrijk dat je (de wereld) probeert te modelleren binnen CRUD acties wil je RESTful controllers implementeren. Dit zorgt er tevens voor dat het aantal acties binnen een controller drastisch verminderd en je controller een opbouw heeft die vele malen consistenter is. Iets anders wat nu ook goed aangepakt kan worden is het gebruik van meerdere acties voor alternatieve weergaven van dezelfde informatie, misschien komt het volgende bekend voor:
    class ArticlesController < ApplicationController
        def show
            @article = Article.find(params[:id])
        end

        def show_rss
            @article = Article.find(params[:id])
            render :rss => @article.to_rss
        end

        def show_atom
            @article = Article.find(params[:id])
            render :atom => @article.to_atom
        end

        def show_xml
            @article = Article.find(params[:id])
            render :xml => @article.to_xml
        end

        def show_ajax
            @article = Article.find(params[:id])
            render :template => show_article.rjs
        end
    end
Je ziet dat er vijf verschillende acties gebruikt worden voor het weergeven van dezelfde informatie, maar dan op een alternatieve wijze (html, rss, atom, xml of javascript). Ik hoef natuurlijk niet te zeggen dat dit niet REST is, maar het is ook belangrijk om te zien dat dit absoluut niet DRY (‘Don’t repeat yourself’) is, De regel @article = Article.find(params[:id]) is namelijk ook in elke actie aanwezig. Waar je dus naar toe zou willen gaan is het gebruik van 1 actie, waar op basis van het request type informatie op een bepaalde wijze weergegeven wordt. Hiervoor kan de Accept Header in HTML gebruikt worden, maar ook de ‘extensie’ van de request, bijvoorbeeld GET /articles.html of GET /articles.rss Dit is in Rails te doen door de respond_to methode aan te roepen, dit ziet er als volgt uit:
    class ArticlesController < ApplicationController
     def show
       @article = Article.find(params[:id])
       respond_to do |format|
         format.html
         format.rss  { render :rss => @article.to_rss }
         format.atom { render :atom => @article.to_atom }
         format.xml  { render :xml => @article.to_xml }
         format.rjs  { render :template => ‘show_article.rjs’ }
       end
     end
    end

Een stuk netter is het niet?

Nu ik de theorie achter REST (binnen Rails) kort uitgelegd heb, zal ik in een volgend artikel een handleiding schrijven waarin ik een voorbeeld applicatie volgens de REST methode opzet. We gaan daar ook echte ondersteuning voor REST webservices inbouwen zodat we deze vanaf een andere rails applicatie kunnen aanroepen.

Geplaatst in ,  | 4 reacties

Reacties

  1. Erik van Oosten zei 4 dagen later:

    Ik denk dat:

    def show
      @task = Task.find(params[:id])

    Eigenlijk moet zijn:

    def show
      @task = Task.find(params[:id])
    end

    Toch?

    Verder: een leuk artikel, verhelderend. Ik was me alleen niet duidelijk of die ‘close’ actie nu een task moet sluiten, of dat het iets anders moet doen.

  2. Remco zei 4 dagen later:

    Dank! Aangepast.

  3. Matthijs Langenberg zei 5 dagen later:

    @Erik: Het doel van de close actie is in dit geval inderdaad het sluiten van een taak, stel je een takenlijst voor waarbij taken ook afgevinkt (closed) kunnen worden.

  4. Frans zei 23 dagen later:

    2 foutjes..

    Aangezien requests binnenkomen bij een controller houd dit dus in dat er een mapping plaatsvind van HTTP methode…

    zou moeten zijn..

    Aangezien requests binnenkomen bij een controller houdt dit dus in dat er een mapping plaatsvindt van HTTP methode…

(Laat url/e-mail achter »)

   Voorvertoning