ActiveResource & REST

Post on 13-May-2015

2.756 views 1 download

Tags:

description

Matthijs Langenberg's slide's of his presentation at the RubyenRails 2007 conf in Amsterdam.

Transcript of ActiveResource & REST

REST & ActiveResourceMatthijs Langenberg

Webservices

Wat zijn webservices

”The W3C defines a Web Service as a software system designed to support interoperable machine to machine interaction over a network.”

-- Wikipedia

Bevorder ‘machine to machine interaction’

HTML is moeilijk te parsen

Geef iets anders terug

XML?

Just Another View

respond_to

class ArticlesController < ApplicationController def show @article = Article.find(params[:id]) respond_to do |format| format.html format.xml { render :xml => @article } end endend

Soorten Webservices

• Remote procedure calls (RPC)

• Service-oriented architecture (SOA)

• Representational state transfer (REST)

Rails votes REST

Rails votes REST

BIG TIME!

Wat is REST?

REpresentional State Transfer

HTTP’s: “convention over configuration”

Schreef geen vervanging voor iets wat HTTP je gratis geeft

HTTP Abuse

Wat is er mis met dit request?

GET http://myblog.com/articles/destroy/1

HTTP Abuse

Wat is er mis met dit request?

GET http://myblog.com/articles/destroy/1

Conflict

HTTP Abuse

Wat is er mis met dit request?

GET http://myblog.com/articles/destroy/1

Conflict

• Actie staat in URL• Uitgevoerd actie is in conflict met HTTP methode

The REST-way

DELETE http://myblog.com/articles/1

URI’s

URI’s

GET /articles/create

GET /articles/show/1

GET /articles/update/1

GET /articles/destroy/1

URI’s

GET /articles/create

GET /articles/show/1

GET /articles/update/1

GET /articles/destroy/1

Mapping

HTTP

GET

POST

PUT

DELETE

Mapping

HTTP

GET

POST

PUT

DELETE

Controller

SHOW

CREATE

UPDATE

DESTROY

Mapping

Resourceful URI’s

GET /articles

POST /articles/create

GET /articles/show/1

POST /articles/update/1

GET /articles/destroy/1

Resourceful URI’s

GET /articles

POST /articles/create

GET /articles/show/1

POST /articles/update/1

GET /articles/destroy/1

Resourceful URI’s

➡ GET /articles

➡ POST /articles

➡ GET /articles/1

➡ PUT /articles/1

➡ DELETE /articles/1

Gratis named routes

ActionController::Routing::Routes.draw do |map|map.resources :articles do |articles|articles.resources :comments

endend

ActionController::Routing::Routes.draw do |map|map.resources :articles do |articles|articles.resources :comments

endend

articles_urlarticle_urlnew_article_urledit_article_url

article_comments_urlarticle_comment_urlarticle_new_comment_urlarticle_edit_comment_url

ActionController::Routing::Routes.draw do |map|map.resources :articles do |articles|articles.resources :comments

endend

articles_urlarticle_urlnew_article_urledit_article_url

article_comments_urlarticle_comment_urlarticle_new_comment_urlarticle_edit_comment_url

➡ /articles➡ /articles/:id➡ /articles/new➡ /articles/:id/edit

➡ /articles/:article_id/comments➡ /articles/:article_id/comments/:id➡ /articles/:article_id/comments/new➡ /articles/:article_id/comments/:id/edit

ActionController::Routing::Routes.draw do |map|map.resources :articles do |articles|articles.resources :comments

endend

link_to article.title, { :controller => ‘article’, :action => ‘show’, :id => article }

link_to article.title, { :controller => ‘article’, :action => ‘show’, :id => article }

link_to article.title, { :controller => ‘article’, :action => ‘show’, :id => article }

link_to article.title, article_url(article)

link_to article.title, { :controller => ‘article’, :action => ‘show’, :id => article }

link_to article.title, article_url(article)

link_to article.title, article

Wat zou je doen?Je wilt comments aan articles toevoegen,ArticlesController is aanwezig.

Wat zou je doen?Je wilt comments aan articles toevoegen,ArticlesController is aanwezig.

1) Voeg een actie ‘add_comment’ aan ArticlesController toe.(POST /articles/1/add_comment)

Wat zou je doen?Je wilt comments aan articles toevoegen,ArticlesController is aanwezig.

1) Voeg een actie ‘add_comment’ aan ArticlesController toe.(POST /articles/1/add_comment)

2) Maak een CommentsController, met een ‘create’ actie.(POST /comments/create?article_id=1)

Mr. RESTful zegt:

Mr. RESTful zegt:Antwoord 2

Mr. RESTful zegt:Antwoord 2

