Reputation:
I'm new to REST but I've built a simple web service and I'm having trouble finding a simple explaination of what URL format would be correct.
The service allows to create an invoice and push it through a series of simple approval phases.
(1) Read all invoices in an XML format:
GET: http://localhost/webapp/ws/invoices
(2) Read one invoice in an XML format (ex. invoice id = 555):
GET: http://localhost/webapp/ws/invoices/555
(3) Submit a new invoice:
POST: http://localhost/webapp/ws/invoices
With the invoice attributes ("userid", "totalprice", etc) are included like the POST parameters of a simple HTML form.
(4) Approve an invoice:
POST: http://localhost/webapp/ws/invoices/action
With the action attributes (ex. "userid=123", invoiceid=567, "action=APPROVE" or "REJECT", etc) are included like the POST parameters of a simple HTML form.
It works fine, but is that even close to what a RESTful web service is supposed to look like?
Any advice is greatly appreciated, thanks.
Rob
Upvotes: 1
Views: 1145
Reputation: 6687
The URLs don't make an API RESTful or not. It is more important to clearly represent your resources and their state transitions (through links and forms) and avoiding out-of-band information that couples clients to your implementation. A RESTful Hypermedia API in Three Easy Steps covers this concept nicely.
1) Create a root resource that provides a well know starting point for all clients and allows them to discover the services available (this can be the same URL used by Browers. Use the Accept
header to determine if HTML or your APIs media-type should be returned).
<webapp href="/webapp">
<invoices href="/webapp/invoices"/>
... any other services ...
</webapp>
2) Create a collection resource for you invoices
GET: http://localhost/webapp/invoices
<invoices href="/webapp/invoices">
<invoice href="/webapp/invoices/555"/>
<invoice href="/webapp/invoices/554"/>
<invoice href="/webapp/invoices/553"/>
<invoice href="/webapp/invoices/552"/>
...
<search href="/webapp/invoices/" method="get">
<query type="xpath" cardinality="required"/>
</search>
<next href="/webapp/invoices?page=2" method="get"/>
<create-draft href="/webapp/invoices" method="post">
<total-price type="decimal" cardinality="optional"/>
... user should be picked up automatically based on the authorised user posting the form ...
... add other optional and required parameters here. ...
</create-draft>
</invoices>
This is a paginated collection, with the next
element telling the client how to get the next page. If there weren't enough invoices (e.g., 5 invoices and each page can contain 10) then the next
element would not be show. Similarly if the requester is no authorised to create invoices then the create-draft
form would not be included.
Getting the next page would look something like:
GET: http://localhost/webapp/invoices?page=2
<invoices href="/webapp/invoices">
<invoice href="/webapp/invoices/545"/>
<invoice href="/webapp/invoices/544"/>
<invoice href="/webapp/invoices/543"/>
<invoice href="/webapp/invoices/542"/>
...
<search href="/webapp/invoices/" method="get">
<query type="xpath" cardinality="required"/>
</search>
<next href="/webapp/invoices?page=3" method="get"/>
<prev href="/webapp/invoices" method="get"/>
<create-draft href="/webapp/invoices" method="post">
<total-price type="xs:decimal" cardinality="optional"/>
... user should be picked up automatically based on the authorised user posting the form ...
... add other optional and required parameters here. ...
</create-draft>
</invoices>
3) Create an item resource for your invoices
GET: http://localhost/webapp/invoices/555
<invoice href="/webapp/invoice/555">
... details go here ...
<reject href="/webapp/invoices/555" method="delete">
<reason type="xs:string" cardinality="required"/>
</reject>
<approve href="/webapp/invoices/555" method="put">
... add approval parameters here ...
</approve>
</invoices>
Similarly, if the user is not authorised to reject or approve invoices then those elements should not be displayed. Same goes if the invoice has already been approved (in which case maybe there is a cancel form).
Upvotes: 2
Reputation: 2559
IMHO for the approval process I would do:
(4) Approve an invoice:
PUT: http://localhost/webapp/ws/invoices/555
As you are going to modify an existing resource, identified by the ID (555), so you only need to pass the attributes that will change.
Upvotes: 0