Create a RESTful api Sept 1, 2012 CakeFest 2012 CakeFest 2012 Manchester
May 10, 2015
Create a RESTful apiSept 1, 2012
CakeFest 2012
CakeFest 2012 Manchester
INTRODUCTION
CakeFest 2012 Manchester
Marc Ypes@Ceeram
CakePHP 4 yearsCore team 1.5 years
Undercover as programmer
OVERVIEW
CakeFest 2012 Manchester
REST my case?REST your Cake
■ Content-type■ Routing■ Interface■ Authentication■ Cache■ Errors
INTRODUCTION TO REST
CakeFest 2012 Manchester
Representational state transfer
Set of architectural principles
- resource focussed- manipulation through representations- HTTP protocol?
INTRODUCTION TO REST
CakeFest 2012 Manchester
Constraints
■ Client-server■ Stateless■ Uniform interface■ Cacheable■ Layered system
INTRODUCTION TO REST
CakeFest 2012 Manchester
Uniform interface
■ resource
■ identification of the resource
■ manipulation through representation
■ self-descriptive
■ hypermedia as the engine of application state HATEOAS
INTRODUCTION TO REST
CakeFest 2012 Manchester
Uniform Interface
Data element Exampleresource user, book etc. (users, books etc.)resource identifier URL, URN (/users/1234)representation
data TXT / HTML / XML /YAML,JSONmetadata content type, last-modified time
resource metadata source link, alternatecontrol data if-modified-since, cache-control, etag
http://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm
INTRODUCTION TO REST
CakeFest 2012 Manchester
Uniform Interface
/api/getUserProfile/1234/api/addVoteForUser?id=1234/api/users?action=vote&id=1234/api/deleteUser/1/api/deleteUser?id=1/api/favoritedUsers/api/getUserData/1?fields=name,email
INTRODUCTION TO REST
CakeFest 2012 Manchester
Uniform Interface
GET /users Get collectionPOST /users Add to collectionGET /users/1234 Get resourcePUT /users/1234 Update resourceDELETE /users/1234 Delete resource
Update is not update?POST /users/1234
INTRODUCTION TO REST
CakeFest 2012 Manchester
Uniform Interface
Typical request:
>GET /books/1849511926 HTTP/1.1>Host: api.amazin.com>Accept: application/json
>If-Modified-Since: Sat, 01 Sep 2012 10:22:36 GMT
INTRODUCTION TO REST
CakeFest 2012 Manchester
Uniform Interface
Typical response:
< HTTP/1.1 200 OK< Date: Sat, 01 Sep 2012 11:45:12 GMT< Server: Apache/2.2.16 (Debian)< Last-Modified: Sat, 01 Sep 2012 11:25:31 GMT< Content-Length: 145< Content-Type: application/json{"book":{........"}}
INTRODUCTION TO REST
CakeFest 2012 Manchester
Uniform Interface
Typical response:
< HTTP/1.1 304 Not Modified< Date: Sat, 01 Sep 2012 11:45:12 GMT< Server: Apache/2.2.16 (Debian)< Vary: Accept-Encoding
INTRODUCTION TO REST
CakeFest 2012 Manchester
Uniform Interface
Safe methodsIdempotent methods
GET (HEAD) is safe (nullipotent)PUT, DELETE are idempotent
POSTPATCH?
INTRODUCTION TO REST
CakeFest 2012 Manchester
Uniform Interface
Normalize the resources
GET /books/1849511926/votes
GET /votes?book=1849511926
INTRODUCTION TO REST
CakeFest 2012 Manchester
Uniform Interface
Normalize the resources
POST /books/1849511926/votes
PUT /books/1849511926data contains votes subresource data
POST /votesdata is book=1849511926
INTRODUCTION TO REST
CakeFest 2012 Manchester
Uniform Interface
PATCH
Edge Rails: PATCH is the new primary HTTP method for updates
http://weblog.rubyonrails.org/2012/2/25/edge-rails-patch-is-the-new-primary-http-method-for-updates/
INTRODUCTION TO REST
CakeFest 2012 Manchester
Uniform Interface / HATEOAS
Level 3 of REST maturity model (RMM)
INTRODUCTION TO REST
CakeFest 2012 Manchester
Uniform Interface / HATEOAS
Level 0
Single URI, single HTTP method
INTRODUCTION TO REST
CakeFest 2012 Manchester
Uniform Interface / HATEOAS
Level 1
Many URI, single HTTP method
INTRODUCTION TO REST
CakeFest 2012 Manchester
Uniform Interface / HATEOAS
Level 2
Many URI, different HTTP methods
INTRODUCTION TO REST
CakeFest 2012 Manchester
Uniform Interface / HATEOAS
Level 2
Many URI, different HTTP methods
INTRODUCTION TO REST
CakeFest 2012 Manchester
Uniform Interface / HATEOAS
Level 3
Self descriptive■ Media types■ Links■ Other protocols
INTRODUCTION TO REST
CakeFest 2012 Manchester
Uniform Interface / HATEOAS
HATEOAS
GET /comments?book=1849511926
Link to api.amazin.com/books/1849511926
Links to all comments
INTRODUCTION TO REST
CakeFest 2012 Manchester
Uniform Interface / HATEOAS
>GET /comments/1<HTTP/1.1 200 Ok<Content-Type: text/xml<?xml version="1.0"><comment>
<foo>great book</foo><book>
<link href="/books/1849511926" title="wow" /></book>
</vote>
INTRODUCTION TO REST
CakeFest 2012 Manchester
Uniform Interface / HATEOAS{
"foo":"bar","book":
{"links":[
{"href":"/book/1849511926", "title":"wow"
}]
}}
INTRODUCTION TO REST
CakeFest 2012 Manchester
Uniform Interface / HATEOAS
Link header
Link: <https://api.github.com/user/repos?page=2&per_page=100>;rel="next", <https://api.github.com/user/repos?page=50&per_page=100>;rel="last"
github.com
INTRODUCTION TO REST
CakeFest 2012 Manchester
Uniform Interface / Errors
HTTP Statuscodes
Human reads messageCode reads code
INTRODUCTION TO REST
CakeFest 2012 Manchester
Uniform Interface / Errors
HTTP Statuscodes
200 OK201 Created204 No Content304 Not Modified400 Bad Request401 Unauthorized404 Not Found405 Method Not Allowed
INTRODUCTION TO REST
CakeFest 2012 Manchester
Uniform Interface / Errors
Link to support page
Link: <http://api.amazin.com/errors/405>; rel="help"
INTRODUCTION TO REST
CakeFest 2012 Manchester
Cacheable
HTTP Cache headers
Use them!
INTRODUCTION TO REST
CakeFest 2012 Manchester
Cacheable
HTTP Cache headers
Cache-control- private- public- max-age / s-maxage- must-revalidate- nocache
INTRODUCTION TO REST
CakeFest 2012 Manchester
Cacheable
HTTP Cache validation
Etag RSLast-Modified RS
If-Modified-Since RQIf-None-Match RQ
If-Match RQ
INTRODUCTION TO REST
CakeFest 2012 Manchester
REST might not be what you are looking for
Questions?
Rest my Cake
REST my Cake
CakeFest 2012 Manchester
Basic setup
Route urls with extensions
http://localhost/cats/index.json
app/Config/routes.php<?php// allow any url extensionRouter::parseExtensions();
//or allow .json extension onlyRouter::parseExtensions('json');
REST my Cake
CakeFest 2012 Manchester
Basic setup
Add RequestHandler component<?phpApp::uses('Controller', 'Controller');
class AppController extends Controller {
public $components = array( 'DebugKit.Toolbar', 'Session', 'RequestHandler', );}
REST my Cake
CakeFest 2012 Manchester
Basic setup
Create view files, the CakePHP 1.3 way (almost)
app/View/Cats/json/index.ctp
<?php echo json_encode($cats) ;?>
REST my Cake
CakeFest 2012 Manchester
Basic setup
Use auto serialization, the CakePHP 2.x way
public function view($id = null) { $this->Cat->id = $id; if (!$this->Cat->exists()) { throw new NotFoundException(__('Invalid cat')); } $this->set('cat', $this->Cat->read(null, $id)); $this->set('_serialize', array('cat')); }
REST my Cake
CakeFest 2012 Manchester
http://localhost/cats/index.json
REST my Cake
CakeFest 2012 Manchester
http://localhost/cats/view/1.json
REST my Cake
CakeFest 2012 Manchester
REST Routing
Resource mapping
http://localhost/catshttp://localhost/cats/1
app/Config/routes.php
Router::mapResources(array('Cats', 'Users'));
REST my Cake
CakeFest 2012 Manchester
REST Routing
Resource mapping
GET /cats index()POST /cats add()GET /cats/1 view(1)POST /cats/1 edit(1)PUT /cats/1 edit(1)DELETE /cats/1 delete(1)
REST my Cake
CakeFest 2012 Manchester
REST Routing
Resource mapping
X-HTTP-Method-Override<Limit PUT DELETE> order deny,allow allow from all</Limit>
REST my Cake
CakeFest 2012 Manchester
REST Routing
Resource mappingapp/Config/routes.phpRouter::resourceMap(array( array('action' => 'index','method' => 'GET','id' => false), array('action' => 'read','method' => 'GET','id' => true), array('action' => 'add','method' => 'POST','id' => false), array('action' => 'edit','method' => 'POST','id' => true), array('action' => 'replace','method' => 'PUT','id' => true), array('action' => 'update','method' => 'PATCH','id' => true), array('action' => 'delete','method' => 'DELETE','id' => true)
array('action' => 'truncate','method'=>'DELETE','id'=>false)));
REST my Cake
CakeFest 2012 Manchester
REST Routing
Resource mappingapp/Config/routes.phpRouter::mapResources(
array('Cats', 'Users', 'Pizza.Orders'));
/cats => CatsController::index() in app/users => UsersController::index() in app/pizza/orders => OrdersController::index() in Pizza plugin
REST my Cake
CakeFest 2012 Manchester
REST Routing
Resource mappingapp/Config/routes.phpRouter::mapResources(
array('Cats', 'Users', 'Pizza.Orders'),array('prefix' => '/api/')
);
/api/cats => CatsController::index() in app/api/users => UsersController::index() in app/api/orders => OrdersController::index() in Pizza plugin
REST my Cake
CakeFest 2012 Manchester
REST Routing
Resource mappingapp/Config/routes.phpRouter::mapResources(
array('Cats', 'Users', 'Pizza.Orders'),array('prefix' => '/api/, 'id' => Router::UUID')
);
/api/cats => CatsController::index() in app/api/users => UsersController::index() in app/api/orders => OrdersController::index() in Pizza plugin
REST my Cake
CakeFest 2012 Manchester
REST Routing
.json?format=json
curl -H "Accept: application/json" http://localhost/cats
REST my Cake
CakeFest 2012 Manchester
REST Routing
GET /cats/sleepingGET /cats?status=sleepingGET /cats?sleeping=1
GET /posts/recentGET /posts?type=recent
REST my Cake
CakeFest 2012 Manchester
REST Routing
/posts/foo/bar routes with passed params/posts/foo:bar routes with named paramsapp/Config/routes.phpRouter::connect('/api/cats/*',
array( 'plugin' => null, 'controller' => 'posts', 'action' => 'index', '[method]' => 'GET'));
REST my Cake
CakeFest 2012 Manchester
REST Routing
GET /users/vote/1234POST /users/vote/1234 $this->Html->postLink()POST /api/users/1234/votespublic function vote($id = null) {
//user id exist, httpmethod checks etc.$this->User->updateAll(
array('User.votes' => 'User.votes + 1'),array('User.id' => $id)
);}
REST my Cake
CakeFest 2012 Manchester
REST Routing
POST /users/1234/votesapp/Config/routes.phpRouter::connect('/api/users/:id/votes',
array( 'plugin' => null, 'controller' => 'users', 'action' => 'vote', '[method]' => 'POST'
),array('id' => Router::ID, 'pass' => 'id')
);
REST my Cake
CakeFest 2012 Manchester
REST Routing
POST /users/1234/votespublic function vote($id = null) {
//user id exist checks etc$this->User->Vote->add($id);
}POST /votespublic function add() {
$this->Vote->add($this->request->data['Vote.user']);}
REST my Cake
CakeFest 2012 Manchester
REST Routing
CakeRequest::addDetector('patch',array(
'env' => 'REQUEST_METHOD','value' => 'PATCH'
));
REST my Cake
CakeFest 2012 Manchester
CakeResponse
header( $header = NULL, $value = NULL )send( )statusCode( $code = NULL )type( $contentType = NULL )
REST my Cake
CakeFest 2012 Manchester
CakeResponse
cache( $since, $time = '+1 day' )checkNotModified( $request )disableCache( )etag( $tag = NULL, $weak = false )expires( $time = NULL )maxAge( $seconds = NULL )modified( $time = NULL )mustRevalidate( $enable = NULL )notModified( )sharable( $public = NULL, $time = NULL )sharedMaxAge( $seconds = NULL )
REST my Cake
CakeFest 2012 Manchester
RequestHandlerComponent
public $components = array('RequestHandler' => array(
'viewClassMap' => array('json' => 'RestJson','xml' => 'RestXml'
))
);
REST my Cake
CakeFest 2012 Manchester
Authentication
PublicApi-keyBasic AuthOAuth
REST my Cake
CakeFest 2012 Manchester
Authentication
public function getUser(CakeRequest $request) {if (!empty($this->settings['header'])) {
$token = $request->header($this->settings['header']);if ($token) {
return $this->_findUser($token, null);}
}if (!empty($request->query[$this->settings['parameter']])) {
$token = $request->query[$this->settings['parameter']];return $this->_findUser($token);
}return false;
}
REST my Cake
CakeFest 2012 Manchester
Versioning
/rest/v1
REST my Cake
CakeFest 2012 Manchester
THANKS