首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用Heist模板使用不来自应用程序monad的值

用Heist模板使用不来自应用程序monad的值
EN

Stack Overflow用户
提问于 2011-11-05 20:52:03
回答 1查看 483关注 0票数 7

我正在尝试使用幸福堆栈、Heist和web路由编写一个应用服务器,但是我很难弄清楚如何让splices访问不是来自我的应用程序的monad堆栈的值。

有两种情况会出现这种情况:

  • 通过网络路径从URL路径中提取参数。当将请求路由到适当的处理程序时,它们来自于类型安全URL上的模式匹配。
  • 会话信息。如果请求是针对一个全新的会话,我无法从请求中的cookie读取会话标识符(因为还没有这样的cookie ),而且如果需要的话,我不能使用剪接来创建一个新会话,因为如果有多个剪接尝试这样做,我最终会为单个请求创建多个新会话。但是,如果我在输入web路由内容之前创建会话,则会话存在于应用程序monad之外。

考虑下面的示例程序,它试图提供以下URL:

  • /阶乘/n输出n的阶乘
  • /反向/ str输出向后输出

由于参数出现在URL路径中而不是查询字符串中,所以它是通过web路由提取的,而不是从ServerPartT单机中提取的。但是,在那里,没有明确的方法可以将参数放在剪接器可以看到的地方,因为它们只能访问应用程序monad。

在monad堆栈上添加一个ReaderT的明显解决方案有两个问题:

  • 拥有高于ReaderT的ServerPartT隐藏了monad堆栈的幸福堆栈部分,因为ReaderT没有实现ServerMonad、FilterMonad等。
  • 它假设我所服务的所有页面都采用相同类型的参数,但在本例中,/factorial需要一个Int,而/reverse需要一个字符串。但是,如果两个页面处理程序都要使用相同的TemplateDirectory,则ReaderT需要携带相同类型的值。

从Snap文档中可以看出,Snap处理URL路径中的参数有效地将它们复制到查询字符串中,从而避免了问题。但是,这不是一个与幸福堆栈和网络路线的选择,此外,有两种不同的方式为一个URL指定相同的值,我觉得是一个糟糕的想法,安全方面。

那么,是否有一种“适当”的方法来将非应用程序单点请求数据暴露给碎片,或者我是否需要放弃Heist,使用类似Blaze-HTML之类的东西,而不是在没有问题的地方使用呢?我觉得我错过了一些显而易见的东西,但我不知道它可能是什么。

示例代码:

代码语言:javascript
复制
{-# 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:

代码语言:javascript
复制
<!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:

代码语言:javascript
复制
<!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>
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2011-11-07 17:36:30

考虑以下形式的函数:

代码语言:javascript
复制
func :: a -> m b

由于Haskell是纯的,并且具有强大的静态类型系统,因此此函数中使用的数据只能来自三个位置:范围内或导入的全局符号、参数( 'a')和单一上下文'm‘。所以你描述的问题并不是海斯特独有的,而是使用Haskell的事实。

这就提出了几种解决问题的方法。一种是将所需的数据作为参数传递给剪接函数。就像这样:

代码语言:javascript
复制
factorialSplice :: Int -> TemplateMonad (RouteT Sitemap (ServerPartT IO)) [X.Node]
factorialSplice n = return [X.TextNode . T.pack . show $ product [1 .. n]]

在Snap中,我们有一个名为renderWithSplices的函数,它允许您在呈现模板之前绑定一些连接。您可以使用这样的函数来绑定当前有“呈现模板”的行上的正确拼接。

第二种方法是使用底层的monad。您会说“没有明确的方法将参数放在剪接器可以看到的地方,因为它们只能访问应用程序monad。”在我看来,访问“应用程序monad”正是您需要在剪接中获取这些内容所需要的。所以我的第二个建议是使用它。如果您正在使用的应用程序monad没有这些数据,那么它是该单板的缺陷,而不是Heist问题。

正如您在上面的类型签名中所看到的那样,TemplateMonad是底层的monad转换器(RouteT Sitemap (ServerPartT IO))。这样就可以通过简单的提升来连接底层单机中的所有内容。我从来没有使用过网络路由,但在我看来,应该有一个RouteT函数来获取站点地图。让我们假设存在以下函数:

代码语言:javascript
复制
getUrlData :: RouteT url m url

那么你应该能够写:

代码语言:javascript
复制
factorialSplice :: TemplateMonad (RouteT Sitemap (ServerPartT IO)) [X.Node]
factorialSplice = do
    url <- lift getUrlData
    return $ case url of
      Factorial n -> [X.TextNode . T.pack . show $ product [1 .. n]]
      _ -> []

或者将其概括一下,您可以这样做:

代码语言:javascript
复制
factorialArgSplice :: TemplateMonad (RouteT Sitemap (ServerPartT IO)) [X.Node]
factorialArgSplice = do
    url <- lift getUrlData
    return $ case url of
      Factorial n -> [X.TextNode . T.pack . show $ n]
      _ -> []

然后您可以将其绑定到标记中,并在模板中执行以下操作。

代码语言:javascript
复制
<p>The factorial of <factorialArg> is <factorial><factorialArg/></factorial>.</p>
票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/8023191

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档