RESTful modules in zf2

28
RESTful Modules in ZF2 Walter Dal Mut – [email protected] - @walterdalmut https://github.com/wdalmut @walterdalmut - www.corley.it - www.upcloo.com

description

Just a simple introduction to RESTful modules in ZF2

Transcript of RESTful modules in zf2

Page 1: RESTful modules in zf2

@walterdalmut - www.corley.it - www.upcloo.com

RESTful Modules in ZF2Walter Dal Mut – [email protected] - @walterdalmut

https://github.com/wdalmut

Page 2: RESTful modules in zf2

@walterdalmut - www.corley.it - www.upcloo.com

About Me

• Walter Dal Mut (@walterdalmut)• Electronic Engineer• Polytechnic University of Turin

• Startupper• Corley S.r.l. – www.corley.it

• Cloud Computing Services• UpCloo Ltd. – www.upcloo.com

• Semantic Most Related Links service

Page 3: RESTful modules in zf2

@walterdalmut - www.corley.it - www.upcloo.com

Summary

• REST introduction• REST constraints• Types of RESTful services• ZF2 RESTful modules• RESTful ZF2 URI tunneling module• RESTful ZF2 CRUD module

Page 4: RESTful modules in zf2

@walterdalmut - www.corley.it - www.upcloo.com

RESTful? What it means in few words…• Representational State Transfer (REST)

• Architecture is based on Client-Server• Clients initiate requests to servers; servers process requests and return

appropriate responses

• Flexible, the application must know messages format• XML• JSON• Etc.

Page 5: RESTful modules in zf2

@walterdalmut - www.corley.it - www.upcloo.com

RESTful services are resource centric• Resources (sources of specific information)• Each resource is referenced with a global identifier (URI, etc.)• Any number of connectors can mediate the request• Clients• Servers• Caches• Tunnels

Page 6: RESTful modules in zf2

@walterdalmut - www.corley.it - www.upcloo.com

REST Constraints

• Client-Server• A uniform interface separates clients from servers.

• Stateless• The client–server communication is further constrained by no client context being stored

on the server between requests.

• Cacheable• Clients can cache responses

• Layered System• Intermediary servers may improve system scalability by enabling load-balancing and by

providing shared caches. They may also enforce security policies.

• Uniform interface• Simplify and decouples the architecture

Page 7: RESTful modules in zf2

@walterdalmut - www.corley.it - www.upcloo.com

Several types of RESTful services

• URI Templates• http://rest.domain.tld/order/{orderID}• One of the major uses for URI templates is as human- and machine-readable

documentation.

• URI Tunneling• http://rest.domain.tld/PlaceOrder?pizza=margherita&type=classic

• Rest.domain.tld service• PlaceOrder method• Pizza=margherita&type=classic arguments

• POX – Plain Old XML over HTTP• Similar to URI Tunneling but information will be sent as XML document in the HTTP

request from the customer.

Page 8: RESTful modules in zf2

@walterdalmut - www.corley.it - www.upcloo.com

POX – Plain Old XML over HTTP example

POST /PlaceOrder HTTP/1.1Content-Type: application/xml

<Order> <Pizza> <Name>margherita</Name> <Type>classic</Type> </Pizza></Order>

Client application Application domain

HTTP/1.1 200 OK

<OrderConfirmation> <OrderID>1345</OrderID></OrderConfirmation>

Page 9: RESTful modules in zf2

@walterdalmut - www.corley.it - www.upcloo.com

CRUD Webservices

• CRUD what it means?• Create, Read, Update and Delete • Patterns for manipulating resources across the network

• Extended usage of HTTP verbs• GET• POST• PUT• DELETE

• Using HTTP as an application protocol instead of a transport protocol• Web is really a big framework for building distributed systems.

Page 10: RESTful modules in zf2

@walterdalmut - www.corley.it - www.upcloo.com

HTTP verbs in CRUD services

• Get Read Operation• Used to retrive resource details• http://rest.service.tld/order/10

• Post Create Operation• Used to create new resources

• Put Update Operation• Used to update existing resources

• Delete Delete Operation• Used to delete existing resources

POST/PUT can be exchanged and sometimes PUT/DELETE can be excluded to enable javascript integration (PUT/DELETE not supported browser side [HTTP_X_HTTP_METHOD_OVERRIDE parameter])

Page 11: RESTful modules in zf2

@walterdalmut - www.corley.it - www.upcloo.com

CRUD Summary

Verb URI or Template Use

POST /order Create a new order, and upon success, receive a Location header specifying the new order’s URI.

GET /order/{id} Request the current state of the order specified by theURI.

