Estoy intentando escribir un servidor de aplicaciones usando Happstack, Heist y rutas web, pero tengo problemas para descubrir cómo permitir que los empalmes accedan a valores que no se originan en la pila de mónadas de mi aplicación.Uso de valores que no provienen de la mónada de aplicación con plantillas de Heist
Hay dos situaciones en las que no sabe cómo proseguir:
- parámetros extraídos de la ruta URL desde la web-rutas. Estos provienen de la coincidencia de patrones en una URL segura para tipos al enrutar la solicitud al controlador adecuado.
- Información de la sesión. Si la solicitud es para una sesión completamente nueva, no puedo leer el identificador de sesión de una cookie en la solicitud (ya que aún no existe esa cookie), y no puedo usar los empalmes para crear una nueva sesión si es necesario, desde entonces, si más de un empalme intenta hacerlo, termino creando múltiples sesiones nuevas para una sola solicitud. Pero si creo la sesión antes de ingresar a las rutas web, la sesión existe fuera de la mónada de la aplicación.
Consideremos el siguiente programa de ejemplo que trata de servir a las siguientes direcciones URL:
- /factorial/n salidas el factorial de n
- /atrás/str salidas str hacia atrás
Dado que el parámetro aparece en la ruta de la URL en lugar de la cadena de consulta, se extrae a través de rutas web en lugar de proceder de la mónada ServerPartT. A partir de ahí, sin embargo, no hay una forma clara de colocar el parámetro en algún lugar donde los empalmes puedan verlo, ya que solo tienen acceso a la mónada de la aplicación.
La solución obvia de pegarse un ReaderT algún lugar de la pila mónada tiene dos problemas:
- Tener un ReaderT anterior ServerPartT oculta las partes Happstack de la pila mónada, ya ReaderT no implementa ServerMonad, FilterMonad, etc.
- Supone que todas las páginas que estoy publicando toman el mismo tipo de parámetro, pero en este ejemplo,/factorial quiere un Int pero/reverse quiere una Cadena. Pero para que ambos manejadores de página utilicen el mismo TemplateDirectory, el ReaderT necesitaría tener un valor del mismo tipo.
Al echar un vistazo a la documentación de Snap, parece que Snap maneja los parámetros en la ruta de URL al copiarlos efectivamente en la cadena de consulta, lo que elude el problema. Pero esa no es una opción con Happstack y las rutas web, y además, tener dos maneras diferentes para que una URL especifique el mismo valor me parece una mala idea para la seguridad.
Entonces, ¿existe una forma "adecuada" de exponer los datos de solicitud de mónada de aplicación a los empalmes, o tengo que abandonar Heist y usar algo como Blaze-HTML en su lugar, donde esto no es un problema? Siento que me falta algo obvio, pero no puedo entender qué podría ser.
código Ejemplo:
{-# 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 = "."
factorial.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>
He usado variaciones a medida happstack con el snap '' rendWithSplices aquí: https://github.com/aslatter/blog/blob/master/Blog/Templates.hs En este ejemplo, los 'appTemplates 'function if of type' App TemplateState '. –