Zend Framework 2 : Step by Step create RESTful Application
Beside AbstractActionController, ZF2 has AbstractRestfulController that provide RESTful implementation that simply maps HTTP request methods to controller methods : GET maps to either get() or getList(), POST maps to create(), PUT maps to update(), and DELETE maps to delete().
REpresentational State Transfer (REST) is a style of software architecture for distributed systems such as the World Wide Web. REST has emerged as a predominant Web service design model. REST-style architectures consist of clients and servers. Clients initiate requests to servers; servers process requests and return appropriate responses. Requests and responses are built around the transfer of representations of resources. A resource can be essentially any coherent and meaningful concept that may be addressed. A representation of a resource is typically a document that captures the current or intended state of a resource.
I want to show you how to create simple RESTful Application.
1. First step, create your Restful Module like the following :
2. Configure module.config.php
return array( 'controllers' => array( 'invokables' => array( 'SanRestful\Controller\SampleRestful' => 'SanRestful\Controller\SampleRestfulController', 'SanRestful\Controller\SampleClient' => 'SanRestful\Controller\SampleClientController', ), ), 'router' => array( 'routes' => array( 'SanRestful' => array( 'type' => 'Literal', 'options' => array( // Change this to something specific to your module 'route' => '/san-restful', 'defaults' => array( // Change this value to reflect the namespace in which // the controllers for your module are found '__NAMESPACE__' => 'SanRestful\Controller', 'controller' => 'SampleRestful', ), ), 'may_terminate' => true, 'child_routes' => array( 'client' => array( 'type' => 'Segment', 'options' => array( 'route' => '/client[/:action]', 'constraints' => array( 'controller' => '[a-zA-Z][a-zA-Z0-9_-]*', ), 'defaults' => array( 'controller' => 'SampleClient', 'action' => 'index' ), ), ), ), ), ), ), );
3.Create The SampleRestfulController
namespace SanRestful\Controller; use Zend\Mvc\Controller\AbstractRestfulController; class SampleRestfulController extends AbstractRestfulController { public function get($id) { $response = $this->getResponseWithHeader() ->setContent( __METHOD__.' get current data with id = '.$id); return $response; } public function getList() { $response = $this->getResponseWithHeader() ->setContent( __METHOD__.' get the list of data'); return $response; } public function create($data) { $response = $this->getResponseWithHeader() ->setContent( __METHOD__.' create new item of data : <b>'.$data['name'].'</b>'); return $response; } public function update($id, $data) { $response = $this->getResponseWithHeader() ->setContent(__METHOD__.' update current data with id = '.$id. ' with data of name is '.$data['name']) ; return $response; } public function delete($id) { $response = $this->getResponseWithHeader() ->setContent(__METHOD__.' delete current data with id = '.$id) ; return $response; } // configure response public function getResponseWithHeader() { $response = $this->getResponse(); $response->getHeaders() //make can accessed by * ->addHeaderLine('Access-Control-Allow-Origin','*') //set allow methods ->addHeaderLine('Access-Control-Allow-Methods','POST PUT DELETE GET'); return $response; } }
4. Create the client Controller
namespace SanRestful\Controller; use Zend\Mvc\Controller\AbstractActionController; use Zend\Http\Client as HttpClient; class SampleClientController extends AbstractActionController { public function indexAction() { $client = new HttpClient(); $client->setAdapter('Zend\Http\Client\Adapter\Curl'); $method = $this->params()->fromQuery('method', 'get'); $client->setUri('http://localhost:80'.$this->getRequest()->getBaseUrl().'/san-restful'); switch($method) { case 'get' : $client->setMethod('GET'); $client->setParameterGET(array('id'=>1)); break; case 'get-list' : $client->setMethod('GET'); break; case 'create' : $client->setMethod('POST'); $client->setParameterPOST(array('name'=>'samsonasik')); break; case 'update' : $data = array('name'=>'ikhsan'); $adapter = $client->getAdapter(); $adapter->connect('localhost', 80); $uri = $client->getUri().'?id=1'; // send with PUT Method, with $data parameter $adapter->write('PUT', new \Zend\Uri\Uri($uri), 1.1, array(), http_build_query($data)); $responsecurl = $adapter->read(); list($headers, $content) = explode("\r\n\r\n", $responsecurl, 2); $response = $this->getResponse(); $response->getHeaders()->addHeaderLine('content-type', 'text/html; charset=utf-8'); $response->setContent($content); return $response; case 'delete' : $adapter = $client->getAdapter(); $adapter->connect('localhost', 80); $uri = $client->getUri().'?id=1'; //send parameter id = 1 // send with DELETE Method $adapter->write('DELETE', new \Zend\Uri\Uri($uri), 1.1, array()); $responsecurl = $adapter->read(); list($headers, $content) = explode("\r\n\r\n", $responsecurl, 2); $response = $this->getResponse(); $response->getHeaders()->addHeaderLine('content-type', 'text/html; charset=utf-8'); $response->setContent($content); return $response; } //if get/get-list/create $response = $client->send(); if (!$response->isSuccess()) { // report failure $message = $response->getStatusCode() . ': ' . $response->getReasonPhrase(); $response = $this->getResponse(); $response->setContent($message); return $response; } $body = $response->getBody(); $response = $this->getResponse(); $response->setContent($body); return $response; } }
5. Testing :
Call in browser : http://localhost/zf2app/public/san-restful/client?method=update
Btw, I published the sourcecode at my github account : https://github.com/samsonasik/SanRestful
References :
1. http://en.wikipedia.org/wiki/Representational_state_transfer
2. http://stackoverflow.com/questions/10384778/curl-request-with-headers-separate-body-a-from-a-header
3. http://stackoverflow.com/questions/1691530/sending-a-file-via-http-put-in-php
4. http://zf2.readthedocs.org/en/latest/modules/zend.mvc.controllers.html#the-abstractrestfulcontroller
[…] https://samsonasik.wordpress.com/2012/10/31/zend-framework-2-step-by-step-create-restful-application/ […]
Hi, another great example,
Do you now how you can make a authorisation layer on this with something like bjyauthorise?
i have not try bjyauthorize yet
Hi,
Thank you for this useful example. I think it would be awesome if you can explain an approach to secure the restful api using basic authentication or digest over https, assuming the client is in other server than the rest api.
Thank you again!
You’re welcome, good idea, thanks for the suggestion.
Hi again.
I’m trying to understand why you are using connect/write/read in update and delete methods and why you just are using send() for the rest of methods. I looked into Zend\Http\Client.php send() method code and I see that there it is used connect/write/read too.
Could you please help me to understand?
Thank you!
for PUT and DELETE, Zend doesn’t have setParameterMETHOD. so use adapter to Send request to the remote server with its request method.
Thanks a lot for your fast response and for clarifying this.
Now I understand.
Great blog!
You’re welcome 😉
case ‘update’ :
$client->setMethod(‘PUT’);
$client->setParameterGET(array(‘id’=>1));
$client->setParameterPOST(array(‘name’=>’ikhsan’));
break;
case ‘delete’ :
$client->setMethod(‘DELETE’);
$client->setParameterGET(array(‘id’=>1));
break;
thanks, i’ll take a look on it.
For me work’s!
nice Blog, is helping me a lot
Hi Samsonasik,
I have an Ajax request that contains some params (username, password) to authenticate. The method I use is POST. I use RestfulController to handle params and do some business logic and return json result. With get($id) method only accept one param, how can I get username, and password to do authentication and return?
Thank you!
if you use POST, you should use create() function that pass $data that can be array.
That’s right. Thank you a lot.
HI, Samsonasik,
I am not getting where this method is from: $this->getResponseWithHeader() – it does not seem to exist in the response class, am I just missing something obvious?
Thank you
This what the stuff I was looking for 🙂
var_dump(
$this->params()->fromQuery(),
$this->getRequest()->getQuery(),
$this->getEvent()->getRouteMatch()->getParams()
);
great !
Hi Abdul,
A couple of questions:
How do you think is the better way to make a search using variable fields? How the getList() method from the restful controller will receive the parameters?
Thanks a lot for any tip!
Best!
No, if you want to pass a parameter, use get().
Ok, but I wonder how can I make a search and return a set of records in a restful webservice like this one?
Or by example if I want to do some pagination sending the offset and limit values to the webservice in order to get a subset of records with the getList() method, I am confused with this scenario.
I apologize if this sounds to obvious but I can’t find the way to do it.
Best regards
Excellent overview! Really helps me with a component I am working on for my app. Curious though:
in module.config.php you have:
‘route’ => ‘/client[/:action]’,
‘constraints’ => array(
‘controller’ => ‘[a-zA-Z][a-zA-Z0-9_-]*’,
),
Should the constraint be on the action rather than the controller? Or is it actually necessary at all. The code works both ways (with ‘controller’ or ‘action’) so I am a bit confused. Otherwise this is a really great starting point for restful API’s in an app.
Peace / Aaron
you’re right, Thank You.
This is a good example. I want to make the API with authentication. If you can help me, how to get it done.
what do you offer to me ? :p
Hello Sam,
How are you send the $file data using restful api?
Thank you for this useful example.
I tried implement your example in my project but I have this error :
A 404 error occurred
Page not found.
The requested controller was unable to dispatch the request.
Controller:
SanRestful\Controller\SampleRestful
No Exception available
an idea ??
1. have you register the module at config/application.config.php ?
2. it seems you access SampleRestfulController, why? you should access the SampleClientController like I describe at the post : http://localhost/YourZF2app/public/san-restful/client?method=update
3. Try it https://github.com/samsonasik/SanRestful
Thank you for your response
it was a problem with ACL.
By cons I have another question :
I want to use a class in models with a method in which I will put the same code of the index action :
$client->setUri(‘http://localhost:80/Patient-webService’);
switch($method) {
case ‘get’ :
/*$client->setMethod(‘GET’);
$client->setParameterGET(array(‘id’=>1));*/
/*exit(‘lopend !!!’);
break;
case ‘get-list’ :
//$client->setMethod(‘GET’);
exit(‘lopend !!!’);
break;
case ‘create’ :
$client->setMethod(‘POST’);
$client->setParameterPOST($post);
break;
case ‘update’ :
exit(‘lopend !!!’);
case ‘delete’ :
exit(‘lopend !!!’);
}
but it doesn’t call to the restful controller
Hi, I have been following your tutorials for a while, I have a question how can i use DynamoDb as back end in the api?
Any help would be great.
[…] https://samsonasik.wordpress.com/2012/10/31/zend-framework-2-step-by-step-create-restful-application/ […]
very nice example but can i call the url like
http://localhost/zf2app/public/san-restful/client/method/update
instead of http://localhost/zf2app/public/san-restful/client?method=update
please read the docs http://framework.zend.com/manual/2.3/en/user-guide/routing-and-controllers.html and do effort
can you please tell me where do these two controllers are linked??
Also can you tell me where do i put the database connection model , in Restful Controller or AbstractAction Controller?
is it fine to use SampleRestfulController ? for all method
I have an issue, I used SampleClientController to find all parameters passed using get method and used these parameters in SampleRestfulController, it’s working fine on local but on main server it show an issue given below:
Strict Standards: Declaration of RestfulApi\Controller\SampleRestfulController::get() should be compatible with Zend\Mvc\Controller\AbstractRestfulController::get($id) in /var/www/html/gaba/module/RestfulApi/src/RestfulApi/Controller/SampleRestfulController.php on line 18
It come because, here I am trying to get all parameters values not only id.But I need to send more params so how it will be solve????
more then one parameters should be handle by create($data) method, use POST instead.
But how I will access the post data in SampleClientController ? Using $method = $this->params()->fromQuery(‘method’); I get method but not able to find all post data.
Please reply for this problem, how post data will access in SampleRestfulController create method ?
I tried $this->getRequest()->isPost() method but not getting posted data.
Hi,Please ignore previous reply for post data problem.
I used post method and get posted data.In SampleClientController I used
$request = $this->getRequest();
$data = (array) $request->getPost();
and get all data and after use $client->setParameterPOST($data); to set data and access data from SampleRestfulController create method.
Is this right???? or other better process ?
there is $request->getPost()->toArray() for that 😉
thanks…
Hi samsonasik,
I need to create more than one api (e.g login, session_update, user_create etc ) using post method ,
then how it should be implement , currently i use $request->getPost()->toArray() method to get the request data in which I pass other params i.e api => ‘apiname’ and in SampleRestfulController I use switch case on apiname and process that functionality.
Is this right method or should create other controller for each api ?
you can make other controller(s) for separate usage to make it easier to manage, managing to much logic in one file should be avoided.
Hi Samsonik,
This is a great blog. But, can you make a blog using PhlyRestfully module from Zend Framework 2? That would be more awesome if you have time.
Thanks
I would be appreciated if you could buy me a coffee before https://samsonasik.wordpress.com/buy-me-a-coffee/
Thank you for this example, it helps understanding. But when I tried it with a simple get (public/san-restful/client), it took some time on each request (1.5 to 2.5 seconds). 99% of spent time is in the SampleClientController on this line : $response = $client->send();
Could there be a faster version ?
it uses curl, so i think it depends on your server configuration 😉
Thanks, I don’t exactly understand why this worked, but I changed the port I listen to in my httpd.conf (8080 instead of 80) and adjusted the ServerName and it’s faster now 🙂
hi abdul the question is :if you want to get the most ten popular clients what should you do adding a new controller ?
hint: specific controller for specific rest task.
Am getting error like,
Error in cURL request: SSL certificate problem: self signed certificate.
please can u suggest what should i do for the above issue.
you may need to set `sslverifypeer` => false for it, see the documentation to configure it https://docs.laminas.dev/laminas-http/client/adapters/#the-socket-adapter