PUT /order/{id} Update an order at the given URI with new information,providing the full representation.

DELETE /order/{id} Logically remove the order identified by the given URI.

Page 12: RESTful modules in zf2

@walterdalmut - www.corley.it - www.upcloo.com

Status code definition (short list)

• 2xx (Positives)• 200 OK – The request has succeeded. • 201 Created – The server accept the request and it has created the resource.• 202 Accepted – The request has been accepted for processing, but the processing has not been completed.

• 4xx (Client Errors)• 400 Bad Request – The request could not be understood by the server due to malformed syntax.• 401 Unauthorized – The request requires user authentication.• 403 Forbidden – The server understood the request, but is refusing to fulfill it.• 404 Not found – The server has not found anything matching the Request-URI.• 405 Method not allowed – The method specified in the Request-Line is not allowed for the resource identified by the

Request-URI.

• 5xx (Server Errors)• 500 Internal Server Error – The server encountered an unexpected condition which prevented it from fulfilling the

request.• 501 Not implemented – The server does not support the functionality required to fulfill the request.• 503 Service unavailable – The server is currently unable to handle the request due to a temporary overloading or

maintenance of the server.

Page 13: RESTful modules in zf2

@walterdalmut - www.corley.it - www.upcloo.com

RESTful module idea

Router

RESTfulControllers

POSTProcessor

Models

requests

Responses

• Router• Wire requests to RESTful controllers

• RESTful Controllers• Uses HTTP verbs to call dedicated

actions• Query models in order to serve

responses• POST Processors

• Create valid messages using formats• JSON• XML• Etc.

Page 14: RESTful modules in zf2

@walterdalmut - www.corley.it - www.upcloo.com

Realize ZF2 tunneling RESTful module• ZF1 provides «Zend_Rest_Server» that realize URI tunneling• We can realize the same thing in 2 minutes thanks to ZF2 flexibility.

• We need to configure• Router• Events

• A simple example here: • https://github.com/wdalmut/ZF2-Tunneling-Restful-Module-Skeleton

Page 15: RESTful modules in zf2

@walterdalmut - www.corley.it - www.upcloo.com

ZF2 URI Tunneling - Configuration<?phpreturn array(    'controllers' => array(        'invokables' => array(         'index' => 'Tunneling\Rest\Controller',        )    ),    'router' => array(        'routes' => array(            'tunneling' => array(                'type' => 'Zend\Mvc\Router\Http\Segment',                'options' => array(                    'route' => '/tunnel',                    'defaults' => array(                        'controller' => 'index',                        'action' => 'index'                    ),                ),                'may_terminate' => true,                'child_routes' => array(                    'default' => array(                        'type' => 'Zend\Mvc\Router\Http\Segment',                        'options' => array(                            'route' => '[/:model/:action]',                            'constraints' => array(                                'controller' => 'index',                                'model' => '[a-zA-Z][a-zA-Z0-9_]*',                                'action' => '[a-zA-Z][a-zA-Z0-9_]*'                            ),                        ),                    ),                ),            ),        ),    ));

We have created a simple base route «/tunnel» and a dynamic rule that select a «model» and an attached «action»

In simple words• http://my-app.local/tunnel/menu/get?id=1• «menu»

• The model «Tunneling\Model\Menu»• «get»

• The model action «get()»• «id=1>

• The action parameters «get(1)»

Page 16: RESTful modules in zf2

@walterdalmut - www.corley.it - www.upcloo.com

ZF2 URI Tunneling - Controller<?phpnamespace Tunneling\Rest;

Use …

class Controller extends AbstractController{    public function onDispatch(MvcEvent $e)    {        $routeMatch = $e->getRouteMatch();        $params = $routeMatch->getParams();        $vars = get_object_vars($e->getRequest()->getQuery());

        $filter = new \Zend\Filter\FilterChain();        $filter->attach(new \Zend\Filter\Word\DashToCamelCase());        $filter->attach(new \Zend\Filter\Callback("lcfirst"));        $action = $filter->filter($params["action"]);        $filter->attach(new \Zend\Filter\Callback("ucfirst"));        $model = $filter->filter($params["model"]);

        $classname = "\\Tunneling\\Model\\{$model}";        if (class_exists($classname)) {         $clazz = new $classname;         if (property_exists($clazz, $action)) {                $ret = call_user_func_array(array($clazz, $action), $vars);                $e->setResult($ret);                return;            } else {                throw new InvalidArgumentException("Method \"{$action}\" doesn't exists'");            }        } else {            throw new InvalidArgumentException("Class \"{$classname}\" doesn't exists'");        }    }}

