【问题标题】:Using monadic validation with Digestive Functors and Snap使用带有消化函子和 Snap 的单子验证
【发布时间】:2012-08-04 23:24:12
【问题描述】:

我现在尝试了很长时间来围绕如何在消化函子表单字段中使用验证,这需要访问另一个 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.Auth 的函数usernameExists 来检查输入的用户名是否唯一。

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

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 中的函数 checkMvalidateM 会帮助我吗?

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

但是没有一个显示 Snap.Snaplet.Auth 和消化仿函数直接一起工作,而且在 monad 转换器和提升方面我仍然是一个菜鸟……也许这对我来说太容易看到了。 :(

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

汉尼斯

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

【问题讨论】:

    标签: forms haskell haskell-snap-framework


    【解决方案1】:

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

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

    这告诉我们验证函数需要具有类型 (a -> m Bool)并且m 必须与表单中的m 相同。这意味着您需要将表单的类型更改为以下内容:

    studentRegistrationForm :: Form Text AppHandler StudentRegistrationData
    

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

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

    这实际上看起来很像我们需要的(a -&gt; m Bool) 类型签名。事实上,这是完全匹配的,因为 Handler b (AuthManager b) 是一个单子。但即使它完全匹配(a -&gt; m Bool) 模式并不意味着我们已经完成了。当您运行表单时,您处于 AppHandler monad 中,它可能只是 Handler App App 的类型别名,其中 App 是您的应用程序的顶级 snaplet 状态类型。所以我们需要做的是将Handler b (AuthManager b) 转换为Handler b b,这将与Handler App App 统一。 snaplet API 中的with 函数正是我们所需要的。这让我们的验证函数变得非常简单:

    validUser :: Text -> Handler App App Bool
    validUser = liftM not . with auth . usernameExists
    

    有了这个,你可以使用checkM usernameErrMsg validUser,就像你在上面的代码中使用检查一样。

    【讨论】:

    • 几乎完美:有一点逻辑错误,如果用户名存在,validUser必须返回True,但如果用户名存在,则usernameExists返回True存在,我们必须在validUser 的开头添加一个not 并将其提升到monad 中。所以它必须是validUser = liftM not . with auth . usernameExists
    猜你喜欢
    • 2012-09-27
    • 2013-04-21
    • 1970-01-01
    • 1970-01-01
    • 2017-11-08
    • 1970-01-01
    • 1970-01-01
    • 2020-01-04
    • 2023-03-27
    相关资源
    最近更新 更多