Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

file 249 lines (219 sloc) 7.289 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
<!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>
Something went wrong with that request. Please try again.