【问题标题】:Handling regular form posts (application/x-www-form-urlencoded) with servant使用仆人处理常规表单帖子(application/x-www-form-urlencoded)
【发布时间】:2016-10-02 17:34:47
【问题描述】:

如何使用 Servant 处理常规表单 POST?特别是,给定一个 HTML 表单,如

<form action="/check" method="post">
  Solution:
  <input name="code" type="text">
  <input type="submit">
</form>

data CheckResult = Correct | Wrong

instance ToHtml CheckResult
    ...

checkCode :: Text -> Handler CheckResult
checkCode code = if code == "secret" then Correct else Wrong

如何把东西串起来?

【问题讨论】:

    标签: forms haskell post servant


    【解决方案1】:

    我只是想为最新版本的 Servant 添加一个答案,因为我必须在谷歌上搜索各种东西才能组装一个完整的、有效的表单处理版本。

    上述答案适用于早期版本的 Servant,但在升级到 Servant 0.9 时,我在使用表单时遇到了困难。

    我是这样做的。

    首先,他们从自定义表单实现切换到http-api-data 中的实现,因此您需要在您的 cabal 文件中使用它:

    some-project.cabal

      build-depends:       base >= 4.7 && < 5
                         , aeson
                         , blaze-html
                         , http-api-data
    

    接下来,您可以像上面一样声明一个表单,但您可以使用GHC.Generics 自动派生一个FromForm 实例:

    {-# LANGUAGE DeriveGeneric     #-}
    
    module Html.Contact where
    
    import           GHC.Generics
    import           Servant
    import           Web.FormUrlEncoded          (FromForm)
    
    data ContactForm = ContactForm
     { cname    :: !T.Text
     , cemail   :: !T.Text
     , cmessage :: !T.Text
     } deriving (Eq, Show, Generic)
    
    instance FromForm ContactForm
    

    之后,您可以在端点中使用来自 Servant 的常规 FormUrlEncoded ContentType:

    type ContactApi = "contact" :> ReqBody '[FormUrlEncoded] ContactForm
                                       :> Post '[HTML] Html
    

    差点忘了:如何渲染这个东西

    您可能还需要一个页面来显示您的表单?好吧,“名称”属性必须与表单中的字段匹配(这是我的做法,使用 Blaze):

    contactForm :: H.Html
    contactForm = H.section ! A.id "contact" ! A.class_ "container contact-us u-full-width u-max-full-width" $
      H.div ! A.class_ "row" $ do
        H.div ! A.class_ "eight columns contact-us-form" $
          H.form ! A.method "post" ! A.action "/contact" $ do
            H.div ! A.class_ "row" $ do
              H.div ! A.class_ "six columns" $
                H.input ! A.class_ "u-full-width" ! A.type_ "text" ! A.name "cname" ! A.placeholder "Name" ! A.id "nameInput"
              H.div ! A.class_ "six columns" $
                H.input ! A.class_ "u-full-width" ! A.type_ "text" !  A.name "cemail" ! A.placeholder "Email" ! A.id "emailInput"
            H.textarea ! A.class_ "u-full-width" ! A.name "cmessage" ! A.placeholder "Message" ! A.id "messageInput" $ ""
            H.input ! A.class_ "button u-pull-right" ! A.type_ "submit" !  A.value "Send"
    

    【讨论】:

      【解决方案2】:

      Servant 通过数据类型 FormUrlEncoded 和类 FromFormUrlEncoded(在 Servant 0.9 中重命名为 FromForm)支持这一点。

      首先我们为表单数据定义一个数据类型,然后重写我们的处理程序来接受它。

      data CheckRequest = CheckRequest { code :: Text }
      
      checkCode :: CheckRequest -> Handler CheckResult
      checkCode (CheckRequest code) = if code == "secret" then Correct else Wrong
      

      然后我们指定一个 application/x-www-form-urlencoded 类型的 POST 正文。

      type API = "check"
               :> ReqBody '[FormUrlEncoded] CheckRequest
               :> Post '[HTML] CheckResult
      

      现在只需将CheckRequest 设为FromFormUrlEncoded 的实例。

      instance FromFormUrlEncoded CheckRequest where
        --fromFormUrlEncoded :: [(Text, Text)] -> Either String CheckRequest
        fromFormUrlEncoded [("code", c)] = Right (CheckRequest c)
        fromFormUrlEncoded _             = Left "expected a single field `code`"
      

      【讨论】:

        猜你喜欢
        • 2015-06-18
        • 1970-01-01
        • 2017-09-11
        • 2014-03-30
        • 2019-02-04
        • 1970-01-01
        • 2011-11-15
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多