ActiveResource & REST
-
Upload
robbert -
Category
Technology
-
view
2.756 -
download
1
description
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?
<[email protected]>#rubyenrails at irc.freenode.org