使用Digestive Functors和Snap的monadic验证

时间:2012-05-25 19:29:07

标签: forms haskell haskell-snap-framework

我现在尝试了很长一段时间,以了解如何在消化函子表单字段中使用验证,这需要访问另一个monad。为了缩短它,我有digestive这样的形式

studentRegistrationForm :: Monad m => Form Text m StudentRegistrationData
studentRegistrationForm = StudentRegistrationData
    <$> "school"    .: choice schools Nothing
    <*> "studentId" .: check studentIdErrMsg (not . T.null) (text Nothing)
    <*> "firstName" .: check firstNameErrMsg (not . T.null) (text Nothing)
    <*> "lastName"  .: check lastNameErrMsg  (not . T.null) (text Nothing)
    <*> "email"     .: check emailErrMsg (E.isValid . T.unpack) (text Nothing)

(studentId基本上是用户名)

并希望使用Snap.Snaplet.AuthusernameExists函数来检查输入的用户名是否唯一。

为了完整起见,这里是相应的数据类型:

data StudentRegistrationData = StudentRegistrationData
  { school    :: School  -- ^ school the student is enroled
  , studentId :: Text    -- ^ matriculation number of the student
  , firstName :: Text    -- ^ first name of the student
  , lastName  :: Text    -- ^ last name of the student
  , email     :: Text    -- ^ email for sending password
  } deriving (Show)

我在一个处理程序中创建我的表单,如:

studentRegistrationHandler :: AppHandler ()
studentRegistrationHandler = do
    (view, registrationData) <- runForm "form" SRF.studentRegistrationForm
    maybe (showForm "registration" view) createUser registrationData

showForm :: String -> View Text -> AppHandler ()
showForm name view =
    heistLocal (bindDigestiveSplices view) $ render template
  where
    template = BS.pack $ "student-" ++ name ++ "-form"

所以我现在遇到的问题是了解如何访问表单中的Auth snaplet的状态。它已经通过了还是我必须自己通过? Text.Digestive.Form中的checkM函数validateM会帮助我吗?

我找到了几个如何使用消化函子和snap auth和session的例子,如:

但是没有一个显示Snap.Snaplet.Auth和消化函子直接一起工作,而且当涉及到monad变换器和提升时,我仍然是一个菜鸟......也许这对我来说太容易了。 :(

我可以在github上传一个独立的例子,如果它有助于说明它,它会显示我的问题。任何提示,指示和建议都非常欢迎! :)

汉纳斯

add on:我创建了一个演示基本身份验证功能的示例应用程序,您可以在这里查看:digestive-functors-snap-auth-example享受!

1 个答案:

答案 0 :(得分:2)

我还没有尝试过,看看是否所有类型检查,但这是一般的想法。您是否正确使用checkM或validateM进行monadic验证。 checkM的类型签名是提供信息的:

checkM :: Monad m => v -> (a -> m Bool) -> Form v m a -> Form v m a

这告诉我们验证函数需要具有类型(a - &gt; m Bool) m必须与m中的studentRegistrationForm :: Form Text AppHandler StudentRegistrationData 相同表格。这意味着您需要将表单类型更改为以下内容:

usernameExists :: Text -> Handler b (AuthManager b) Bool

现在让我们编写验证器。由于我们计划在验证器中使用usernameExists函数,因此我们需要查看该类型签名:

(a -> m Bool)

这实际上看起来很像我们需要的Handler b (AuthManager b)类型签名。事实上,它是完全匹配的,因为(a -> m Bool)是一个monad。但即使它与Handler App App模式匹配也并不意味着我们已经完成了。当你运行表单时,你在AppHandler monad中,它可能只是Handler b (AuthManager b)的类型别名,其中App是你的应用程序的顶级snaplet状态类型。因此,我们需要做的是将Handler b b转换为Handler App App,这将与validUser :: Text -> Handler App App Bool validUser = liftM not . with auth . usernameExists 统一。 snaplet API中的with函数正是我们所需要的。这使我们的验证功能非常简单:

checkM usernameErrMsg validUser

使用此功能,您可以使用{{1}},就像使用上面的代码一样。