The base «AbstractController» is very flexible and enable us to use the «dispatch» action to realize what we need in few lines.

In practice we allocate the model and call the requested action. The return variable is used as response.

In case of missing model or missing action an «InvalidArgumentException» is thrown.

Page 17: RESTful modules in zf2

@walterdalmut - www.corley.it - www.upcloo.com

ZF2 URI Tunneling – Model example<?phpnamespace Tunneling\Model;

class Menu{    public function get($id)    {        return array("id" => $id);    }

    public function add($name, $value)    {        return array("name" => $name, "value" => $value);    }}

As you can see, the «model» is a simple class definition.

• /tunnel/menu/get?id=1• /tunnel/menu/add?x=pizza&y=4

Very simple implementation

Page 18: RESTful modules in zf2

@walterdalmut - www.corley.it - www.upcloo.com

ZF2 URI Tunneling – JSON responses<?phpnamespace Tunneling;

use Zend\Mvc\MvcEvent;

class Module{    public function onBootstrap($e)    {        /** @var \Zend\ModuleManager\ModuleManager $moduleManager */        $moduleManager = $e->getApplication()->getServiceManager()->get('modulemanager');        /** @var \Zend\EventManager\SharedEventManager $sharedEvents */        $sharedEvents = $moduleManager->getEventManager()->getSharedManager();

        $sharedEvents->attach(            'Zend\Mvc\Controller\AbstractController',            MvcEvent::EVENT_DISPATCH,            array($this, 'postProcess'),            -100        );    }//…    public function postProcess(MvcEvent $e)    {        $routeMatch = $e->getRouteMatch();        if (\strpos($routeMatch->getMatchedRouteName(), "tunneling") !== false) {            $e->getResponse()->setContent(json_encode($e->getResult()->getVariables()));            return $e->getResponse();        }    }}

Thanks to events we can wire controller output to a post processor that converts responses in json messages.

• Attach a «postProcess» action and create a json message.

Page 19: RESTful modules in zf2

@walterdalmut - www.corley.it - www.upcloo.com

ZF2 RESTful CRUD modules

• ZF2 provides a base controller class that can help us to realize RESTful modules in few steps• Zend\Mvc\Controller\AbstractRestfulController

• CRUD based implementation (Extended)• get($id)• delete($id)• update($id)• create($id)

Page 20: RESTful modules in zf2

@walterdalmut - www.corley.it - www.upcloo.com

ZF2 RESTful CRUD module example

• Clone ZF2 Skeleton Application• Git clone https://github.com/zendframework/ZendSkeletonApplication.git my-app• Git submodule init• Git submodule update

• Clone a ZF2 RESTful module• Git submodule add https://github.com/wdalmut/ZF2-Restful-Module-Skeleton.git module/Main

• Add «Main» module into «configs/application.config.php»• Create you application virtual host and try it

• http://my-app.local/rest/info/json [getList]• http://my-app.local/rest/info/json/1 [get]• curl –X POST –d ‘hello=world’ http://my-app.local/rest/info [create]• curl –X PUT –d ‘hello=ciao’ http://my-app.local/rest/info/1 [update]• curl –X DELETE http://my-app.local/rest/info/1 [delete]

Page 21: RESTful modules in zf2

@walterdalmut - www.corley.it - www.upcloo.com

RESTful Controllers<?php

namespace Main\Controller;

use Zend\Mvc\Controller\AbstractRestfulController;

class InfoController extends AbstractRestfulController{    public function getList()    {        return array('ciao' => 'mondo');    }

    public function get($id)    {

    }

    public function delete($id)    {

    }

    public function update($id, $data)    {

    }

