285 lines
12 KiB
HTML
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> [<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"> ¶</a></span></h4>
|
|
|
|
<p>Well, enough about the tedious internals. Let’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"> ¶</a></span></h4>
|
|
|
|
<p>The first program we have to write, of course, is “Hello, World!”.
|
|
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)))
|
|
"Hello World!"))
|
|
</pre></div>
|
|
|
|
<p>Now let’s test it, by running a server with this handler. Load up the
|
|
web server module if you haven’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"> ¶</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’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)
|
|
'("hacker"))
|
|
(values '((content-type . (text/plain)))
|
|
"Hello hacker!")
|
|
(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 – 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"><unnamed port>:38:7: In procedure module-lookup:
|
|
<unnamed port>:38:7: Unbound variable: not-found
|
|
|
|
Entering a new prompt. Type `,bt' for a backtrace or `,q' to continue.
|
|
scheme@(guile-user) [1]>
|
|
</pre></div>
|
|
|
|
<p>So let’s define the function, right there in the debugger. As you
|
|
probably know, we’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 "Resource not found: "
|
|
(uri->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’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"> ¶</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->xml (templatize "Hello!" '((b "Hi!"))))
|
|
-|
|
|
<html><head><title>Hello!</title></head><body><b>Hi!</b></body></html>
|
|
</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 "Hello hello!")
|
|
(doctype "<!DOCTYPE html>\n")
|
|
(content-type-params '((charset . "utf-8")))
|
|
(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->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 "hello world!")
|
|
(table
|
|
(tr (th "header") (th "value"))
|
|
,@(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"> ¶</a></span></h4>
|
|
|
|
<p>Well, this is about as far as Guile’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> [<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>
|