One of the biggest changes in Drupal 8 is the integration of Rest services in the core. With use of Views it become very easy to create RESTful Services.
But there are certain situations when you need to create your own custom REST Resources. There are documentations available for creating a GET request using Views and using custom module. However there isn’t much documentation available for creating Rest Resources for POST methods.
In this article I will be sharing, how to create a REST Resource for POST methods. This Rest API will create an article in Drupal 8 site from an external application.
Note: I will be using Drupal Console for generating the module and code boilerplates.
Create the module
drupal generate:module
Create the Rest Resource Plugin
drupal generate:plugin:rest:resource
The above command will create a Plugin for rest resource in your module. The url /api/custom is the url for your resource which can be accessed like localhost:8000/api/custom?_format=json
Enable the resource from RestUI
This was the easiest part. Now we need to modify the Plugin created by Drupal Console to get the Resource working for POST methods.
<?php | |
/** | |
* @file | |
* Contains Drupal\custom_rest\Plugin\rest\resource\custom_rest. | |
*/ | |
namespace Drupal\custom_rest\Plugin\rest\resource; | |
use Drupal\Core\Session\AccountProxyInterface; | |
use Drupal\rest\Plugin\ResourceBase; | |
use Drupal\rest\ResourceResponse; | |
use Symfony\Component\DependencyInjection\ContainerInterface; | |
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; | |
use Symfony\Component\HttpKernel\Exception\HttpException; | |
use Psr\Log\LoggerInterface; | |
/** | |
* Provides a resource to get view modes by entity and bundle. | |
* | |
* @RestResource( | |
* id = "custom_rest_resource", | |
* label = @Translation("Custom rest resource"), | |
* uri_paths = { | |
* "canonical" = "//api/custom" | |
* } | |
* ) | |
*/ | |
class CustomRestResource extends ResourceBase { | |
/** | |
* A current user instance. | |
* | |
* @var \Drupal\Core\Session\AccountProxyInterface | |
*/ | |
protected $currentUser; | |
/** | |
* Constructs a Drupal\rest\Plugin\ResourceBase object. | |
* | |
* @param array $configuration | |
* A configuration array containing information about the plugin instance. | |
* @param string $plugin_id | |
* The plugin_id for the plugin instance. | |
* @param mixed $plugin_definition | |
* The plugin implementation definition. | |
* @param array $serializer_formats | |
* The available serialization formats. | |
* @param \Psr\Log\LoggerInterface $logger | |
* A logger instance. | |
* @param \Drupal\Core\Session\AccountProxyInterface $current_user | |
* A current user instance. | |
*/ | |
public function __construct( | |
array $configuration, | |
$plugin_id, | |
$plugin_definition, | |
array $serializer_formats, | |
LoggerInterface $logger, | |
AccountProxyInterface $current_user) { | |
parent::__construct($configuration, $plugin_id, $plugin_definition, $serializer_formats, $logger); | |
$this->currentUser = $current_user; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { | |
return new static( | |
$configuration, | |
$plugin_id, | |
$plugin_definition, | |
$container->getParameter('serializer.formats'), | |
$container->get('logger.factory')->get('rest'), | |
$container->get('current_user') | |
); | |
} | |
/** | |
* Responds to POST requests. | |
* | |
* Returns a list of bundles for specified entity. | |
* | |
* @throws \Symfony\Component\HttpKernel\Exception\HttpException | |
* Throws exception expected. | |
*/ | |
public function post() { | |
// You must to implement the logic of your REST Resource here. | |
// Use current user after pass authentication to validate access. | |
/* | |
if(!$this->currentUser->hasPermission($permission)) { | |
throw new AccessDeniedHttpException(); | |
} | |
*/ | |
// Throw an exception if it is required. | |
// throw new HttpException(t('Throw an exception if it is required.')); | |
return new ResourceResponse("Implement REST State POST!"); | |
} | |
} |
In the above file change the Annotation lines
* uri_paths = {
* "canonical" = "//api/custom"
* }
To
* uri_paths = {
* "canonical" = "//api/custom”,
* "drupal.org/link-relations/create" = "//api/custom"
* }
Otherwise your API will be be expecting the request to use the /entity/{entity_type} endpoint, which will conflict with the endpoint provided by core.
Next we need a serializer class for normalising the data being passed.
Add serialization_class = "Drupal\node\Entity\Node", in the Annotation for the Resource.
This will ensure that the data being passed is of entity type Node.
To run the resource use a Rest Client for example Advanced Rest Client.
Now to create the node from the Rest Client change the create() function in your Resource class.
<?php | |
/** | |
* Responds to POST requests. | |
* | |
* Returns a list of bundles for specified entity. | |
* | |
* @param $node_type | |
* @param $data | |
* @return \Drupal\rest\ResourceResponse Throws exception expected. | |
* Throws exception expected. | |
*/ | |
public function post($node_type, $data) { | |
// You must to implement the logic of your REST Resource here. | |
// Use current user after pass authentication to validate access. | |
if (!$this->currentUser->hasPermission('access content')) { | |
throw new AccessDeniedHttpException(); | |
} | |
$node = Node::create( | |
array( | |
'type' => $node_type, | |
'title' => $data->title->value, | |
'body' => [ | |
'summary' => '', | |
'value' => $data->body->value, | |
'format' => 'full_html', | |
], | |
) | |
); | |
$node->save(); | |
return new ResourceResponse($node); | |
} |
All done! This, is how we can create a node using Rest Resource for POST methods in Drupal 8. Here is the complete code for the Resource Plugin file:
<?php | |
namespace Drupal\ccms_rest\Plugin\rest\resource; | |
use Drupal\Core\Session\AccountProxyInterface; | |
use Drupal\node\Entity\Node; | |
use Drupal\rest\Plugin\ResourceBase; | |
use Drupal\rest\ResourceResponse; | |
use Symfony\Component\DependencyInjection\ContainerInterface; | |
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; | |
use Psr\Log\LoggerInterface; | |
/** | |
* Provides a resource to get view modes by entity and bundle. | |
* | |
* @RestResource( | |
* id = "site_bundle_post", | |
* label = @Translation("Custom rest resource"), | |
* serialization_class = "Drupal\node\Entity\Node", | |
* uri_paths = { | |
* "canonical" = "/api/custom", | |
* "https://www.drupal.org/link-relations/create" = "/api/custom" | |
* } | |
* ) | |
*/ | |
class SiteBundlePost extends ResourceBase { | |
/** | |
* A current user instance. | |
* | |
* @var \Drupal\Core\Session\AccountProxyInterface | |
*/ | |
protected $currentUser; | |
/** | |
* Constructs a Drupal\rest\Plugin\ResourceBase object. | |
* | |
* @param array $configuration | |
* A configuration array containing information about the plugin instance. | |
* @param string $plugin_id | |
* The plugin_id for the plugin instance. | |
* @param mixed $plugin_definition | |
* The plugin implementation definition. | |
* @param array $serializer_formats | |
* The available serialization formats. | |
* @param \Psr\Log\LoggerInterface $logger | |
* A logger instance. | |
* @param \Drupal\Core\Session\AccountProxyInterface $current_user | |
* A current user instance. | |
*/ | |
public function __construct( | |
array $configuration, | |
$plugin_id, | |
$plugin_definition, | |
array $serializer_formats, | |
LoggerInterface $logger, | |
AccountProxyInterface $current_user) { | |
parent::__construct($configuration, $plugin_id, $plugin_definition, $serializer_formats, $logger); | |
$this->currentUser = $current_user; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { | |
return new static( | |
$configuration, | |
$plugin_id, | |
$plugin_definition, | |
$container->getParameter('serializer.formats'), | |
$container->get('logger.factory')->get('ccms_rest'), | |
$container->get('current_user') | |
); | |
} | |
/** | |
* Responds to POST requests. | |
* | |
* Returns a list of bundles for specified entity. | |
* | |
* @param $node_type | |
* @param $data | |
* @return \Drupal\rest\ResourceResponse Throws exception expected. | |
* Throws exception expected. | |
*/ | |
public function post($node_type, $data) { | |
// You must to implement the logic of your REST Resource here. | |
// Use current user after pass authentication to validate access. | |
if (!$this->currentUser->hasPermission('access content')) { | |
throw new AccessDeniedHttpException(); | |
} | |
$node = Node::create( | |
array( | |
'type' => $node_type, | |
'title' => $data->title->value, | |
'body' => [ | |
'summary' => '', | |
'value' => $data->body->value, | |
'format' => 'full_html', | |
], | |
) | |
); | |
$node->save(); | |
return new ResourceResponse($node); | |
} | |
} |