1
0
Fork 0
cl-sites/guile.html_node/Web-Examples.html
2024-12-17 12:49:28 +01:00

285 lines
12 KiB
HTML

<!DOCTYPE html>
<html>
<!-- Created by GNU Texinfo 7.1, https://www.gnu.org/software/texinfo/ -->
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<!-- This manual documents Guile version 3.0.10.
Copyright (C) 1996-1997, 2000-2005, 2009-2023 Free Software Foundation,
Inc.
Copyright (C) 2021 Maxime Devos
Copyright (C) 2024 Tomas Volf
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
any later version published by the Free Software Foundation; with no
Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A
copy of the license is included in the section entitled "GNU Free
Documentation License." -->
<title>Web Examples (Guile Reference Manual)</title>
<meta name="description" content="Web Examples (Guile Reference Manual)">
<meta name="keywords" content="Web Examples (Guile Reference Manual)">
<meta name="resource-type" content="document">
<meta name="distribution" content="global">
<meta name="Generator" content=".texi2any-real">
<meta name="viewport" content="width=device-width,initial-scale=1">
<link href="index.html" rel="start" title="Top">
<link href="Concept-Index.html" rel="index" title="Concept Index">
<link href="index.html#SEC_Contents" rel="contents" title="Table of Contents">
<link href="Web.html" rel="up" title="Web">
<link href="Web-Server.html" rel="prev" title="Web Server">
<style type="text/css">
<!--
a.copiable-link {visibility: hidden; text-decoration: none; line-height: 0em}
div.example {margin-left: 3.2em}
span:hover a.copiable-link {visibility: visible}
-->
</style>
<link rel="stylesheet" type="text/css" href="https://www.gnu.org/software/gnulib/manual.css">
</head>
<body lang="en">
<div class="subsection-level-extent" id="Web-Examples">
<div class="nav-panel">
<p>
Previous: <a href="Web-Server.html" accesskey="p" rel="prev">Web Server</a>, Up: <a href="Web.html" accesskey="u" rel="up"><abbr class="acronym">HTTP</abbr>, the Web, and All That</a> &nbsp; [<a href="index.html#SEC_Contents" title="Table of contents" rel="contents">Contents</a>][<a href="Concept-Index.html" title="Index" rel="index">Index</a>]</p>
</div>
<hr>
<h4 class="subsection" id="Web-Examples-1"><span>7.3.10 Web Examples<a class="copiable-link" href="#Web-Examples-1"> &para;</a></span></h4>
<p>Well, enough about the tedious internals. Let&rsquo;s make a web application!
</p>
<ul class="mini-toc">
<li><a href="#Hello_002c-World_0021" accesskey="1">Hello, World!</a></li>
<li><a href="#Inspecting-the-Request" accesskey="2">Inspecting the Request</a></li>
<li><a href="#Higher_002dLevel-Interfaces" accesskey="3">Higher-Level Interfaces</a></li>
<li><a href="#Conclusion" accesskey="4">Conclusion</a></li>
</ul>
<div class="subsubsection-level-extent" id="Hello_002c-World_0021">
<h4 class="subsubsection"><span>7.3.10.1 Hello, World!<a class="copiable-link" href="#Hello_002c-World_0021"> &para;</a></span></h4>
<p>The first program we have to write, of course, is &ldquo;Hello, World!&rdquo;.
This means that we have to implement a web handler that does what we
want.
</p>
<p>Now we define a handler, a function of two arguments and two return
values:
</p>
<div class="example">
<pre class="example-preformatted">(define (handler request request-body)
(values <var class="var">response</var> <var class="var">response-body</var>))
</pre></div>
<p>In this first example, we take advantage of a short-cut, returning an
alist of headers instead of a proper response object. The response body
is our payload:
</p>
<div class="example">
<pre class="example-preformatted">(define (hello-world-handler request request-body)
(values '((content-type . (text/plain)))
&quot;Hello World!&quot;))
</pre></div>
<p>Now let&rsquo;s test it, by running a server with this handler. Load up the
web server module if you haven&rsquo;t yet done so, and run a server with this
handler:
</p>
<div class="example">
<pre class="example-preformatted">(use-modules (web server))
(run-server hello-world-handler)
</pre></div>
<p>By default, the web server listens for requests on
<code class="code">localhost:8080</code>. Visit that address in your web browser to
test. If you see the string, <code class="code">Hello World!</code>, sweet!
</p>
</div>
<div class="subsubsection-level-extent" id="Inspecting-the-Request">
<h4 class="subsubsection"><span>7.3.10.2 Inspecting the Request<a class="copiable-link" href="#Inspecting-the-Request"> &para;</a></span></h4>
<p>The Hello World program above is a general greeter, responding to all
URIs. To make a more exclusive greeter, we need to inspect the request
object, and conditionally produce different results. So let&rsquo;s load up
the request, response, and URI modules, and do just that.
</p>
<div class="example">
<pre class="example-preformatted">(use-modules (web server)) ; you probably did this already
(use-modules (web request)
(web response)
(web uri))
(define (request-path-components request)
(split-and-decode-uri-path (uri-path (request-uri request))))
(define (hello-hacker-handler request body)
(if (equal? (request-path-components request)
'(&quot;hacker&quot;))
(values '((content-type . (text/plain)))
&quot;Hello hacker!&quot;)
(not-found request)))
(run-server hello-hacker-handler)
</pre></div>
<p>Here we see that we have defined a helper to return the components of
the URI path as a list of strings, and used that to check for a request
to <code class="code">/hacker/</code>. Then the success case is just as before &ndash; visit
<code class="code">http://localhost:8080/hacker/</code> in your browser to check.
</p>
<p>You should always match against URI path components as decoded by
<code class="code">split-and-decode-uri-path</code>. The above example will work for
<code class="code">/hacker/</code>, <code class="code">//hacker///</code>, and <code class="code">/h%61ck%65r</code>.
</p>
<p>But we forgot to define <code class="code">not-found</code>! If you are pasting these
examples into a REPL, accessing any other URI in your web browser will
drop your Guile console into the debugger:
</p>
<div class="example">
<pre class="example-preformatted">&lt;unnamed port&gt;:38:7: In procedure module-lookup:
&lt;unnamed port&gt;:38:7: Unbound variable: not-found
Entering a new prompt. Type `,bt' for a backtrace or `,q' to continue.
scheme@(guile-user) [1]&gt;
</pre></div>
<p>So let&rsquo;s define the function, right there in the debugger. As you
probably know, we&rsquo;ll want to return a 404 response.
</p>
<div class="example">
<pre class="example-preformatted">;; Paste this in your REPL
(define (not-found request)
(values (build-response #:code 404)
(string-append &quot;Resource not found: &quot;
(uri-&gt;string (request-uri request)))))
;; Now paste this to let the web server keep going:
,continue
</pre></div>
<p>Now if you access <code class="code">http://localhost/foo/</code>, you get this error
message. (Note that some popular web browsers won&rsquo;t show
server-generated 404 messages, showing their own instead, unless the 404
message body is long enough.)
</p>
</div>
<div class="subsubsection-level-extent" id="Higher_002dLevel-Interfaces">
<h4 class="subsubsection"><span>7.3.10.3 Higher-Level Interfaces<a class="copiable-link" href="#Higher_002dLevel-Interfaces"> &para;</a></span></h4>
<p>The web handler interface is a common baseline that all kinds of Guile
web applications can use. You will usually want to build something on
top of it, however, especially when producing HTML. Here is a simple
example that builds up HTML output using SXML (see <a class="pxref" href="SXML.html">SXML</a>).
</p>
<p>First, load up the modules:
</p>
<div class="example">
<pre class="example-preformatted">(use-modules (web server)
(web request)
(web response)
(sxml simple))
</pre></div>
<p>Now we define a simple templating function that takes a list of HTML
body elements, as SXML, and puts them in our super template:
</p>
<div class="example">
<pre class="example-preformatted">(define (templatize title body)
`(html (head (title ,title))
(body ,@body)))
</pre></div>
<p>For example, the simplest Hello HTML can be produced like this:
</p>
<div class="example">
<pre class="example-preformatted">(sxml-&gt;xml (templatize &quot;Hello!&quot; '((b &quot;Hi!&quot;))))
-|
&lt;html&gt;&lt;head&gt;&lt;title&gt;Hello!&lt;/title&gt;&lt;/head&gt;&lt;body&gt;&lt;b&gt;Hi!&lt;/b&gt;&lt;/body&gt;&lt;/html&gt;
</pre></div>
<p>Much better to work with Scheme data types than to work with HTML as
strings. Now we define a little response helper:
</p>
<div class="example">
<pre class="example-preformatted">(define* (respond #:optional body #:key
(status 200)
(title &quot;Hello hello!&quot;)
(doctype &quot;&lt;!DOCTYPE html&gt;\n&quot;)
(content-type-params '((charset . &quot;utf-8&quot;)))
(content-type 'text/html)
(extra-headers '())
(sxml (and body (templatize title body))))
(values (build-response
#:code status
#:headers `((content-type
. (,content-type ,@content-type-params))
,@extra-headers))
(lambda (port)
(if sxml
(begin
(if doctype (display doctype port))
(sxml-&gt;xml sxml port))))))
</pre></div>
<p>Here we see the power of keyword arguments with default initializers. By
the time the arguments are fully parsed, the <code class="code">sxml</code> local variable
will hold the templated SXML, ready for sending out to the client.
</p>
<p>Also, instead of returning the body as a string, <code class="code">respond</code> gives a
procedure, which will be called by the web server to write out the
response to the client.
</p>
<p>Now, a simple example using this responder, which lays out the incoming
headers in an HTML table.
</p>
<div class="example">
<pre class="example-preformatted">(define (debug-page request body)
(respond
`((h1 &quot;hello world!&quot;)
(table
(tr (th &quot;header&quot;) (th &quot;value&quot;))
,@(map (lambda (pair)
`(tr (td (tt ,(with-output-to-string
(lambda () (display (car pair))))))
(td (tt ,(with-output-to-string
(lambda ()
(write (cdr pair))))))))
(request-headers request))))))
(run-server debug-page)
</pre></div>
<p>Now if you visit any local address in your web browser, we actually see
some HTML, finally.
</p>
</div>
<div class="subsubsection-level-extent" id="Conclusion">
<h4 class="subsubsection"><span>7.3.10.4 Conclusion<a class="copiable-link" href="#Conclusion"> &para;</a></span></h4>
<p>Well, this is about as far as Guile&rsquo;s built-in web support goes, for
now. There are many ways to make a web application, but hopefully by
standardizing the most fundamental data types, users will be able to
choose the approach that suits them best, while also being able to
switch between implementations of the server. This is a relatively new
part of Guile, so if you have feedback, let us know, and we can take it
into account. Happy hacking on the web!
</p>
</div>
</div>
<hr>
<div class="nav-panel">
<p>
Previous: <a href="Web-Server.html">Web Server</a>, Up: <a href="Web.html"><abbr class="acronym">HTTP</abbr>, the Web, and All That</a> &nbsp; [<a href="index.html#SEC_Contents" title="Table of contents" rel="contents">Contents</a>][<a href="Concept-Index.html" title="Index" rel="index">Index</a>]</p>
</div>
</body>
</html>