exyus workflow example

2007-11-14 @ 23:32#

i was giving a talk this week on the patterns i use in the Exyus HTTP engine and the topic of 'workflow' came up. "These HTTP-based classes are nice, but what if you need to do some workflow in there?" is the way the question usual goes. it's all there - all cool, but i had no example.

until now [grin].

as part of my project to shadow scottgu's ASP.NET MVC demo using my own Exyus engine, i worked up a small example that validates the existing of a 'foreign key' field in a PUT entity body. works like this:

i need to PUT a product record that contains a *valid* (existing) category reference. if the category record does not exist, kill the PUT. keep in mind i am currently implementing this products/categories example using my XmlFileHandler that uses files for storage. ok, the product XML looks like this:

<product>
	<id>123</id>
	<name>shoe</name>
	<quantity>10</quantity>
	<category>junk</category>
</product>

and here's the Exyus class that contains the resource definition (see my previous post on how i did this) along with some custom code in the Put() method to validate the category:

public override void Put()
{
    // make sure the category exists!

    XmlDocument xmldoc = new System.Xml.XmlDocument();
    XmlNode node = null;
    string cid = string.Empty;

    // get category from the incoming body
    xmldoc.Load(this.Context.Request.InputStream);
    node = xmldoc.SelectSingleNode("//category");
    cid = (node != null ? node.InnerXml : string.Empty);

    try
    {
        // make sure the category already exists!
        Requestor req = new Requestor();
        ExyusPrincipal ep = (ExyusPrincipal)this.Context.User;
        req.Credentials = new NetworkCredential(((ExyusIdentity)ep.Identity).Name, ((ExyusIdentity)ep.Identity).Password);
        string url = string.Format("{0}://{1}{2}{3}",
                    this.Context.Request.Url.Scheme,
                    this.Context.Request.Url.DnsSafeHost,
                    "/xcs/data/products/categories/",
                    cid);
        req.Execute(url,"head");
    }
    catch (HttpException hex)
    {
        // not found!
        if (hex.GetHttpCode() == 404)
            throw new HttpException(404, "category [" + cid + "] not found");
        else
            throw new HttpException(hex.GetHttpCode(), hex.Message);
    }

    // go ahead and do the rest of the work
    base.Put();
}

note that i simply make a HEAD request back to the server using the supplied category value in the URL. if it doesn't exist, i get a 404 back and can then format an informative response for the caller.

here's the whole HTTP exchange:

REQUEST: **************
PUT /xcs/data/products/shoe HTTP/1.1
if-match: "ihkMhXfJIqQJOQcgubHk9g"
Host: localhost
Accept: */*
Content-Length:96

<product>
	<id>123</id>
	<name>shoe</name>
	<quantity>10</quantity>
	<category>junk</category>
</product>

RESPONSE: **************
HTTP/1.1 404 Not Found
Server: Microsoft-IIS/5.1
Date: Thu, 15 Nov 2007 04:31:31 GMT
X-Powered-By: ASP.NET
Connection: close
X-AspNet-Version: 2.0.50727
Cache-Control: private
Content-Type: text/html

<html>
<head><title>404 Error</title></head>
<body><h1>404 Error</h1><p>category [junk] not found</p></body>
</html>

and there's lots of other things that can be done as well. so handling workflow within the Exyus resource classes is a piece of cake, really.

code