- What is a RESTful semantic resource?
- Defining RESTful resources
- RESTful semantic resources default features
- Service descriptions
- Customizing RESTful semantic resources
What is a RESTful semantic resource?
Web development is changing quickly. Data APIs and rich clients (wether web or mobile) are becoming increasingly important, at the same time, web infrastructure is becoming increasingly distributed with complex interactions among components taking place outside the request-response cycle: SQL databases, key value stores, memory caches, messaging systems, application servers and front-end javascript are some of the mobile parts that are part of today’s web heterogeneous infrastructure. The classical impedance mismatch problem between relational databases and programming language level objects in the application layer has worsened, and now a piece of data stored in a database table can be transformed into a set of ruby objects to be later stored in YAML format in a memcache transient memory zone, or travels through an AMQP message bus to finally be serialized as JSON and consumed by javascript code being executed in the browser. All these subsystems doesn’t share a common understanding of the data and perform ad-hoc transformations on the data retrieved through fragile interfaces always prone to break. All this complexity makes systems hard to maintain, extend and reuse.
Plaza tries to explore ways to address these problems combining semantic meta data and RESTful architectural principles. Is our understanding that common semantics and a shared general data model could simplify the design of web infrastructure and allow easy reuse and composition of data.
REST is an architectural approach to the design of web systems introduced by Roy Fielding famous doctoral thesis and has become one of the preferred design guidelines to build web applications.
Conversely, semantic technologies developed by the W3C, have become a mature technology that could allow a new generation of web services where discovery and consumption of web data can become fully automated.
Plaza builds on these foundations trying to find a way of building web applications relying on:
- a sound theoretical model based on process calculi and generative communication
- a powerful data model with well defined semantics provided by W3C semantic standards like RDF, RDFS, SPARQL or OWL
- a flexible distributed infrastructure based on the use of triple spaces, processes and triple space operations
In this section we will show how Plaza triple spaces can be exposed as RESTful HTTP resources to be consumed by application clients.
The first question to address is the exact nature of a REST resource. The disjunctive between the use of the term ‘RESTful web resource’ or ‘RESTful web service’ shows two possible ways of understanding web interfaces: as remote processes that perform actions that can be invoked through HTTP requests or as data in the form of resources that are manipulated using HTTP protocol verbs. The first approach is closer to the RPC view of web services representative of WS-* web services standards, the latter is closer to the REST approach to web resources. Nevertheless the REST approach also requires thinking about resources as processes to a certain degree: resources are created and destroyed, new identifiers (URIs) are introduced and bound to the resources etc. This point of view is also present in Fielding thesis when analyzing the process and data views of REST architectures.
Plaza model combines both views, describing RESTful semantic resources as processes with an associated URI accepting HTTP requests and manipulating their associated triple spaces according to REST semantics.
The main intuition behind this approach is that main components of REST architectures can be mapped to triple spaces architectures, using a process with an associated URI as the translator between both models:
- a URI identifying a REST resource identifies a RDF graph in a triple space and a triple space process
- a HTTP GET operation becomes a rd triple space operation
- a HTTP POST operation becomes introduces a new URI and spawns a process associated process and RDF graph that is stored in the triple space with an out operation
- a HTTP PUT operation becomes a swap triple space operation
- a HTTP DELETE operation becomes a in operation, extracting the RDF graph for the triple space and finishing the execution of the triple space process
Defining RESTful resources
The support for RESTful semantic resources in plaza can be found in the plaza.rest.core
. All the code is integrated in the Compojure Clojure web framework and the Ring middleware infrastructure so it can be safely used in a regular Compojure application.
The definition of a RESTful semantic resources requires three components: a RDF model describing the kind of RDF graphs that will be exposed by the resource, a triple space where the triples will be stored and manipulated and the registration of a process with the URI designated for the resource.
Defining the RDF model
The definition of a resource can be accomplished using the plaza.rdf.schemas
package and the RDF ontologies included in the Plaza library or defined by the developer. For example, the following snippet defines a new model call ComputationCelebrity
describing famous people in the computer science field. This model can be described with the following Clojure code, using the bundled FOAF vocabulary:
(use 'plaza.rdf.core)
(use 'plaza.rdf.implementations.jena)
;; We start Plaza using Jena as the backend
(init-jena-framework)
;; We will use the FOAF vocabulary to describe our model
(use 'plaza.rdf.vocabularies.foaf)
(def ComputationCelebrity-schema
(make-rdfs-schema foaf:Person
:name {:uri foaf:name :range :string}
:surname {:uri foaf:surname :range :string}
:nick {:nick foaf:nick :range :string}
:birthday {:uri foaf:birthday :range :date}
:interest {:uri foaf:topic_interest :range :string}
:wikipedia_entry {:uri foaf:holdsAccount :range rdfs:Resource}))
(tbox-register-schema :celebrity ComputationCelebrity-schema)
The previous code declares a new RDF schema named ComputationCelebrity
defined using the make-rdfs-schema
declared to be of class foaf:Person
. In that function we also provide a mapping from the properties of the model with their defining URI and ranges, to an alias for that property. For example, the name of the celebrity is defined as a FOAF name property that can hold a value of type string and receives the alias ‘name’.
Once the model is defined it is stored in the TBox of the application, using the function tbox-register-schema
. The TBox or “terminological box” can be regarded as a special triple space where meta data about meta data is stored.
Defining triple spaces
After defining the model for the resource, we need to define a triple space where RDF graphs for that resource will be stored. In this case we will use a plaza.triple-spaces.distributed-server.DistributedTripleSpace
with a Mulgara backend.
(use 'plaza.triple-spaces.server.mulgara)
(use 'plaza.triple-spaces.distributed-server)
;; We create a Triple Space for the resources
(defonce *mulgara-backend* (build-model :mulgara :rmi "rmi://localhost/server1"))
(def-ts :celebrities (make-distributed-triple-space "test" *mulgara-backend* :redis-host "localhost" :redis-db "testdist" :redis-port 6379))
Registering the resource
Once the triple space is defined and the resource model is registered in the TBox, we can create a new resource process that will accept HTTP requests for a certain URI and will manipulate the RDF graph in the associated triple space. We can use the functions spawn-rest-resource!
and spawn-rest-collection-resource!
to start new resource process in a Compojure application.
;; Application routes for a new Compojure application
(defroutes example
;; a regular Compojure request
(GET "/" [] "<h1>Testing plaza...</h1>")
;; a RESTful semantic resource accepting GET, PUT and DELETE requests
(spawn-rest-resource! :celebrity "/Celebrity/:id" :celebrities)
;; a RESTful semantic resource accepting GET, POST and DELETE requests
(spawn-rest-collection-resource! :celebrity "/Celebrity" :celebrities)
;; a default Compojure handler
(route/not-found "Page not found"))
;; Running the application
(run-jetty (var example) {:port 8081})
Both functions accept 3 mandatory parameters: the name of the resource registered in the TBox, the URI path of the resource, and the name of the registered triple space to manipulate. The definition of the resources can be mixed with regular Compojure routes.
Two different kinds of default RESTful semantic resources can be defined: collection resources and individual resources. Collection resources manage sets of RDF graphs where the subject are all of the same kind, creating new resources with POST requests, retrieving full collections with GET requests and deleting collections with DELETE requests. Individual resources manage RDF graphs with the same subject, pattern the URI of the resource. These graphs can be retrieved with GET requests, modified with PUT requests and deleted with DELETE requests. Default definition of individual resources require to include the :id
key in the URI pattern, so the newly created identifier for the resource can be inserted.
The following table summarizes the requests for each kind of resource and
HTTP Method | Collection Resource | Individual Resource | Triple Space Operation |
---|---|---|---|
GET | Yes | Yes | rd |
POST | Yes | No | out |
PUT | No | Yes | swap |
DELETE | Yes | Yes | in |
All the code shown before can be found in the plaza.examples.webapp
namespace inside the Plaza source code.
RESTful semantic resources default features
Retrieving RDF graphs
Once the application is running, in the example using the Jetty application server, it can be accessed through any HTTP regular agent, for example using curl.
If we want to retrieve all the resources of kind Celebrity, we can issue a GET request to the collection RESTful resource:
nb-agarrote ~$ curl -X GET "http://localhost:8081/Celebrity" <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" > </rdf:RDF>
Creating RDF graphs
We can create a new RDF graph for a resource that will be inserted in the triple space issuing a HTTP POST request to the URI of a collection RESTful semantic resource. We can pass the values for the properties of the new resource as URL parameters or as form encoded parameters. The name of the parameters matches the alias defined in the resource description inserted in the TBox. A new URI for the resource will be created and will be used for the subject component of the triples of the new resource:
nb-agarrote ~$ curl -X POST "http://localhost:8081/Celebrity?name=Alonzo&surname=Church&birthday=1904-06-14&wikipedia_entry=http://en.wikipedia.org/wiki/Alonzo_Church" <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:j.0="http://xmlns.com/foaf/0.1/" > <rdf:Description rdf:about="http://localhost:8081/Celebrity/42cc3fa6754548499d56f81cb1fc22cd"> <j.0:birthday rdf:datatype="http://www.w3.org/2001/XMLSchema#date">1904-06-14</j.0:birthday> <j.0:holdsAccount rdf:resource="http://en.wikipedia.org/wiki/Alonzo_Church"/> <j.0:surname rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Church</j.0:surname> <j.0:name rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Alonzo</j.0:name> <rdf:type rdf:resource="http://xmlns.com/foaf/0.1/Person"/> </rdf:Description> </rdf:RDF>
The newly created graph will be returned with a 201
HTTP status code if the creation of the resource is successful. Due to the Open World semantics of RDF, not all the properties are required for the creation of the resource. A newly created URI will be introduced for the resource. By default a UUID will be used for the new URI.
Supported encodings
Since we don’t have created any Celebrity
resource, the triple space is empty and the retrieved RDF graph is also empty. By default, Plaza resources return XML/RDF encoded triples. This behavior can be modified sending the right Accept
HTTP header or appending an extension to the resource URI.
If we would prefer obtaining the NTriples representation of the same RDF graph,
we could pass the corresponding accept header for that reprsentation:
nb-agarrote ~$ curl -X GET "http://localhost:8081/Celebrity.n3" <http://localhost:8081/Celebrity/42cc3fa6754548499d56f81cb1fc22cd> a <http://xmlns.com/foaf/0.1/Person> ; <http://xmlns.com/foaf/0.1/birthday> "1904-06-14"^^<http://www.w3.org/2001/XMLSchema#date> ; <http://xmlns.com/foaf/0.1/holdsAccount> <http://en.wikipedia.org/wiki/Alonzo_Church> ; <http://xmlns.com/foaf/0.1/name> "Alonzo"^^<http://www.w3.org/2001/XMLSchema#string> ; <http://xmlns.com/foaf/0.1/surname> "Church"^^<http://www.w3.org/2001/XMLSchema#string> . nb-agarrote ~$ curl -X GET -H "accept: text/rdf+n3" "http://localhost:8081/Celebrity/42cc3fa6754548499d56f81cb1fc22cd" <http://localhost:8081/Celebrity/42cc3fa6754548499d56f81cb1fc22cd> a <http://xmlns.com/foaf/0.1/Person> ; <http://xmlns.com/foaf/0.1/birthday> "1904-06-14"^^<http://www.w3.org/2001/XMLSchema#date> ; <http://xmlns.com/foaf/0.1/holdsAccount> <http://en.wikipedia.org/wiki/Alonzo_Church> ; <http://xmlns.com/foaf/0.1/name> "Alonzo"^^<http://www.w3.org/2001/XMLSchema#string> ; <http://xmlns.com/foaf/0.1/surname> "Church"^^<http://www.w3.org/2001/XMLSchema#string> .
JSON encoding of RDF graphs
Plaza semantic RESTful resources support JSON encoded for the resources in two different flavors. If we ask for a JSON representation of the forma we will obtain a JSON object where all the values have been encoded as properties whose name is the alias provided for the property in the TBox. Additionally, a uri
property is added with the URI of the resource:
nb-agarrote ~$ curl -X GET "http://localhost:8081/Celebrity/42cc3fa6754548499d56f81cb1fc22cd.json" {"uri":"http:\/\/localhost:8081\/Celebrity\/42cc3fa6754548499d56f81cb1fc22cd", "birthday":"1904-06-14", "wikipedia_entry":"http:\/\/en.wikipedia.org\/wiki\/Alonzo_Church", "surname":"Church", "name":"Alonzo", "type":"http:\/\/xmlns.com\/foaf\/0.1\/Person"}
The other possible encoding of the graph as JSON returns the full list of triples in the RDF graph as an array of array where the first element is the subject for the triple, the second one is the predicate and the third one is a URI or a new object with two keys, the value of the object and the URI of the data type for that value.
nb-agarrote ~$ curl -X GET "http://localhost:8081/Celebrity/42cc3fa6754548499d56f81cb1fc22cd.js3" [["http:\/\/localhost:8081\/Celebrity\/42cc3fa6754548499d56f81cb1fc22cd", "http:\/\/www.w3.org\/1999\/02\/22-rdf-syntax-ns#type", "http:\/\/xmlns.com\/foaf\/0.1\/Person"], ["http:\/\/localhost:8081\/Celebrity\/42cc3fa6754548499d56f81cb1fc22cd", "http:\/\/xmlns.com\/foaf\/0.1\/name", {"value":"Alonzo", "datatype":"http:\/\/www.w3.org\/2001\/XMLSchema#string"}], ["http:\/\/localhost:8081\/Celebrity\/42cc3fa6754548499d56f81cb1fc22cd", "http:\/\/xmlns.com\/foaf\/0.1\/surname", {"value":"Church", "datatype":"http:\/\/www.w3.org\/2001\/XMLSchema#string"}], ["http:\/\/localhost:8081\/Celebrity\/42cc3fa6754548499d56f81cb1fc22cd", "http:\/\/xmlns.com\/foaf\/0.1\/holdsAccount", "http:\/\/en.wikipedia.org\/wiki\/Alonzo_Church"], ["http:\/\/localhost:8081\/Celebrity\/42cc3fa6754548499d56f81cb1fc22cd", "http:\/\/xmlns.com\/foaf\/0.1\/birthday", {"value":"1904-06-14", "datatype":"http:\/\/www.w3.org\/2001\/XMLSchema#date"}]]
JSONP support
Plaza RESTful semantic resources support JSONP requests by the means of an extra “_callback” parameter where the name of the returning function can be provided:
nb-agarrote ~$ curl -X GET "http://localhost:8081/Celebrity/42cc3fa6754548499d56f81cb1fc22cd.json?_callback=foo" foo({"uri":"http:\/\/localhost:8081\/Celebrity\/42cc3fa6754548499d56f81cb1fc22cd", "birthday":"1904-06-14", "wikipedia_entry":"http:\/\/en.wikipedia.org\/wiki\/Alonzo_Church", "surname":"Church", "name":"Alonzo", "type":"http:\/\/xmlns.com\/foaf\/0.1\/Person"});
RDFa support
Plaza also offers a default representation of RDF graphs using HTML with RDFa annotations. These representation can be retrieved using a text/hml
accept HTTP header or a html, rdfa
or xhtml
extensions. This allows users to browse Plaza resources from a web browser:
Updating resources
An individual RESTful resource can be updated passing the new state for the resource encoded in a HTTP PUT request:
nb-agarrote ~$ curl -X PUT "http://localhost:8081/Celebrity/42cc3fa6754548499d56f81cb1fc22cd?name=Alonzo&surname=Church&birthday=1903-06-14&wikipedia_entry=http://en.wikipedia.org/wiki/Alonzo_Church" <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:j.0="http://xmlns.com/foaf/0.1/" > <rdf:Description rdf:about="http://localhost:8081/Celebrity/42cc3fa6754548499d56f81cb1fc22cd"> <j.0:birthday rdf:datatype="http://www.w3.org/2001/XMLSchema#date">1904-06-14</j.0:birthday> <j.0:holdsAccount rdf:resource="http://en.wikipedia.org/wiki/Alonzo_Church"/> <j.0:surname rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Church</j.0:surname> <j.0:name rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Alonzo</j.0:name> <rdf:type rdf:resource="http://xmlns.com/foaf/0.1/Person"/> </rdf:Description> </rdf:RDF>
The previous state of the resource is returned as a response from the resource process.
Selecting parts of a RDF graph
In order to select parts of a RDF graph values can be passed to the GET request of a collection resource, only the resources matching this value will be returned:
nb-agarrote ~$ curl -X POST "http://localhost:8081/Celebrity?name=Yukihiro&surname=Matsumoto&birthday=1965-04-14&wikipedia_entry=http://en.wikipedia.org/wiki/Yukihiro_Matsumoto&interest=ruby" <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:j.0="http://xmlns.com/foaf/0.1/" > <rdf:Description rdf:about="http://localhost:8081/Celebrity/917512c59efb4b1e876762ba8595a8d7"> <j.0:birthday rdf:datatype="http://www.w3.org/2001/XMLSchema#date">1965-04-14</j.0:birthday> <j.0:holdsAccount rdf:resource="http://en.wikipedia.org/wiki/Yukihiro_Matsumoto"/> <j.0:surname rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Matsumoto</j.0:surname> <j.0:name rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Yukihiro</j.0:name> <j.0:topic_interest rdf:datatype="http://www.w3.org/2001/XMLSchema#string">ruby</j.0:topic_interest> <rdf:type rdf:resource="http://xmlns.com/foaf/0.1/Person"/> </rdf:Description> </rdf:RDF> nb-agarrote ~$ curl -X GET "http://localhost:8081/Celebrity?interest=ruby> " <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:j.0="http://xmlns.com/foaf/0.1/" > <rdf:Description rdf:about="http://localhost:8081/Celebrity/917512c59efb4b1e876762ba8595a8d7"> <j.0:birthday rdf:datatype="http://www.w3.org/2001/XMLSchema#date">1965-04-14</j.0:birthday> <j.0:holdsAccount rdf:resource="http://en.wikipedia.org/wiki/Yukihiro_Matsumoto"/> <j.0:surname rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Matsumoto</j.0:surname> <j.0:name rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Yukihiro</j.0:name> <j.0:topic_interest rdf:datatype="http://www.w3.org/2001/XMLSchema#string">ruby</j.0:topic_interest> <rdf:type rdf:resource="http://xmlns.com/foaf/0.1/Person"/> </rdf:Description> </rdf:RDF> nb-agarrote ~$ curl -X GET "http://localhost:8081/Celebrity?surname=Church" <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:j.0="http://xmlns.com/foaf/0.1/" > <rdf:Description rdf:about="http://localhost:8081/Celebrity/42cc3fa6754548499d56f81cb1fc22cd"> <j.0:birthday rdf:datatype="http://www.w3.org/2001/XMLSchema#date">1903-06-14</j.0:birthday> <j.0:holdsAccount rdf:resource="http://en.wikipedia.org/wiki/Alonzo_Church"/> <j.0:name rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Alonzo</j.0:name> <j.0:surname rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Church</j.0:surname> <rdf:type rdf:resource="http://xmlns.com/foaf/0.1/Person"/> </rdf:Description> </rdf:RDF>
Deleting RDF graphs
RDF graphs can be deleted sending DELETE HTTP requests to the resource. The deleted RDF graph will be returned as the response:
nb-agarrote ~$ curl -X DELETE "http://localhost:8081/Celebrity/a44f1be3e1b84c4d9c8e342aa5dcf054" <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:j.0="http://xmlns.com/foaf/0.1/" > <rdf:Description rdf:about="http://localhost:8081/Celebrity/a44f1be3e1b84c4d9c8e342aa5dcf054"> <j.0:birthday rdf:datatype="http://www.w3.org/2001/XMLSchema#date">1903-06-14</j.0:birthday> <j.0:holdsAccount rdf:resource="http://en.wikipedia.org/wiki/Alonzo_Church"/> <j.0:surname rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Church</j.0:surname> <j.0:name rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Alonzo</j.0:name> <rdf:type rdf:resource="http://xmlns.com/foaf/0.1/Person"/> </rdf:Description> </rdf:RDF>
Offset and limit params
Plaza collection resources supports two additional parameters in the queries: _limit
and _offset
. These methods can be used to paginate a collection of results returned by the server:
nb-agarrote ~$ curl -X GET "http://localhost:8081/Celebrity?_limit=2&_offset=2"
Simulating HTTP methods
Some HTTP agents, most notably web browsers, cannot send requests using all the HTTP methods. Requests for a different method can be simulated using the special _method
parameter in the URL. For example, the previous DELETE request could have been requested using a GET request and a _method=delete
parameter:
nb-agarrote ~$ curl -X GET "http://localhost:8081/Celebrity/a44f1be3e1b84c4d9c8e342aa5dcf054?_method=delete"
Querying the TBox
For every defined collection or individual resource defined it is possible to
query the application TBox for information about the service and schema of the
resource exposed.
To query for the schema of any resource the prefix /schema
can be
appended to the resource URI.
For example. to query about the schema of the previously created
http://localhost:8081/Celebrity/917512c59efb4b1e876762ba8595a8d7
a
query with URI
http://localhost:8081/Celebrity/917512c59efb4b1e876762ba8595a8d7/schema
can be issued. The RDF graph describing the resource will be returned:
Service descriptions
Plaza can generate automatic descriptions of the services exposed using the
library.
These descriptions consist of set of triples describing the resources, URIs
where these resources are bound, allowed HTTP methods, input and output
parameters.
Plaza service descriptions is based on hRESTS and MicroWSMO a vocabulary that can be
used to describe semantic web services RESTfully using a subset of the Web Services Modeling Ontology.
This description for any resource can be accessed using the
/single_resource_service
and
/collection_resource_service
prefixes:
nb-agarrote~$ curl -X GET "http://localhost:8081/Celebrity/single_resource_service.n3" <http://localhost:8081/Celebrity/single_resource_service.n3> a <http://www.wsmo.org/ns/wsmo-lite#Service> ; <http://www.wsmo.org/ns/wsmo-lite#hasOperation> [ a <http://www.wsmo.org/ns/wsmo-lite#Operation> ; <http://www.wsmo.org/ns/hrests#hasAddress> "http://localhost:8081/Celebrity/{id}"^^<http://www.wsmo.org/ns/hrests#URITemplate> ; <http://www.wsmo.org/ns/hrests#hasMethod> "get"^^<http://www.w3.org/2001/XMLSchema#string> ; <http://www.wsmo.org/ns/wsmo-lite#hasInputMessage> [ a <http://www.wsmo.org/ns/wsmo-lite#Message> ; <http://schemas.xmlsoap.org/wsdl/http/urlReplacement> "id"^^<http://www.w3.org/2001/XMLSchema#string> ; <http://www.w3.org/ns/sawsdl#modelReference> <http://plaza.org/vocabularies/restResourceId> ] ; <http://www.wsmo.org/ns/wsmo-lite#hasOutputMessage> [ a <http://www.wsmo.org/ns/wsmo-lite#Message> ; <http://www.w3.org/ns/sawsdl#modelReference> "http://localhost:8081/Celebrity/{id}/schema"^^<http://www.w3.org/2001/XMLSchema#string> ] ] ; <http://www.wsmo.org/ns/wsmo-lite#hasOperation> [ a <http://www.wsmo.org/ns/wsmo-lite#Operation> ; <http://www.wsmo.org/ns/hrests#hasAddress> "http://localhost:8081/Celebrity/{id}"^^<http://www.wsmo.org/ns/hrests#URITemplate> ; <http://www.wsmo.org/ns/hrests#hasMethod> "put"^^<http://www.w3.org/2001/XMLSchema#string> ; <http://www.wsmo.org/ns/wsmo-lite#hasInputMessage> [ a <http://www.wsmo.org/ns/wsmo-lite#Message> ; <http://schemas.xmlsoap.org/wsdl/http/urlReplacement> "birthday"^^<http://www.w3.org/2001/XMLSchema#string> ; <http://www.w3.org/ns/sawsdl#modelReference> <http://xmlns.com/foaf/0.1/birthday> ] ; <http://www.wsmo.org/ns/wsmo-lite#hasInputMessage> [ a <http://www.wsmo.org/ns/wsmo-lite#Message> ; <http://schemas.xmlsoap.org/wsdl/http/urlReplacement> "wikipedia_entry"^^<http://www.w3.org/2001/XMLSchema#string> ; <http://www.w3.org/ns/sawsdl#modelReference> <http://xmlns.com/foaf/0.1/holdsAccount> ] ; <http://www.wsmo.org/ns/wsmo-lite#hasInputMessage> [ a <http://www.wsmo.org/ns/wsmo-lite#Message> ; <http://schemas.xmlsoap.org/wsdl/http/urlReplacement> "name"^^<http://www.w3.org/2001/XMLSchema#string> ; <http://www.w3.org/ns/sawsdl#modelReference> <http://xmlns.com/foaf/0.1/name> ] ; <http://www.wsmo.org/ns/wsmo-lite#hasInputMessage> [ a <http://www.wsmo.org/ns/wsmo-lite#Message> ; <http://schemas.xmlsoap.org/wsdl/http/urlReplacement> "interest"^^<http://www.w3.org/2001/XMLSchema#string> ; <http://www.w3.org/ns/sawsdl#modelReference> <http://xmlns.com/foaf/0.1/topic_interest> ] ; <http://www.wsmo.org/ns/wsmo-lite#hasInputMessage> [ a <http://www.wsmo.org/ns/wsmo-lite#Message> ; <http://schemas.xmlsoap.org/wsdl/http/urlReplacement> "nick"^^<http://www.w3.org/2001/XMLSchema#string> ; <http://www.w3.org/ns/sawsdl#modelReference> <http://xmlns.com/foaf/0.1/nick> ] ; <http://www.wsmo.org/ns/wsmo-lite#hasInputMessage> [ a <http://www.wsmo.org/ns/wsmo-lite#Message> ; <http://schemas.xmlsoap.org/wsdl/http/urlReplacement> "surname"^^<http://www.w3.org/2001/XMLSchema#string> ; <http://www.w3.org/ns/sawsdl#modelReference> <http://xmlns.com/foaf/0.1/surname> ] ; <http://www.wsmo.org/ns/wsmo-lite#hasInputMessage> [ a <http://www.wsmo.org/ns/wsmo-lite#Message> ; <http://schemas.xmlsoap.org/wsdl/http/urlReplacement> "id"^^<http://www.w3.org/2001/XMLSchema#string> ; <http://www.w3.org/ns/sawsdl#modelReference> <http://plaza.org/vocabularies/restResourceId> ] ; <http://www.wsmo.org/ns/wsmo-lite#hasOutputMessage> [ a <http://www.wsmo.org/ns/wsmo-lite#Message> ; <http://www.w3.org/ns/sawsdl#modelReference> "http://localhost:8081/Celebrity/{id}/schema"^^<http://www.w3.org/2001/XMLSchema#string> ] ] ; <http://www.wsmo.org/ns/wsmo-lite#hasOperation> [ a <http://www.wsmo.org/ns/wsmo-lite#Operation> ; <http://www.wsmo.org/ns/hrests#hasAddress> "http://localhost:8081/Celebrity/{id}"^^<http://www.wsmo.org/ns/hrests#URITemplate> ; <http://www.wsmo.org/ns/hrests#hasMethod> "delete"^^<http://www.w3.org/2001/XMLSchema#string> ; <http://www.wsmo.org/ns/wsmo-lite#hasInputMessage> [ a <http://www.wsmo.org/ns/wsmo-lite#Message> ; <http://schemas.xmlsoap.org/wsdl/http/urlReplacement> "id"^^<http://www.w3.org/2001/XMLSchema#string> ; <http://www.w3.org/ns/sawsdl#modelReference> <http://plaza.org/vocabularies/restResourceId> ] ; <http://www.wsmo.org/ns/wsmo-lite#hasOutputMessage> [ a <http://www.wsmo.org/ns/wsmo-lite#Message> ; <http://www.w3.org/ns/sawsdl#modelReference> "http://localhost:8081/Celebrity/{id}/schema"^^<http://www.w3.org/2001/XMLSchema#string> ] ] .
The service description, as any other resource, can be requested in different
formats. HTML description of the services returns a RDFa encoded graph that can
be used for software application to consume the service and by human readers as
documentation for the API the service is providing:
Customizing RESTful semantic resources
In the previous sections we have explored the default features of Plaza RESTful semantic resources. This standard behavior can be nevertheless customized to suit a specific use case for a resource. This customization can be achieved using a number of options that can be passed as additional arguments in the description of the resource.
The request environment
All the customization handlers receive a couple of arguments, the Ring request hash, with all the information about the request, and a environment argument with the current state in the processing of the semantic request: the resource being requested, the triple space for the resource, or the functions handling GET, PUT, POST and DELETE requests. This information can be used to generate the right behavior for the request.
Allowed methods
The :allowed-methods
option can be passed providing a collection of methods allowed for a resource. If a request with a different method is issued the resource will answer with a
;; defining a read-only resource
(spawn-rest-collection-resource! :my-resource "/WriteOnlyResource" :resource-ts
:allowed-methods [:post :delete])
Custom URIs
When a new resource is created, Plaza generates a new identifier for the
resource and associate it to the RDF graph for the resource using the RDF
property http://plaza.org/vocabularies/restResourceId
. This
property is also inserted in the description of the resource schema.
Any different property with a different alias can be used as the identifier of
the resource. The right property to be used can be specified using two special
keys: :id-property-uri
and :id-property-alias
.
The URI generated for the new resource can also be customized passing a pair of function identified by the keys :id-gen-fn
and the :id-match-fn
. The id-gen-fn
can be passed to a collection resource, it will be invoked in all the POST requests passing the request and the environment for that request and must generate the URI for the new resource.
The complementary id-match-fn
can be passed to a single resource definition, receiving a request and environment. It must generate the URI for the resource that is being requested.
In the following example, two resources are created for a FOAF Agent resource, that will generate URIs for the resources based on the foaf:name
property of the resource:
(require [clojure.contrib.str-utils2 :as str2])
(spawn-rest-resource! :foaf-agent "/CustomIds/:name" :resource
:allowed-methods [:get]
:id-match-fn (fn [req env]
(let [pattern (str (:resource-qname-prefix env)
(:resource-qname-local env))]
(str2/replace pattern ":name" (get (:params req) "name")))))
(spawn-rest-collection-resource! :foaf-agent "/CustomIds" :resource
:allowed-methods [:post]
:id-gen-fn (fn [req env]
(let [prefix (:resource-qname-prefix env)
local (:resource-qname-local env)]
(str prefix local "/" (get (:params req) "name"))))))
Custom handlers
Developers can fully customize the behavior of the agent passing as optional arguments handler functions for each type of request using the keys :get-handle-fn :post-handle-fn :put-handle-fn :delete-handle-fn
. This functions will receive the Ring request and the environment and must generate the suitable Ring response hash for the request. Single resources will receive additionally the identifier URI for the resource.
Developers can use all the regular operation over triple spaces to fulfill the requests besides the functions defined in the plaza.rest.core
namespace to parse the parameters in the request or generating the right representation for the RDF graph.
The following example shows a simple handler that just returns a fixed string as a response
(spawn-rest-resource! :foaf-agent "/CustomGet/:id" :resource
:allowed-methods [:get]
:get-handle-fn (fn [id request environment]
{:body "custom get handler"
:headers {"Content-Type" "text-plain"}
:status 200}))
Custom handles must be careful in not breaking the semantics of the RESTful interface for the resource, as the shown example does.