haskellhaskell-spockhaskell-lucid

How to serve static files using Spock and Lucid?


Getting started with web development using Haskell, Spock, and Lucid, I can't figure out how to serve my static files. In the directory of Main.hs I have /static/css/main.css, which contains just a background colour to see if the css is indeed applied. So my directory tree looks like

app
├── Main.hs
└── static
    └── css
        └── main.css

However, with the below configuration, the main.css file is not found (it contains a 404 when inspecting it with Firefox). Other than that, the site shows fine.

I have tried to mimick the funblog example when it comes to serving these files (with Wai), altered for Lucid instead of Blaze. In particular the middleware $ staticPolicy (addBase "static") from Web/Blog.hs and the line where we link the css from Web/Views/Site.hs.

module Main where    

import           Network.Wai.Middleware.Static
import           Lucid
import           Web.Spock
import           Web.Spock.Config
import           Web.Spock.Lucid         (lucid)

type Server a = SpockM () () () a

main :: IO ()
main = do
  cfg <- defaultSpockCfg () PCNoDatabase ()
  runSpock 8080 (spock cfg app)

app :: Server ()
app = do
  middleware $ staticPolicy (addBase "static")
  get root $ do
    lucid $ do
        head_ $ link_ [ rel_ "stylesheet"
                      , type_ "text/css"
                      , href_ "/css/main.css" 
                      ]
        body_ $ h1_ "Hello."


Edit/Addition Translating the Lucid to Blaze gives the same result (translation as below), so I must be missing something somewhere else.

-- Additional imports
import           Text.Blaze.XHtml5             ((!))
import qualified Text.Blaze.XHtml5             as H
import qualified Text.Blaze.XHtml5.Attributes  as A
import           Control.Monad.Trans           (MonadIO)
import           Text.Blaze.Html.Renderer.Utf8 (renderHtml)

-- The main function did not change. 

blaze :: MonadIO m => H.Html -> ActionCtxT ctx m a
blaze = lazyBytes . renderHtml

app :: Server ()
app = do
  middleware $ staticPolicy (addBase "static")
  get root $
    blaze $ do 
      H.head $
        H.link ! A.href "/css/main.css" ! A.rel "stylesheet"
      H.body $
        H.h1 "Hello Blaze."

Solution

  • Make sure your static folder is in the root directory of your project, and not of your main file, i.e., the static folder should live next to (and not in) your app and/or src folder.

    In this case that would be

    ├── app
    │   └── Main.hs
    ├── static
    │   ├── css
    │   │   └── main.css