LeeTee
LeeTee

Reputation: 6601

building RESTful API - handling POST requests and validation

I am building an API and am struggling with the best way to deal with Posting and updating resources. At the moment the user will post data to a products resource using XML. I validate this XML against an xsd file. This works great but means users wanting to update one field have to post all fields relating to a product. Is this expected or should I be doing this some other way?

I have a another resource that will either need the "status" or "dispatched" updating but would only update one or the other and never at the same time. Therefore would I build this in the same way I have the product resource and require that they always POST both fields even if only one will be updated or have these as two separate resources?

Upvotes: 2

Views: 1619

Answers (2)

Tom Howard
Tom Howard

Reputation: 6687

You can handle updates another way. The first question is how do you clients know how to update the resource? If they are receiving the information out-of-band, then you're not doing REST. By including update information in-band, it makes how to update explicit and thereby gives you far greater freedom and flexibility. For instance, a product resource can be represented as follows

<product href="/products/123"
    name="iPad 64GB + 4G"
    price="829.00">
    <description>It's cool, ya</description>
    <update href="/products/123" method="PUT">
        <name type="xs:string" cardinality="optional"/>
        <price type="xs:decimal" cardinality="optional"/>
        <description type="xs:string" cardinality="optional"/>
    </update>
    ... you could have a delete form here as well ...
</person>

meanwhile, the products collection can be represented as

<people href="/products">
    ... the first set of items in the collection and pagination links could go here ...
    <create href="/product" method="POST">
        <name type="xs:string" cardinality="required"/>
        <price type="xs:decimal" cardinality="required"/>
        <description type="xs:string" cardinality="required"/>
    </create>
</people>

Notice how the cardinality of the arguments are different between update and create.

In terms of PUT replacing the entire resource state, think of it this way: When I update a product and only specify the price, the name and description default to their existing values and then the entire resource is updated. The only thing we need to do is clearly define this logic in our media-type.

From what you have described, you currently have something that could be represented along the lines of

<product href="/products/123"
    name="iPad 64GB + 4G"
    price="829.00">
    <description>It's cool, ya</description>
    <update href="/products/123" method="PUT">
        <product type="my:product" cardinality="required"/>
    </update>
    ... you could have a delete form here as well ...
</person>


<people href="/products">
    ... the first set of items in the collection and pagination links could go here ...
    <create href="/product" method="POST">
        <product type="my:product" cardinality="required"/>
    </create>
</people>

This isn't bad per-say, it just the updates kind-of suck, because you've got to include all the fields. You are right to feel this is wrong and question it.

Also, I strongly recommend you don't use an XSD to validate the requests. Unless the XSD is designed very carefully, it will create a tight coupling between your clients and your API, that will either force you to update both at the same time, or in a particular order (e.g., server, then client). Have a look at Schematron instead.

Now, in terms of the order that will have either a status or dispatch details, think of the order resource as a little state machine. It might look like this:

Start ----> Received ----> Processed -------> Dispatched ------> End
               |               |                                  ^
               |               V                                  |
               ----------> Cancelled ------------------------------ 

So, when an order is created, is automatically gets it's status set to received. From there it can either got to processed or cancelled and from processed it can either go to dispatched or cancelled. The idea is to represent the transitions through the forms and links provided within the resource. Based on this, here is how we can represent the order, for the various states

Received (from a customer's perspective):

<order href="/orders/123" status="received">
    ... order details go here ...
    <cancel href="/orders/123" method="delete"/>
</order>

Received (from an employee's perspective):

<order href="/orders/123" status="received">
    ... order details go here ...
    <process href="/orders/123" method="put">
        ... whatever details need to be submitted at the same time ...
    </process>
</order>

Processed (from a customer's perspective)

<order href="/orders/123" status="processed">
    ... order details go here ...
    <cancel href="/orders/123" method="delete"/>
</order>

Processed (from an employee's perspective):

<order href="/orders/123" status="processed">
    ... order details go here ...
    <dispatch href="/orders/123" method="POST">
        <company type="xs:string" cardinality="required"/>
        <con-note type="xs:string" cardinality="required"/>
        <tracking type="xs:anyURI" cardinality="optional"/>
        ... whatever details need to be submitted at the same time ...
    </dispatch>
</order>

Dispatched (from a customer's and employees perspective)

<order href="/orders/123" status="dispatched">
    ... order details go here ...
    <shipping-details href="/order/123/shipping">
</order>

Cancelled (from a customer's and employees perspective)

<order href="/orders/123" status="cancelled">
    ... order details go here ...
</order>

The different state transitions are presented to the different users based on there permissions. An employee can't cancel an order and similarly a customer cannot process or dispatch an order. Also only the allowed state transitions are presented depending on the current state of the order.

Upvotes: 2

Sixto Saez
Sixto Saez

Reputation: 12680

With POST you have flexibility in what state you define for a given resource. POST can be a kind of catch-all method. If you were using the PUT method, then you would need to replace the entire resource state since the HTTP spec defines this as the correct behavior. It may actually make sense for you to create separate resources for status and dispatched with their own GET/POST/PUT/DELETE behavior to represent the use case you are describing.

Upvotes: 1

Related Questions