webmachine / www / example_resources.html

<!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
&gt; GET /demo/authdemo HTTP/1.1
&gt; Host: localhost:8000
&gt; Accept: */*
&gt; 
&lt; HTTP/1.1 401 Unauthorized
&lt; WWW-Authenticate: Basic realm=webmachine
&lt; Server: MochiWeb/1.1 WebMachine/0.97
&lt; Date: Sun, 15 Mar 2009 02:57:43 GMT
&lt; Content-Length: 0

&lt; 
</pre>
<p></p>
<pre>
$ curl -v -u authdemo:demo1 http://localhost:8000/demo/authdemo 
* Server auth using Basic with user 'authdemo'
&gt; GET /demo/authdemo HTTP/1.1
&gt; Authorization: Basic YXV0aGRlbW86ZGVtbzE=
&gt; Host: localhost:8000
&gt; Accept: */*
&gt; 
&lt; HTTP/1.1 200 OK
&lt; Vary: Accept
&lt; Server: MochiWeb/1.1 WebMachine/0.97

&lt; Date: Sun, 15 Mar 2009 02:59:02 GMT
&lt; Content-Type: text/html
&lt; Content-Length: 59
&lt; 
&lt;html&gt;&lt;body&gt;Hello authdemo from webmachine.
&lt;/body&gt;&lt;/html&gt;
</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

&lt;html&gt;&lt;body&gt;Hello authdemo from webmachine.
&lt;/body&gt;&lt;/html&gt;
</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>
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.