• Een comment is een een aparte resource

Mr. RESTful zegt:Antwoord 2

• Een comment is een een aparte resource• Er bestaat geen ‘add_comment’ methode in HTTP

Mr. RESTful zegt:Antwoord 2

• Een comment is een een aparte resource• Er bestaat geen ‘add_comment’ methode in HTTP• Er bestaat wel een ‘create’ (POST) methode in HTTP

Geen Namespaces!

• POST /articles/create

• POST /articles/create_comment

• GET /articles/destroy

• GET /articles/destroy_comment

Teveel vrijheid is niet goed

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 endend

Geen aparte actie voor alternatieve view!

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 endend

Wauw! HTTP method naar controller actie mapping actie klinkt tof!

Maar er zit een adder ...

Maar er zit een adder ...

Browsers ondersteunen PUT en DELETE niet!

Browsers ondersteunen PUT en DELETE niet!

<input name="_method" type="hidden" value="put" />

Gelukkig zijn de helpers ook aangepast. ;-)

HTML_options

• link_to “delete”, article_path(1), :method => ‘delete’

• link_to_remote, “delete”, article_path(1), :method => ‘delete’

• form_tag(member_path(2), :method => :put)

form_forremote_form_for

Bepalen op basis van AR object de method:

form_for(Movie.new):

<form action="/movies" class="new_movie" id="new_movie" method="post">

form_for(Movie.find(:first)):

<form action="/movies/1" class="edit_movie" id="edit_movie_1" method="post">

<input name="_method" type="hidden" value="put" />

Controller Acties

MoviesController#index

MoviesController#index

# GET /movies # GET /movies.xml def index @movies = Movie.find(:all)

respond_to do |format| format.html # index.html.erb format.xml { render :xml => @movies } end end

MoviesController#index

# GET /movies # GET /movies.xml def index @movies = Movie.find(:all)

respond_to do |format| format.html # index.html.erb format.xml { render :xml => @movies } end end

<?xml version="1.0" encoding="UTF-8"?><movies> <movie> <director>Chris Miller</director> <id type="integer">1</id> <rating type="decimal">7.0</rating> <title>Shrek the Third</title> </movie> <movie> <director>Sam Raimi</director> <id type="integer">2</id> <rating type="decimal">6.9</rating> <title>Spider-Man 3</title> </movie> <movie> <director>Juan Carlos Fresnadillo</director> <id type="integer">3</id> <rating type="decimal">7.7</rating> <title>28 Weeks Later</title> </movie></movies>

MoviesController#show

MoviesController#show # GET /movies/1 # GET /movies/1.xml def show @movie = Movie.find(params[:id])

respond_to do |format| format.html # show.html.erb format.xml { render :xml => @movie } end end

MoviesController#show # GET /movies/1 # GET /movies/1.xml def show @movie = Movie.find(params[:id])

respond_to do |format| format.html # show.html.erb format.xml { render :xml => @movie } end end

<?xml version="1.0" encoding="UTF-8"?><movie> <director>Chris Miller</director> <id type="integer">1</id> <rating type="decimal">7.0</rating> <title>Shrek the Third</title></movie>

MoviesController#create

MoviesController#create

# POST /movies # POST /movies.xml def create @movie = Movie.new(params[:movie])

respond_to do |format| if @movie.save flash[:notice] = 'Movie was successfully created.' format.html { redirect_to(@movie) } format.xml { render :xml => @movie,

:status => :created, :location => @movie }

else format.html { render :action => "new" } format.xml { render :xml => @movie.errors, :status => 422 } end end end

MoviesController#create

# POST /movies # POST /movies.xml def create @movie = Movie.new(params[:movie])

respond_to do |format| if @movie.save flash[:notice] = 'Movie was successfully created.' format.html { redirect_to(@movie) } format.xml { render :xml => @movie,

:status => :created, :location => @movie }

else format.html { render :action => "new" } format.xml { render :xml => @movie.errors, :status => 422 } end end end

Status: 201 CreatedLocation: http://localhost:3000/movies/16<?xml version="1.0" encoding="UTF-8"?><movie> <director>Steven Spielbergh</director> <id type="integer">15</id> <rating type="decimal">8.3</rating> <title>Letters from Iwo Jima</title></movie>

MoviesController#create

# POST /movies # POST /movies.xml def create @movie = Movie.new(params[:movie])

respond_to do |format| if @movie.save flash[:notice] = 'Movie was successfully created.' format.html { redirect_to(@movie) } format.xml { render :xml => @movie,

:status => :created, :location => @movie }

else format.html { render :action => "new" } format.xml { render :xml => @movie.errors, :status => 422 } end end end

