Я пытаюсь написать сервер приложений с использованием Happstack, Heist и веб-маршрутов, но не могу понять, как разрешить соединениям доступ к значениям, которые не происходят из стека монад моего приложения.
Есть две ситуации, когда это происходит:
- Параметры, извлеченные из пути URL через веб-маршруты.Это происходит из-за сопоставления с образцом в безопасном типе URL-адреса при маршрутизации запроса к соответствующему обработчику.
- Информация о сеансе.Если запрос относится к новому сеансу, я не могу прочитать идентификатор сеанса из файла cookie в запросе (поскольку такого файла cookie еще не существует), и я не могу использовать соединения для создания нового сеанса, если это необходимо,с тех пор, если несколько попыток сделать это, я создаю несколько новых сессий для одного запроса.Но если я создаю сеанс до ввода материала веб-маршрутов, этот сеанс существует вне монады приложения.
Рассмотрим следующий пример программы, которая пытается обслуживать следующие URL-адреса:
- / factorial / n выводит факториал n
- / реверс / стр вывод стр назад
Поскольку параметр отображается в пути URL-адреса, а не в строке запроса, он извлекается через веб-маршруты, а не из монады ServerPartT.Оттуда, однако, нет четкого способа поместить параметр в такое место, где сращивания могут его видеть, поскольку они имеют доступ только к монаде приложения.
Очевидное решение поместить ReaderT где-то в стек монады:две проблемы:
- Наличие ReaderT выше ServerPartT скрывает части Happstack стека монад, поскольку ReaderT не реализует ServerMonad, FilterMonad и т. д.
- Предполагается, что все страницыЯ использую параметр того же типа, но в этом примере / factorial хочет Int, а / reverse хочет String.Но для того, чтобы оба обработчика страниц использовали один и тот же TemplateDirectory, ReaderT должен иметь значение одного и того же типа.
При взгляде на документацию Snap, похоже, что Snap обрабатывает параметры в URL-адресе.путем эффективного копирования их в строку запроса, которая обходит проблему.Но это не вариант с Happstack и веб-маршрутами, и, кроме того, наличие двух разных способов указания URL-адреса для одного и того же значения кажется мне плохой идеей в плане безопасности.
Итак, есть ли "правильный "способ предоставления данных запросов, не связанных с монадой приложения, к сплайсингу, или мне нужно отказаться от Heist и использовать что-то вроде Blaze-HTML вместо этого, где это не проблема?Я чувствую, что упускаю что-то очевидное, но не могу понять, что это может быть.
Пример кода:
{-# 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>