Personal tools

Web/Literature/Practical web programming in Haskell

From HaskellWiki

< Web | Literature(Difference between revisions)
Jump to: navigation, search
(Switch from chained $ to compositional style)
(remove stray /Static_linking from example code)
 
(6 intermediate revisions by 2 users not shown)
Line 1: Line 1:
  +
[[Category:Web|*]]
  +
{{Web infobox}}
 
[[Category:Tutorials]]
 
[[Category:Tutorials]]
[[Category:Web]]
 
 
{{Template:Formal under construction}}
 
{{Template:Formal under construction}}
   
'''NOTE: I believe a lot of the information on this page is dated, and will lead beginners in the wrong direction. I've begun documenting some of the more modern options for Haskell web development on the [[Web]] page of this wiki. -- Michael Snoyman'''
+
This tutorial focuses on CGI and FastCGI programming. For more introductory information, see [[Web/Literature|tutorials, blogs and research]] and [[Web/Forums_and_Discussion|discussion]].
 
   
 
== Introduction ==
 
== Introduction ==
   
 
This tutorial aims to get you started with writing web applications
 
This tutorial aims to get you started with writing web applications
in Haskell. We describe a relatively light-weight
+
in Haskell. We describe a relatively light-weight approach to Haskell web programming
approach to Haskell web programming
 
 
which uses a CGI library and an XHTML combinator library.
 
which uses a CGI library and an XHTML combinator library.
   
Line 26: Line 27:
 
loaded Haskell code.
 
loaded Haskell code.
   
