Skip to content
This repository was archived by the owner on Jan 16, 2019. It is now read-only.

Docs Service Rest

Frank Kleine edited this page Apr 7, 2012 · 1 revision

Table of Contents

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 with Stubbles <= 1.6.x

Applies to Stubbles 1.6.x and lower

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 <sup>([</sup>/]+)(/(.*)?)? 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 processor with Stubbles >= 1.7.x

Applies to Stubbles 1.7.0 and greater

In your index.php file within the docroot, add the following call for the net::stubbles::webapp::stubUriConfigurator:

stubUriConfigurator::createWithXmlProcessorAsDefault()
                   ->provideRest()

or alternatively, if you only have ReST services in the app:

stubUriConfigurator::createWithRestProcessorAsDefault()

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.

Of course all those different options can be combined.

Input parameter validation

If you create URLs where parts of the URL are passed as parameter into the method you should take care of input validation. By default the parameters are passed through unchecked into your method! You should validate it to be sure no harmful data is passed into your method.

Starting with release 1.3.0 the single parameters can be annotated with filter annotations, so that you don't have to worry with parameter validation in your method:

<?php
class HelloWorld extends stubBaseObject
{
    /**
***some weird example with proper doc comments
     *
***@return  string
***@RestMethod(requestMethod='GET', pathSeparator=',')
***@Filter[StringFilter]{from}(minLength=3, maxLength=10)
***@Filter[StringFilter]{to}(minLength=3, maxLength=10)
***/
    public function sayHello($from, $target)
    {
        return $from . ' says: Hello ' . $target;
    }
}
?>

Here the environment will make sure that $from and $to are filtered with the annotated filters so the method can be sure the passed values are valid. In case the filters fail to validate the value the handler method will not be called, instead the REST processor will respond with status code 400 Bad Request.

Of course you can use this to transform the input value into meaningful objects:

<?php
class HelloDate extends stubBaseObject
{
    /**
***some weird example with proper doc comments
     *
***@return  string
***@RestMethod(requestMethod='GET', pathSeparator=',')
***@Filter[DateFilter]{date}()
***@Filter[StringFilter]{to}(minLength=3, maxLength=10)
***/
    public function sayHello(stubDate $date, $target)
    {
        return 'On ' . $date->format('Y-m-d') . ': Hello ' . $target;
    }
}
?>

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.

Clone this wiki locally