# Monday, January 14, 2008

Wes Dyer's paper http://blogs.msdn.com/wesdyer/archive/2008/01/11/the-marvels-of-monads.aspx has a great introduction to using Monads in C# and specifically how to create a continuation Monad that leverages the LINQ syntax.

After a lot of re-reading and coding, I managed to construct an asynchronous web request program.

UPDATE: Added Select<T> method for the monad so "let" syntax works.

class Program
{
    static void Main(string[] args)
    {
        var requests = new[]
        {
            WebRequest.Create("
http://www.google.com/"),
            WebRequest.Create("
http://www.yahoo.com/"),
            WebRequest.Create("
http://channel9.msdn.com/")
        };
        var pages = from request in requests
            select
                from response in request.GetResponseAsync()
                let stream = response.GetResponseStream()
                from html in stream.ReadToEndAsync()
                select new { html, response };

        foreach (var page in pages)
        {
            page(d =>
            {
                Console.WriteLine(d.response.ResponseUri.ToString());
                Console.WriteLine(d.html.Substring(0, 40));
                Console.WriteLine();
            });
        }

        Console.ReadKey();
    }
}

The actual web requests are deferred until the for loop invokes each "page" method. If you run the example a few times, you see that the results come back in a non-deterministic order, as expected with concurrent asynchronous operations. In addition, the web response stream is read asynchronously. So the main thread is never blocked - in fact that's why we need the Console.ReadKey() at the end, to stop the process ending before the results come back!

I had to change the type of the continuations from what Wes used. Instead of Func<T, Answer> I am just using Action<T>. The following code implements the continuation monad and the additional WebRequest and Stream extension methods.

delegate void K<T>(Action<T> c);

static class Continuations
{
    public static K<T> ToContinuation<T>(this T value)
    {
        return c => c(value);
    }

    public static K<U> SelectMany<T, U>(this K<T> m, Func<T, K<U>> k)
    {
        return c => m(x => k(x)(c));
    }

    public static K<V> SelectMany<T, U, V>(this K<T> m, Func<T, K<U>> k, Func<T, U, V> s)
    {
        return m.SelectMany(x => k(x).SelectMany(y => s(x, y).ToContinuation<V>()));
    }

    public static K<U> Select<U, T>(this K<T> m, Func<T, U> k)
    {
        return c => m(x => c(k(x)));
    }

    public static K<WebResponse> GetResponseAsync(this WebRequest request)
    {
        return send => request.BeginGetResponse(result =>
        {
            var response = request.EndGetResponse(result);
            send(response);
        }, null);
    }

    public static K<string> ReadToEndAsync(this Stream stream)
    {
        return send =>
        {
            byte[] buffer = new byte[1024];
            StringBuilder sb = new StringBuilder();
            Func<IAsyncResult> readChunk = null;
            readChunk = () => stream.BeginRead(buffer, 0, 1024, result =>
            {
                int read = stream.EndRead(result);
                if (read > 0)
                {
                    sb.Append(Encoding.UTF8.GetString(buffer, 0, read));
                    readChunk();
                }
                else
                {
                    stream.Dispose();
                    send(sb.ToString());
                }
            }, null);
            readChunk();
        };
    }

}

I imagine we will see something like this come out of the Volta project soon. Hopefully, it will form a generic asynchronous library that we can all use with anything that implements the Begin/End Invoke pattern.

Monday, January 14, 2008 1:22:13 PM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [0]  | 
# Thursday, January 10, 2008

I have released an initial version of Snooze. http://www.assembla.com/wiki/show/snooze

Snooze is an REST framework for ASP.NET. It puts the emphasis back on the "R" of URI. The Resource concept is a first-class citizen.

In Snooze you create a resource class that defines the URI template and HTTP methods it supports.

[HttpResource("customer/{CustomerId}")] // Defines the URI template for this resource
public class CustomerResource : HttpResource // A useful base class that implements IHttpResource
{
    // URI template variables are mapped to properties
    public int CustomerId { get; set; }

    // Handles the HTTP GET method
    public override void Get()
    {
        RenderView("Customer", Data.Customers[CustomerId]);
    }

    public override void Put()
    {
        // Read data sent in Request body and update the customer...
    }

    public override void Delete()
    {
        // Delete the customer...
    }

    // HTTP POST can also be provided if it makes sense for the resource.
}

This will mean a URI like "http://yourserver.com/customer/42" will invoke the Get method of an instance where CustomerId equals 42.

What about creating a new Customer?

Well you don't have an ID for the new customer since that is probably created in the database. Therefore you don't know what the URI will be yet. The correct solution here is to POST the new customer data to a CustomerList resource, with URI "/customers".

Alternative GET Views

You can create extra GET methods:

[HttpGetView("edit")]
public void GetEdit()
{
    RenderView("CustomerEdit", Data.Customers[CustomerId]);
}

This results in URIs like "/customer/42?edit"

File Extensions

To support different file formats, such as XML and JSON use:

[HttpGetFile(".xml")]
public void GetEdit()
{
    SendXml(Data.Customers[CustomerId]);
}

This will be mapped to by URIs like /customer/42.xml

Sub-Resources

Resources can be naturally nested. For example, a Customer may have Orders.

[HttpSubResource(typeof(CustomerResource), "order/{OrderId}", true)]
public class OrderResource : HttpSubResource<CustomerResource>
{
    public int OrderId { get; set; }
    // ...
}

In this sub-resource, you can have access the parent CustomerResource, using the strongly-typed, ParentResource property.

URIs for this sub-resource will look like: "/customer/42/order/123"

The "true" argument in the attribute, specifies that parent query string parameters are added to this sub-resource URI as well. So a parent with URI template "customer/{CustomerId}?foo={Foo}" will yield sub-resource URI template "/customer/{CustomerId}/order/{OrderId}?foo={Foo}".

Nullable Resource Properties

Imagine a blog posts resource. Its URI could be /posts/{Year}/{Month}/{Day} to get all the posts on a given date. Now what if you want to naturally get the posts for a whole month, year, or ever?

By declaring the properties as nullable, e.g. "int?" in C#, Snooze will also map the "reduced" URIs to your class. You can then test the properties for "null" and return data accordingly.

Reusing Resource URIs

When rendering an HTML view it is likely you will need to link to other resources. Instead of manually typing the URI string, you can call the Uri property available on HttpResource.

new CustomerResource { CustomerId = 42 } .Uri

Resource Areas

Snooze supports resource "areas". These let you map all the resources of a given assembly into a given sub-path. If, for example, a third-party blogging package was built using Snooze you could include it as follows:

web.config:

<configuration>
  <restAreas>
    <area path="" assembly="MyWebsiteAssembly"/>
    <area path="blog" assembly="SomeCompany.BlogEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
  </restAreas>

So the blog resource URIs would look something like: http://mysite.com/blog/posts/2007/01

HTML Form Support

HTML forms only support GET and POST. To overcome this limitation you can include: <input type="hidden" name="__method" value="DELETE" /> to override the name of the HTTP method used by Snooze.

Feedback

This project came mostly out of curiosity! I wanted to see if REST could be done better under ASP.NET. The code is still very new (less than two days!) It is open source (BSD license) and available via SubVersion. A quick sample project is also provided.

Let me know what you think. Everything is open to changes. Join the assembla project if you would like to contribute your help.

.net | c# | programming | REST | web
Thursday, January 10, 2008 9:42:04 PM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [1]  |