+
== Assumed knowledge ==
=== Other approaches ===
 
 
[Web Authoring System Haskell
 
(WASH) [http://www.informatik.uni-freiburg.de/~thiemann/WASH/].
 
Domain-specific embedded language. Type-safe forms handling.
 
Threads continuation through client. This gives good
 
back-button and session splitting properties.
 
 
Haskell Application Server
 
(HAppS) [http://happs.org/].
 
Complete system including web server in one program.
 
Uses XSLT for output.
 
 
[http://www.happstack.com Happstack] is the successor to HAppS. It is a complete system including a web server and datatbase system in one program. It has many template options including HSP, HStringTemplate, Hamlet, XSLT, and more!
 
 
Haskell Server Pages
 
(HSP) [http://www.cs.chalmers.se/~d00nibro/hsp/].
 
Uses preprocessor to make XML tags into Haskell expressions.
 
Dynamic compilation.
 
 
 
=== Assumed knowledge ===
 
   
 
This tutorial is not meant as an introduction to Haskell or web programming.
 
This tutorial is not meant as an introduction to Haskell or web programming.
 
We will assume that you have some familiarity with the following
 
We will assume that you have some familiarity with the following
concepts:
+
concepts: We assume knowledge of the following:
 
 
==== Haskell ====
 
 
This tutorial is not meant as a first introduction to Haskell. If you
 
want to learn about Haskell in general, have a look at the lists of
 
[[books and tutorials]]. You may want to start with [[Haskell in 5 steps]].
 
 
 
==== (X)HTML ====
 
 
[http://www.w3.org/MarkUp/ HTML (HyperText Markup Language)] is the
 
"the lingua franca for publishing hypertext on the World Wide Web''.
 
The XHtml library which we use in this tutorial produces
 
[http://www.w3.org/TR/xhtml1/ XHTML 1.0], which is
 
[http://www.w3.org/TR/html401/ HTML 4.0] formulated as
 
[http://www.w3.org/XML/ XML].
 
 
The combinators in the XHtml library do not make much sense unless you
 
understand at least some parts of HTML.
 
 
 
==== CGI ====
 
 
CGI (Common Gateway Interface) programs are programs which run on the
 
web server. They are given input which comes from the user's browser,
 
and their output is given to the browser.
 
 
To really understand how the CGI library works, you probably need to know
 
a thing or two about CGI. ([http://www.comp.leeds.ac.uk/Perl/Cgi/start.html Tutorial].)
 
The authoritative resource on CGI is the
 
[http://hoohoo.ncsa.illinois.edu/cgi/interface.html CGI specification].
 
   
  +
* Haskell: This tutorial is not meant as a first introduction to Haskell. If you want to learn about Haskell in general, have a look at the lists of [[books and tutorials]]. You may want to start with [[Haskell in 5 steps]].
  +
* (X)HTML: [http://www.w3.org/MarkUp/ HTML (HyperText Markup Language)] is the "the lingua franca for publishing hypertext on the World Wide Web''. The XHtml library which we use in this tutorial produces [http://www.w3.org/TR/xhtml1/ XHTML 1.0], which is [http://www.w3.org/TR/html401/ HTML 4.0] formulated as [http://www.w3.org/XML/ XML]. The combinators in the XHtml library do not make much sense unless you understand at least some parts of HTML.
  +
* CGI: CGI (Common Gateway Interface) programs are programs which run on the web server. They are given input which comes from the user's browser, and their output is given to the browser. To really understand how the CGI library works, you probably need to know a thing or two about CGI. ([http://www.comp.leeds.ac.uk/Perl/Cgi/start.html Tutorial].)
   
 
== Required software ==
 
== Required software ==
   
=== Haskell compiler ===
+
* [[GHC]], the Glasgow Haskell Compiler, is the Haskell implementation that we will use in this tutorial. However, any Haskell implementation that supports Haskell98 and multi-parameter type classes should work.
+
* xhtml: This package provides combinators for producing XHTML 1.0, including the Strict, Transitional and Frameset variants. [http://hackage.haskell.org/package/xhtml Download here.]
[[GHC]], the Glasgow Haskell
+
* cgi: This is a Haskell library for writing CGI programs. [http://hackage.haskell.org/package/cgi Download here.]
Compiler, is the Haskell implementation that we will use in this tutorial.
+
* A web server: You need to have access to a web server on which you can run CGI programs. The most convenient way to do this when learning and developing is to run a web server on your development machine. If you run the programs on some other machine you need to make sure that you compile your programs so that they can run on that machine. This normally means that the machines must to have the same architecture and run the same operating system.
However, any Haskell implementation that supports Haskell98 and multi-parameter
 
type classes should work.
 
 
 
=== Libraries: xhtml and cgi ===
 
 
If your Haskell implementation does not come with the <tt>xhtml</tt> and
 
<tt>cgi</tt> packages, download them from
 
[http://hackage.haskell.org/packages/hackage.html HackageDB].
 
 
 
=== Web server ===
 
 
You need to have access to a web server on which you can run CGI programs.
 
The most convenient way to do this when learning and developing is to run
 
a web server on your development machine. If you run the programs on some
 
other machine you need to make sure that you compile your programs
 
so that they can run on that machine. This normally means that the machines
 
must to have the same architecture and run the same operating system.
 
 
 
====Deploying statically linked applications ====
 
 
Linking your applications statically by giving the flags <tt>-static
 
-optl-static</tt> to GHC will avoid problems with missing libraries on
 
the web server.
 
 
For example, this simple program,
 
 
<haskell>
 
import Database.SQLite
 
main = print "hey, test this"
 
</haskell>
 
 
when compiled as $ ghc A.hs --make is dynamically linked against:
 
 
<haskell>
 
$ ldd A
 
A:
 
Start End Type Open Ref GrpRef Name
 
0000000000000000 0000000000000000 exe 1 0 0 A
 
0000000041a85000 0000000041ee5000 rlib 0 1 0 /usr/local/lib/libsqlite3.so.9.0
 
0000000049b04000 0000000049f1d000 rlib 0 1 0 /usr/lib/libm.so.2.3
 
0000000042213000 000000004264f000 rlib 0 1 0 /usr/local/lib/libgmp.so.7.0
 
0000000047d0e000 00000000481e0000 rlib 0 1 0 /usr/lib/libc.so.42.0
 
0000000047900000 0000000047900000 rtld 0 1 0 /usr/libexec/ld.so
 
</haskell>
 
 
Now, we can just pass some linker flags through to statically link this lot,
 
 
<haskell>
 
$ ghc A.hs --make -optl-static -no-recomp
 
$ ldd A
 
ldd: A: not a dynamic executable
 
$ file A
 
A: ELF 64-bit LSB executable, AMD64, version 1, for OpenBSD, statically linked, not stripped
 
</haskell>
 
 
You could also use the [[Haskell Web Server]].
 
 
'''Caveats:'''
 
 
* The <tt>-static</tt> flag in GHC 6.8.2 does not link the libraries in the correct order, resulting in a link failure (which you can hack around if you have to by shuffling <tt>-lpthread</tt> after <tt>-lrt</tt> in the gargantuan linker invocation). This problem should disappear with GHC 6.8.3.
 
* Sometimes you will need to add <tt>extra-libraries</tt> fields to various libraries' <tt>.cabal</tt> files. This manifests as missing symbols. Note that many linkers are sensitive to the order of the <tt>-l</tt> arguments, so the order of libraries in this field matters.
 
 
   
 
== Compiling and running web applications ==
 
== Compiling and running web applications ==
Line 44: Line 48:
 
Use GHC to produce a binary executable called <tt>prog.cgi</tt> from the Haskell
 
Use GHC to produce a binary executable called <tt>prog.cgi</tt> from the Haskell
 
source code file <tt>prog.hs</tt>:
 
source code file <tt>prog.hs</tt>:
  +
 
<pre>
 
<pre>
ghc --make -package cgi -package xhtml -o prog.cgi prog.hs
+
ghc --make -o prog.cgi prog.hs
 
</pre>
 
</pre>
   
 
Put the compiled program in the cgi-bin directory,
 
Put the compiled program in the cgi-bin directory,
 
or give it the extension .cgi, depending on the configuration
 
or give it the extension .cgi, depending on the configuration
of the web server.
+
of the web server. [[Web/Literature/Static_linking|Linking your applications statically]]
 
Linking your applications statically
 
by giving the flags <tt>-static -optl-static</tt> to GHC
 
 
will avoid problems with missing libraries on the web server.
 
will avoid problems with missing libraries on the web server.
   
Line 56: Line 61:
 
program with your web browser.
 
program with your web browser.
   
+
== Hello World! ==
== Simple examples ==
 
 
=== Hello World ===
 
   
 
Here is a very simple example which just outputs some static HTML.
 
Here is a very simple example which just outputs some static HTML.
Line 81: Line 86:
 
as a string, and produces that string as output. The <code>main</code> function
 
as a string, and produces that string as output. The <code>main</code> function
 
runs <code>cgiMain</code>, using the normal CGI protocol for input and output.
 
runs <code>cgiMain</code>, using the normal CGI protocol for input and output.
It also uses <code>handleErrors</code> to output an error page in case |cgiMain|
+
It also uses <code>handleErrors</code> to output an error page in case <code>cgiMain</code>
 
throws an exception.
 
throws an exception.
   
Line 113: Line 118:
 
</haskell>
 
</haskell>
   
+
== HTML combinators ==
==== HTML combinators ====
 
   
 
See also [http://cpansearch.perl.org/src/AUTRIJUS/Language-Haskell-0.01/hugs98-Nov2003/fptools/hslibs/text/html/doc/doc.htm].
 
See also [http://cpansearch.perl.org/src/AUTRIJUS/Language-Haskell-0.01/hugs98-Nov2003/fptools/hslibs/text/html/doc/doc.htm].
Line 136: Line 141:
 
document.
 
document.
   
+
== Getting user input ==
=== Getting user input ===
 
   
 
This program shows a form which asks the user for her name.
 
This program shows a form which asks the user for her name.
Line 167: Line 172:
 
</haskell>
 
</haskell>
   
+
== Cookies ==
=== Cookies ===
 
   
 
<haskell>
 
<haskell>
Line 197: Line 202:
 
cookie in the browser. If you want to get the string value of a cookie, use <code>getCookie</code> instead of <code>readCookie</code>.
 
cookie in the browser. If you want to get the string value of a cookie, use <code>getCookie</code> instead of <code>readCookie</code>.
   
+
== File uploads ==
=== File uploads ===
 
   
 
FIXME: use a safer example
 
FIXME: use a safer example
Line 243: Line 248:
 
getInputFPS gets the value of an input variable as a lazy ByteString.
 
getInputFPS gets the value of an input variable as a lazy ByteString.
   
+
== Error handling ==
=== Error handling ===
 
   
 
handleErrors catches all exceptions and
 
handleErrors catches all exceptions and
Line 251: Line 256:
 
the response code, e.g. 404.
 
the response code, e.g. 404.
   
+
== Returning non-HTML ==
=== Returning non-HTML ===
 
   
 
Of course we do not have to output HTML. Use setHeader to set the value
 
Of course we do not have to output HTML. Use setHeader to set the value
Line 276: Line 281:
 
Examples: RSS
 
Examples: RSS
   
+
== Response headers ==
=== Setting response headers ===
 
   
 
You can use the <code>setHeader</code> function to set arbitrary HTTP response headers.
 
You can use the <code>setHeader</code> function to set arbitrary HTTP response headers.
Line 283: Line 288:
 
Example: output raw file data (with last-modified)
 
Example: output raw file data (with last-modified)
   
  +
== The CGI Monad ==
   
== Going further ==
+
At this point, you should be able to create many useful CGI scripts. As your scripts get more ambitious, however, you may find yourself needing to pass "global" parameters to your CGI actions (e.g. database connections, session information.) Rather than explicitly passing these values around, you can extend the CGI monad to do this work for you.
   
This section explores some of possibilities beyond the basic web application
+
The <hask>Network.CGI.Monad</hask> module defines a CGI monad transformer, allowing us to build a new monad that does everything the CGI monad does -- and more!
programming.
 
   
  +
For example, let's define a new CGI monad that provides a database connection (in this example, we use the <hask>Database.HSQL.PostgreSQL</hask> module for our database.) Since it will be used by the CGI application, I'll call the new monad "App".
   
=== Extending the CGI monad with monad transformers ===
+
Should this not compile for you, you need to enable some extensions:
   
At this point, you should be able to create many useful CGI scripts.
 
As your scripts get more ambitious, however, you may find yourself
 
needing to pass "global" parameters to your CGI actions (e.g. database
 
connections, session information.) Rather than explicitly passing
 
these values around, you can extend the CGI monad to do this work for
 
you.
 
 
The <hask>Network.CGI.Monad</hask> module defines a CGI monad
 
transformer, allowing us to build a new monad that does everything the
 
CGI monad does -- and more!
 
 
For example, let's define a new CGI monad that provides a database
 
connection (in this example, we use the
 
<hask>Database.HSQL.PostgreSQL</hask> module for our database.) Since
 
it will be used by the CGI application, I'll call the new monad "App".
 
 
Should this not compile for you, you need to enable some extensions:
 
 
<haskell>
 
<haskell>
 
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
 
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
Line 313: Line 303:
 
</haskell>
 
</haskell>
   
After importing the appropriate modules, we define a new type,
+
<br/>After importing the appropriate modules, we define a new type, <hask>AppT</hask> that is made up of two monad transformers, <hask>CGIT</hask> and <hask>ReaderT</hask>. The <hask>CGIT</hask> monad "wraps" the base monad "m". The <hask>CGIT</hask> monad, in turn, is wrapped by the <hask>ReaderT</hask> monad, which contains, in its environment, the database <hask>Connection</hask>.
<hask>AppT</hask> that is made up of two monad transformers,
 
<hask>CGIT</hask> and <hask>ReaderT</hask>. The <hask>CGIT</hask>
 
monad "wraps" the base monad "m". The <hask>CGIT</hask> monad, in
 
turn, is wrapped by the <hask>ReaderT</hask> monad, which contains, in
 
its environment, the database <hask>Connection</hask>.
 
   
<hask>AppT</hask> takes two type parameters. The first is the base
+
<hask>AppT</hask> takes two type parameters. The first is the base monad that the monad transformers are modifying. Usually this will be the <hask>IO</hask> monad. The second type is the data type that an action in the monad will return.<br/><br/>
monad that the monad transformers are modifying. Usually this will be
 
the <hask>IO</hask> monad. The second type is the data type that an
 
action in the monad will return.
 
   
 
<haskell>
 
<haskell>
Line 327: Line 317:
 
</haskell>
 
</haskell>
   
Like <hask>CGI</hask>, we make a type synonym that defines the most
+
<br/>Like <hask>CGI</hask>, we make a type synonym that defines the most common use of this new monad.<br/><br/>
common use of this new monad.
 
   
 
<haskell>
 
<haskell>
Line 333: Line 323:
 
</haskell>
 
</haskell>
   
We're not quite finished defining <hask>App</hask> yet. In order to be
+
<br/>We're not quite finished defining <hask>App</hask> yet. In order to be used like the CGI monad, <hask>App</hask> needs to be an instance of the <hask>MonadCGI</hask> class. This class defines two functions that we must support.<br/><br/>
used like the CGI monad, <hask>App</hask> needs to be an instance of
 
the <hask>MonadCGI</hask> class. This class defines two functions that
 
we must support.
 
   
 
<haskell>
 
<haskell>
Line 341: Line 331:
 
</haskell>
 
</haskell>
   
So now we have an App monad that gives us all the functionality of
+
<br/>So now we have an App monad that gives us all the functionality of CGI, but also carries around a database connection. The last step is to define the function that creates the monad so we can run actions inside it.
CGI, but also carries around a database connection. The last step is
 
to define the function that creates the monad so we can run actions
 
inside it.
 
   
 
<haskell>
 
<haskell>
Line 356: Line 346:
 
</haskell>
 
</haskell>
   
(either fill in your account/password information, or change
+
<br/>(either fill in your account/password information, or change <hask>runApp</hask> to accept the parameters as function arguments.) The function uses <hask>bracket</hask> so that the database connection gets released properly when the monad ends or if an exception is thrown.
<hask>runApp</hask> to accept the parameters as function arguments.)
 
The function uses <hask>bracket</hask> so that the database connection
 
gets released properly when the monad ends or if an exception is
 
thrown.
 
   
+
== Templating ==
=== Templating ===
 
   
 
There are times when you absolutely do not want to embed (X)HTML in Haskell. You can separate the code and the presentation (the Holy Grail of erm, web development). The code will be, well, Haskell, and the presentation will be buried inside templates. This might not be the case: fortunately, there is a very nice templating engine available: [[HStringTemplate]]; also very useful is [http://hackage.haskell.org/package/hakyll hakyll], a simple static site generator library, mainly aimed at creating blogs and brochure sites.
 
There are times when you absolutely do not want to embed (X)HTML in Haskell. You can separate the code and the presentation (the Holy Grail of erm, web development). The code will be, well, Haskell, and the presentation will be buried inside templates. This might not be the case: fortunately, there is a very nice templating engine available: [[HStringTemplate]]; also very useful is [http://hackage.haskell.org/package/hakyll hakyll], a simple static site generator library, mainly aimed at creating blogs and brochure sites.
   
+
== FastCGI ==
=== FastCGI ===
 
   
 
[http://www.fastcgi.com/ FastCGI] is a standard for CGI-like programs that are not restarted
 
[http://www.fastcgi.com/ FastCGI] is a standard for CGI-like programs that are not restarted
Line 377: Line 367:
   
 
Take a look at lightweight, minimalistic FastCGI-based web frameworks: [http://community.haskell.org/~sclv/hvac/ HVAC] (Haskell view and controller) and [http://hackage.haskell.org/cgi-bin/hackage-scripts/package/kibro Kibro].
 
Take a look at lightweight, minimalistic FastCGI-based web frameworks: [http://community.haskell.org/~sclv/hvac/ HVAC] (Haskell view and controller) and [http://hackage.haskell.org/cgi-bin/hackage-scripts/package/kibro Kibro].
 
 
=== SCGI ===
 
 
[http://www.mems-exchange.org/software/scgi/ SCGI] is a simpler alternative to FastCGI for writing CGI-like programs in persistent processes, external to the web server. SCGI is less featureful than FastCGI, but has the advantage that it does not require an external library.
 
 
Install [http://hackage.haskell.org/cgi-bin/hackage-scripts/package/scgi-0.1 SCGI], import Network.SCGI, and use runSCGI. Everything else is then done inside a CGI monad as above.
 
 
 
=== URL rewriting / dispatching ===
 
 
Easy to use and expressive [http://hackage.haskell.org/cgi-bin/hackage-scripts/package/UrlDisp URL dispatching library] is available on Hackage.
 
 
 
=== Dynamic loading ===
 
 
   
 
== Database-driven web-applications ==
 
== Database-driven web-applications ==
 
 
=== Database connectivity ===
 
   
 
See [http://hackage.haskell.org/package/Takusen Takusen] and [http://software.complete.org/software/projects/show/hdbc HDBC]. If you would like to write queries in Haskell (and not SQL), see also [http://haskelldb.sourceforge.net/ HaskellDB], which integrates with HDBC.
 
See [http://hackage.haskell.org/package/Takusen Takusen] and [http://software.complete.org/software/projects/show/hdbc HDBC]. If you would like to write queries in Haskell (and not SQL), see also [http://haskelldb.sourceforge.net/ HaskellDB], which integrates with HDBC.
 
 
==== Persistent DB connections with FastCGI ====
 
   
 
FastCGI aren't restarted for each request, only the runFastCGI part is re-run. Everything (handles, datastructures etc.) you do outside of that loop will be persistent. However you need to handle errors yourself, because you're operating outside of handleErrors.
 
FastCGI aren't restarted for each request, only the runFastCGI part is re-run. Everything (handles, datastructures etc.) you do outside of that loop will be persistent. However you need to handle errors yourself, because you're operating outside of handleErrors.
 
 
=== Web services ===
 
 
=== Static web site generators ===
 
 
* [http://hackage.haskell.org/package/hakyll Hakyll]
 
* [http://hackage.haskell.org/package/yst Yst]
 
* [http://hackage.haskell.org/package/halipeto Halipeto]
 
 
== Web frameworks ==
 
 
* [http://turbinado.org/Home Turbinado], an early stab at Ruby On Rails.
 
* [http://docs.yesodweb.com/ Yesod Web Framework], for RESTful, type-safe web applications. Built on top of the WAI, so you can serve via standalone server, CGI, FastCGI or anything else with a WAI handler.
 
 
== Existing applications ==
 
 
* [http://hackage.haskell.org/ HackageDB web interface].
 
 
* [http://tutorial.happstack.com/ Real World HAppS: The Cabalized, Self-Demoing HAppS Tutorial].
 
 
* [http://github.com/jgm/gitit Gitit, a wiki written using Git, HAppS and Pandoc].
 
 
* [http://www.bringert.net/ Hope].
 
 
* [http://www.happstack.com Happstack]
 
 
   
 
== See also ==
 
== See also ==
   
* The blog article [http://wrwills.webfactional.com/2009/10/30/Haskell-on-a-Webfaction-Host Haskell on a Webfaction Host].
+
* [[Web/Literature|Haskell web development blogs, research and literature]]
 
   
 
''Authors: Björn Bringert''
 
''Authors: Björn Bringert''

Latest revision as of 14:59, 6 June 2011

Haskell Web Development

Software:
Servers - Libraries - Frameworks
Deploy - Cloud
Interfaces to frameworks
Databases and Persistence
Testing and Verification
Content Management

Community & Research:
Forums and Discussion
Literature (research, talks and blogs)
Existing Haskell web applications
Ongoing projects and ideas

This page is under construction. Feel free to help out. If you make substantial edits, please add your name to the authors list at the bottom of this page, so that you can be credited if this is ever published in another medium.

This tutorial focuses on CGI and FastCGI programming. For more introductory information, see tutorials, blogs and research and discussion.

Contents

[edit] 1 Introduction

This tutorial aims to get you started with writing web applications in Haskell. We describe a relatively light-weight approach to Haskell web programming which uses a CGI library and an XHTML combinator library.

We think that while the approach we describe here is not as sophisticated or innovative as some other approaches, it is simple, portable and easy to understand if you are already familiar with web programming in other languages.

The tutorial starts with preliminaries such as how to install the necessary software and how to compile and run your web applications. We then show a number of working small example programs which introduce the basic features of the CGI and XHtml libraries. We then move on to how to use monad transformers to add application specific functionality such as sessions to the CGI monad, and how to create database-driven web applications. We also present FastCGI, and an approach to using dynamically loaded Haskell code.

[edit] 2 Assumed knowledge

This tutorial is not meant as an introduction to Haskell or web programming. We will assume that you have some familiarity with the following concepts: We assume knowledge of the following:

  • Haskell: This tutorial is not meant as a first introduction to Haskell. If you want to learn about Haskell in general, have a look at the lists of books and tutorials. You may want to start with Haskell in 5 steps.
  • (X)HTML: HTML (HyperText Markup Language) is the "the lingua franca for publishing hypertext on the World Wide Web. The XHtml library which we use in this tutorial produces XHTML 1.0, which is HTML 4.0 formulated as XML. The combinators in the XHtml library do not make much sense unless you understand at least some parts of HTML.
  • CGI: CGI (Common Gateway Interface) programs are programs which run on the web server. They are given input which comes from the user's browser, and their output is given to the browser. To really understand how the CGI library works, you probably need to know a thing or two about CGI. (Tutorial.)

[edit] 3 Required software

  • GHC, the Glasgow Haskell Compiler, is the Haskell implementation that we will use in this tutorial. However, any Haskell implementation that supports Haskell98 and multi-parameter type classes should work.
  • xhtml: This package provides combinators for producing XHTML 1.0, including the Strict, Transitional and Frameset variants. Download here.
  • cgi: This is a Haskell library for writing CGI programs. Download here.
  • A web server: You need to have access to a web server on which you can run CGI programs. The most convenient way to do this when learning and developing is to run a web server on your development machine. If you run the programs on some other machine you need to make sure that you compile your programs so that they can run on that machine. This normally means that the machines must to have the same architecture and run the same operating system.

[edit] 4 Compiling and running web applications

Use GHC to produce a binary executable called prog.cgi from the Haskell source code file prog.hs:

ghc --make -o prog.cgi prog.hs

Put the compiled program in the cgi-bin directory, or give it the extension .cgi, depending on the configuration of the web server. Linking your applications statically will avoid problems with missing libraries on the web server.

To run the compiled program, visit the URL of the CGI program with your web browser.

[edit] 5 Hello World!

Here is a very simple example which just outputs some static HTML. The type signatures in this code are optional. We show them here for clarity, but omit them in some later examples.

import Network.CGI
import Text.XHtml
 
page :: Html 
page = body << h1 << "Hello World!"
 
cgiMain :: CGI CGIResult
cgiMain = output $ renderHtml page
 
main :: IO ()
main = runCGI $ handleErrors cgiMain

The page function constructs an HTML document which consists of a body containing a single header element which contains the text "Hello World". The CGI-action cgiMain renders the HTML document as a string, and produces that string as output. The main function runs cgiMain, using the normal CGI protocol for input and output. It also uses handleErrors to output an error page in case cgiMain throws an exception.

Fans of one-liners may like this version better (handleErrors has been omitted since this simple program will not throw any exceptions):

import Text.XHtml
import Network.CGI
 
main = runCGI . output . renderHtml $ body << h1 << "Hello World!"

These are some of the important functions used in this example:

-- creates a string containing the HTML document.
renderHtml :: Html -> String
 
-- outputs a string as the body of the HTTP response.
output :: String -> CGI CGIResult
 
-- Catches any exception thrown by the given CGI action, returns an 
-- error page with a 500 Internal Server Error, showing the exception 
-- information, and logs the error.
handleErrors :: CGI CGIResult -> CGI CGIResult
 
-- Runs a CGI action which produces a CGIResult, using the CGI protocol
-- to get the inputs and send the outputs.
runCGI :: CGI CGIResult -> IO ()

[edit] 6 HTML combinators

See also [1].

Html is the type of HTML fragments. It comes from the Text.XHtml module. There are functions for all XHTML 1.0 elements. Some examples:

  • header, body
  • h1, h2, ...
  • thediv
  • p
  • image

The << operator is used for nesting HTML.

+++ concatenates HTML.

Attributes are added to tags using the ! operator.

The function renderHtml (FIXME: explain variants) produces a string containing the document.

[edit] 7 Getting user input

This program shows a form which asks the user for her name. When the form is submitted, the program greets the user by name.

import Network.CGI
import Text.XHtml
 
inputForm = form << [paragraph << ("My name is " +++ textfield "name"),
                     submit "" "Submit"]
 
greet n = paragraph << ("Hello " ++ n ++ "!")
 
page t b = header << thetitle << t +++ body << b
 
cgiMain = do mn <- getInput "name"
             let x = maybe inputForm greet mn
             output . renderHtml $ page "Input example" x
 
main = runCGI $ handleErrors cgiMain

This code makes use of the getInput function from the CGI library:

-- Get the value of an input variable, for example from a form. 
-- If the variable has multiple values, the first one is returned.
getInput :: String -> CGI (Maybe String)

[edit] 8 Cookies

import Network.CGI
import Text.XHtml
 
import Control.Monad (liftM)
import Data.Maybe (fromMaybe)
 
hello :: Int -> Html
hello 0 = h1 << "Welcome!"
          +++ p << "This is the first time I see you."
hello c = h1 << "Welcome back!"
          +++ p << ("I have seen you " ++ show c ++ " times before.")
 
page :: String -> Html -> Html
page t b = header << thetitle << t +++ body << b
 
cgiMain :: CGI CGIResult
cgiMain = do c <- liftM (fromMaybe 0) $ readCookie "mycookie"
             setCookie (newCookie "mycookie" (show (c+1)))
             output . renderHtml . page "Cookie example" $ hello c
 
main :: IO ()
main = runCGI $ handleErrors cgiMain

Here we use newCookie, setCookie and readCookie to store and retrieve a counter cookie in the browser. If you want to get the string value of a cookie, use getCookie instead of readCookie.

[edit] 9 File uploads

FIXME: use a safer example

-- Accepts file uploads and saves the files in the given directory.
-- WARNING: this script is a SECURITY RISK and only for 
-- demo purposes. Do not put it on a public web server.
 
import Network.CGI
import Text.XHtml
 
import qualified Data.ByteString.Lazy as BS
 
import Control.Monad (liftM)
import Data.Maybe (fromJust)
 
uploadDir = "../upload"
 
fileForm = form ! [method "post", enctype "multipart/form-data"]
             << [afile "file", submit "" "Upload"]
saveFile n =
    do cont <- liftM fromJust $ getInputFPS "file"
       let f = uploadDir ++ "/" ++ basename n
       liftIO $ BS.writeFile f cont
       return $ paragraph << ("Saved as " +++ anchor ! [href f] << f +++ ".")
 
page t b = header << thetitle << t +++ body << b
 
basename = reverse . takeWhile (`notElem` "/\\") . reverse
 
cgiMain = 
    do mn <- getInputFilename "file"
       h <- maybe (return fileForm) saveFile mn
       output . renderHtml $ page "Upload example" h
 
main = runCGI $ handleErrors cgiMain

We first output a file upload form, which should use the HTTP POST method, and the multipart/form-data content type. Here we seen an example of the use of HTML attributes, added with the ! operator.

For efficiency reasons, we use Data.ByteString.Lazy to represent the file contents. getInputFPS gets the value of an input variable as a lazy ByteString.

[edit] 10 Error handling

handleErrors catches all exceptions and outputs a default error page with some information about the exception. You can write you own exception handler if you want to do something else when an exception is thrown. It can be useful to set the response code, e.g. 404.

[edit] 11 Returning non-HTML

Of course we do not have to output HTML. Use setHeader to set the value of the Content-type header, and you can output whatever string you like.

In this example we return an image:

import Network.CGI
import System.IO
import qualified Data.ByteString.Lazy as B
 
main = do
	b <- B.readFile "./img/test.jpg" -- read the image
	runCGI . handleErrors . cgiMain $ b
 
cgiMain :: B.ByteString -> CGI CGIResult
cgiMain p = do
        -- we need to set the appropriate content-type
	setHeader "Content-type" "image/jpg"
	outputFPS p

Examples: RSS

[edit] 12 Response headers

You can use the setHeader function to set arbitrary HTTP response headers. You can also set the response code, as seen above.

Example: output raw file data (with last-modified)

[edit] 13 The CGI Monad

At this point, you should be able to create many useful CGI scripts. As your scripts get more ambitious, however, you may find yourself needing to pass "global" parameters to your CGI actions (e.g. database connections, session information.) Rather than explicitly passing these values around, you can extend the CGI monad to do this work for you.

The
Network.CGI.Monad
module defines a CGI monad transformer, allowing us to build a new monad that does everything the CGI monad does -- and more! For example, let's define a new CGI monad that provides a database connection (in this example, we use the
Database.HSQL.PostgreSQL
module for our database.) Since it will be used by the CGI application, I'll call the new monad "App".

Should this not compile for you, you need to enable some extensions:

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE FlexibleInstances #-}

After importing the appropriate modules, we define a new type,
AppT
that is made up of two monad transformers,
CGIT
and
ReaderT
. The
CGIT
monad "wraps" the base monad "m". The
CGIT
monad, in turn, is wrapped by the
ReaderT
monad, which contains, in its environment, the database
Connection
.
AppT
takes two type parameters. The first is the base monad that the monad transformers are modifying. Usually this will be the
IO
monad. The second type is the data type that an action in the monad will return.

import Control.Monad.Reader
import Network.CGI
import Network.CGI.Monad
import Database.HSQL.PostgreSQL
 
newtype AppT m a = App (ReaderT Connection (CGIT m) a)
   deriving (Monad, MonadIO, MonadReader Connection)

Like
CGI
, we make a type synonym that defines the most common use of this new monad.

type App a = AppT IO a

We're not quite finished defining
App
yet. In order to be used like the CGI monad,
App
needs to be an instance of the
MonadCGI
class. This class defines two functions that we must support.

instance MonadCGI (AppT IO) where
    cgiAddHeader n v = App . lift $ cgiAddHeader n v
    cgiGet x = App . lift $ cgiGet x


So now we have an App monad that gives us all the functionality of CGI, but also carries around a database connection. The last step is to define the function that creates the monad so we can run actions inside it.

import Control.Exception (bracket)
import System.IO (stdin, stdout)
 
runApp :: App CGIResult -> IO ()
runApp (App a) =
    bracket (connect "host" "db" "user" "password")
            disconnect
            (\c -> do { env <- getCGIVars
                      ; hRunCGI env stdin stdout (runCGIT (runReaderT a c))
                      ; return () } )

(either fill in your account/password information, or change
runApp
to accept the parameters as function arguments.) The function uses
bracket
so that the database connection gets released properly when the monad ends or if an exception is thrown.

[edit] 14 Templating

There are times when you absolutely do not want to embed (X)HTML in Haskell. You can separate the code and the presentation (the Holy Grail of erm, web development). The code will be, well, Haskell, and the presentation will be buried inside templates. This might not be the case: fortunately, there is a very nice templating engine available: HStringTemplate; also very useful is hakyll, a simple static site generator library, mainly aimed at creating blogs and brochure sites.

[edit] 15 FastCGI

FastCGI is a standard for CGI-like programs that are not restarted for every request. This reduces the overhead involved in handling each request, and reduces the servers response time for each request. The overhead involved in starting a new process for each request can also include the need to set up new DB connections every time. With FastCGI, DB connections can be reused.

Install FastCGI. Get a web server which can run FastCGI programs. Import Network.FastCGI. Use runFastCGI.

See also a tutorial by Paul R Brown: Wiring Haskell Into a FastCGI Web Server

Take a look at lightweight, minimalistic FastCGI-based web frameworks: HVAC (Haskell view and controller) and Kibro.

[edit] 16 Database-driven web-applications

See Takusen and HDBC. If you would like to write queries in Haskell (and not SQL), see also HaskellDB, which integrates with HDBC.

FastCGI aren't restarted for each request, only the runFastCGI part is re-run. Everything (handles, datastructures etc.) you do outside of that loop will be persistent. However you need to handle errors yourself, because you're operating outside of handleErrors.

[edit] 17 See also

Authors: Björn Bringert Authors: Don Stewart