How to Create Custom RESTful Resources in Drupal 8

Tags: Drupal 8


The RESTful Web Services API is new in Drupal 8. Expose entities as RESTful resources either to build a decoupled Drupal site, to let a native mobile iOS/Android app talk consume/feed a Drupal site, or to integrate with some web service which is amazing! In some use cases, these default REST resources are not sufficient to fulfill the requirements. In this situation, we have to write a custom REST resource. Let’s examine how we can develop this.

Before proceeding further, first let’s see a use case where the user has to write a custom REST resource. If a user wants to submit a file along with some metadata using a single webservice call currently it is not possible with the default REST resources.

Now, let’s build our own REST resource for the above use case.

Test Resource module (test_resource.module) contains the following files:


  • test_resource.info.yml - Module .info file
  • test_resource.module - Empty module file
  • test_resource.routing.yml - Routing file with all the API paths

test_resource.route:

path: test/ws

defaults:

  _controller: 'Drupal\test_resource\Controller\TestResourceController::create'

methods:  [POST]

options:

  _auth: [ 'basic_auth' ]

requirements:

    _user_is_logged_in: 'TRUE'

    _custom_access: '\Drupal\test_resource\Controller\TestResourceController::access'


  • test_resource.permissions.yml - Permission file to define custom permission

access_webservice:

title: 'Access webservice'

description: 'Gives permission to the user to access the webservice.'


  • test_resource.services.yml - Routing event subscriber.

services:

tal_webservice.route_subscriber:

  class: 'Drupal\test_resource\Routing\RouteSubscriber'

  tags:

    - { name: event_subscriber }


  • src/Routing/RouteSubscriber.php

It is good practice to use a CSRF token with REST request. Here, at the event subscriber class for routing, we are setting CSRF token as a requirement.


<?php


namespace Drupal\test_resource\Routing;


use Drupal\Core\Routing\RouteSubscriberBase;

use Symfony\Component\Routing\RouteCollection;


/**

* Route subscriber class.

*/

class RouteSubscriber extends RouteSubscriberBase {


/**

 * {@inheritdoc}

 *

 * Add a CSRF-Token  as requirement.

 */

public function alterRoutes(RouteCollection $collection) {

  if ($route = $collection->get('tal_webservice.ingredient')) {

    $route->setRequirement('_access_rest_csrf', 'TRUE');

  }

}


}


  • src/Controller/TestResoutceController.php

<?php


namespace Drupal\test_resource\Controller;


use Drupal\Core\Session\AccountInterface;

use Symfony\Component\HttpFoundation\Request;

use Symfony\Component\HttpFoundation\JsonResponse;

use Drupal\Core\Access\AccessResult;


/**

* Process Resource REST request.

*/

class TestResourceController {


/**

 * Allow access for logged-in, authenticated users.

 *

 * @param \Drupal\Core\Session\AccountInterface $account

 *   Run access checks for this account.

 *

 * @return bool

 *   Return true or false on the basis of criteria specified.

 */

public function access(AccountInterface $account) {

  return AccessResult::allowedIf($account->hasPermission('access_webservice'));

}


/**

 * Process REST request.

 */

public function create(Request $request) {


  if (strpos($request->headers->get('Content-Type'), 'multipart/form-data;') !== 0) {

    $res = new JsonResponse();

    $res->setStatusCode(400, 'must submit multipart/form-data');

    return $res;

  }


  // Validate the request data.

  $error = $this->validate($request);

  if ($error !== TRUE) {

    return new JsonResponse($error, 400);

  }


// Here your business logic goes.

  try {

      $data = file_get_contents($_FILES['files']['tmp_name']);

      $destination = 'public://' . $request->request->get('file_name');

      $file = file_save_data($data, $destination, FILE_EXISTS_REPLACE);

     }

  catch (\Exception $error) {

    return new JsonResponse($error->getMessage(), 400);

  }


  return new JsonResponse(‘Request processed successfully’, 200);

}



/**

 * Validate the POST data.

 */

private function validate(Request $request) {

  $error = '';


  // Material Id is mandatory.

  if ($request->request->get(metadata) == '') {

    $error = t('Invalid post data, metadata was missing.');

    return $error;

  }


     return TRUE;

}


}

 

That's it! We hope you found this post helpful.

If you have any questions, do share them with us in the comments below. 

Abhishek Dhariwal, Technical Architect
Posted by

Abhishek Dhariwal, Technical Architect

After logging off, he continues his day as a detail-oriented electronics geek, dedicated Netflix fanatic, and avid tennis player.