Status: 422 Unprocessable Entity<?xml version="1.0" encoding="UTF-8"?><errors> <error>Rating can't be blank</error> <error>Director can't be blank</error> <error>Title can't be blank</error></errors>

Status: 201 CreatedLocation: http://localhost:3000/movies/16<?xml version="1.0" encoding="UTF-8"?><movie> <director>Steven Spielbergh</director> <id type="integer">15</id> <rating type="decimal">8.3</rating> <title>Letters from Iwo Jima</title></movie>

MoviesController#update

MoviesController#update

# PUT /movies/1 # PUT /movies/1.xml def update @movie = Movie.find(params[:id])

respond_to do |format| if @movie.update_attributes(params[:movie]) flash[:notice] = 'Movie was successfully updated.' format.html { redirect_to(@movie) } format.xml { head :ok } else format.html { render :action => "edit" } format.xml { render :xml => @movie.errors, :status => 422 } end end end

MoviesController#update

# PUT /movies/1 # PUT /movies/1.xml def update @movie = Movie.find(params[:id])

respond_to do |format| if @movie.update_attributes(params[:movie]) flash[:notice] = 'Movie was successfully updated.' format.html { redirect_to(@movie) } format.xml { head :ok } else format.html { render :action => "edit" } format.xml { render :xml => @movie.errors, :status => 422 } end end end

Status: 200 OK

MoviesController#update

# PUT /movies/1 # PUT /movies/1.xml def update @movie = Movie.find(params[:id])

respond_to do |format| if @movie.update_attributes(params[:movie]) flash[:notice] = 'Movie was successfully updated.' format.html { redirect_to(@movie) } format.xml { head :ok } else format.html { render :action => "edit" } format.xml { render :xml => @movie.errors, :status => 422 } end end end

Status: 422 Unprocessable Entity<?xml version="1.0" encoding="UTF-8"?><errors> <error>Title can't be blank</error></errors>

Status: 200 OK

MoviesController#destroy

MoviesController#destroy

# DELETE /movies/1 # DELETE /movies/1.xml def destroy @movie = Movie.find(params[:id]) @movie.destroy

respond_to do |format| format.html { redirect_to(movies_url) } format.xml { head :ok } end end

MoviesController#destroy

# DELETE /movies/1 # DELETE /movies/1.xml def destroy @movie = Movie.find(params[:id]) @movie.destroy

respond_to do |format| format.html { redirect_to(movies_url) } format.xml { head :ok } end end

Status: 200 OK

Scaffolding

maar er is meer!

ActiveResource

ActiveResource

• Object-oriented REST services

• Transparent met een RESTful service (Rails) werken

• Net als ActiveRecord, maar dan voor REST

Browser

Controller(RESTful)

Browser

GET /movies.html

Controller(RESTful)

ActiveRecord

Browser

GET /movies.html

Movie.find(:all)

Controller(RESTful)

ActiveRecord

Browser

DB

GET /movies.html

Movie.find(:all)

SELECT * FROM MOVIES

Controller(RESTful)

Browser

ActiveRecord

Browser

DB

GET /movies.html

Movie.find(:all)

SELECT * FROM MOVIES

Controller(RESTful)

Browser

ActiveRecord

Browser

Controller

DB

GET /movies.html GET /movies.html

Movie.find(:all)

SELECT * FROM MOVIES

Controller(RESTful)

Browser

ActiveRecord

Browser

Controller

ActiveResource

DB

GET /movies.html

Movie.find(:all)

GET /movies.html

Movie.find(:all)

SELECT * FROM MOVIES

Controller(RESTful)

Browser

ActiveRecord

Browser

Controller

ActiveResource

DB

GET /movies.html

Movie.find(:all)

GET /movies.xml

GET /movies.html

Movie.find(:all)

SELECT * FROM MOVIES

Configuratie

Werken met RESTful webservices(ActiveResource)

Create

Create

Read

Read

Update

Update

Destroy

Destroy

Wat doet ARes?

Wat doet ARes?

• Genereer URL

Wat doet ARes?

• Genereer URL

• Request URL (XML)

Wat doet ARes?

• Genereer URL

• Request URL (XML)

• Verwerk request (XML)

Wat doet ARes?

• Genereer URL

• Request URL (XML)

• Verwerk request (XML)

• Biedt ActiveRecord-like API

Demo

Demo

1) RESTful Rails applicatie (met curl)

Demo

1) RESTful Rails applicatie (met curl)

2) Rails applicatie met ActiveResource

Demo

1) RESTful Rails applicatie (met curl)

2) Rails applicatie met ActiveResource

3) ActiveResource gem

Vragen?

<mlangenberg@gmail.com>#rubyenrails at irc.freenode.org