Reading
Resources can be commonly accessed (i.e. read) using HTTP GET requests. Solid servers are encouraged to perform content negotiation for RDF resources, depending on the value of the Accept header.
IMPORTANT: a default text/turtle Content-Type will be used for requests for RDF resources or views (e.g. containers) that do not have an Accept header.
Content representation
The Resource Description Framework (RDF) is a framework for representing information in the Web [RDF1.1], originally designed as a graph-based data model, where the core structure of the abstract syntax is a set of triples, each consisting of a subject, a predicate and an object.
Solid uses several serialization syntaxes for storing and exchanging RDF such as Turtle and JSON-LD. When creating new RDF resources, the preferred default serialization is Turtle. Solid-compliant servers should implement content negotiation in order to handle different serialization formats.
Objects
A very important aspect of Solid revolves around naming resources, and keep the namespaces consistent across both the Web and local file systems.
Our motivation is threefold:
- Aspect: app developers care about URLs -- i.e.
https://example.org/posts/1
instead ofhttps://example.org/posts/1.ttl
- Portability: resources should still resolve after being exported/imported into different servers. For instance, if Server A decides to store RDF resources as Turtle files, then Server B needs to be able to understand and use those resources (including doing content negotiation on them -- i.e. serve JSON-LD even though the resource is stored as a Turtle file).
- Direct mapping: the URLs map directly to the file system resources -- i.e.
https://example.org/test.ttl
maps to/home/user/www/test.ttl
Servers must support the HEAD method for reading data. This returns a list of headers related to the resource in question. Among these headers, two very important Link
headers contain pointers to corresponding ACL and metadata resources. More information on naming conventions for these resources can be found here.
REQUEST:
HEAD /data/ HTTP/1.1
Host: example.org
RESPONSE:
HTTP/1.1 200 OK
....
Link: <https://example.org/data/.acl>; rel="acl"
Link: <https://example.org/data/.meta>; rel="describedby"
Handling metadata for non-RDF resources
The metadata (i.e. extra RDF triples such as types, titles, comments, etc.) about non-RDF resources (e.g. containers, images, binaries, etc.) will be stored in a corresponding meta resources. Solid servers use a specific naming convention when referring to these meta resources. Basically, every non-RDF resource may have a corresponding metadata resource, with a name composed of the resource's name together with a .meta suffix.
For example, the corresponding metadata resource for the newly created container will be accessible through this URI: https://example.org/data/.meta
. Alternatively, the photo at https://example.org/data/image.jpg
will have it's metadata stored in https://example.org/data/image.jpg.meta
.
Metadata resources are a "special" type of resources, which are not publicly listed by the server when browsing files (typically when doing a GET on an LDP container). However, they can still be modified by client apps using the methods described in this section. The corresponding metadata resources are advertised and can be discovered when doing HTTP GET/HEAD on regular resources, as mentioned before.
Streams
Being LDP (BasicContainer) compliant, Solid servers MUST return a full listing of container contents when receiving requests for containers. For every resource in a container, a Solid server may include additional metadata, such as the time the resource was modified, the size of the resource, and more importantly any other RDF type specified for the resource in its metadata. You will notice in the example below that the <profile>
resource has the extra RDF type <http://xmlns.com/foaf/0.1/PersonalProfileDocument>
, and also that the resource <workspace/>
has the RDF type <http://www.w3.org/ns/pim/space#Workspace>
.
Extra medata can be also be added, describing whether each resource in the container maps to a file or a directory on the server, using the POSIX vocabulary. Here is an example that reflects how our current server implementations handle such a request:
REQUEST:
GET /
Host: example.org
RESPONSE:
HTTP/1.1 200 OK
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
<>
a <http://www.w3.org/ns/ldp#BasicContainer>, <http://www.w3.org/ns/ldp#Container>, <http://www.w3.org/ns/posix/stat#Directory> ;
<http://www.w3.org/ns/ldp#contains> <profile>, <data/>, <workspace/> ;
<http://www.w3.org/ns/posix/stat#mtime> "1436281776" ;
<http://www.w3.org/ns/posix/stat#size> "4096" .
<profile>
a <http://xmlns.com/foaf/0.1/PersonalProfileDocument>, <http://www.w3.org/ns/posix/stat#File> ;
<http://www.w3.org/ns/posix/stat#mtime> "1434583075" ;
<http://www.w3.org/ns/posix/stat#size> "780" .
<data/>
a <http://www.w3.org/ns/ldp#BasicContainer>, <http://www.w3.org/ns/ldp#Container>, <http://www.w3.org/ns/posix/stat#Directory> ;
<http://www.w3.org/ns/posix/stat#mtime> "1435064562" ;
<http://www.w3.org/ns/posix/stat#size> "4096" .
<workspace/>
a <http://www.w3.org/ns/pim/space#Workspace>, <http://www.w3.org/ns/ldp#BasicContainer>, <http://www.w3.org/ns/ldp#Container>, <http://www.w3.org/ns/posix/stat#Directory> ;
<http://www.w3.org/ns/posix/stat#mtime> "1435064562" ;
<http://www.w3.org/ns/posix/stat#size> "4096" .
Globbing (inlining on GET)
We have found that in some cases, using the existing LDP features was not enough. For instace, to optimize certain applications we needed to aggregate all RDF resources from a container and retrieve them with a single GET operation. We implemented this feature on the servers and decided to call it "globbing". Similar to UNIX shell glob), doing a GET on any URI which ends with a * will return an aggregate view of all the resources that match the indicated pattern.
For example, let's assume that /data/res1 and /data/res2 are two resources containing one triple each, which defines their type as follows:
For res1:
<> a <https://example.org/ns/type#One> .
For res2:
<> a <https://example.org/ns/type#Two> .
If one would like to fetch all resources of a container begining with res (e.g. /data/res1, /data/res2) in one request, they could do a GET on /data/res*
as follows.
REQUEST:
GET /data/res* HTTP/1.1
Host: example.org
RESPONSE:
HTTP/1.1 200 OK
<res1>
a <https://example.org/ns/type#One> .
<res2>
a <https://example.org/ns/type#Two> .
Alternatively, one could ask the server to inline all resources of a container, which includes the triples corresponding to the container itself:
REQUEST:
GET /data/* HTTP/1.1
Host: example.org
RESPONSE:
HTTP/1.1 200 OK
<>
a <http://www.w3.org/ns/ldp#BasicContainer> ;
<http://www.w3.org/ns/ldp#contains> <res1>, <res2> .
<res1>
a <https://example.org/ns/type#One> .
<res2>
a <https://example.org/ns/type#Two> .
Note: the aggregation process is not currently recursive, therefore it will not apply to children containers.
Alternative: using SPARQL
Another possible way of reading and writing data is to use SPARQL. Currently, our Solid servers support a subset of SPARQL 1.0, where each resource is its own SPARQL endpoint, accepting basic SELECT, INSERT and DELETE statements.
To read (query) a resource, the client can send a SPARQL SELECT through a form-encoded HTTP GET request. The server will use the given resource as the default graph that is being queried. The resource can be an RDF document or even a container. The response will be serialized using application/json
mime type.
For instance, the client can send the following form-encoded query SELECT * WHERE { ?s ?p ?o . }
:
REQUEST:
GET /data/?query=SELECT%20*%20WHERE%20%7B%20%3Fs%20%3Fp%20%3Fo%20.%20%7D HTTP/1.1
Host: example.org
RESPONSE:
HTTP/1.1 200 OK
{
"head": {
"vars": [ "s", "p", "o" ]
},
"results": {
"ordered" : false,
"distinct" : false,
"bindings" : [
{
"s" : { "type": "uri", "value": "https://example.org/data/" },
"p" : { "type": "uri", "value": "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" },
"o" : { "type": "uri", "value": "http://www.w3.org/ns/ldp#BasicContainer" }
},
{
"s" : { "type": "uri", "value": "https://example.org/data/" },
"p" : { "type": "uri", "value": "http://purl.org/dc/terms/title" },
"o" : { "type": "literal", "value": "Basic container" }
}
]
}
}