Abstract
Decouple resource discovery from resource creation and use links to conduct introspection.
Status
Open
Rationale
Creating and discovering resources are two distinct operations. The current protocol model conflates the two using the metaphor of a "collection". This is confusing as the behavior of implementations will often be quite different from that of a set of physical collections. Sometimes they will be more like views, gateways, etc. There is no denying that the collection is a useful and common pattern, but there is no reason to limit ourselves to it.
Perhaps even more importantly, the protocol is easier to explain in a more literal fashion. For instance, we can simply write about "a representation" rather than "a member representation".
The approach shown here also obviates the need for an introspection resource. Link elements (and content@src) are used to locate resources. Resources describe themselves and each other. An introspection resource can still exist, of course, but is just bonus.
This Pace still provides the machinery for introspection (as a process) to take place. If desired, a central manifest of endpoints can still be constructed but it is not needed.
An introspection document provides two things:
1. resource discovery (find "collections")
We can find resources by following links. The rel attribute is there to explain what kind of relationship exists between linked resources.
2. resource "capabilities" and other metadata
Where can I POST entries? Where can I POST media?
This is actually discovery; see above.
Proposal
Replace section 1 with:
1. Introduction The Atom Publishing Protocol is an application-level protocol for publishing and editing Web resources using HTTP [RFC2616] and XML 1.0 [W3C.REC-xml-20040204]. The protocol provides facilities for discovering, creating, reading, updating and deleting resources.
Remove from section 2:
The Introspection Document allows the use of IRIs [RFC3987], as well as URIs [RFC3986].
Remove from section 3:
collection - A resource that contains a set of member IRIs, as described in Section 8 of this specification. member - A resource whose IRI is listed in a collection. IRI template - A parameterized string that becomes a IRI when the parameters are filled in. See Section 9. introspection document - A document that describes the location and capabilities of one or more collections. See Section 7
Add to section 3:
edit link - an Atom or HTML link element whose rel attribute value is 'edit' (See section 10.1) feed link - an Atom or HTML link element whose rel attribute value is 'feed' (See section 10.2) entrypost link - an Atom or HTML link element whose rel attribute value is 'entrypost (See section 10.3) mediapost link - an Atom or HTML link element whose rel attribute value is 'mediapost' (See section 10.4) workspace link - an Atom or HTML link element whose rel attribute value is 'workspace' (See section 10.5) editable representation - an editable representation is one that actors MAY be allowed to edit.
Replace section 4 with:
4. Protocol Model The Atom Publishing Protocol uses HTTP to edit resources on the web. Atom Feed Documents are used to list entry resources. Atom Entry Documents are used to represent entry resources. The APP uses these HTTP verbs: o GET is used to retrieve a representation of a resource. o POST is used to create a new resource. o PUT is used to update a known resource. o DELETE is used to remove a resource. The protocol allows editing of resources with representations of any media-type.
Replace section 5 with two sections:
5. Discovering Resources
Atom Feeds are used to discover the IRIs and other metadata of
various resources.
Client Server
| |
| 1.) GET |
|------------------------------->|
| |
| 2.) 200 OK, Atom Feed Doc |
|<-------------------------------|
| |
1. The client sends a GET request to a known IRI.
2. The server responds with an Atom Feed Document containing the
IRIs and other metadata of various resources.
The methods of identifying the IRIs of resources are detailed in
sections 5.1 - 5.6.
5.1 Feeds
The IRIs of Atom Feeds are found in the href attributes of feed links
(see section 3).
5.1.1 Paging
Feeds can be extremely large. A naive client such as a web spider or
web browser would be overwhelmed if the response to a GET contained
every entry in the feed, and the server would waste large amounts of
bandwidth and processing time on clients unable to handle the
response.
To cope with this, a feed can be represented by a series of Atom Feed
Documents connected together using next links, prev links, start
links and end links.
5.2 Entries
The IRI of the source (editable) Atom Entry Document representation
can be found in the href attribute of an edit link (see section 3).
The IRIs of other (derived) representations can be found in the
href attributes of alternate links.
5.3 Media
The IRI of an editable representation that may be of any media type
can be found in the src attribute of an atom content element.
5.4 Entry Post
IRIs used to create resources by POSTing Atom Entry Documents can be
found in the href attributes of entrypost links (see section 3).
5.5 Media Post
IRIs used to create resources by POSTing representations of any media
type can be found in the href attributes of mediapost links (see
section 3).
5.6 Workspaces
Any resource with links that can be used to discover the IRIs
of Atom resources can be considered to be a "workspace". The IRIs of
workspace resources can be found in the href attributes of workspace
links (see section 3). Typically workspace resources will be HTML
documents or empty Atom Feeds with links to Atom resources. The purpose
of such documents is simply to organize the discovery of Atom resources.
6. Protocol Operations
6.1 Creating a Resource
Client Server
| |
| 1.) POST |
|------------------------------------------>|
| |
| 2.) 201 Created |
|<------------------------------------------|
| |
1. The client POSTs a representation.
2. If the resource was created successfully the server responds with
a status code of 201 and a Location: header that contains the IRI
of the newly created resource.
6.1.1 Creating a Resource via Entry Post
If the representation was POSTed to an entry post (see section 5.1.4)
the IRI returned is that of the editable Atom Entry Document.
6.1.2 Creating a Resource via Media Post
If the representation was POSTed to a media post (see section 5.1.5)
the IRI returned will be that of a representation of the same type
POSTed.
6.2 Reading a Resource
Client Server
| |
| 1.) GET |
|------------------------------------------>|
| |
| 2.) Representation |
|<------------------------------------------|
| |
1. The client sends a GET request to a known IRI to retrieve
a representation of a resource.
2. The server responds with the representation of the resource.
6.3 Updating a Resource
Client Server
| |
| 1.) PUT |
|------------------------------------------>|
| |
| 2.) 200 OK |
|<------------------------------------------|
1. The client PUTs an updated representation to a known IRI.
2. Upon a successful update of the resource the server responds with
a status code of 200.
6.4 Deleting a Resource
Client Server
| |
| 1.) DELETE |
|------------------------------------------>|
| |
| 2.) 200 Ok |
|<------------------------------------------|
| |
1. The client sends a DELETE request to a known IRI.
2. Upon the successful deletion of the resource the server responds
with a status code of 200.
6.5 Success and Failure
The Atom Protocol uses HTTP status codes to signal the results of
protocol operations. Status codes of the form 2xx signal that a
request was successful. HTTP status codes of the form 4xx or 5xx
signal that an error has occurred. Consult the HTTP specification
[RFC2616] for the definitions of HTTP status codes.
Remove sections 7, 8, and 9
Replace the text at the beginning of section 10:
10. Atom Entry Extensions This specification adds four new values to the Registry of Link Relations and adds a new element to Atom Entries called "app:control" for controlling publishing.
Replace section 10.1
10.1 The 'edit' Link Relation This specification adds the value "edit" to the Registry of Link Relations. The value of "edit" signifies that the IRI in the value of the href attribute is the IRI of the resource, and is intended to be used to update and delete resources as described in section 6.
Add the following subsection to section 10:
10.2 The 'feed' Link Relation This specification adds the value "feed" to the Registry of Link Relations. The value of "feed" signifies that the IRI in the value of the href attribute is the IRI of a feed resource, and is intended to be used to discover resources as described section 5. 10.3 The 'entrypost' Link Relation This specification adds the value "entrypost" to the Registry of Link Relations. The value of "entrypost" signifies that the IRI in the value of the href attribute is the IRI of a service resource, which is intended to accept POST requests containing entry representations for the purpose of creating resources as described section 6.1.1. 10.4 The 'mediapost' Link Relation This specification adds the value "mediapost" to the Registry of Link Relations. The value of "mediapost" signifies that the IRI in the value of the href attribute is the IRI of a service resource, which is intended to accept POST requests containing representations of various types for the purpose of creating resources as described section 6.1.1. 10.5 The 'workspace' Link Relation This specification adds the value "workspace" to the Registry of Link Relations. The value of "workspace" signifies that the IRI in the value of the href attribute is the IRI of a resource, which is intended for use in discovering other Atom resources.
Examples
Workspaces
Lets say that you have two blogs: one about motorcycles and one about gardening.
Lets say you would like to lay out interface in a pattern of workspaces containing collections such that your service looks like this:
-spaces
|
-motorcycles
| |
| -entries
| -media
-gardening
|
-entries
-media
You start by pointing your client at /spaces
GET /spaces/index.html
<html>
<head>
<link rel="workspace" title="Motorcycles Blog" href="/motorcycles/index.html"/>
<link rel="workspace" title="Gardening Blog" href="/gardening/index.html"/>
...
</head>
...
</html>
GET /motorcycles/index.html
<html>
<head>
<link rel="alternate" title="Motorcycles Feed" href="/motorcycles/entries"/>
<link rel="feed" title="Entries Feed" href="/motorcycles/entries"/>
<link rel="entrypost" title="Entries POST" href="/motorcycles/entries"/>
<link rel="feed" title="Media Feed" href="/motorcycles/media"/>
<link rel="mediapost" title="Media POST" href="/motorcycles/media"/>
...
</head>
...
</html>
GET /motorcycles/entries
<feed> <link rel="self" title="Motorcycles Entries" href="/motorcycles/entries"/> <link rel="feed" title="Motorcycles Feed" href="/motorcycles/entries"/> <link rel="entrypost" title="Motorcycles POST" href="/motorcycles/entries"/> <link rel="mediapost" title="Media POST" href="/motorcycles/media"/> <link rel="workspace" title="Motorcycles" href="/motorcycles"/> ... </feed>
You could point your client directly at one of these:
-
/motorcycles
-
/motorcycles/index.html
-
/motorcycles/entries
Then go ahead and POST an entry with a pic of your cat sitting on your new bike.
If you point your client at spaces you have to follow one link.
Nested Collectons
The following is taken from an email exchange between myself (LukeArno) and JamesSnell:
So I have the following:
-
/entries
-
/stories
-
/photos
-
/various
-
/bookmarks
I am going to choose to treat '/entries' as the 'root' collection. I am treating everything as a collection except for the media. Their are two media feeds that share a gateway. If you POST an image file it ends up in the Photo Album feed. Otherwise it ends up in various.
I am implementing this with a "default collection" pattern a la Robert S. In other words, this is nested collections without workspaces. If you would like to see a "workspaces" style layout of collections or one gateway and many feeds, see above.
- entries - photos - stories - various - bookmarks
GET /entries:
<feed>
<link rel="self"
title="Entries"
href="/entries"/>
<link rel="entrypost"
title="POST Entries"
href="/entries"/>
<link rel="mediapost"
title="POST Media"
href="/mediapost"/>
<link rel="feed"
title="Stories"
href="/stories"/>
<link rel="feed"
title="Photoalbum"
href="/photos"/>
<link rel="feed"
title="Various"
href="/various"/>
<link rel="feed"
title="Bookmarks"
href="/bookmarks"/>
...
</feed>
GET /stories:
<feed>
<link rel="self"
title="Stories"
href="/stories"/>
<link rel="entrypost"
title="POST Stories"
href="/stories"/>
<link rel="mediapost"
title="POST Media"
href="/mediapost"/>
...
</feed>
GET /photos:
<feed>
<link rel="self"
title="Photoalbum"
href="/photos"/>
<link rel="mediapost"
title="POST Media"
href="/mediapost"/>
...
</feed>
GET /various:
<feed>
<link rel="self"
title="Various"
href="/various"/>
<link rel="mediapost"
title="POST Media"
href="/mediapost"/>
...
</feed>
GET /bookmarks:
<feed>
<link rel="self"
title="Bookmarks"
href="/bookmarks"/>
<link rel="entrypost"
title="POST Bookmarks"
href="/bookmarks"/>
...
</feed>
GET /index.html:
<html>
<head>
<link rel="feed" title="Entries" href="/entries"/>
...
</head>
...
</html>
Point your client at / and off you go.
Multiple Blogs
In some cases a service provider may want to expose multiple blogs available through one account.
One way to do this would be to provide a common "root" feed that links to the resources of multiple services. A service may not want to provide a root feed but still enable the user to point the client at one IRI:
Nested collections:
GET /myaccounts/index.html
<html>
<head>
<link rel="feed" title="Blog One" href="/blogone/entries"/>
<link rel="feed" title="Blog Two" href="/blogtwo/entries"/>
<link rel="feed" title="Blog Three" href="/blogthree/entries"/>
...
</head>
...
</html>
Others may want to use a "workspaces" pattern:
GET /myaccounts/index.html
<html>
<head>
<link rel="workspace" title="Blog One" href="/blogone/index.html"/>
<link rel="workspace" title="Blog Two" href="/blogtwo/index.html"/>
<link rel="workspace" title="Blog Three" href="/blogthree/index.html"/>
...
</head>
...
</html>
Librarian
Lets say you have a system where entries are arranged by category and there is a librarian who is responsible for categorization. You POST entries to some common gateway and the librarian puts them in categories which lands them in the appropriate feed.
To begin you are reading the "Birding" (bird watching) feed and you decide to POST an entry:
GET /birding
<feed> <link rel="entrypost" title="New Entry" href="/new-entry"/> ... </feed>
You client puts together the entry with a category of "birds". That was the wrong name, but that is why the librarian has to approve it. You POST the entry to /new-entries. You entry is flagged with a <x:needs-review/> extension and appears in a feed for the librarian at /needs-review.
GET /needs-review
<feed>
...
<entry>
<category term="birds" label="Birds"/>
<link rel="edit" href="/new-entries/346"/>
<x:needs-review/>
...
</entry>
</feed>
}}
The librarian sees that you have mis-categorized you entry but
that it is otherwise valid. Librarian edits the entry:
GET /new-entries/346
{{{
<entry>
<category term="birds" label="Birds"/>
<link rel="edit" href="/new-entries/346"/>
<x:needs-review/>
...
</entry>
Librarian changes the category to "birding", adds the category "nature", removes <x:needs-review/> and PUTs it back.
The entry no longer appears in the /nees-review feed but now appears in /birding and in /nature.
Entries Can Accept Comments
Clients (including aggregators) need to know where to post comments. Since feeds, entries, and entry and media POST services are fully independent of each other, this is no problem. For instance, if I wanted to be clever I could accept entries POSTed to my entry as comments on that entry. So if I have:
GET /blog/archive/2005/11/13/1
<entry>
<link rel="entrypost"
title="POST a Comment on this Entry"
href="/blog/archive/2005/11/13/1"/>
...
</entry>
Anything POSTed to one of my permalinks in this case would have a link[@rel="about" and href="my permalink"] or an appropriate extension added to it, identifying it as a comment and what on.
If I wanted to expose a comment feed for that entry I could add another link:
GET /blog/archive/2005/11/13/1
<entry>
<link rel="entrypost"
title="POST a Comment on this Entry"
href="/blog/archive/2005/11/13/1"/>
<link rel="feed"
title="Comments Feed for this Entry"
href="/blog/archive/2005/11/13/1/comments"/>
...
</entry>
Private Public
There is no reason why you can't have private and public interfaces in any arrangement that you want. Lets take a blog with an address book, drafts, published, and plans for world domination on the private side and entries (a read only view of published) and comments on the public side.
The private side will all be under /app and the public side will all be under /blog to make securing things at a low level easy.
-
/app/index.html
-
/app/people
-
/app/drafts
-
/app/published
-
/app/plots
-
/blog/index.html
-
/blog/entries
-
/blog/comments
All these feeds are discovered by the blogger at /app/index.html
GET /app/index.html
<html>
<head>
<link rel="feed" title="Address Book" href="/app/people"/>
<link rel="feed" title="Drafts" href="/app/drafts"/>
<link rel="feed" title="Published" href="/app/"/>
<link rel="feed" title="Plans for World Domination" href="/app/plots"/>
<link rel="feed" title="Blog Entries" href="/blog/entries"/>
<link rel="feed" title="Comments" href="/blog/comments"/>
...
</head>
...
</html>
All entries in feeds under /app have an edit link.
GET /app/people
<feed>
<link rel="entrypost"
title="POST hCards"
href="/people"/>
...
</feed>
GET /app/drafts
<feed>
<link rel="entrypost"
title="POST Drafts"
href="/drafts"/>
...
</feed>
Entries are moved from drafts to published. Anything in published appears in /blog/entries (see below) as well
GET /app/published
<feed>
<link rel="entrypost"
title="POST Entries to be Publicly"
href="/published"/>
...
</feed>
GET /app/plots
<feed>
<link rel="entrypost"
title="POST Plans for World Domination"
href="/plots"/>
...
</feed>
The public HTML interface only exposes the appropriate endpoints:
GET /blog/index.html
<html>
<head>
<link rel="feed" title="Blog Entries" href="/blog/entries"/>
<link rel="feed" title="Comments" href="/blog/comments"/>
<link rel="entrypost" title="POST Comments" href="/comments"/>
...
</head>
...
</html>
Note that entries that appear in /blog/entries have no edit link and the only entrypost is for comments:
GET /blog/entries
<feed>
<link rel="entrypost"
title="POST Comments"
href="/comments"/>
...
</feed>
Entries in /blog/comments have no edit link but the comments endpoint does accept POST.
GET /blog/comments
<feed>
<link rel="entrypost"
title="POST Comments"
href="/comments"/>
...
</feed>
Obviously this could all have been arranged an many many other ways.
Cell Phone Manifest
A simple manifest to bootstrap a cell phone client:
GET /cell
<html>
<head>
<title>My Blog</title>
<link rel="feed" title="Entries" href="/entries"/>
<link rel="entrypost" title="POST Entries" href="/entries"/>
<link rel="feed" title="Media" href="/media"/>
<link rel="mediapost" title="POST Media" href="/media" type="*/*"/>
</head>
<body>
<h1>Cell Phone Loader</h1>
<p>
Point your cell phone client here find
all your endpoints in 1 round-trip.
</p>
</body>
</html>
Extensions
Extended metadata can be embeded in links as attributes or elements:
<feed>
<link rel="entrypost" title="POST Entries" href="/entries">
<accepted-type version="x.x">hCard</accpeted-type>
</link>
...
</feed>
Extensions and Workspaces
XHTML link elements do not allow any child elements so if you have a need for extension elements in workspace links you will need to use another format, such as an Atom Feed Document:
GET /index.html
<html> <head> <title>My Blog</title> <link rel="workspace" title="Workspace" href="/workspace"/> ... </head> ... </html>
GET /workspace
<feed>
<link rel="entrypost" title="POST Entries" href="/entries">
<accepted-type version="x.x">hCard</accpeted-type>
</link>
...
</feed>
This will probably not be a concern in most cases.
Lazy Loading
"But how in the world does my client fill out it's purdy tree diagram of folders in the left panel?"
1. Retrieve "root" feed and display with plus sign
-
(or closed folder icon or whatever)
+ root feed
2. Click on plus sign retrieve feeds linked from root
- root feed + posts feed [ post entries] [ post media ] + all comments feed + all media [ post media ]
3. Click on "posts feed" plus sign to expand that
-
and retrieve more feeds.
Note that you have buttons for POSTing to POST endpoints linked to by the feed. You might just right click to see what you can POST
- root feed
- posts feed [ post entries] [ post media ]
+ category a [ post cat a entries ]
- category b [ post cat a entries ]
+ category c [ post cat a entries ]
+ all comments feed
+ all media [ post media ]
Heck, you could even have expandable entries in your top right entry listing panel for entries that have comment feeds or what have you. You could right click an entry and fire off a comment if an entry has an "entrypost" link (probably titled with "POST a comment" or some such.
You can keep track to avoid circular references quite easily. That is the only problem I could think of.
Categories Breakdown
What happens if you have a bunch of category feeds and you don't want to clog the main feed with links. In fact, lets assume that you have a really gargantuan set of categories. How do you break things down in to some neat and tidy arrangement?
Here is one way:
GET /mainfeed
<feed> <link rel="workspace" title="Categories" href="/categories"/> ... </feed>
GET /categories
<html>
<head>
<link rel="workspace" title="A-F" href="/categories/A-F"/>
<link rel="workspace" title="G-L" href="categories/G-L"/>
...
</head>
...
</html>
GET /categories/A-F
<html>
<head>
<link rel="feed" title="aardvark" href="/categories/A-F/aardvark"/>
<link rel="feed" title="abacus" href="/categories/A-F/abacus"/>
...
</head>
...
</html>
Cat Picture (Section 11)
11. Example
This is an example of a client creating a new entry with an image.
The client has an image to publish and an entry that includes an HTML
'img' element that uses that image. In this scenario we consider a
client that has the IRIs of both an entry post and a media post. (see
section 5) The IRI of the entry post is:
http://example.net/blog/edit/
The IRI of the media post is:
http://example.net/binary/edit
First the client creates a new image resource by POSTing the image to
the IRI of the media post.
PO ST /binary/edit/ HTTP/1.1
Host: example.net
Us er-Agent: Thingio/1.0
Con tent-Type: image/png
Con tent-Length: nnnn
Title: A picture of the beach
...binary data...
The resource is created and an HTTP status code of 201 is returned.
HTTP/1.1 201 Created
Date: Fri, 25 Mar 2005 17:17:11 GMT
Con tent-Length: nnnn
Con tent-Type: application/atom+xml
Loc ation: http://example.net/binary/edit/b/129.png
<?xml version="1.0" encoding="utf-8"?>
<entry xmlns="http://www.w3.org/2005/Atom";>
<title>A picture of the beach.</title>
<link rel="edit"
href="http://example.net/binary/edit/b/129.png"/>
<id>urn:uuid:1225c695-cfb8-4ebb-aaaa-568596895695</id>
<updated>2005-09-02T10:30:00Z</updated>
<summary>Waves</summary>
<content type="image/png"
src="http://example.net/binary/readonly/129.png"/>
</entry>
The client then POSTs the Atom Entry that refers to the newly created
image resource. Note that the client takes the IRI
http://example.net/binary/readonly/129.png and uses it in the 'img'
element in the Entry content:
PO ST /blog/edit/ HTTP/1.1
Host: example.net
Us er-Agent: Thingio/1.0
Con tent-Type: application/atom+xml
Con tent-Length: nnnn
<?xml version="1.0" encoding="utf-8"?>
<entry xmlns="http://www.w3.org/2005/Atom";>
<title>What I did on my summer vacation</title>
<id>urn:uuid:1225c695-cfb8-4ebb-aaaa-568599874695</id>
<updated>2005-09-02T10:30:00Z</updated>
<summary>Beach!</summary>
<content type="xhtml" xml:lang="en">
<div xmlns="http://www.w3.org/1999/xhtml";>
<p>We went to the beach for summer vacation.
Here is a picture of the waves rolling in:
<img
src="http://example.net/binary/readonly/129.png";
alt="A picture of the beach."
/>
</p>
</div>
</content>
</entry>
References
http://www.sixapart.com/pronet/docs/typepad_atom_api#1._Listing_the_User's_Weblogs http://lukearno.com/projects/atom-pub/app-model.html
Impacts
draft06
