This document describes the protocol used by the AtomServer data APIs ("AtomServer"), including information about what a query looks like, what results look like, and so on. This the reference document and is intended for anyone wanting to understand the XML format and protocol used by AtomServer.
For a further, detailed description of the actual protocol, either
- See the Atom Publishing Protocol Reference for further information about the elements of the feed itself. Note that we do not exhaustingly document this information herein, because we are using Atom entirely as dictated by the standard.
- See the AtomServer Protocol
Basics document for general information about communicating with
the AtomServer service.
Nor does this document explain the basics of XML, namespaces, syndicated feeds, and the GET, POST, PUT, and DELETE requests in HTTP, as well as HTTP's concept of a "resource." For more information about those things, see the Additional resources section of this document.
This document doesn't rely on any particular programming language; you can send and receive AtomServer messages using any programming language that lets you issue HTTP requests and parse XML-based responses.
Finally, giving credit where it is due, this document is "liberally derived" from the GData Protocol Reference document.
Contents
- Protocol details
- Optimistic Concurrency
- Locale Sensitivity
- Http Response Codes
- Additional resources
Protocol details
This section describes the AtomServer document format and query syntax.
Document format
AtomServer and Atom share the same basic data model: a container that holds both some global data and any number of entries. For each protocol, the format is defined by a base schema, but it can be extended using foreign namespaces.
Note: AtomServer feeds are in Atom
format
and thus, use the Atom namespace as the default namespace by specifying
an xmlns
attribute on the feed element; see the examples section for examples of
how to do that. Thus, the examples in this document don't explicitly
specify atom: for elements in an Atom-format feed.
The following tables show the Atom representations of the elements of the schema. All data not mentioned in these tables is treated as plain XML and shows up the same in both representations. Unless indicated otherwise, the XML elements in a given column are in the namespace corresponding to that column. This summary uses standard XPath notation: in particular, slashes show the element hierarchy, and an @ sign indicates an attribute of an element.
In each of the following tables, the highlighted items are required.
The following table shows the elements of a AtomServer feed:
| Feed Schema Item | Atom Representation |
|---|---|
| Feed Title | /feed/title |
| Feed ID | /feed/id |
| Feed HTML Link | /feed/link[@rel="alternate"] [@type="text/html"]/@href |
| Feed Language | /feed/@xml:lang |
| Feed Author |
|
| Feed Last Update Date | /feed/updated(RFC 4287 format) See the About Date Constructs section for further details. |
The following table shows the elements of a AtomServer feed. Note that AtomServer exposes some of the OpenSearch 1.1 Response elements in its search-results feeds.
| Search Result Feed Schema Item | Atom Representation |
|---|---|
| Number of Search Results | /feed/openSearch:totalResults |
| Search Result Start Index | /feed/openSearch:startIndex |
| Number of Search Results Per Page | /feed/openSearch:itemsPerPage |
The following table shows the elements of a AtomServer Entry:
| Entry Schema Item | Atom Representation |
|---|---|
| Entry ID | /feed/entry/id |
| Entry Title | /feed/entry/title |
| Entry Link | /feed/entry/link |
| Entry Content |
|
| Entry Publication Date | /feed/entry/published(RFC 4287) See the About Date Constructs section for further details. |
| Entry Update Date | /feed/entry/updated(RFC 4287) See the About Date Constructs section for further details. |
Queries for Time-sensitive Feeds
This section describes how to use the query system to request Feeds that are time-sensitive. The query model is intentionally very simple. The basic tenet is that queries are expressed as HTTP URIs, rather than as HTTP headers or as part of the payload. One benefit of this approach is that you can link to a query.
Query requests
A client queries the AtomServer service by issuing an HTTP GET
request. The query URI consists of the resource's URI (called FeedURI
in Atom) followed by query parameters. Most query parameters are
represented as traditional ?name=value[&...] URL
parameters. Category parameters are handled differently; see below.
For example, if the FeedURI is http://foo.com/myserver/v1/widgets/,
then you might send a query with the following URI:
http://foo.com/myserver/v1/widgets/acme?updated-min=2005-04-19T15:30:00
AtomServer services support HTTP Conditional GET.
AtomServer
sets
the Last-Modified
response header based upon the value of the <atom:updated>
element in the returned feed or entry. A client can send this value
back as the value of the If-Modified-Since
request header to avoid
retrieving the content again if it hasn't changed. If the content
hasn't changed since the If-Modified-Since
time, then the AtomServer
service
returns a 304(Not Modified) HTTP
response. Although a better way to do this is to use the start-index parameter, where
the client sets this value to the <as:endIndex>
returned in the previous page. Using start-index ensures that the
client will never see the same response twice, or miss Entries, since
several Entries could have the same "update date", but will never have
the same "update index".
http://foo.com/myserver/v1/widgets/acme?start-index=12345
Passing a standard
parameter not understood by a given service results in a 403
Forbidden response. Passing an unsupported nonstandard parameter
results in a 400 Bad Request response. For information on
other status codes, see the HTTP status
codes section of this document.
The standard AtomServer query parameters are summarized in the following table. Note that AtomServer supports category queries. All parameter values need to be URL encoded.
| Parameter | Meaning | Notes |
|---|---|---|
/-/category |
Category queries |
|
updated-min, updated-max |
Bounds on the entry update date |
|
start-index |
The "update index" of the last result retrieved |
|
max-results |
Maximum number of results to be retrieved |
|
| locale |
The locale Id of the
requested info |
|
| entry-type |
The type of Entry to return. |
|
| entryID.localeID.xml | ID of a specific entry to be retrieved |
|
Query responses
Queries return an Atom Feed or an Atom Entry, depending on the request parameters and the URL structure.
Query results contain the following OpenSearch
elements directly
under the <feed> element:
| Parameter | Meaning | Notes |
|---|---|---|
openSearch:totalResults |
The total number of search results for the query |
|
openSearch:startIndex |
The index of the first result. | See the start-index in the table above for important information about this element. |
openSearch:itemsPerPage |
The maximum number of items that appear on one page. | This allows clients to
generate direct links to any set of subsequent pages. However, for a
possible pitfall in using this number, see the note regarding start-index
in the table in the Query
requests
section. |
Query results also contain the following AtomServer extension
elements
directly
under the <feed> element: (Note further elements are defined
by Aggregate Feeds and Batch processing)
| Parameter | Meaning | Notes |
|---|---|---|
atomserver:endIndex |
The "update index" of the final result. | See the start-index description in the table above for important information about this element. Note; the endIndex returned by a Feed can be used as the start-index for a subsequent Feed page request. |
Query results also contain the following AtomServer extension
elements
directly
under the <entry> element: (Note further elements
are defined by Aggregate Feeds and Batch processing)
| Parameter | Meaning | Notes |
|---|---|---|
atomserver:entryId |
The Id of the Atom Entry. | This element is provided as a convenience to the User. It is particularly important for POST requests, when the Id was created within AtomServer. It saves the User the trouble of parsing the Id out of the <id> element, because inevitably the User will need it for further processing. |
| atomserver:revision |
The revision number of the Atom
Entry |
This element is provided as a convenience to the User. It is used for optimistic concurrency requests (see below). |
| atomserver:updateIndex |
The "update index" of the Atom
Entry |
This element is provided as a convenience to the User. It is primarilly used for debugging. |
The Atom response Feed and Entries may also include any of the following Atom and AtomServer elements (as well as others listed in the Atom specification):
<link rel="self" type="..." href="..."/>- For Entries, this contains the URI of this resource, without its revision number. For
Feeds, this contains the URI required to get back this particular page.
The value of the
typeattribute depends on the requested format. If no data changes in the interim, sending another GET to the self URI returns the same response. <link rel="edit" type="..." href="..."/>- Contains the URI of the next,
presumably unwritten, resource, including
its revision number. The value of the
typeattribute depends on the requested format. If no data changes in the interim, sending PUT to the edit URI will produce a successful update. It is always recommended that you PUT using the "edit" URI.
<link rel="next" type="application/atom+xml" href="..."/>- Specifies the URI of the next chunk of this query result set,
if
it is chunked. The client must
use the next link when
accessing the next page of results.
Here's a sample response body, in response to a Feed query:
<?xml version='1.0' encoding='UTF-8'?>
<feed xmlns="http://www.w3.org/2005/Atom"
xmlns:as="http://atomserver.org/namespaces/1.0/"
xmlns:os="http://a9.com/-/spec/opensearchrss/1.1/">
<os:totalResults>65801</os:totalResults>
<os:startIndex>0</os:startIndex>
<os:itemsPerPage>2</os:itemsPerPage>
<as:endIndex>153</as:endIndex>
<link href="/myserver/v1/widgets/acme?start-index=153&max-results=2" rel="next" />
<link href="/myserver/v1/widgets/acme?start-index=0&max-results=2" rel="self" />
<author><name>AtomServer APP Service</name></author>
<title type="text">acme entries</title>
<updated>2007-10-05T19:17:42.750Z</updated>
<id>tag:atomserver.org,2007:v1:acme</id>
<entry>
<id>/myserver/v1/widgets/acme/205390.en.xml</id>
<as:entryId>205390</as:entryId>
<updated>2007-10-05T18:48:23.437Z</updated>
<published>2007-10-05T18:48:23.437Z</published>
<title type="text"> Entry: acme 205390.en</title><author>
<name>AtomServer Atom Service</name></author>
<link href="/myserver/v1/widgets/acme/205390.en.xml" rel="self" />
<link href="/myserver/v1/widgets/acme/205390.en.xml/2" rel="edit" />
</entry>
<entry>
<id>/myserver/v1/widgets/acme/205395.en.xml</id>
<as:entryId>205395</as:entryId>
<updated>2007-10-05T15:48:56.437Z</updated>
<published>2007-10-05T12:50:76.437Z</published>
<title type="text"> Entry: acme 205395.en</title><author>
<name>AtomServer Atom Service</name></author>
<link href="/myserver/v1/widgets/acme/205395.en.xml" rel="self" />
<link href="/myserver/v1/widgets/acme/205395.en.xml/1" rel="edit" />
</entry>
</feed>
If
the requested feed is in the Atom format, if no query parameters are
specified, and if the result doesn't contain all the entries, the
following element is inserted into the top-level feed: <link
rel="next" type="application/atom+xml" href="..."/>. It
points to a feed containing the next set of entries. Subsequent sets
contain a corresponding <link rel="previous"
type="application/atom+xml" href="..."/> element. By
following all the next links, a client can retrieve all
entries from a feed. Note that following
"next links" is the only reliable way to retrieve pages.
Dealing with Time-sensitive
Feeds
A time-sensitive Feed is one that involves requests using the Last Modified Date, which is the
date from which you expect to receive Entries that have have been
modified. Using AtomServer, it is possible to specify the Last
Modified Date two different ways; using the If-Modified-Since HTTP Request
Header, or using the updated-min
Query Request Parameter (e.g. widgets/acme?updated-min=2005-04-19T15:30:00
)The Last Modified Date must be provided using the RFC4287 standard, which is detailed below. And the Last Modified Date is inclusive. In other words, the time-sensitive Feed will contain all Entries from the Last Modified Date onwards, including those at the Last Modified Date itself.
It is important to understand that a time-sensitive Feed contains any Entries that have been modified since the provided date. And since AtomServer is a store, Entries may be continually modified. This means that it is entirely possible that you will see Entries you've seen previously in a Feed. For example, Let's imagine that you've requested a Paged Feed, and you've seen Entries A, B, C ,D, and E, and let's say that B has been deleted (remember, deleted Entries are not really deleted, but are simply marked as deleted) and that D has been modified. Then you will see both B and D again in a later page.
The best way to access a time-sensitive Feed is to use the start-index URL Query (e.g. GET /v1/widgets/acme?start-index=153). Using this method ensures that you will never receive any inappropriate duplicate Entries. The reason for this is that a "last modified date" operation is, by definition, an inclusive operation (i.e. ">="). And that many Entries may have the same Date. To work around this, AtomServer indexes every Entry monotonically. And a "get next index" operation is exclusive (i.e. ">"), so you are therefore assured of getting the appropriate next Entry. The start-index for a Feed is equal to the endIndex returned by the last page you received.
You may also place an upper bound on time-sensitive Feeds using either the updated-max and end-index query parameters. (e.g. GET /v1/widgets/acme?start-index=153&end-index=299 or widgets/acme?updated-min=2005-04-19&updated-max=2008-10-07). Note that updated-max is "exclusive" and end-index is inclusive. It is an error (400 BAD REQUEST) to request updated-max less than updated-min, or end-index less than start-index. If start-index equals end-index, a 304 NOT MODIFIED is returned.
The RFC4287 Date Specification
Any Dates provided must match the RFC4287 specification, which is described below. Most important, dates are assumed to be in GMT, unless you provide a timezone offset!3.3. Date Constructs
A Date construct is an element whose content MUST conform to the
"date-time" production in [RFC3339]. In addition, an uppercase "T"
character MUST be used to separate date and time, and an uppercase
"Z" character MUST be present in the absence of a numeric time zone
offset.
atomDateConstruct =
atomCommonAttributes,
xsd:dateTime
Such date values happen to be compatible with the following
specifications: [ISO.8601.1988], [W3C.NOTE-datetime-19980827], and
[W3C.REC-xmlschema-2-20041028].
Example Date constructs:
<updated>2003-12-13T18:30:02Z</updated>
<updated>2003-12-13T18:30:02.25Z</updated>
<updated>2003-12-13T18:30:02+01:00</updated>
<updated>2003-12-13T18:30:02.25+01:00</updated>
Date values SHOULD be as accurate as possible. For example, it would
be generally inappropriate for a publishing system to apply the same
timestamp to several entries that were published during the course of
a single day.
Optimistic concurrency (versioning)
It is important to ensure that multiple clients don't inadvertently overwrite one another's changes. This is most easily accomplished by ensuring that the current version of an Entry that a client is modifying is the same as the version that the client is basing its modifications on. If a second client makes an update before the first client does, then the first client's update is denied, because the first client is no longer basing its modifications on the latest version.In AtomServer, we achieve these semantics by appending a version ID to each entry's edit URI. Note that the edit URI always points to the version you should PUT to (i.e. to one version greater than the current version), while the self link always points to an "unversioned" URI. Note that only the edit URI is affected, not the entry ID. In this scheme, each update changes the entry's revision number, and its corresponding edit URI, thus guaranteeing that subsequent updates based on the original version fail. Deletes, of course, are identical to updates with respect to this feature; if you send a delete with an old version number, the delete fails.
If the server detects a version conflict on PUT or DELETE, the server responds with 409 Conflict. The body of the response contains the correct edit URI of the entry. The client is advised to resolve the conflict and resubmit the request, using the edit URI from the 409 response. An example error response follows;
<a:error xmlns:a="http://incubator.apache.org/abdera">
<a:code>409</a:code>
<a:message>Optimistic Concurrency Error:: /myserver/v1/widgets/acme/12345.en.xml/2</a:message>
<link xmlns="http://www.w3.org/2005/Atom" href="/myserver/v1/widgets/acme/12345.en.xml/3" rel="edit" />
</a:error>
A couple of things to note:
* you are not required to send the version ID on a GET (in fact, it is discouraged), but if you supply one, it must be correct, and you will get a 404 NOT FOUND if it is wrong
* if you try to DELETE a resource that does not exist, you will get a 404 NOT FOUND response.
* if you try to PUT or DELETE a revision that already has been written, you will get a 409 CONFLICT.
Overriding Optimistic Concurrency
By its very nature, Optimistic Concurrency is easily ignored by clients - that is to say, whenever AtomServer refuses to write a resource because of a mismatched version ID, it will respond telling the client what the correct version ID is. If the client wishes, it can always use that link to immediately overwrite the resource. In the case of resources that have a "single writer", where you will always know that the data you want to write should be written, you can avoid having to make that first round trip to determine the version ID by specifying a version ID of "*". For example, if you do a PUT to http://foo.com/myserver/v1/widgets/acme/1234.en.xml/*, your write will succeed, regardless of the current version ID. The response will still contain the correct entry links and version ID.
Motivation and design notes
This approach to optimistic concurrency allows us to implement the semantics we want without requiring new markup for version IDs, which makes AtomServer's responses compatible with non-AtomServer Atom endpoints.
Instead of specifying version IDs, we could have chosen to look at the update timestamp on each entry (/atom:entry/atom:updated). However, there are two problems with using the update timestamp:
* It only works for updates and not deletions.
* It forces applications to use date/time values as version IDs, which are problematic.
Locale sensitivity
At one time AtomServer was "locale sensitive", but there is no longer any "locale sensitivity". What this means is that you must be specific when requesting locales, and less generic locales are no longer returned when their specific counterparts are requested. For example, if you request 123.en_GB.xml and there is none, but there is a 123.en.xml, you will not receive 123.en.xml - you will receive a 404 NOT FOUND. Similar for Feed requests, when you request /widgets?locale=en_GB, you will receive only en_GB Entries -- and if there are none; 404. It was decided that this behavior was more consistent with a "Data Service". And that locale sensitivity was confusing and complicating things in the client Feed Readers.HTTP status codes
The following table describes what various HTTP status codes mean in the context of AtomServer.
| Code | Explanation |
|---|---|
| 200 OK | No error. |
| 201 CREATED | Creation of a resource was successful. |
| 304 NOT MODIFIED | The resource hasn't changed since the time specified in the request's If-Modified-Since header. |
| 400 BAD REQUEST | Invalid request URI or header, or unsupported nonstandard parameter. |
| 401 UNAUTHORIZED | Authorization required. |
| 403 FORBIDDEN | Unsupported standard parameter, or authentication or authorization failed. |
| 404 NOT FOUND | Resource (such as a feed or entry) not found. |
| 409 CONFLICT | Specified version number doesn't match resource's latest version number. |
| 422 BAD CONTENT |
The data within this entry's
<content> is not valid. For example, this may indicate not
"well-formed" XML |
| 500 INTERNAL SERVER ERROR | Internal error. This is the default code that is used for all unrecognized errors. |
Additional resources
You may find the following third-party documents useful:
- Overview of Atom from IBM
- HTTP 1.1 method
definitions; specification for
GET,POST,PUT, andDELETE - HTTP 1.1 status code definitions
- How to Create a REST Protocol
- Building Web Services the REST Way
- A Technical Introduction to XML
- XML Namespaces by Example
AtomServer, Protocol Reference 