...
Requirements
As of version 2013.7 / 5.2, REST routes are required to use the eZ Publish 5 REST API prefix, /api/ezp/v2
. You can create new resources below this prefix.
...
- a Controller that will handle your route actions
- a Route, in your bundle's routing file
- a Controller action
- Optionally, a
ValueObjectVisitor
(if your Controller returns an object that doesn't already have a converter) - Optionally, an
InputParser
...
To create a REST controller, you need to extend the ezpublish_rest.controller.base
service, as well as the eZ\Publish\Core\REST\Server\Controller
class.
Code Block | ||
---|---|---|
| ||
services:
myRestBundle.controller.default:
class: My\Bundle\RestBundle\Rest\Controller\Default
parent: ezpublish_rest.controller.base |
Let's create a very simple controller, that has a sayHello()
method, that takes a name as an argument.
Code Block | ||||
---|---|---|---|---|
| ||||
namespace My\Bundle\RestBundle\Rest\Controller; use eZ\Publish\Core\REST\Server\Controller as BaseController; class DefaultDefaultController extends BaseController { public function sayHello( $name ) { // @todo Implement me } } |
...
Next, you need to create the REST route. We need to define the route's controller as a service since our controller was defined as such.
Code Block | ||
---|---|---|
| ||
myRestBundle_hello_world: pattern: /my_rest_bundle/hello/{name} defaults: _controller: myRestBundle.controller.default:sayHello methods: [GET] |
Warning | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
Due to
ezpublish_rest_ , or they won't be detected correctly. |
Controller action
Unlike standard Symfony 2 controllers, the REST ones don't return an HttpFoundation\Response
object, but a ValueObject
. This object will during the kernel run be converted, using a ValueObjectVisitor, to a proper Symfony 2 response. One benefit is that when multiple controllers return the same object, such as a Content item or a Location, the visitor will be re-used.
...
Code Block | ||||
---|---|---|---|---|
| ||||
namespace My\Bundle\RestBundle\Rest\ValueObjectValues; class Hello { public $name; public function __construct( $name ) { $this->name = $name; } } |
...
Code Block | ||||
---|---|---|---|---|
| ||||
namespace My\Bundle\RestBundle\Rest\Controller; use eZ\Publish\Core\REST\Server\Controller as BaseController; use My\Bundle\RestBundle\Rest\Controller\Values\Hello as HelloValue; class DefaultDefaultController extends BaseController { public function sayHello( $name ) { return new HelloValue( $name ); } } |
And that's it. Converting Outputting this object in the Response requires that we create a ValueObjectVisitor.
...
Let's create the service for our ValueObjectVisitor first.
Code Block | ||
---|---|---|
| ||
services: myRestBundle.value_object_visitor.hello: parent: ezpublish_rest.output.value_object_visitor.base class: My\Bundle\RestBundle\Rest\ValueObjectVisitor\Hello tags: - { name: ezpublish_rest.output.value_object_visitor, type: My\Bundle\RestBundle\Rest\Values\Hello } |
...
Code Block | ||||
---|---|---|---|---|
| ||||
namespace My\Bundle\RestBundle\Rest\ValueObjectVisitor; use eZ\Publish\Core\REST\Common\Output\ValueObjectVisitor; use eZ\Publish\Core\REST\Common\Output\Generator; use eZ\Publish\Core\REST\Common\Output\Visitor; class Hello extends ValueObjectVisitor { public function visit( Visitor $visitor, Generator $generator, $data ) { $this$generator->generator->startValueElement( 'Hello', $data->name ); $this$generator->generator->endValueElement( 'Hello' ); } } |
Do not hesitate to look into the built-in ValueObjectVisitors, in eZ/Publish/Core/REST/Server/Output/ValueObjectVisitor
, for more examples.
Cache handling
The easiest way to handle cache is to re-use the CachedValue
Value Object. It acts as a proxy, and adds the cache headers, depending on the configuration, for a given object and set of options.
When you want the response to be cached, return an instance of CachedValue, with your Value Object as the argument. You can also pass a location id using the second argument, so that the Response is tagged with it:
Code Block |
---|
return new CachedValue($helloValue, ['locationId', 42]); |
Input parser
What we have seen above covers requests that don't require an input payload, such as GET or DELETE. If you need to provide your controller with parameters, either in JSON or XML, the parameter struct requires an Input Parser so that the payload can be converted to an actual ValueObject.
...
First, we need to create a service with the appropriate tag in services.yml.
Code Block | ||
---|---|---|
| ||
services:
myRestBundle.input_parser.Greetings:
parent: ezpublish_rest.input.parser
class: My\Bundle\RestBundle\Rest\InputParser\Greetings
tags:
- { name: ezpublish_rest.input.parser, mediaType: application/vnd.ez.my.Greetings }
|
The mediaType attribute of the ezpublish_rest.input.parser tag maps our Content Type to the input parser.
...
Code Block | ||||
---|---|---|---|---|
| ||||
namespace My\Bundle\RestBundle\Rest\InputParser; use eZ\Publish\Core\REST\ServerCommon\Input\Parser\Base as BaseParser; use eZ\Publish\Core\REST\Common\Input\ParsingDispatcher; use My\Bundle\RestBundle\Rest\Value\Hello; use eZ\Publish\Core\REST\Common\Exceptions; class Greetings extends BaseParser { /** * @return My\Bundle\RestBundle\Rest\Value\Hello */ public function parse( array $data, ParsingDispatcher $parsingDispatcher ) { // re-using the REST exceptions will make sure that those already have a ValueObjectVisitor if ( !array_key_existsisset( $data['name',] $data ) ) throw new Exceptions\Parser( "Missing or invalid 'name' element for Greetings." ); //return thenew root level of the parsed structure is ignored Hello( $data['name'] ); } } |
Code Block | ||
---|---|---|
| ||
services: myRestBundle.controller.default: $name = $data['name'] class: My\Bundle\RestBundle\Rest\Controller\Default return new Hello( $name ); } }parent: ezpublish_rest.controller.base |
Do not hesitate to look into the built-in InputParsers, in eZ/Publish/Core/REST/Server/Input/Parser
, for more examples.