RESTful Web Services Module Basics

A Solution for Working with Entities over the Network

By

This article was published in the print magazine Drupal Watchdog, Volume 4 Issue 2, 2014-09, on pages 30-33, by Tag1 Publishing. The magazine was distributed at Drupalcon Amsterdam 2014, 2014-09-29. The article was also published on their website.

Drupal 7 does not have built-in support for representational state transfer (REST) functionality. However, the RESTful Web Services module is arguably the most efficient way to provide resource representations for all the entity types, by leveraging Drupal's powerful Entity API. Unmodified, the module makes it possible to output the instances of the core entity types — node, file, and user — in JSON or XML format. Further entity type resources and formats are possible utilizing hooks in added code.

As with any REST solution, the RESTful Web Services module supports all four of the fundamental operations of data manipulation: create, read, update, and delete (CRUD). The corresponding RESTful API HTTP methods are POST, GET, PUT, and DELETE, respectively.

Anyone hoping to learn and make use of this module — especially for the first time — will likely be frustrated by the current project documentation, which is incomplete, uneven, and lacking clear examples. This article — a brief overview — is intended to introduce what is possible with this module, and help anyone getting started with it.

We begin with a clean Drupal 7 installation (using the Standard profile) running on a virtual host with the domain name "drupal_7_test". After installing and enabling the module, we find that it does not have the configuration user interface one might expect. In the demonstration code below, we focus on the node entity type for brevity.

Nabbing a Node

The simplest operation — reading an entity instance — is performed using a simple GET request containing the machine name of the entity type and the entity's ID.

To allow an anonymous user to read the node using REST, it is insufficient to grant that role the "View published content". Moreover, the "Bypass content access control" permission has no effect. Rather, the module establishes an "Access the resource" permission for each entity type, which also must be enabled for anonymous users. (When testing anonymous access from a web page, be certain you're not logged into the website in the same browser, because then you are already authenticated.)

In this example, we read the fields in the first page in the Drupal database (node/1), which has a title of "Page Title" and a body field of only "Body text". To display the information in JSON format, in a web browser, the URL would be: http://drupal_7_test/node/1.json

The results are, as expected, in JSON:

{"body":{"value":"\u003Cp\u003EBody text.\u003C\/p\u003E\n","summary":"","format":"full_html"},"nid":"1","vid":"1","is_new":false,"type":"page","title":"Page Title","language":"und","url":"http:\/\/drupal_7_test\/node\/1","edit_url":"http:\/\/drupal_7_test\/node\/1\/edit","status":"1","promote":"0","sticky":"0","created":"1402821494","changed":"1403394617","log":"","revision":null,"comment":"1","comments":[],"comment_count":"0","comment_count_new":"0"}

If you get an HTTP 403 error ("Forbidden"), verify the two required permission settings for anonymous users accessing nodes.

To display the same information as XML, we need only alter the path extension: http://drupal_7_test/node/1.xml

The information is the same, but in a substantially different format:

<node>
    <body>
        <value>
            Body text
        </value>
        <summary/>
        <format>full_html</format>
    </body>
    <nid>1</nid>
    <vid>1</vid>
    <is_new/>
    <type>page</type>
    <title>Page Title</title>
    <language>und</language>
    <url>http://drupal_7_test/node/1</url>
    <edit_url>http://drupal_7_test/node/1/edit</edit_url>
    <status>1</status>
    <promote>0</promote>
    <sticky>0</sticky>
    <created>1402821494</created>
    <changed>1403394617</changed>
    <log/>
    <comment>1</comment>
    <comments/>
    <comment_count>0</comment_count>
    <comment_count_new>0</comment_count_new>
</node>

To get all of the nodes — or at least the first 100, by default — remove the entity ID: http://drupal_7_test/node.json

If we again want to access that first node only, but not use a URL, then we can employ cURL on the commandline:

curl -X GET http://drupal_7_test/node/1.json

More descriptive options, such as --request GET, can be chosen. To see details of operations that may be helpful for debugging, add the -v option (a.k.a. --verbose).

If you do not want anonymous REST requests to have access to your website's content, use HTTP authorization. Apparently, the simplest way to do so is to enable the Basic Authentication Login submodule (restws_basic_auth). Connect by utilizing a username that has the appropriate "Access the resource" permission and begins with the (mandatory lowercase) string "restws". Assuming that the user "restws_user" has a password of "password", then we can display the first node with this command:

curl -X GET http://drupal_7_test/node/1.json -u restws_user:password

The aforesaid username pattern could be represented as the regular expression /^restws.*/. That pattern can be customized in the website's settings.php file. For instance, to grant authorization only to those usernames beginning with the string "WS_", one adds the following (case-sensitive) regex to the settings.php file:

$conf[ 'restws_basic_auth_user_regex' ] = '/^WS_.*/';

Authentication is a substantial topic unto itself, and here we will not delve into cookie and session handling.

Creating or Changing a Node

Rather than using the conventional content management UI for adding an entity resource, we can do it with REST — specifically, a POST request. There are several ways to accomplish this: using the core function drupal_http_request(), or cURL PHP code, or cURL on the commandline, which we will use. Returning to the XML format, we can use a file based upon the one output earlier, containing only those elements needed to create a new node with some content in the body field:

<node>
    <type>page</type>
    <title>New Page Title</title>
    <body>
        <value>
            New body text
        </value>
    </body>
</node>

Assuming that the file is named create.xml, we can create a new node with this command:

curl -X POST -H "Content-Type: application/xml" -d @create.xml http://drupal_7_test/node -u restws_user:password

Assuming the AUTO_INCREMENT value of the nid field in the node table is 1, for example, then the output in the command window would indicate that a second node, node/2, had been created:

<?xml version="1.0" encoding="utf-8"?>
<uri resource="node" id="2">http://drupal_7_test/node/2</uri>

If the XML file were missing an essential element, such as the node type, then we would receive an error message such as: 406 Not Acceptable: Missing bundle: type

Modifying an existing node is similar, but instead of using the POST method, use PUT and specify a node ID. The XML file needs even fewer elements:

<node>
    <title>Modified Page Title</title>
    <body>
        <value>
            Modified body text
        </value>
    </body>
</node>

The cURL command is straightforward:

curl -X PUT -H "Content-Type: application/xml" -d @modify.xml http://drupal_7_test/node/2 -u restws_user:password

The output will not include any <uri> element containing the node ID, since no new one was created.

Nuking a Node

To remove any entity resource — in this case, the first node in the database — use the DELETE method:

curl -X DELETE http://drupal_7_test/node/1 -u restws_user:password

The baffling yet correct output is merely:

[]

Using this module, one can also: filter the results when querying for multiple resources (by specifying the user ID or taxonomy ID); sort by one of the properties (either in ascending or descending direction); change the default limit of 100 resource references. For debugging purposes, you can enable the logging of the details of all web service requests to a specified file, with a simple addition to the Drupal settings.php file, for example:

$conf[ 'restws_debug_log' ] = DRUPAL_ROOT . '/restws_debug.log';

If in your own explorations, you discover additional techniques and pitfalls, please consider publishing them, as there appears to be both interest and confusion as to how to leverage this module fully.

Copyright © 2014 Michael J. Ross. All rights reserved.

Add new comment

Filtered HTML

  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <address> <area> <blockquote> <br> <cite> <code> <dd> <div> <dl> <dt> <em> <fieldset> <h1> <h2> <h3> <h4> <h5> <h6> <hr> <img> <input> <li> <map> <ol> <p> <pre> <span> <strong> <sup> <u> <ul>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
4 + 0 =
Solve this simple math problem and enter the result. E.g. for 1+3, enter 4.