My favorites | Sign in
Project Logo
       
Search
for
Updated Sep 27, 2008 by justin.sheehy
ExampleResources  

The simplest possible example is the one produced via the QuickStart.

For an example of a read/write filesystem server showing several interesting features and supporting GET, PUT, DELETE, and POST, see demo_fs_resource.

For a very simple resource demonstrating content negotiation, basic auth, and some caching headers, see webmachine_demo_resource.

Some example code, from webmachine_demo_resource, follows.

The simplest resource could export only one function in addition to init/1:

to_html(_ReqProps, Context) -> {"<html><body>Hello, new world</body></html>", Context}.

That's it. That resource will respond to all valid GET requests with the exact same response.

Many interesting bits of HTTP are handled inside Webmachine. For instance, if a client sends a request to that trivial resource with an Accept header that does not allow for a text/html response, they will receive a 406 Not Acceptable.

Suppose I wanted to serve a plaintext client as well. I could note that I provide more than just HTML:

content_types_provided(_ReqProps, Context) ->
   {[{"text/html", to_html},{"text/plain",to_text}], Context}.

I already have my HTML representation produced, so I add a text one: (and while I'm at it, I'll show that it's trivial dynamic content as well)

to_text(ReqProps, Context) ->
   Path = ?PATH(ReqProps),
   Body = io_lib:format("Hello ~p text~n", [Path]),
   {Body, Context}.

Now that this resource provides multiple media types, it automatically performs conneg:

$ telnet localhost 8000
Connected to localhost.
Escape character is '^]'.
GET /demo/a/resource/path HTTP/1.1
Accept: text/plain

HTTP/1.1 200 OK
Vary: Accept
Server: MochiWeb/1.1 WebMachine/0.12
Date: Tue, 23 Sep 2008 00:47:55 GMT
Content-Type: text/plain
Content-Length: 29

Hello "a/resource/path" text

What about authorization? Webmachine resources default to assuming the client is authorized, but that can easily be overridden. Here's an overly simplistic but illustrative example:

is_authorized(ReqProps, Context) ->
   Req = ?REQ(ReqProps),
   case ?PATH(ReqProps) of
       "authdemo" ->
           case Req:get_header_value("authorization") of
               "Basic "++Base64 ->
                   Str = base64:mime_decode_to_string(Base64),
                   case string:tokens(Str, ":") of
                       ["authdemo", "demo1"] ->
                           {true, Context};
                       _ ->
                           {"Basic realm=webmachine", Context}
                   end;
               _ ->
                   {"Basic realm=webmachine", Context}
           end;
       _ -> {true, Context}
   end.

With that function in the resource, all paths except "/authdemo" from this resource's root are authorized. For that one path, the UA will be asked to do basic authorization with the user/pass of authdemo/demo1. It should go without saying that this isn't quite the same function that we use in our real apps, but it is nice and simple.

HTTP caching support is also quite easy, with functions allowing resources to define last_modified, expires, and generate_etag. For instance, since representations of this resource vary only by URI Path, I could use an extremely simple entity tag unfit for most real applications but sufficient for this example:

generate_etag(ReqProps, Context) ->
   {?PATH(ReqProps), Context}.

And now the response from our earlier request is appropriately entity-tagged:

HTTP/1.1 200 OK
Vary: Accept
Server: MochiWeb/1.1 WebMachine/0.12 (A Very Small Sandwich)
ETag: a/resource/path
Date: Tue, 23 Sep 2008 01:30:20 GMT
Content-Type: text/plain
Content-Length: 29

For more details, read the source of the resources linked at the top of this page.

Hosted by Google Code