8.4 Reliable Posting
The POST operation needs special care if we want it to work reliably over communication channels. For example, any network that involves the public Internet will have a level of unreliability.
The problem is that the HTTP protocol does not guarantee a request is delivered once and only once. Instead, the request may never reach the destination in which case the sender will get an error. Or it may be delivered several times even if sent only once, and the sender will not know it. Also, the sender may get a timeout even though the request has been successfully delivered and processed on the provider side. This can be summarized by saying that HTTP guarantees almost nothing when it comes to message delivery. The only guarantee is that you know that the message has been received at least once if you receive an HTTP response, i.e., a 2xx, 3xx, 4xx or 5xx status code.
This is not an issue for GET operations because these operations are “safe”. They don’t have observable side-effects on the provider side. Neither is it an issue for PUT and DELETE operations because these operations are “idempotent” (their side effects remain the same if called several times). Executing them a second time does not change the observable state of the service provider. GET operations are also idempotent because they are “safe”.
However, this is a problem for POST operations. If a resource creation request is delivered twice and if we don’t take any preventive action, the service provider will create two resources.
This problem can be handled at two levels:
At the application level, by designing the resources so that a duplicate POST will not do any harm. This is the solution presented in the POST example of this section. The <salesOrder> payload contains sdata:uuid values that the provider stores in its database. The universally unique identifiers (UUIDs) are generated by the consumer. With this approach, the provider can detect a duplicate POST of the same request.
At the protocol level, by passing additional information outside the payload (outside the <salesOrder> element in the example). The advantage of this approach is that it does not require any change to the application’s data model (although the systematic introduction of a UUID is encouraged because it can help with other issues like synchronization).
SData provides a “protocol level” mechanism for reliable POSTing. The mechanism consists in adding a trackingID parameter to the POST URL. It is similar the SData protocol for Asynchronous Service Operations. Here is an example:
The consumer generates the UUID and passes it via the trackingID parameter.
The provider will treat this request as an asynchronous request and it will send a response like:
202 Accepted Location: http://www.example.com/sdata/myApp/myContract/-/salesOrders/$post('abc42b0d-d110-4f5c-ac79-d3aa11bd20cb') Content-Type: application/xml <?xml version="1.0" encoding="utf-8"?> <tracking xmlns="http://schemas.sage.com/sdata/2008/1"> <phase>Initialization</phase> <phaseDetail>Starting thread</phaseDetail> <progress>0.0</progress> <elapsedSeconds>0</elapsedSeconds> <remainingSeconds>7</remainingSeconds> <pollingMillis>500</pollingMillis> </tracking>
The Accepted status code indicates that the server has accepted the request and the Location header gives the URL that the consumer MUST use to track the progress of the operation (see section on Asynchronous Service Operations for more details).
The consumer MUST track the progress of the POST operation by sending GET requests on the tracking URL: http://www.example.com/sdata/myApp/myContract/-/salesOrders/$post(‘abc42b0d-d110-4f5c-ac79-d3aa11bd20cb’), until its gets a response status other than 202.
If the POST is successful, the consumer will receive a response like:
201 Created Content-Type: application/atom+xml; type=entry Location: http://www.example.com/sdata/myApp/myContract/-/salesOrders('43663') ETag: 2nXz9DZYR2pqmcXi/ZCbYA== <entry xmlns:sdata="http://schemas.sage.com/sdata/2008/1" xmlns:http="http://schemas.sage.com/sdata/http/2008/1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.w3.org/2005/Atom"> <id>http://www.example.com/sdata/myApp/myContract/-/salesOrders('43663')</id> <title>Sales Order 43663</title> <updated>2008-03-31T13:46:45Z</updated> <!-- more elements - skipped for clarity --> </entry>
At this stage, the consumer MUST delete the tracking context with a call like:
The tracking id identifies the context of the POST operation. It allows the caller to track the progress of the operation and delete the context at the end. It should not be confused with the id of the resource created by the operation (43663 in the example).
SData providers MAY support the reliable posting protocol based on the trackingID parameter.