    public function create($data = null)    {

    }}

• RESTful Controller should extends AbstractRestfulController• 5 abstract methods (CRUD + List)

• getList• GET operation without parameters• /rest/json/info

• Get• READ resource with parameters (ID)• /rest/json/info/1

• Delete• DELETE a resource

• Update• UPDATE a resource

• Create• CREATE a new resource

Page 22: RESTful modules in zf2

@walterdalmut - www.corley.it - www.upcloo.com

First of all play with routes

'router' => array(        'routes' => array(            'restful' => array(                'type' => 'Zend\Mvc\Router\Http\Segment',                'options' => array(                    'route' => '/rest[/:formatter]',                    'constraints' => array(                        'formatter' => '[a-zA-Z0-9_-]*',                    ),                ),                'may_terminate' => true,                'child_routes' => array(                    'default' => array(                        'type' => 'Zend\Mvc\Router\Http\Segment',                        'options' => array(                            'route' => '[/:controller[/:id]]',                            'constraints' => array(                                'controller' => '[a-zA-Z][a-zA-Z0-9_-]*',                                'id' => '[a-zA-Z0-9_-]*'                            ),                        ),                    ),                ),            ),        ),

• Create a base route /rest that enable also formatters• Formatter allow us to switch easly to JSON,

XML etc.• Child routes play with controller and identifiers

Page 23: RESTful modules in zf2

@walterdalmut - www.corley.it - www.upcloo.com

Formatters

• RESTful services can handle different type of messages• JSON• XML• Images• Etc

• The high modularity of ZF2 MVC implementation enable us to add different layers of abstractions and formatters is one of this.• Client must know what kind of messages type have to handle.• Post Processors are used to render messages

Page 24: RESTful modules in zf2

@walterdalmut - www.corley.it - www.upcloo.com

Formatters «configs/module.config.php»

'errors' => array(        'post_processor' => 'json-pp',        'show_exceptions' => array(         'message' => true,         'trace' => true        )    ),    'di' => array(        'instance' => array(         'alias' => array(         'json-pp' => 'Main\PostProcessor\Json',         'jsonp-pp' => 'Main\PostProcessor\Jsonp',         'image-pp' => 'Main\PostProcessor\Image'         )        )    ),

• The configuration allows us to define different «post processors»• Errors can be detailed more, for example traces, messages etc.

Page 25: RESTful modules in zf2

@walterdalmut - www.corley.it - www.upcloo.com

Wiring events (Module.php)  /** * @param MvcEvent $e */    public function onBootstrap($e)    {        /** @var \Zend\ModuleManager\ModuleManager $moduleManager */        $moduleManager = $e->getApplication()->getServiceManager()->get('modulemanager');        /** @var \Zend\EventManager\SharedEventManager $sharedEvents */        $sharedEvents = $moduleManager->getEventManager()->getSharedManager();

        $sharedEvents->attach(            'Zend\Mvc\Controller\AbstractRestfulController',            MvcEvent::EVENT_DISPATCH,            array($this, 'postProcess'),            -100        );

        $sharedEvents->attach(            'Main\Controller\InfoController',            MvcEvent::EVENT_DISPATCH,            array($e->getApplication()->getServiceManager()->get('Main\Http\Restful'), 'onDispatch'),            100        );

        $sharedEvents->attach(            'Zend\Mvc\Application',            MvcEvent::EVENT_DISPATCH_ERROR,            array($this, 'errorProcess'),            999        );    }

• Event position -100• Handle post processors

• Event position 100• Handle HTTP Method Override

• Event position 999• Handle errors

Page 26: RESTful modules in zf2

@walterdalmut - www.corley.it - www.upcloo.com

Attach Post Processor to Actions public function postProcess(MvcEvent $e)    {        $routeMatch = $e->getRouteMatch();        $formatter = $routeMatch->getParam('formatter', false);

        $di = $e->getTarget()->getServiceLocator()->get('di');

        if ($formatter !== false) {            if ($e->getResult() instanceof \Zend\View\Model\ViewModel) {                if (($e->getResult()->getVariables())) {                    $vars = $e->getResult()->getVariables();                } else {                    $vars = null;                }            } else {                $vars = $e->getResult();            }

            $postProcessor = $di->get($formatter . '-pp', array(                'request' => $e->getRequest(),                'response' => $e->getResponse(),                'vars' => $vars,            ));

            $postProcessor->process();

            return $postProcessor->getResponse();        }

        return null;    }

Page 27: RESTful modules in zf2

@walterdalmut - www.corley.it - www.upcloo.com

Example of JSON Post Processor<?phpnamespace Main\PostProcessor;

/** * */class Json extends AbstractPostProcessor{ public function process() {        $result = json_encode($this->_vars);

$headers = $this->getResponse()->getHeaders(); $headers->addHeaderLine('Content-Type', 'application/json');

$this->getResponse()->setHeaders($headers); $this->getResponse()->setContent($result); }}

<?phpnamespace Main\PostProcessor;

abstract class AbstractPostProcessor{ protected $_vars = null;    private $_request = null; private $_response = null;

    public function __construct (\Zend\Http\Request $request, \Zend\Http\Response $response, $vars = null) { $this->_vars = $vars; $this->_response = $response;        $this->_request = $request; }

public function getResponse() { return $this->_response; }

    public function getRequest()    {        return $this->_request;    }

abstract public function process();}

Page 28: RESTful modules in zf2

@walterdalmut - www.corley.it - www.upcloo.com

ZF2 RESTful Modules

Thanks for listening…

Any questions?