<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta name="author" content="Basho Technologies" /> <meta name="description" content="Webmachine examples" /> <meta name="keywords" content="webmachine http rest web" /> <meta http-equiv="content-type" content="text/html;charset=utf-8" /> <link rel="stylesheet" href="css/style-1c.css" type="text/css" /> <title>Webmachine examples</title> </head> <body> <div id="content"> <h1><span class="hr"></span><a href="/">webmachine</a></h1> <ul id="top"> <li><a href="/">Home</a></li> <li><a href="http://bitbucket.org/justin/webmachine/">Source Code</a></li> <li><a href="contact.html">Contact</a></li> </ul> <div id="left"> <h3>Webmachine examples</h3> <p> The simplest possible example is the one produced via the <a href="quickstart.html">quickstart</a>. </p> <p> For an example of a read/write filesystem server showing several interesting features and supporting GET, PUT, DELETE, and POST, see <a href="http://bitbucket.org/justin/webmachine/src/tip/demo/src/demo_fs_resource.erl">demo_fs_resource</a>. </p> <p> For a very simple resource demonstrating content negotiation, basic auth, and some caching headers, see <a href="http://bitbucket.org/justin/webmachine/src/tip/demo/src/webmachine_demo_resource.erl">webmachine_demo_resource</a>. </p> <p> Some example code based on webmachine_demo_resource follows. </p> <p> The simplest working resource could export only one function in addition to init/1: </p> <pre> -module(webmachine_demo_resource). -export([init/1, to_html/2]). -include_lib("webmachine/include/webmachine.hrl"). init([]) -> {ok, undefined}. to_html(ReqData, Context) -> {"<html><body>Hello, new world</body></html>", ReqData, Context}. </pre> <p> That's really it -- a working webmachine resource. That resource will respond to all valid GET requests with the exact same response. </p> <p> Many interesting bits of HTTP are handled automatically by 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. </p> <p> Suppose I wanted to serve a plaintext client as well. I could note that I provide more than just HTML: </p> <pre> content_types_provided(ReqData, Context) -> {[{"text/html", to_html},{"text/plain",to_text}], ReqData, Context}. </pre> <p> 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 to produce dynamic content as well) </p> <pre> to_text(ReqData, Context) -> Path = wrq:disp_path(ReqData), Body = io_lib:format("Hello ~s from webmachine.~n", [Path]), {Body, ReqData, Context}. </pre> <p> Now that this resource provides multiple media types, it automatically performs conneg: </p> <pre> $ telnet localhost 8000 Trying 127.0.0.1... 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.97 Date: Sun, 15 Mar 2009 02:54:02 GMT Content-Type: text/plain Content-Length: 39 Hello a/resource/path from webmachine. </pre> <p> 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: </p> <pre> is_authorized(ReqData, Context) -> case wrq:disp_path(ReqData) of "authdemo" -> case wrq:get_req_header("authorization", ReqData) of "Basic "++Base64 -> Str = base64:mime_decode_to_string(Base64), case string:tokens(Str, ":") of ["authdemo", "demo1"] -> {true, ReqData, Context}; _ -> {"Basic realm=webmachine", ReqData, Context} end; _ -> {"Basic realm=webmachine", ReqData, Context} end; _ -> {true, ReqData, Context} end. </pre> <p> With that function in the resource, all paths except <code>/authdemo</code> 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. </p> <p> If you've generated the application from the <a href="quickstart.html">quickstart</a>, make sure you've added this line to your dispatch.conf file: </p> <pre> {["demo", '*'], mywebdemo_resource, []}. </pre> <p> Now you can point your browser at <code>http://localhost:8000/demo/authdemo</code> with the demo app running: </p> <pre> $ curl -v http://localhost:8000/demo/authdemo > GET /demo/authdemo HTTP/1.1 > Host: localhost:8000 > Accept: */* > < HTTP/1.1 401 Unauthorized < WWW-Authenticate: Basic realm=webmachine < Server: MochiWeb/1.1 WebMachine/0.97 < Date: Sun, 15 Mar 2009 02:57:43 GMT < Content-Length: 0 < </pre> <p></p> <pre> $ curl -v -u authdemo:demo1 http://localhost:8000/demo/authdemo * Server auth using Basic with user 'authdemo' > GET /demo/authdemo HTTP/1.1 > Authorization: Basic YXV0aGRlbW86ZGVtbzE= > Host: localhost:8000 > Accept: */* > < HTTP/1.1 200 OK < Vary: Accept < Server: MochiWeb/1.1 WebMachine/0.97 < Date: Sun, 15 Mar 2009 02:59:02 GMT < Content-Type: text/html < Content-Length: 59 < <html><body>Hello authdemo from webmachine. </body></html> </pre> <p> HTTP caching support is also quite easy, with functions allowing resources to define (e.g.) <code>last_modified</code>, <code>expires</code>, and <code>generate_etag.</code> 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: </p> <pre> generate_etag(ReqData, Context) -> {wrq:raw_path(ReqData), ReqData, Context}. </pre> <p>Similarly, here's a trivial expires rule:</p> <pre> expires(ReqData, Context) -> {{{2021,1,1},{0,0,0}}, ReqData, Context}. </pre> <p> And now the response from our earlier request is appropriately tagged: </p> <pre> HTTP/1.1 200 OK Vary: Accept Server: MochiWeb/1.1 WebMachine/0.97 Expires: Fri, 01 Jan 2021 00:00:00 GMT ETag: /demo/authdemo Date: Sun, 15 Mar 2009 02:59:02 GMT Content-Type: text/html Content-Length: 59 <html><body>Hello authdemo from webmachine. </body></html> </pre> <p> For more details, read the source of the resources linked at the top of this page. </p> </div> <div id="footer"> </div> </div> <script type="text/javascript"> var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www."); document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E")); </script> <script type="text/javascript"> try { var pageTracker = _gat._getTracker("UA-4979965-5"); pageTracker._trackPageview(); } catch(err) {}</script> </body> </html>