Sto provando a scrivere un server applicazioni utilizzando Happstack, Heist e percorsi web, ma sto avendo problemi a capire come consentire alle giunzioni di accedere a valori che non provengono da lo stack monad della mia applicazione.Utilizzo dei valori non dalla monade dell'applicazione con i modelli di Heist
ci sono due situazioni in cui questo viene up:
- parametri estratti dal sentiero URL tramite web-percorsi. Questi provengono da pattern matching su un URL di sicurezza del tipo quando si esegue il routing della richiesta al gestore corretto.
- Informazioni sulla sessione. Se la richiesta è per una sessione nuova di zecca, non posso leggere l'identificatore di sessione da un cookie nella richiesta (poiché non esiste ancora tale cookie), e non posso usare le giunzioni per creare una nuova sessione se necessario, da allora se più di uno splice tenta di farlo, finisco per creare più sessioni nuove per una singola richiesta. Ma se creo la sessione prima di entrare nella roba del web-routing, la sessione esiste al di fuori della monade dell'applicazione.
Si consideri il seguente esempio di programma che cerca di servire i seguenti URL:
- /fattoriale/n uscite il fattoriale di n
- /indietro/str uscite str indietro
Poiché il parametro viene visualizzato nel percorso URL anziché nella stringa di query, viene estratto tramite percorsi Web anziché venire dalla monade ServerPartT. Da lì, però, non esiste un modo chiaro per posizionare il parametro da qualche parte dove le giunzioni possono vederlo, poiché hanno solo accesso alla monade dell'applicazione.
La soluzione ovvia di attaccare un ReaderT qualche parte in pila monade ha due problemi:
- Having a ReaderT sopra ServerPartT nasconde le parti Happstack della pila Monade, poiché ReaderT non implementa ServerMonad, FilterMonad, ecc.
- Presuppone che tutte le pagine che sto servendo abbiano lo stesso tipo di parametro, ma in questo esempio,/factorial vuole un Int ma/reverse vuole una String. Ma per entrambi i gestori di pagine utilizzare la stessa TemplateDirectory, il ReaderT dovrebbe avere un valore dello stesso tipo.
dal dare una occhiata alla documentazione di Snap, sembra che Snap gestisce i parametri nel percorso URL copiandoli in modo efficace nella stringa di query, che elude il problema. Ma questa non è un'opzione con Happstack e le rotte del web, e inoltre, avere due modi diversi in cui un URL per specificare lo stesso valore mi sembra una cattiva idea per quanto riguarda la sicurezza.
Quindi, c'è un modo "corretto" per esporre i dati delle richieste di monade non dell'applicazione alle giunzioni, o devo abbandonare Heist e usare qualcosa come Blaze-HTML invece dove questo non è un problema? Mi sento come se mi mancasse qualcosa di ovvio, ma non riesco a capire cosa potrebbe essere.
codice Esempio:
{-# LANGUAGE TemplateHaskell #-}
import Prelude hiding ((.))
import Control.Category ((.))
import Happstack.Server (Response, ServerPartT, nullConf, ok, simpleHTTP)
import Happstack.Server.Heist (render)
import Text.Boomerang.TH (derivePrinterParsers)
import Text.Templating.Heist (Splice, bindSplices, emptyTemplateState, getParamNode)
import Text.Templating.Heist.TemplateDirectory (TemplateDirectory, newTemplateDirectory')
import Web.Routes (RouteT, Site, runRouteT)
import Web.Routes.Boomerang (Router, anyString, boomerangSite, int, lit, (<>), (</>))
import Web.Routes.Happstack (implSite)
import qualified Data.ByteString.Char8 as C
import qualified Data.Text as T
import qualified Text.XmlHtml as X
data Sitemap = Factorial Int
| Reverse String
$(derivePrinterParsers ''Sitemap)
-- Conversion between type-safe URLs and URL strings.
sitemap :: Router Sitemap
sitemap = rFactorial . (lit "factorial" </> int)
<> rReverse . (lit "reverse" </> anyString)
-- Serve a page for each type-safe URL.
route :: TemplateDirectory (RouteT Sitemap (ServerPartT IO)) -> Sitemap -> RouteT Sitemap (ServerPartT IO) Response
route templates url = case url of
Factorial _num -> render templates (C.pack "factorial") >>= ok
Reverse _str -> render templates (C.pack "reverse") >>= ok
site :: TemplateDirectory (RouteT Sitemap (ServerPartT IO)) -> Site Sitemap (ServerPartT IO Response)
site templates = boomerangSite (runRouteT $ route templates) sitemap
-- <factorial>n</factorial> --> n!
factorialSplice :: (Monad m) => Splice m
factorialSplice = do input <- getParamNode
let n = read . T.unpack $ X.nodeText input :: Int
return [X.TextNode . T.pack . show $ product [1 .. n]]
-- <reverse>text</reverse> --> reversed text
reverseSplice :: (Monad m) => Splice m
reverseSplice = do input <- getParamNode
return [X.TextNode . T.reverse $ X.nodeText input]
main :: IO()
main = do templates <- newTemplateDirectory' path . bindSplices splices $ emptyTemplateState path
simpleHTTP nullConf $ implSite "http://localhost:8000" "" $ site templates
where splices = [(T.pack "factorial", factorialSplice), (T.pack "reverse", reverseSplice)]
path = "."
fattoriale.tpl:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<title>Factorial</title>
</head>
<body>
<p>The factorial of 6 is <factorial>6</factorial>.</p>
<p>The factorial of ??? is ???.</p>
</body>
</html>
reverse.tpl:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<title>Reverse</title>
</head>
<body>
<p>The reverse of "<tt>hello world</tt>" is "<tt><reverse>hello world</reverse></tt>".</p>
<p>The reverse of "<tt>???</tt>" is "<tt>???</tt>".</p>
</body>
</html>
Ho usato variazioni happstack personalizzato sul Snap 'rendWithSplices' qui: https://github.com/aslatter/blog/blob/master/Blog/Templates.hs In questo esempio, il 'appTemplates 'funzione se di tipo' App TemplateState '. –