her.esy.fun/src/Scratch/en/blog/Yesod-tutorial-for-newbies/index.html
Yann Esposito (Yogsototh) 03610908ce
Old site match new style
2021-05-25 22:25:47 +02:00

495 lines
48 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>YBlog - Haskell web programming</title>
<meta name="keywords" content="yesod, haskell, programming, web" />
<link rel="shortcut icon" type="image/x-icon" href="../../../../Scratch/img/favicon.ico" />
<link rel="stylesheet" type="text/css" href="/css/y.css" />
<link rel="stylesheet" type="text/css" href="/css/legacy.css" />
<link rel="alternate" type="application/rss+xml" title="RSS" href="/rss.xml" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="apple-touch-icon" href="../../../../Scratch/img/about/FlatAvatar@2x.png" />
<!--[if lt IE 9]>
<script src="http://ie7-js.googlecode.com/svn/version/2.1(beta4)/IE9.js"></script>
<![endif]-->
<!-- IndieAuth -->
<link href="https://twitter.com/yogsototh" rel="me">
<link href="https://github.com/yogsototh" rel="me">
<link href="mailto:yann.esposito@gmail.com" rel="me">
<link rel="pgpkey" href="../../../../pubkey.txt">
</head>
<body lang="en" class="article">
<div id="content">
<div id="header">
<div id="choix">
<span id="choixlang">
<a href="../../../../Scratch/fr/blog/Yesod-tutorial-for-newbies/">French</a>
</span>
<span class="tomenu"><a href="#navigation">↓ Menu ↓</a></span>
<span class="flush"></span>
</div>
</div>
<div id="titre">
<h1>Haskell web programming</h1>
<h2>A Yesod tutorial</h2>
</div>
<div class="flush"></div>
<div id="afterheader" class="article">
<div class="corps">
<div>
<img src="../../../../Scratch/img/blog/Yesod-tutorial-for-newbies/flying_neo.jpg" alt="Neo Flying at warp speed" />
</div>
<div class="intro">
<p><em>update</em>: updated for Yesod 1.2</p>
<p><span class="sc"><abbr title="Too long; didn't read">tl;dr</abbr>: </span> A simple Yesod tutorial. Yesod is a Haskell web framework. You shouldnt need to know Haskell.</p>
</div>
<p>Why Haskell?</p>
<div>
<img src="../../../../Scratch/img/blog/Yesod-tutorial-for-newbies/haskell-benchmark.png" alt="Impressive Haskell Benchmark" />
</div>
<p>Its efficiency (see <a href="http://snapframework.com/blog/2010/11/17/snap-0.3-benchmarks">Snap Benchmark</a> <em>&amp;</em> <a href="http://www.yesodweb.com/blog/2011/03/preliminary-warp-cross-language-benchmarks">Warp Benchmark</a><a href="#fn1" class="footnote-ref" id="fnref1"><sup>1</sup></a>). Haskell is an order of magnitude faster than interpreted languages like <a href="http://shootout.alioth.debian.org/u64q/benchmark.php?test=all&amp;lang=ghc&amp;lang2=yarv">Ruby</a> and <a href="http://shootout.alioth.debian.org/u64q/benchmark.php?test=all&amp;lang=ghc&amp;lang2=python3">Python</a><a href="#fn2" class="footnote-ref" id="fnref2"><sup>2</sup></a>.</p>
<p>Haskell is a high level language that makes it harder to shoot yourself in the foot than <code>C</code>, <code>C++</code> or <code>Java</code>, for example. One of the best properties of Haskell is:</p>
<blockquote>
<p>“If your program compiles it will be very close to what the programmer intended”.</p>
</blockquote>
<p>Haskell web frameworks handle parallel tasks perfectly—even better than node.js<a href="#fn3" class="footnote-ref" id="fnref3"><sup>3</sup></a>, for example.</p>
<div>
<img src="../../../../Scratch/img/blog/Yesod-tutorial-for-newbies/thousands_smiths.jpg" alt="Thousands of Agent Smith" class="left" />
</div>
<p>From a purely technical point of view, Haskell seems to be the perfect web development tool. Weaknesses of Haskell certainly wont be technical:</p>
<ul>
<li>Hard to grasp Haskell</li>
<li>Hard to find a Haskell programmer</li>
<li>The Haskell community is smaller than the community for <code>/.*/</code></li>
<li><strike>There is not yet a <a href="http://heroku.com">heroku</a> for Haskell. Even In fact, I use heroku to host my websites but this isnt straightforward (see the <a href="https://github.com/yesodweb/yesod/wiki/Deploying-Yesod-Apps-to-Heroku">how to</a>).</strike> <a href="http://fpcomplete.com">FPComplete</a> has now filled this hole. And they provide not only cloud hosting but also a complete IDE and Haskell environment to work with.</li>
</ul>
<p>I wont say these are not important drawbacks. But with Haskell your web application will be able to both absorb an impressive number of parallel requests securely and to adapt to change.</p>
<p>Actually there are three main Haskell web frameworks:</p>
<ol type="1">
<li><a href="http://happstack.com">Happstack</a></li>
<li><a href="http://snapframework.com">Snap</a></li>
<li><a href="http://yesodweb.com">Yesod</a></li>
</ol>
<p>I dont think there is a real winner between these three framework. The choice I made for Yesod is highly subjective. I just lurked a bit and tried some tutorials. I had the feeling Yesod did a better job at helping newcomers. Furthermore, the Yesod team seems the most active. Of course I might be wrong since this is just an impression.</p>
<div>
<img src="../../../../Scratch/img/blog/Yesod-tutorial-for-newbies/owl_draw.png" alt="1. Draw some circles. 2. Draw the rest of the fucking owl" />
</div>
<p>Why did I write this article? The Yesod documentation and particularly the book are excellent. But I missed an intermediate tutorial. This tutorial wont explain all details. I tried to give a step by step of how to start from a five minute tutorial to an almost production ready architecture. Furthermore explaining something to others is a great way to learn. If you are used to Haskell and Yesod, this tutorial wont teach you much. If you are completely new to Haskell and Yesod, hopefully it will help you. Also if you find yourself too confused by the syntax, it might helps to read this <a href="http://blog.ezyang.com/2011/11/how-to-read-haskell/">article</a></p>
<p>During this tutorial youll install, initialize, and configure your first Yesod project. Then there is a very minimal 5 minute Yesod tutorial to get us warmed up and confirm the awesomeness of Yesod. Then we will clean up the 5 minute tutorial to use some “best practices”. Finally there will be a more standard real world example: a minimal blog system.</p>
<h2 id="before-the-real-start">Before the real start</h2>
<h3 id="install">Install</h3>
<p>The recommended way to install <a href="http://www.haskell.org">Haskell</a> is to download the <a href="http://www.haskell.org/platform">Haskell Platform</a>.</p>
<p>If you want optimal performances I suggest you to download and compile the latest <a href="http://ghc.haskell.org">GHC Release</a> (at least 7.8). It is generally very easy to install from source. If you go this way, youll need to install <a href="http://www.haskell.org/cabal/download.html">cabal</a>. You should use at least cabal 1.18. Dont forget to add cabal binaries to your PATH. Generally:</p>
<pre><code>echo 'export PATH=$HOME/.cabal/bin:$PATH' &gt;&gt; ~/.profile</code></pre>
<p>And to be sure you have the latest cabal release:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode bash"><code class="sourceCode bash"><a class="sourceLine" id="cb2-1" title="1"><span class="ex">cabal</span> update</a>
<a class="sourceLine" id="cb2-2" title="2"><span class="ex">cabal</span> install cabal-install</a></code></pre></div>
<p>Once done, you need to install Yesod.</p>
<p>Then open a terminal session and do:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode bash"><code class="sourceCode bash"><a class="sourceLine" id="cb3-1" title="1"><span class="ex">cabal</span> install yesod-bin</a></code></pre></div>
<p>There are few steps but it should take some time to finish.</p>
<h3 id="initialize">Initialize</h3>
<p>You are now ready to initialize your first Yesod project. Open a terminal and type:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode bash"><code class="sourceCode bash"><a class="sourceLine" id="cb4-1" title="1"><span class="ex">yesod</span> init</a></code></pre></div>
<p>Enter your name, choose <code>yosog</code> for the project name and enter <code>Yosog</code> for the name of the Foundation. Finally choose <code>sqlite</code>. Now, start the development cycle:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode bash"><code class="sourceCode bash"><a class="sourceLine" id="cb5-1" title="1"><span class="bu">cd</span> yosog</a>
<a class="sourceLine" id="cb5-2" title="2"><span class="ex">cabal</span> sandbox init</a>
<a class="sourceLine" id="cb5-3" title="3"><span class="ex">cabal</span> install --enable-tests . yesod-platform yesod-bin --max-backjumps=-1 --reorder-goals</a>
<a class="sourceLine" id="cb5-4" title="4"><span class="ex">yesod</span> devel</a></code></pre></div>
<p>This will compile the entire project. Be patient, since it will take a while the first time. Once finished a server is launched and you can visit it by clicking this link:</p>
<p><a href="http://localhost:3000"><code>http://localhost:3000</code></a></p>
<p>Congratulation! Yesod works!</p>
<blockquote>
<p>Note: if something is messed up use the following command at the command line inside the project directory.</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode bash"><code class="sourceCode bash"><a class="sourceLine" id="cb6-1" title="1">\<span class="fu">rm</span> -rf .cabal-sandbox cabal.sandbox.config</a>
<a class="sourceLine" id="cb6-2" title="2"><span class="ex">cabal</span> sandbox init <span class="kw">&amp;&amp;</span> <span class="ex">cabal</span> install --enable-tests . yesod-platform yesod-bin --max-backjumps=-1 --reorder-goals <span class="kw">&amp;&amp;</span> <span class="ex">yesod</span> devel</a></code></pre></div>
</blockquote>
<p>For the rest of the tutorial, use another terminal and keep this one open in a corner to see what happens.</p>
<h3 id="configure-git">Configure git</h3>
<blockquote>
<p>Of course this step is not mandatory for the tutorial but it is a good practice.</p>
</blockquote>
<p>Fortunately, there is already a <code>.gitignore</code> file into the <code>yosog</code> folder. You just have to initialize your git repository:</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode bash"><code class="sourceCode bash"><a class="sourceLine" id="cb7-1" title="1"><span class="fu">git</span> init .</a>
<a class="sourceLine" id="cb7-2" title="2"><span class="fu">git</span> add .</a>
<a class="sourceLine" id="cb7-3" title="3"><span class="fu">git</span> commit -a -m <span class="st">&quot;Initial yesod commit&quot;</span></a></code></pre></div>
<p>We are almost ready to start.</p>
<h3 id="a-few-words-before-we-start">A few words before we start</h3>
<p>Up until here, we have a directory containing a bunch of files and a local web server listening the port 3000. If we modify a file inside this directory, Yesod should try to recompile as fast as possible the site. Instead of explaining the role of every file, lets focus only on the important files/directories for this tutorial:</p>
<ol type="1">
<li><code>config/routes</code></li>
<li><code>Handler/</code></li>
<li><code>templates/</code></li>
<li><code>config/models</code></li>
</ol>
<p>Obviously:</p>
<table>
<tr>
<td>
<code>config/routes</code>
</td>
<td>
is where youll configure the map <span class="sc"><abbr title="Uniform Ressource Locator">url</abbr></span> → Code.
</td>
</tr>
<tr>
<td>
<code>Handler/</code>
</td>
<td>
contains the files that will contain the code called when a <span class="sc"><abbr title="Uniform Ressource Locator">url</abbr></span> is accessed.
</td>
</tr>
<tr>
<td>
<code>templates/</code>
</td>
<td>
contains <span class="sc"><abbr title="HyperText Markup Language">html</abbr></span>, js and <span class="sc">css</span> templates.
</td>
</tr>
<tr>
<td>
<code>config/models</code>
</td>
<td>
is where youll configure the persistent objects (database tables).
</td>
</tr>
</table>
<p>During this tutorial well modify other files as well, but we wont explore them in detail.</p>
<p>Also note, shell commands are executed in the root directory of your project unless specified otherwise.</p>
<p>We are now ready to start!</p>
<h2 id="echo">Echo</h2>
<p>To verify the quality of the security of the Yesod framework, lets make a minimal echo application.</p>
<blockquote>
<p>Goal:</p>
<p>Make a server that when accessed <code>/echo/[some text]</code> should return a web page containing “some text” inside an <code>h1</code> bloc.</p>
</blockquote>
<pre class="no-highlight"><code>~/Sites/yosog $ <span class="highlight">yesod add-handler</span>
Name of route (without trailing R): <span class="highlight">Echo</span>
Enter route pattern (ex: /entry/#EntryId): <span class="highlight">/echo/#String</span>
Enter space-separated list of methods (ex: GET POST): <span class="highlight">GET</span></code></pre>
<p>Almost all the work is done for us. The <code>add-handler</code> does the following:</p>
<p>Updates the <code>config/route</code> file by appending:</p>
<pre><code>/echo/#String EchoR GET</code></pre>
<p>This line contains three elements: the <span class="sc"><abbr title="Uniform Ressource Locator">url</abbr></span> pattern, a handler name, an <span class="sc"><abbr title="HyperText Transfer Protocol">http</abbr></span> method.</p>
<ul>
<li>creates a <code>Handler/Echo.hs</code> file</li>
<li>imports <code>Handler.Echo</code> in the main <code>Application.hs</code> file</li>
<li>declares <code>Handler.Echo</code> in the cabal file for building the application</li>
</ul>
<p>Now try to go to <a href="http://localhost:3000/echo/foo"><code>localhost:3000/echo/foo</code></a>. You should get a message explaining that <code>getEchoR</code> is not yet implemented.</p>
<p>So lets take a look at <code>Handler/Echo.hs</code>:</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode haskell"><code class="sourceCode haskell"><a class="sourceLine" id="cb10-1" title="1"><span class="kw">module</span> <span class="dt">Handler.Echo</span> <span class="kw">where</span></a>
<a class="sourceLine" id="cb10-2" title="2"></a>
<a class="sourceLine" id="cb10-3" title="3"><span class="kw">import</span> <span class="dt">Import</span></a>
<a class="sourceLine" id="cb10-4" title="4"></a>
<a class="sourceLine" id="cb10-5" title="5"><span class="ot">getEchoR ::</span> <span class="dt">String</span> <span class="ot">-&gt;</span> <span class="dt">Handler</span> <span class="dt">Html</span></a>
<a class="sourceLine" id="cb10-6" title="6">getEchoR <span class="ot">=</span> <span class="fu">error</span> <span class="st">&quot;Not yet implemented: getEchoR&quot;</span></a></code></pre></div>
<p>This should be straightforward. Now we can replace it with this:</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode haskell"><code class="sourceCode haskell"><a class="sourceLine" id="cb11-1" title="1"><span class="kw">module</span> <span class="dt">Handler.Echo</span> <span class="kw">where</span></a>
<a class="sourceLine" id="cb11-2" title="2"></a>
<a class="sourceLine" id="cb11-3" title="3"><span class="kw">import</span> <span class="dt">Import</span></a>
<a class="sourceLine" id="cb11-4" title="4"></a>
<a class="sourceLine" id="cb11-5" title="5"><span class="ot">getEchoR ::</span> <span class="dt">String</span> <span class="ot">-&gt;</span> <span class="dt">Handler</span> <span class="dt">Html</span></a>
<a class="sourceLine" id="cb11-6" title="6">getEchoR theText <span class="ot">=</span> defaultLayout [whamlet|<span class="kw">&lt;h1&gt;#{</span>theText<span class="kw">}</span>|]</a></code></pre></div>
<p>Dont worry if you find all of this a bit cryptic. In short it just declares a function named <code>getEchoR</code> with one argument (<code>theText</code>) of type <code>String</code>. When this function is called, it returns a <code>Handler Html</code> whatever it is. But mainly this will encapsulate our expected result inside an <span class="sc"><abbr title="HyperText Markup Language">html</abbr></span> text.</p>
<p>After saving the file, you should see Yesod recompile the application. When the compilation is finished youll see the message: <code>Starting devel application</code>.</p>
<p>Now you can visit: <a href="http://localhost:3000/echo/Yesod%20rocks!"><code>http://localhost:3000/echo/Yesod%20rocks!</code></a></p>
<p>TADA! It works!</p>
<h3 id="bulletproof">Bulletproof?</h3>
<div>
<img src="../../../../Scratch/img/blog/Yesod-tutorial-for-newbies/neo_bullet_proof.jpg" alt="Neo stops a myriad of bullets" />
</div>
<p>Even this extremely minimal web application has some impressive properties. For example, imagine an attacker entering this <span class="sc"><abbr title="Uniform Ressource Locator">url</abbr></span>:</p>
<div class="small">
<p><a href="http://localhost:3000/echo/I'm%20%3Cscript%3Ealert(%22Bad!%22);%3C%2Fscript%3E"><code>http://localhost:3000/echo/I'm &lt;script&gt;alert(\"Bad!\");&lt;/script&gt;</code></a></p>
</div>
<p>You can click on it to test it.</p>
<p>The special characters are protected for us, preventing a malicious user from hiding a bad script within the url.</p>
<p>This behavior is a direct consequence of <em>type safety</em>. The <span class="sc"><abbr title="Uniform Ressource Locator">url</abbr></span> string is put inside a <span class="sc"><abbr title="Uniform Ressource Locator">url</abbr></span> type. Then the interesting part in the <span class="sc"><abbr title="Uniform Ressource Locator">url</abbr></span> is put inside a String type. To pass from <span class="sc"><abbr title="Uniform Ressource Locator">url</abbr></span> type to String type some transformations are made. For example, all instances of “<code>%20</code>” are replaced by space characters. Then to show the String inside an <span class="sc"><abbr title="HyperText Markup Language">html</abbr></span> document, the string is put inside an <span class="sc"><abbr title="HyperText Markup Language">html</abbr></span> type. Some transformations occurs like replace “<code>&lt;</code>” by “<code>&amp;lt;</code>”. Thanks to Yesod, this tedious job is done for us.</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode bash"><code class="sourceCode bash"><a class="sourceLine" id="cb12-1" title="1"><span class="st">&quot;http://localhost:3000/echo/some%20text%3Ca%3E&quot;</span> :: <span class="ex">URL</span></a>
<a class="sourceLine" id="cb12-2" title="2"></a>
<a class="sourceLine" id="cb12-3" title="3"> <span class="st">&quot;some text&lt;a&gt;&quot;</span> :: <span class="ex">String</span></a>
<a class="sourceLine" id="cb12-4" title="4"></a>
<a class="sourceLine" id="cb12-5" title="5"> <span class="st">&quot;some text &amp;amp;lt;a&amp;amp;gt;&quot;</span> :: <span class="ex">Html</span></a></code></pre></div>
<p>Yesod is not only fast, it helps us to remain secure. It protects us from many common errors in other paradigms. Yes, I am looking at you, PHP!</p>
<h3 id="cleaning-up">Cleaning up</h3>
<p>Even this very minimal example should be enhanced. We will clean up many details:</p>
<ul>
<li>Use <code>Data.Text</code> instead of <code>String</code></li>
<li>Put our “views”<a href="#fn4" class="footnote-ref" id="fnref4"><sup>4</sup></a> inside the <code>template</code> directory</li>
</ul>
<h4 id="data.text"><code>Data.Text</code></h4>
<p>It is a good practice to use <code>Data.Text</code> instead of <code>String</code>.</p>
<p>To declare it, add this import directive to <code>Foundation.hs</code> (just after the last one):</p>
<div class="sourceCode" id="cb13"><pre class="sourceCode diff"><code class="sourceCode diff"><a class="sourceLine" id="cb13-1" title="1">import Data.Text</a></code></pre></div>
<p>We have to modify <code>config/routes</code> and our handler accordingly. Replace <code>#String</code> by <code>#Text</code> in <code>config/routes</code>:</p>
<pre>
/echo/<span class="highlight">#Text</span> EchoR GET
</pre>
<p>And do the same in <code>Handler/Echo.hs</code>:</p>
<div class="sourceCode" id="cb14"><pre class="sourceCode haskell"><code class="sourceCode haskell"><a class="sourceLine" id="cb14-1" title="1"><span class="kw">module</span> <span class="dt">Handler.Echo</span> <span class="kw">where</span></a>
<a class="sourceLine" id="cb14-2" title="2"></a>
<a class="sourceLine" id="cb14-3" title="3"><span class="kw">import</span> <span class="dt">Import</span></a>
<a class="sourceLine" id="cb14-4" title="4"></a>
<a class="sourceLine" id="cb14-5" title="5"><span class="ot">getEchoR ::</span> <span class="highlight"><span class="dt">Text</span></span> <span class="ot">-&gt;</span> <span class="dt">Handler</span> <span class="dt">Html</span></a>
<a class="sourceLine" id="cb14-6" title="6">getEchoR theText <span class="ot">=</span> defaultLayout [whamlet|<span class="kw">&lt;h1&gt;#{</span>theText<span class="kw">}</span>|]</a></code></pre></div>
<h4 id="use-templates">Use templates</h4>
<p>Some <span class="sc"><abbr title="HyperText Markup Language">html</abbr></span> (more precisely hamlet) is written directly inside our handler. We should put this part inside another file. Create the new file <code>templates/echo.hamlet</code> containing:</p>
<div class="sourceCode" id="cb15"><pre class="sourceCode haskell"><code class="sourceCode haskell"><a class="sourceLine" id="cb15-1" title="1"><span class="op">&lt;</span>h1<span class="op">&gt;</span> <span class="op">#</span>{theText}</a></code></pre></div>
<p>and modify the handler <code>Handler/Echo.hs</code>:</p>
<div class="sourceCode" id="cb16"><pre class="sourceCode haskell"><code class="sourceCode haskell"><a class="sourceLine" id="cb16-1" title="1"><span class="kw">module</span> <span class="dt">Handler.Echo</span> <span class="kw">where</span></a>
<a class="sourceLine" id="cb16-2" title="2"></a>
<a class="sourceLine" id="cb16-3" title="3"><span class="kw">import</span> <span class="dt">Import</span></a>
<a class="sourceLine" id="cb16-4" title="4"></a>
<a class="sourceLine" id="cb16-5" title="5"><span class="ot">getEchoR ::</span> <span class="dt">Text</span> <span class="ot">-&gt;</span> <span class="dt">Handler</span> <span class="dt">Html</span></a>
<a class="sourceLine" id="cb16-6" title="6">getEchoR theText <span class="ot">=</span> defaultLayout <span class="highlight"><span class="op">$</span>(widgetFile <span class="st">&quot;echo&quot;</span>)</span></a></code></pre></div>
<p>At this point, our web application is nicely structured. We use <code>Data.Text</code> and our views are in templates. It is the time to make a slightly more complex example.</p>
<h2 id="mirror">Mirror</h2>
<div>
<img src="../../../../Scratch/img/blog/Yesod-tutorial-for-newbies/mirror.jpg" alt="Neo touching a mirror" class="left" />
</div>
<p>Lets make another minimal application. You should see a form containing a text field and a validation button. When you enter some text (for example “Jormungad”) and validate it, the next page presents the content to you with its reverse appended to it. In our example, it should return “JormungaddagnumroJ”.</p>
<p>First, add a new handler:</p>
<pre class="no-highlight"><code> ~/Sites/yosog (master) $ <span class="highlight">yesod add-handler</span>
Name of route (without trailing R): <span class="highlight">Mirror</span>
Enter route pattern (ex: /entry/#EntryId): <span class="highlight">/mirror</span>
Enter space-separated list of methods (ex: GET POST): <span class="highlight">GET POST</span></code></pre>
<p>This time the path <code>/mirror</code> will accept GET and POST requests. Update the corresponding new Handler file (<code>Handler/Mirror.hs</code>):</p>
<div class="sourceCode" id="cb18"><pre class="sourceCode haskell"><code class="sourceCode haskell"><a class="sourceLine" id="cb18-1" title="1"><span class="kw">module</span> <span class="dt">Handler.Mirror</span> <span class="kw">where</span></a>
<a class="sourceLine" id="cb18-2" title="2"></a>
<a class="sourceLine" id="cb18-3" title="3"><span class="kw">import</span> <span class="dt">Import</span></a>
<a class="sourceLine" id="cb18-4" title="4"><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">Data.Text</span> <span class="kw">as</span> <span class="dt">T</span></a>
<a class="sourceLine" id="cb18-5" title="5"></a>
<a class="sourceLine" id="cb18-6" title="6"><span class="ot">getMirrorR ::</span> <span class="dt">Handler</span> <span class="dt">Html</span></a>
<a class="sourceLine" id="cb18-7" title="7">getMirrorR <span class="ot">=</span> defaultLayout <span class="op">$</span>(widgetFile <span class="st">&quot;mirror&quot;</span>)</a>
<a class="sourceLine" id="cb18-8" title="8"></a>
<a class="sourceLine" id="cb18-9" title="9"><span class="ot">postMirrorR ::</span> <span class="dt">Handler</span> <span class="dt">Html</span></a>
<a class="sourceLine" id="cb18-10" title="10">postMirrorR <span class="ot">=</span> <span class="kw">do</span></a>
<a class="sourceLine" id="cb18-11" title="11"> postedText <span class="ot">&lt;-</span> runInputPost <span class="op">$</span> ireq textField <span class="st">&quot;content&quot;</span></a>
<a class="sourceLine" id="cb18-12" title="12"> defaultLayout <span class="op">$</span>(widgetFile <span class="st">&quot;posted&quot;</span>)</a></code></pre></div>
<p>We will need to use the <code>reverse</code> function provided by <code>Data.Text</code> which explains the additional import.</p>
<p>The only new thing here is the line that gets the POST parameter named “content”. If you want to know more detail about it and forms in general you can take a look at <a href="http://www.yesodweb.com/book/forms">the Yesod book</a>.</p>
<p>Create the two corresponding templates (<code>templates/mirror.hamlet</code> and <code>templates/posted.hamlet</code>):</p>
<div class="sourceCode" id="cb19"><pre class="sourceCode html"><code class="sourceCode html"><a class="sourceLine" id="cb19-1" title="1"><span class="kw">&lt;h1&gt;</span> Enter your text</a>
<a class="sourceLine" id="cb19-2" title="2"><span class="kw">&lt;form</span><span class="ot"> method=</span><span class="st">post</span><span class="ot"> action=</span><span class="st">@{MirrorR}</span><span class="kw">&gt;</span></a>
<a class="sourceLine" id="cb19-3" title="3"> <span class="kw">&lt;input</span><span class="ot"> type=</span><span class="st">text</span><span class="ot"> name=</span><span class="st">content</span><span class="kw">&gt;</span></a>
<a class="sourceLine" id="cb19-4" title="4"> <span class="kw">&lt;input</span><span class="ot"> type=</span><span class="st">submit</span><span class="kw">&gt;</span></a></code></pre></div>
<div class="sourceCode" id="cb20"><pre class="sourceCode html"><code class="sourceCode html"><a class="sourceLine" id="cb20-1" title="1"><span class="kw">&lt;h1&gt;</span>You've just posted</a>
<a class="sourceLine" id="cb20-2" title="2"><span class="kw">&lt;p&gt;</span>#{postedText}#{T.reverse postedText}</a>
<a class="sourceLine" id="cb20-3" title="3"><span class="kw">&lt;hr&gt;</span></a>
<a class="sourceLine" id="cb20-4" title="4"><span class="kw">&lt;p&gt;&lt;a</span><span class="ot"> href=</span><span class="st">@{MirrorR}</span><span class="kw">&gt;</span>Get back</a></code></pre></div>
<p>And that is all. This time, we wont need to clean up. We might have generated the form a different way, but well see how to do this in the next section.</p>
<p>Just try it by <a href="http://localhost:3000/mirror">clicking here</a>.</p>
<p>Also you can try to enter strange values (like <code>&lt;script&gt;alert('Bad');&lt;/script&gt;</code>). As before, your application is quite secure.</p>
<h2 id="a-blog">A Blog</h2>
<p>We saw how to retrieve <span class="sc"><abbr title="HyperText Transfer Protocol">http</abbr></span> parameters. It is the time to save things into a database.</p>
<p>This example will be very minimal:</p>
<ul>
<li><code>GET</code> on <code>/blog</code> should display the list of articles,</li>
<li><code>POST</code> on <code>/blog</code> should create a new article,</li>
<li><code>GET</code> on <code>/blog/&lt;article id&gt;</code> should display the content of the article.</li>
</ul>
<p>As before, well start by adding some handlers:</p>
<pre><code>~/Sites/yosog (master) $ yesod add-handler
Name of route (without trailing R): Blog
Enter route pattern (ex: /entry/#EntryId): /blog
Enter space-separated list of methods (ex: GET POST): GET POST
~/Sites/yosog (master) $ yesod add-handler
Name of route (without trailing R): Article
Enter route pattern (ex: /entry/#EntryId): /blog/#ArticleId
Enter space-separated list of methods (ex: GET POST): GET</code></pre>
<p>Then we declare another model object. Append the following content to <code>config/models</code>:</p>
<pre><code>Article
title Text
content Html
deriving</code></pre>
<p>As <code>Html</code> is not an instance of <code>Read</code>, <code>Show</code> and <code>Eq</code>, we had to add the <code>deriving</code> line. If you forget it, there will be an error.</p>
<p>After the route and the model, we write the handler. Lets write the content of <code>Handler/Blog.hs</code>. We start by declaring the module and by importing some block necessary to handle Html in forms.</p>
<div class="sourceCode" id="cb23"><pre class="sourceCode haskell"><code class="sourceCode haskell"><a class="sourceLine" id="cb23-1" title="1"><span class="kw">module</span> <span class="dt">Handler.Blog</span></a>
<a class="sourceLine" id="cb23-2" title="2"> ( getBlogR</a>
<a class="sourceLine" id="cb23-3" title="3"> , postBlogR</a>
<a class="sourceLine" id="cb23-4" title="4"> )</a>
<a class="sourceLine" id="cb23-5" title="5"><span class="kw">where</span></a>
<a class="sourceLine" id="cb23-6" title="6"></a>
<a class="sourceLine" id="cb23-7" title="7"><span class="kw">import</span> <span class="dt">Import</span></a>
<a class="sourceLine" id="cb23-8" title="8"></a>
<a class="sourceLine" id="cb23-9" title="9"><span class="co">-- to use Html into forms</span></a>
<a class="sourceLine" id="cb23-10" title="10"><span class="kw">import</span> <span class="dt">Yesod.Form.Nic</span> (<span class="dt">YesodNic</span>, nicHtmlField)</a>
<a class="sourceLine" id="cb23-11" title="11"><span class="kw">instance</span> <span class="dt">YesodNic</span> <span class="dt">App</span></a></code></pre></div>
<p><em>Remark</em>: it is a best practice to add the YesodNic instance inside <code>Foundation.hs</code>. I put this definition here to make things easier but you should see a warning about this orphan instance. To put the include inside Foundation.hs is left as an exercice to the reader.</p>
<p><em>Hint</em>: Do not forget to put <code>YesodNic</code> and <code>nicHtmlField</code> inside the exported objects of the module.</p>
<div class="sourceCode" id="cb24"><pre class="sourceCode haskell"><code class="sourceCode haskell"><a class="sourceLine" id="cb24-1" title="1"><span class="ot">entryForm ::</span> <span class="dt">Form</span> <span class="dt">Article</span></a>
<a class="sourceLine" id="cb24-2" title="2">entryForm <span class="ot">=</span> renderDivs <span class="op">$</span> <span class="dt">Article</span></a>
<a class="sourceLine" id="cb24-3" title="3"> <span class="op">&lt;$&gt;</span> areq textField <span class="st">&quot;Title&quot;</span> <span class="dt">Nothing</span></a>
<a class="sourceLine" id="cb24-4" title="4"> <span class="op">&lt;*&gt;</span> areq nicHtmlField <span class="st">&quot;Content&quot;</span> <span class="dt">Nothing</span></a></code></pre></div>
<p>This function defines a form for adding a new article. Dont pay attention to all the syntax. If you are curious you can take a look at Applicative Functor. You just have to remember <code>areq</code> is for required form input. Its arguments being: <code>areq type label default_value</code>.</p>
<div class="sourceCode" id="cb25"><pre class="sourceCode haskell"><code class="sourceCode haskell"><a class="sourceLine" id="cb25-1" title="1"><span class="co">-- The view showing the list of articles</span></a>
<a class="sourceLine" id="cb25-2" title="2"><span class="ot">getBlogR ::</span> <span class="dt">Handler</span> <span class="dt">Html</span></a>
<a class="sourceLine" id="cb25-3" title="3">getBlogR <span class="ot">=</span> <span class="kw">do</span></a>
<a class="sourceLine" id="cb25-4" title="4"> <span class="co">-- Get the list of articles inside the database.</span></a>
<a class="sourceLine" id="cb25-5" title="5"> articles <span class="ot">&lt;-</span> runDB <span class="op">$</span> selectList [] [<span class="dt">Desc</span> <span class="dt">ArticleTitle</span>]</a>
<a class="sourceLine" id="cb25-6" title="6"> <span class="co">-- We'll need the two &quot;objects&quot;: articleWidget and enctype</span></a>
<a class="sourceLine" id="cb25-7" title="7"> <span class="co">-- to construct the form (see templates/articles.hamlet).</span></a>
<a class="sourceLine" id="cb25-8" title="8"> (articleWidget, enctype) <span class="ot">&lt;-</span> generateFormPost entryForm</a>
<a class="sourceLine" id="cb25-9" title="9"> defaultLayout <span class="op">$</span> <span class="kw">do</span></a>
<a class="sourceLine" id="cb25-10" title="10"> <span class="op">$</span>(widgetFile <span class="st">&quot;articles&quot;</span>)</a></code></pre></div>
<p>This handler should display a list of articles. We get the list from the DB and we construct the form. Just take a look at the corresponding template:</p>
<div class="sourceCode" id="cb26"><pre class="sourceCode html"><code class="sourceCode html"><a class="sourceLine" id="cb26-1" title="1"><span class="kw">&lt;h1&gt;</span> Articles</a>
<a class="sourceLine" id="cb26-2" title="2">$if null articles</a>
<a class="sourceLine" id="cb26-3" title="3"> <span class="kw">&lt;p&gt;</span> There are no articles in the blog</a>
<a class="sourceLine" id="cb26-4" title="4">$else</a>
<a class="sourceLine" id="cb26-5" title="5"> <span class="kw">&lt;ul&gt;</span></a>
<a class="sourceLine" id="cb26-6" title="6"> $forall Entity articleId article <span class="er">&lt;</span>- articles</a>
<a class="sourceLine" id="cb26-7" title="7"> <span class="kw">&lt;li&gt;</span></a>
<a class="sourceLine" id="cb26-8" title="8"> <span class="kw">&lt;a</span><span class="ot"> href=</span><span class="st">@{ArticleR</span><span class="ot"> articleId</span><span class="er">}</span> <span class="kw">&gt;</span> #{articleTitle article}</a>
<a class="sourceLine" id="cb26-9" title="9"><span class="kw">&lt;hr&gt;</span></a>
<a class="sourceLine" id="cb26-10" title="10"> <span class="kw">&lt;form</span><span class="ot"> method=</span><span class="st">post</span><span class="ot"> enctype=</span><span class="st">#{enctype}</span><span class="kw">&gt;</span></a>
<a class="sourceLine" id="cb26-11" title="11"> ^{articleWidget}</a>
<a class="sourceLine" id="cb26-12" title="12"> <span class="kw">&lt;div&gt;</span></a>
<a class="sourceLine" id="cb26-13" title="13"> <span class="kw">&lt;input</span><span class="ot"> type=</span><span class="st">submit</span><span class="ot"> value=</span><span class="st">&quot;Post New Article&quot;</span><span class="kw">&gt;</span></a></code></pre></div>
<p>Notice we added some logic inside the template. There is a test and a “loop”.</p>
<p>Another very interesting part is the creation of the form. The <code>articleWidget</code> was created by Yesod. We have given it the right parameters (input required or optional, labels, default values), and now we have a protected form made for us. But now we have to create the submit button.</p>
<p>You can take a first look by <a href="localhost:3000/blog">clicking here</a>. Of course, you cant post something yet.</p>
<p>Get back to <code>Handler/Blog.hs</code>.</p>
<div class="sourceCode" id="cb27"><pre class="sourceCode haskell"><code class="sourceCode haskell"><a class="sourceLine" id="cb27-1" title="1"><span class="ot">postBlogR ::</span> <span class="dt">Handler</span> <span class="dt">Html</span></a>
<a class="sourceLine" id="cb27-2" title="2">postBlogR <span class="ot">=</span> <span class="kw">do</span></a>
<a class="sourceLine" id="cb27-3" title="3"> ((res,articleWidget),enctype) <span class="ot">&lt;-</span> runFormPost entryForm</a>
<a class="sourceLine" id="cb27-4" title="4"> <span class="kw">case</span> res <span class="kw">of</span></a>
<a class="sourceLine" id="cb27-5" title="5"> <span class="dt">FormSuccess</span> article <span class="ot">-&gt;</span> <span class="kw">do</span></a>
<a class="sourceLine" id="cb27-6" title="6"> articleId <span class="ot">&lt;-</span> runDB <span class="op">$</span> insert article</a>
<a class="sourceLine" id="cb27-7" title="7"> setMessage <span class="op">$</span> toHtml <span class="op">$</span> (articleTitle article) <span class="op">&lt;&gt;</span> <span class="st">&quot; created&quot;</span></a>
<a class="sourceLine" id="cb27-8" title="8"> redirect <span class="op">$</span> <span class="dt">ArticleR</span> articleId</a>
<a class="sourceLine" id="cb27-9" title="9"> _ <span class="ot">-&gt;</span> defaultLayout <span class="op">$</span> <span class="kw">do</span></a>
<a class="sourceLine" id="cb27-10" title="10"> setTitle <span class="st">&quot;Please correct your entry form&quot;</span></a>
<a class="sourceLine" id="cb27-11" title="11"> <span class="op">$</span>(widgetFile <span class="st">&quot;articleAddError&quot;</span>)</a></code></pre></div>
<p>This function should be used to create a new article. We handle the form response. If there is an error we display an error page, for example if we left some required value blank. If things go well:</p>
<ul>
<li>we add the new article inside the DB (<code>runDB $ insert article</code>)</li>
<li>we add a message to be displayed (<code>setMessage $ ...</code>)</li>
<li>we are redirected to the article web page.</li>
</ul>
<p>Here is the content of the error Page:</p>
<div class="sourceCode" id="cb28"><pre class="sourceCode haskell"><code class="sourceCode haskell"><a class="sourceLine" id="cb28-1" title="1"><span class="op">&lt;</span>form method<span class="ot">=</span>post enctype<span class="op">=#</span>{enctype}<span class="op">&gt;</span></a>
<a class="sourceLine" id="cb28-2" title="2"> <span class="op">^</span>{articleWidget}</a>
<a class="sourceLine" id="cb28-3" title="3"> <span class="op">&lt;</span><span class="fu">div</span><span class="op">&gt;</span></a>
<a class="sourceLine" id="cb28-4" title="4"> <span class="op">&lt;</span>input <span class="kw">type</span><span class="ot">=</span>submit value<span class="ot">=</span><span class="st">&quot;Post New Article&quot;</span><span class="op">&gt;</span></a></code></pre></div>
<p>Finally we need to display an article. For this we will modify <code>Handler/Article.hs</code></p>
<div class="sourceCode" id="cb29"><pre class="sourceCode haskell"><code class="sourceCode haskell"><a class="sourceLine" id="cb29-1" title="1"><span class="ot">getArticleR ::</span> <span class="dt">ArticleId</span> <span class="ot">-&gt;</span> <span class="dt">Handler</span> <span class="dt">Html</span></a>
<a class="sourceLine" id="cb29-2" title="2">getArticleR articleId <span class="ot">=</span> <span class="kw">do</span></a>
<a class="sourceLine" id="cb29-3" title="3"> article <span class="ot">&lt;-</span> runDB <span class="op">$</span> get404 articleId</a>
<a class="sourceLine" id="cb29-4" title="4"> defaultLayout <span class="op">$</span> <span class="kw">do</span></a>
<a class="sourceLine" id="cb29-5" title="5"> setTitle <span class="op">$</span> toHtml <span class="op">$</span> articleTitle article</a>
<a class="sourceLine" id="cb29-6" title="6"> <span class="op">$</span>(widgetFile <span class="st">&quot;article&quot;</span>)</a></code></pre></div>
<p>The <code>get404</code> function tries to do a get on the DB. If it fails, it returns a 404 page. The rest should be clear. Here is the content of <code>templates/article.hamlet</code>:</p>
<div class="sourceCode" id="cb30"><pre class="sourceCode html"><code class="sourceCode html"><a class="sourceLine" id="cb30-1" title="1"><span class="kw">&lt;h1&gt;</span> #{articleTitle article}</a>
<a class="sourceLine" id="cb30-2" title="2"><span class="kw">&lt;article&gt;</span> #{articleContent article}</a>
<a class="sourceLine" id="cb30-3" title="3"><span class="kw">&lt;hr&gt;</span></a>
<a class="sourceLine" id="cb30-4" title="4"><span class="kw">&lt;a</span><span class="ot"> href=</span><span class="st">@{BlogR}</span><span class="kw">&gt;</span></a>
<a class="sourceLine" id="cb30-5" title="5"> Go to article list.</a></code></pre></div>
<p>The blog system is finished. You can jump to it by clicking <a href="http://localhost:3000/blog">here</a>.</p>
<p>Just for fun, you can try to create an article with the following content:</p>
<div class="sourceCode" id="cb31"><pre class="sourceCode html"><code class="sourceCode html"><a class="sourceLine" id="cb31-1" title="1">Cross Script:</a>
<a class="sourceLine" id="cb31-2" title="2"> <span class="kw">&lt;script&gt;</span><span class="at">alert</span>(<span class="st">&quot;You loose&quot;</span>)<span class="op">;</span><span class="kw">&lt;/script&gt;</span></a>
<a class="sourceLine" id="cb31-3" title="3"></a>
<a class="sourceLine" id="cb31-4" title="4">SQL injection: &quot;); DROP TABLE ARTICLE;;</a></code></pre></div>
<h2 id="conclusion">Conclusion</h2>
<p>This is the end of this tutorial. I made it very minimal.</p>
<p>If you already know Haskell and you want to go further, you should take a look at the recent <a href="http://yesodweb.com/blog/2012/01/blog-example">i18n blog tutorial</a>. It will be obvious I based my own tutorial on it. Youll learn in a very straightforward way how easy it is to use authorization, Time and internationalization.</p>
<p>If, on the other hand, you dont know Haskell, then you shouldnt jump directly to web programming with it. Haskell is a very complex and unusual language. My advice to go as fast as possible in using Haskell for web programming is:</p>
<ol type="1">
<li>Start by <a href="http://tryhaskell.org">trying Haskell in your browser</a></li>
<li>Read my tutorial <a href="https://www.fpcomplete.com/school/haskell-fast-hard">Learn Haskell Fast and Hard on School of Haskell</a> or directly <a href="../../../../Scratch/en/blog/Haskell-the-Hard-Way/">on this blog</a></li>
<li>Then read the excellent <a href="http://learnyouahaskell.com">Learn you a Haskell for Great Good</a></li>
<li>If you have difficulty understanding concepts like monads, you should really read <a href="http://homepages.inf.ed.ac.uk/wadler/topics/monads.html">these articles</a>. For me they were enlightening.</li>
<li>If you feel confident, you should be able to follow the <a href="http://yesodweb.com/book">Yesod book</a> but if you find it difficult to follow the Yesod book, you should read <a href="http://book.realworldhaskell.org">real world Haskell</a> first.</li>
</ol>
<p>Also, note that:</p>
<ul>
<li><a href="http://haskell.org">haskell.org</a> is full of excellent resources.</li>
<li><a href="http://www.haskell.org/hoogle/">hoogle</a> will be very useful</li>
<li>Use <a href="http://community.haskell.org/~ndm/hlint/">hlint</a> as soon as possible to get good habits.</li>
</ul>
<p>As you should see, if you dont already know Haskell, the path is long but I guarantee you it will be very rewarding!</p>
<p><em>ps:</em> You can download the source of this Yesod blog tutorial at <a href="http://github.com/yogsototh/yosog">github.com/yogsototh/yosog</a>.</p>
<section class="footnotes">
<hr />
<ol>
<li id="fn1"><p>One can argue these benchmark contains many problems. But the benchmarks are just here to give the basic idea, namely that Haskell is very fast.<a href="#fnref1" class="footnote-back"></a></p></li>
<li id="fn2"><p>Generally <em>high level</em> Haskell is slower than C, but <em>low level</em> Haskell is equivalent to C speed. It means that even if you can easily link C code with Haskell, this is not needed to reach the same speed. Furthermore writing a web service in C/C++ seems to be a very bad idea. You can take a look at a <a href="http://news.ycombinator.com/item?id=3449388">discussion on HN about this</a>.<a href="#fnref2" class="footnote-back"></a></p></li>
<li id="fn3"><p>If you are curious, you can search about <a href="http://www.unlimitednovelty.com/2011/10/nodejs-has-jumped-shark.html">the Fibonacci node.js troll</a>. Without any tweaking, <a href="http://mathias-biilmann.net/posts/2011/10/is-haskell-the-cure">Haskell handled this problem perfectly</a>. I tested it myself using Yesod instead of Snap.<a href="#fnref3" class="footnote-back"></a></p></li>
<li id="fn4"><p>By view I mean yesod widgets hamlet, lucius and julius files.<a href="#fnref4" class="footnote-back"></a></p></li>
</ol>
</section>
</div>
<div id="afterarticle">
<div id="social">
<a href="/rss.xml" target="_blank" rel="noopener noreferrer nofollow" class="social">RSS</a>
·
<a href="https://twitter.com/home?status=http%3A%2F%2Fyannesposito.com/Scratch/en/blog/Yesod-tutorial-for-newbies/%20via%20@yogsototh" target="_blank" rel="noopener noreferrer nofollow" class="social">Tweet</a>
·
<a href="http://www.facebook.com/sharer/sharer.php?u=http%3A%2F%2Fyannesposito.com/Scratch/en/blog/Yesod-tutorial-for-newbies/" target="_blank" rel="noopener noreferrer nofollow" class="social">FB</a>
<br />
<a class="message" href="../../../../Scratch/en/blog/Social-link-the-right-way/">These social sharing links preserve your privacy</a>
</div>
<div id="navigation">
<a href="../../../../">Home</a>
<span class="sep">¦</span>
<a href="../../../../Scratch/en/blog">Blog</a>
<span class="sep">¦</span>
<a href="../../../../Scratch/en/softwares">Softwares</a>
<span class="sep">¦</span>
<a href="../../../../Scratch/en/about">About</a>
</div>
<div id="totop"><a href="#header">↑ Top ↑</a></div>
<div id="bottom">
<div>
Published on 2012-01-15
</div>
<div>
<a href="https://twitter.com/yogsototh">Follow @yogsototh</a>
</div>
<div>
<a rel="license" href="http://creativecommons.org/licenses/by/3.0/deed.en_US">Yann Esposito©</a>
</div>
<div>
Done with
<a href="http://www.vim.org" target="_blank" rel="noopener noreferrer nofollow"><strike>Vim</strike></a>
<a href="http://spacemacs.org" target="_blank" rel="noopener noreferrer nofollow">spacemacs</a>
<span class="pala">&amp;</span>
<a href="http://nanoc.ws" target="_blank" rel="noopener noreferrer nofollow"><strike>nanoc</strike></a>
<a href="http://jaspervdj.be/hakyll" target="_blank" rel="noopener noreferrer nofollow">Hakyll</a>
</div>
<hr />
<div style="max-width: 100%">
<a href="https://cardanohub.org">
<img src="../../../../Scratch/img/ada-logo.png" class="simple" style="height: 16px;
border-radius: 50%;
vertical-align:middle;
display:inline-block;" />
ADA:
</a>
<code style="display:inline-block;
word-wrap:break-word;
text-align: left;
vertical-align: top;
max-width: 85%;">
DdzFFzCqrhtAvdkmATx5Fm8NPJViDy85ZBw13p4XcNzVzvQg8e3vWLXq23JQWFxPEXK6Kvhaxxe7oJt4VMYHxpA2vtCFiP8fziohN6Yp
</code>
</div>
</div>
</div>
</div>
</div>
</body>
</html>