During the work on my thesis over the last year, I played around a lot with RESTful services based upon the Entity API. What I needed was a simple service that just exposes Drupal's entities in a RESTful manner, while obeying Drupal's permission and access systems. Now, me and klausi have created a small module that does exactly that: Restful web services.
So how does it work?
The module makes use of the Entity API and the information about entity properties (provided via hook_entity_property_info()
) to provide resource representations for all entity types (nodes, comments, users, taxonomy terms, ..). It aims to be fully compliant to the REST principles. Drupal's entities are exposed at the unified $entity_type/$id
paths, while respecting the Content Accept/Content Type headers of the HTTP requests. That means if a client requests node/1
with usual HTTP accept headers it will get Drupal's usual output, if it requests node/1
while accepting only JSON, it will get the JSON representation of the node. Similarly, all CRUD operations are supported as common for RESTful services. Then, the module supports GET requests on paths like node/1.json
, node/1.xml
or node/1.rdf
too.
And authentication...?
As mentioned above, the solution just obeys Drupal's permission and access system. If there is an active session and the user has sufficient permission for the request, it will be served. So any add-on authentication strategies would have to plug into Drupal's usual user system. For example, the RestWS module comes with a small add-on module that authenticates users via HTTP basic authentication. So you can define a regular user for a client, configure their access permissions as usual, and just pass its credentials with a request.
So what about the property information?
The module makes use of the property information the entity API collects for all entity types, as well as the accompanying wrapper classes. While the API also allows providing non-entities as resources, it requires the existence of property information. Representations of entities are provided according to their property information. What does that mean?
So let's have a look at an example: The node author. In the property information about nodes, there is no uid
property, instead there is an 'author' property, pointing to the according user entity. So the module makes use of that information to output a proper reference to the author, being the author's URI (URIs are the proper way to do references in RESTful designs). So instead of just outputting user id as uid
property with an integer value, we output a proper reference to the node's author. Apart from that, the property information includes access permissions - so updating the node author will only be possible if you have sufficient permissions.
Then the property information could be used to provide a description of the web service for the caller, in a human as well as in a machine-readable way.
Which formats are supported?
The module currently comes with support for JSON, XML and RDF/XML whereas modules may add more formatters. As the property information is available to the formatters too, it's possible to do formatters that output some properties in a certain way, e.g. using a special XML namespace. Similarly the RDF formatter looks up the RDF mapping being defined for a property, in order to generate meaningful RDF output.
What's different to the Services module?
The main differences are:
* RestWS provides only RESTful services (no message-oriented or RPC-style web services like SOAP, XML-RPC etc.).
* RestWS strongly builds upon the Entity API and its property information, thus utilizes it for CRUD, access checks, getting property information, ..
* Property information is built into the API, so formatters may make use of it to format the data in a sensible way.
* There are no "service endpoints" to configure as resources are just available at uniform paths like node/1
, user/1
. We do not see a need to have multiple endpoints for the same resource in a RESTful desgin.
For more about the relation and partial overlap to the Services module, read and participate in the discussion over at http://drupal.org/node/1042512.
Example output
You might be interested in the output, so here is the output the module currently produces for a testing-node:
JSON:
{
"nid":"3",
"vid":"3",
"is_new":false,
"type":"article",
"title":"asdfdsf",
"language":"und",
"url":"https:\/\/example.com\/node\/3",
"edit_url":"https:\/\/example.com\/node\/3\/edit",
"status":"1",
"promote":"1",
"sticky":"0",
"created":"1294913241",
"changed":"1296405309",
"author":{
"uri":"https:\/\/example.com\/user\/1",
"id":"1",
"resource":"user"
},
"log":"",
"revision":null,
"comment":"2",
"comment_count":"0",
"comment_count_new":"0",
"body":{
"value":"\u003cp\u003etest2\u003c\/p\u003e\n",
"summary":"\u003cp\u003eha\u003c\/p\u003e\n",
"format":"filtered_html"
},
"field_tags":[
],
"field_image":[
],
"field_test":"1",
"field_file":[
]
}
The same node in XML:
<?xml version="1.0" encoding="utf-8"?>
<node>
<nid>3</nid>
<vid>3</vid>
<is_new/>
<type>article</type>
<title>asdfdsf</title>
<language>und</language>
<url>https://example.com/node/3</url>
<edit_url>https://example.com/node/3/edit</edit_url>
<status>1</status>
<promote>1</promote>
<sticky>0</sticky>
<created>1294913241</created>
<changed>1296405309</changed>
<author resource="user" id="1">https://example.com/user/1</author>
<log/>
<revision/>
<comment>2</comment>
<comment_count>0</comment_count>
<comment_count_new>0</comment_count_new>
<body>
<value><p>test2</p>
</value>
<summary><p>ha</p>
</summary>
<format>filtered_html</format>
</body>
<field_tags/>
<field_image/>
<field_test>1</field_test>
<field_file/>
</node>
And finally in RDF/XML:
<?xml version="1.0"?>
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description xmlns:site="https://example.com/" xmlns:dc="http://purl.org/dc/terms/" xmlns:sioc="http://rdfs.org/sioc/ns#" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:og="http://ogp.me/ns#" rdf:about="https://example.com/node/3">
<rdf:type rdf:resource="http://rdfs.org/sioc/ns#Item"/>
<rdf:type rdf:resource="http://xmlns.com/foaf/0.1/Document"/>
<site:nid xmlns:site="https://example.com/">3</site:nid>
<site:vid xmlns:site="https://example.com/">3</site:vid>
<site:is_new xmlns:site="https://example.com/"/>
<site:type xmlns:site="https://example.com/">article</site:type>
<dc:title xmlns:dc="http://purl.org/dc/terms/">asdfdsf</dc:title>
<site:language xmlns:site="https://example.com/">und</site:language>
<site:url xmlns:site="https://example.com/">https://example.com/node/3</site:url>
<site:edit_url xmlns:site="https://example.com/">https://example.com/node/3/edit</site:edit_url>
<site:status xmlns:site="https://example.com/">1</site:status>
<site:promote xmlns:site="https://example.com/">1</site:promote>
<site:sticky xmlns:site="https://example.com/">0</site:sticky>
<dc:date xmlns:dc="http://purl.org/dc/terms/" rdf:datatype="xsd:dateTime">1294913241</dc:date>
<dc:modified xmlns:dc="http://purl.org/dc/terms/" rdf:datatype="xsd:dateTime">1296405309</dc:modified>
<site:author xmlns:site="https://example.com/">
<rdf:Description rdf:about="https://example.com/user/1"/>
</site:author>
<site:log xmlns:site="https://example.com/"/>
<site:revision xmlns:site="https://example.com/"/>
<site:comment xmlns:site="https://example.com/">2</site:comment>
<sioc:num_replies xmlns:sioc="http://rdfs.org/sioc/ns#" rdf:datatype="xsd:integer">0</sioc:num_replies>
<site:comment_count_new xmlns:site="https://example.com/">0</site:comment_count_new>
<content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/">
<rdf:Description>
<site:value xmlns:site="https://example.com/"><p>test2</p>
</site:value>
<site:summary xmlns:site="https://example.com/"><p>ha</p>
</site:summary>
<site:format xmlns:site="https://example.com/">filtered_html</site:format>
</rdf:Description>
</content:encoded>
<dc:subject xmlns:dc="http://purl.org/dc/terms/">
<rdf:Description/>
</dc:subject>
<og:image xmlns:og="http://ogp.me/ns#">
<rdf:Description>
<site:alt xmlns:site="https://example.com/"/>
</rdf:Description>
</og:image>
<site:field_test xmlns:site="https://example.com/">1</site:field_test>
<site:field_file xmlns:site="https://example.com/">
<rdf:Description/>
</site:field_file>
</rdf:Description>
</rdf:RDF>
Awesome!!
JSONP
There is no JSONP as of now.
Done
fantastic!
I'm afraid this will not be
Image fields
Yes What about Image Fields, media fields ?
new to RESTful
I'm wondering if this module
REST Client entities
REST Client entities
How can I retrieve the image field data
receive multiple values from services
please guide me to create custom webservice with restws
Consume RESTful Service
REST API Giving 404 Error
restful API Support for I10n module
Rest module support
Please post REST module support requests to the modules issue track and/or drupal stackexchange! Thanks!