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 |
(Required in certain cases; see Atom specification.) |
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 |
(If no content element, then entry must contain at least one |
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
. They
set
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.
AtomServer supports category queries. 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 query parameters are summarized in the following table. 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 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.
Query results contain the following OpenSearch elements directly
under the <feed>
element:
openSearch:totalResults
- The total number of search results for the query. This element is
optional, and is not provided by default.
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:
atomserver:endIndex
- The 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.
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="..."/>
- Contains the URI of this resource. The value of the
type
attribute 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="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.
Query results also contain the following AtomServer extension
elements
directly
under the <entry>
element:
atomserver:entryId
- The Id of the Atom Entry. This is 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.
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="https://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" />
<link href="/myserver/v1/widgets/acme/205390.en.xml" rel="alternate" />
</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" />
<link href="/myserver/v1/widgets/acme/205395.en.xml" rel="alternate" />
</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.
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 only the edit URI is affected, not the entry ID. In this scheme, each update changes the entry's 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 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 409 Conflict if it is wrong
* if you try to DELETE a resource that does not exist, you will get a 404 Not Found response.
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 link 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