Providing ReST services
Available since Stubbles 1.1.0
Stubbles eases development of ReST services by providing the basic infrastructure and letting the developer concentrate on the business logic of a ReST service. If you want to know more about what ReST is see Wikipedia.
Setting up ReST infrastructure
Configure ReST processor
In your index.php file within the docroot, add the following call for the net::stubbles::websites::ioc::stubWebsiteBindingModule:
stubApp::createFrontController(stubWebsiteBindingModule::createWithXmlProcessorAsDefault('interceptors')
->enableRest('interceptors')
)
->process();
or alternatively, if you only have ReST services in the app:
stubApp::createFrontController(stubWebsiteBindingModule::createWithRestProcessorAsDefault('interceptors'))
->process();
Of course you might want to add other binding modules to the stubApp::createFrontController() call as well depending on which bindings you want to have configured.
In your .htaccess configure the following rewrite rules (the rule might differ on your system or on how you wish the rest services should be accessed):
RewriteEngine on
RewriteCond %{REQUEST_URI} !^/index.php
RewriteRule ^([^/]+)(/(.*)?)? index.php?processor=rest&handler=$1&dispatch=$3 [L]
The most important thing here is to rewrite the requests to the ReST processor, and that the first part must become the handler param value and the remaining parts the dispatch param value.
Configure ReST handlers and formatters
Create a file rest.ini in the config directory of the project which should offer ReST services. It should have the following format:
[handler] helloworld="your::package::rest::HelloWorld" another="your::package::rest::AnotherCoolClass" [formatter] application/json="net::stubbles::service::rest::format::stubJsonFormatter" application/xml="net::stubbles::service::rest::format::stubXmlFormatter" [errorformatter] application/json="net::stubbles::service::rest::format::stubJsonFormatter" application/xml="net::stubbles::service::rest::format::stubXmlFormatter"
The handler part should contain all classes which offer ReST services. The key will be used to identify the class named in the value. See below on more informations about handlers.
The formatter part marks classes implementing the net::stubbles::service::rest::format::stubFormatter interface, while the errorformatter part marks classes implementing the net::stubbles::service::rest::format::stubErrorFormatter. The classes given in the example are provided by Stubbles and implement both interfaces. The key in the configuration will be used to identify the (error) formatter to be used based on the Accept header send with the HTTP request. See below for more informations about formatters.
Write your own ReST handler
A simple example
A ReST handler can be any arbitrary class, it does not need to implement a special interface or extend a certain class except net::stubbles::lang::stubBaseObject, but you want to extend from this anyway when using Stubbles.
So let's write a simple Hello World class:
<?php
class HelloWorld extends stubBaseObject
{
/**
* some weird example with proper doc comments
*
* @return string
*/
public function sayHello()
{
return 'Hello World!';
}
}
?>
Now, how do we make this available as ReST service? First, the class needs to be configured as handler in the config/rest.ini file as described above. Second, we need to add an annotation so that ReST processor knows what to do:
<?php
class HelloWorld extends stubBaseObject
{
/**
* some weird example with proper doc comments
*
* @return string
* @RestMethod(requestMethod='GET')
*/
public function sayHello()
{
return 'Hello World!';
}
}
?>
With the annotation, the ReST processor knows that on each GET request for http://example.com/helloworld it needs to execute sayHello(). If you use a POST request instead, you will receive an error 405 ("Method Not Allowed") with an additional "Allow" header which lists the allowed request methods.
As you can see the class does not have to care to put the data into a proper format. It just needs to return the data which will be formatted by ReST processor using a formatter (see below).
Adding more complexity
In the next step we want to extend our HelloWorld service so it says specialised messages depending on request data.
<?php
class HelloWorld extends stubBaseObject
{
/**
* some weird example with proper doc comments
*
* @return string
* @RestMethod(requestMethod='GET')
*/
public function sayHello()
{
return 'Hello World!';
}
/**
* another weird example with proper doc comments
*
* @return string
* @RestMethod(requestMethod='GET', path='from/lolcat')
*/
public function checkLolcat()
{
return 'I can haz Cheeseburger?';
}
/**
* another weird example with proper doc comments
*
* @return string
* @RestMethod(requestMethod='GET', path='from/swineflu')
*/
public function getCaughtBySwineflu()
{
return 'Now you are infected.';
}
}
?>
When requesting http://example.com/helloworld it still says "Hello World!", but if you request http://example.com/helloworld/from/lolcat it now says "I can haz Cheeseburger?", and when accessing http://example.com/helloworld/from/swineflu it says "Now you are infected.".
Another way could be to give additional data into the method as parameters:
<?php
class HelloWorld extends stubBaseObject
{
/**
* some weird example with proper doc comments
*
* @return string
* @RestMethod(requestMethod='GET')
*/
public function sayHello($target)
{
return 'Hello ' . $target;
}
}
?>
When requesting http://example.com/helloworld you will receive an error 400 now because a parameter is missing. If you call http://example.com/helloworld/schst instead the response says "Hello schst". This works with every data given after helloworld/ in the URL. There is no limit to how much parameters can be processed, however you should limit it for sanity reasons: :-)
<?php
class HelloWorld extends stubBaseObject
{
/**
* some weird example with proper doc comments
*
* @return string
* @RestMethod(requestMethod='GET')
*/
public function sayHello($from, $target)
{
return $from . ' says: Hello ' . $target;
}
}
?>
Calling http://example.com/helloworld/mikey/schst now gives "mikey says: Hello schst". You can also change the separator between the parameters:
<?php
class HelloWorld extends stubBaseObject
{
/**
* some weird example with proper doc comments
*
* @return string
* @RestMethod(requestMethod='GET', pathSeparator=',')
*/
public function sayHello($from, $target)
{
return $from . ' says: Hello ' . $target;
}
}
?>
To receive "mikey says: Hello schst" you have to call http://example.com/helloworld/mikey,schst now.
Important note: the parameters are passed through unchecked into your method! You should validate it to be sure no harmful data is passed into your method. (Enabling filters is currently on the plan for a later version of Stubbles.)
Of course all those different options can be combined.
Formatting the response
As describes above the called method do not have to care about the format of the response, it just needs to return the pure data, be it a simple string as in the examples above or even complex object structures. The response is formatted by implementations of the net::stubbles::service::rest::format::stubFormatter interface. Stubbles itself delivers three different formatter implementations: one void formatter which simply turns any result into nothing, a JSON formatter which serializes the return value into JSON, and a XML formatter which uses the Stubbles XML Serializer to serialize the return value into XML.
Of course those are very general, and if you need specific response formats you can implement your own formatter class. You can even go as far to have a different formatter for each of your methods:
<?php
class HelloWorld extends stubBaseObject
{
/**
* some weird example with proper doc comments
*
* @return string
* @RestMethod(requestMethod='GET', formatterClass=your::own::MyFormatter.class)
*/
public function sayHello()
{
return 'Hello World!';
}
/**
* another weird example with proper doc comments
*
* @return string
* @RestMethod(requestMethod='GET', path='from/lolcat', formatterClass=your::own::LolCatFormatter.class, errorFormatterClass=your::own::LolCatErrorFormatter.class)
*/
public function checkLolcat()
{
return 'I can haz Cheeseburger?';
}
}
?>
If no specific formatter class is given it will always fall back to the formatter classes configured in config/rest.ini. If there's more than one formatter class configured in this file the ReST processor will try to find the best match in compliance with the Accept header send with the HTTP request.
