- From: Steve Battle <steve.battle@sysemia.co.uk>
- Date: Tue, 9 Oct 2012 11:38:59 +0100
- To: "'Henry Story'" <henry.story@bblfish.net>, "'Linked Data Platform \(LDP\) Working Group'" <public-ldp-wg@w3.org>
- Message-ID: <001f01cda60a$4bf084b0$e3d18e10$@sysemia.co.uk>
See
<http://www.w3.org/2012/ldp/wiki/Use_Cases_And_Requirements#UC-BPC2:_Create_
resource_within_a_container>
This use-case is about creating an RDF resource within a container using
HTTP POST.
The issue to be determined boils down to 'what is the base URI of a POSTed
document.?'
In Turtle URIs in angle brackets may be relative URIs (relative to the
base), so '<>' is the base URI itself.
However, depending on how you interpret the standards, the base URI might be
the URI of the container or the URI of the resource.
Whichever it is we have solutions (A) and (B) below.
In either case the response is 201 Created with the 'Location' header
identifying the new resource.
A) The base URI is the URI of the resource:
This is in many ways the neater solution. It is less verbose and has the
advantage that we can refer directly to the resource.
<> a foaf:PersonalProfileDocument;
foaf:primaryTopic <#me> .
<#me> a foaf:Person;
foaf:name "Henry" .
B) The base URI is the URI of the container:
This approach requires the container to Skolemize the anonymous resource
(See <http://www.w3.org/wiki/BnodeSkolemization>).
<> rdfs:member [
a helios_bt:BugtrackerIssue;
dc:identifier "58365";
dc:type "bug";
]
I would have used the same example, but since (in this interpretation) the
URI of the resource isn't known until it is created, we would have to make
assertions about #me in a separate step.
This also has the slightly ugly disadvantage that the client needs to know
and use the membership predicate (rdfs:member).
The Standards
-------------
RFC 3986: Uniform Resource Identifier (URI): Generic Syntax
5.1. Establishing a Base URI
The base URI of a reference can be established in one of four ways,
discussed below in order of precedence:
1. Base URI Embedded in Content
2. Base URI of the encapsulating entity
3. URI used to retrieve the entity
4. Default Base URI (application-dependent)
In the context of an RDF POST:
1. For example, the current base URI may be altered in a Turtle document
using the @base directive.
2. For a document that is enclosed within another entity, such as a message
or archive, the retrieval context is that entity.
3. For a POST there is no retrieval URI.
4. The default can be determined by the Linked Data Platform.
My reading of this is that in the absence of an embedded @base, for a
resource in a container, the retrieval context is the container.
This would be identical with the request URI.
RFC 2616: Hypertext Transfer Protocol -- HTTP/1.1
9.5 POST
"The actual function performed by the POST method is determined by the
server and is usually dependent on the Request-URI. The posted entity
is subordinate to that URI in the same way that a file is subordinate
to a directory containing it, a news article is subordinate to a
newsgroup to which it is posted, or a record is subordinate to a
database."
This isn't really much help. It reinforces the idea that the posted resource
is subordinate to the container identified by the request-URI. Yet it
doesn't define the base URI for the subordinate entity.
14.14 Content-Location
"The value of Content-Location also defines the base URI for the entity."
However.. "The meaning of the Content-Location header in PUT or POST
requests is undefined"
My reading of this is inconclusive.
--
Steve Battle
Semantic Engineer
Mobile: +44 (0)7503 624 613
E-mail: <mailto:steve.battle@sysemia.co.uk> steve.battle@sysemia.co.uk
Web: <http://www.sysemia.com/> www.sysemia.com
Sysemia Limited
The Innovation Centre, Bristol & Bath Science Park, Dirac Crescent,
Emerson's Green, Bristol BS16 7FR
Registered in England and Wales. Company Number: 7555456
DISCLAIMER
Information contained in this e-mail is intended for the use of the
addressee only, and is confidential and may also be privileged. If you
receive this message in error, please advise us immediately. If you are not
the intended recipient(s), please note that any form of distribution,
copying or use of this communication or the information in it is strictly
prohibited and may be unlawful. Attachments to this e-mail may contain
software viruses which may damage your systems. Sysemia Ltd have taken
reasonable steps to minimise this risk, but we advise that any attachments
are virus checked before they are opened.
From: Henry Story [mailto:henry.story@bblfish.net]
Sent: 09 October 2012 10:36
To: Linked Data Platform (LDP) Working Group
Subject: Re: ldp-ISSUE-20 (POSTed resources): Identifying and naming POSTed
resources [Use Cases and Requirements]
On 8 Oct 2012, at 19:35, Henry Story <henry.story@bblfish.net> wrote:
On 8 Oct 2012, at 11:52, Linked Data Platform (LDP) Working Group Issue
Tracker <sysbot+tracker@w3.org> wrote:
ldp-ISSUE-20 (POSTed resources): Identifying and naming POSTed resources
[Use Cases and Requirements]
http://www.w3.org/2012/ldp/track/issues/20
Raised by: Steve Battle
On product: Use Cases and Requirements
Regarding use-case :
<http://www.w3.org/2012/ldp/wiki/Use_Cases_And_Requirements#UC-BPC2:_Create_
resource_within_a_container>
User-story
<http://www.w3.org/2012/ldp/wiki/Use_Cases_And_Requirements#Hosting_POSTed_R
esources> raises questions about POSTed resources.
* How is the inserted resource identified?
The use-case scenario assumes that the inserted resource is identified by
including its relation via the membership predicate, to the membership
subject.
e.g.
<> rdfs:member [
a helios_bt:BugtrackerIssue;
dc:identifier "58365";
dc:type "bug";
helios_bt:isInBugtracker eg:bugtracker
]
* How does the created resource relate to the RDF description?
See user-story
<http://www.w3.org/2012/ldp/wiki/Use_Cases_And_Requirements#Hosting POSTed
Resources>.
The example above assumes that the object of the insert is an anonymous
(existentially quantified) resource that can be skolemized to produce a URI
that can be returned in the Location header.
e.g. The response the the POST
HTTP/1.1 201 Created
Location: http://example.com/bugtracker/a0001
ETag: W/"1234567890"
I think the answer to this problem is simple and requires some text in
section 4.3
http://www.w3.org/2012/ldp/hg/ldbp.html#http-post
to the effect that: one should POST an RDF document to a collection with
relative
URIs such that the relatives URIs in the document will be resolve relative
to the
URI created by the server for that resource.
So if you post
------------------------
<> a foaf:PersonalProfileDocument;
foaf:primaryTopic <#me> .
<#me> a foaf:Person;
foaf:name "Henry" .
------------------------
to a collection
http://profile.example/2012/
then the server will create a resource http://profile.example/2012/93
against which the above document with relative URLs will be resolved
so that one ends up with a document which is isomorphic to
------------------------
<http://profile.example/2012/93> a foaf:PersonalProfileDocument;
foaf:primaryTopic <#me> .
<http://profile.example/2012/93#me> a foaf:Person;
foaf:name "Henry" .
------------------------
Btw, I had implemented this a while ago here, so it does work, and pretty
nicely too:
https://dvcs.w3.org/hg/read-write-web/file/258d2757ef3d/src/main/scala/ReadW
riteWeb.scala#l150
<https://dvcs.w3.org/hg/read-write-web/file/258d2757ef3d/src/main/scala/Read
WriteWeb.scala#l150> 150 case POST(_) & RequestContentType(ct) if
representation == DirectoryRepr =>
<https://dvcs.w3.org/hg/read-write-web/file/258d2757ef3d/src/main/scala/Read
WriteWeb.scala#l151> 151 val createType =
Representation.fromAcceptedContentTypes(List(ct))
<https://dvcs.w3.org/hg/read-write-web/file/258d2757ef3d/src/main/scala/Read
WriteWeb.scala#l152> 152 r.create(createType) failMap { t
=> NotFound ~> ResponseString(t.getStackTraceString)} flatMap { rNew =>
<https://dvcs.w3.org/hg/read-write-web/file/258d2757ef3d/src/main/scala/Read
WriteWeb.scala#l153> 153 Post.parse(Body.stream(req),
rNew.name, ct) match {
<https://dvcs.w3.org/hg/read-write-web/file/258d2757ef3d/src/main/scala/Read
WriteWeb.scala#l154> 154 case PostRDF(model) => {
<https://dvcs.w3.org/hg/read-write-web/file/258d2757ef3d/src/main/scala/Read
WriteWeb.scala#l155> 155 logger.info("RDF content:\n"
+ model.toString())
<https://dvcs.w3.org/hg/read-write-web/file/258d2757ef3d/src/main/scala/Read
WriteWeb.scala#l156> 156 for {
<https://dvcs.w3.org/hg/read-write-web/file/258d2757ef3d/src/main/scala/Read
WriteWeb.scala#l157> 157 _ <- rNew.save(model)
failMap {
<https://dvcs.w3.org/hg/read-write-web/file/258d2757ef3d/src/main/scala/Read
WriteWeb.scala#l158> 158 t => InternalServerError
~> ResponseString(t.getStackTraceString)
<https://dvcs.w3.org/hg/read-write-web/file/258d2757ef3d/src/main/scala/Read
WriteWeb.scala#l159> 159 }
<https://dvcs.w3.org/hg/read-write-web/file/258d2757ef3d/src/main/scala/Read
WriteWeb.scala#l160> 160 } yield Created ~>
ResponseHeader("Location",Seq(rNew.name.toString))
<https://dvcs.w3.org/hg/read-write-web/file/258d2757ef3d/src/main/scala/Read
WriteWeb.scala#l161> 161 }
<https://dvcs.w3.org/hg/read-write-web/file/258d2757ef3d/src/main/scala/Read
WriteWeb.scala#l162> 162 case PostBinary(is) => {
<https://dvcs.w3.org/hg/read-write-web/file/258d2757ef3d/src/main/scala/Read
WriteWeb.scala#l163> 163 for (_ <- rNew.putStream(is)
failMap { t=> InternalServerError ~> ResponseString(t.getStackTraceString)})
<https://dvcs.w3.org/hg/read-write-web/file/258d2757ef3d/src/main/scala/Read
WriteWeb.scala#l164> 164 yield Created ~>
ResponseHeader("Location",Seq(rNew.name.toString))
<https://dvcs.w3.org/hg/read-write-web/file/258d2757ef3d/src/main/scala/Read
WriteWeb.scala#l165> 165 }
<https://dvcs.w3.org/hg/read-write-web/file/258d2757ef3d/src/main/scala/Read
WriteWeb.scala#l166> 166 case _ => {
<https://dvcs.w3.org/hg/read-write-web/file/258d2757ef3d/src/main/scala/Read
WriteWeb.scala#l167> 167 logger.info("Couldn't parse
the request")
<https://dvcs.w3.org/hg/read-write-web/file/258d2757ef3d/src/main/scala/Read
WriteWeb.scala#l168> 168 (BadRequest ~>
ResponseString("You MUST provide valid content for given Content-Type: " +
ct)).success
<https://dvcs.w3.org/hg/read-write-web/file/258d2757ef3d/src/main/scala/Read
WriteWeb.scala#l169> 169 }
<https://dvcs.w3.org/hg/read-write-web/file/258d2757ef3d/src/main/scala/Read
WriteWeb.scala#l170> 170 }
<https://dvcs.w3.org/hg/read-write-web/file/258d2757ef3d/src/main/scala/Read
WriteWeb.scala#l171> 171 }
<https://dvcs.w3.org/hg/read-write-web/file/258d2757ef3d/src/main/scala/Read
WriteWeb.scala#l172> 172
rNew is the new resource created in the collection.
Post.parse parses the inputstream and de-relativises all URLs to the new
url.
( Of course one would get the same effect if one just placed the unparsed
document at that location with its relative urls )
so the advantage of this is that it does not even require the rdf to be
parsed to work correctly.
Henry
* Should POST support a user supplied local-name 'hint'; e.g. based on the
supplied rdfs:label, to support more human-readable URIs?
yes, Atom has something on this I think
http://tools.ietf.org/html/rfc5023#section-9.7
If it is good enough one should probably use that as they spent enormous
amounts of time
discussing that.
Alternatively, an owl:sameAs could be used in the above to provide a
user-friendly name.
Social Web Architect
http://bblfish.net/
Social Web Architect
http://bblfish.net/
Received on Tuesday, 9 October 2012 10:39:31 UTC