Haskell 在WAI中使用请求参数,但不使用;IO";引起问题

Haskell 在WAI中使用请求参数,但不使用;IO";引起问题,haskell,web-applications,haskell-wai,Haskell,Web Applications,Haskell Wai,我正在努力学习如何使用WAI建立和运行API。主要问题是处理感染一切的IO。我相信,一旦我更好地理解单子,我的问题就会解决,但希望这个问题的答案将是一个很好的起点 下面是一个简短的示例,它在根url上提供一个静态html页面,并接受一个用户名为to/api/my data的请求,该请求应返回相应的用户数据。我不知道如何使用请求的body体的IO Bytestring执行映射查找、检索数据并将结果以json编码发送回 我曾尝试使用fmap通过testring提取,然后unpack将其转换为字符串进

我正在努力学习如何使用WAI建立和运行API。主要问题是处理感染一切的
IO
。我相信,一旦我更好地理解单子,我的问题就会解决,但希望这个问题的答案将是一个很好的起点

下面是一个简短的示例,它在根url上提供一个静态html页面,并接受一个用户名为to
/api/my data
的请求,该请求应返回相应的用户数据。我不知道如何使用请求的
body
IO Bytestring
执行映射查找、检索数据并将结果以json编码发送回

我曾尝试使用
fmap
通过testring
提取
,然后
unpack
将其转换为字符串进行查找,但无论我做什么,我最终都会跟踪与该死的
IO
monad相关的类型错误

无论如何,以下是相关代码:

{-# LANGUAGE OverloadedStrings #-}
import qualified Data.ByteString as B
import qualified Data.ByteString.Char8 as B8
import qualified Data.Map as Map
import Data.Aeson
import Network.Wai
import Network.Wai.Parse
import Network.Wai.Middleware.Static
import Network.HTTP.Types
import Network.Wai.Handler.Warp (run)

userInfo :: Map.Map String (Map.Map String String)
userInfo = Map.fromList [("jsmith", Map.fromList [("firstName", "John"),
                                                  ("lastName", "Smith"),
                                                  ("email", "jsmith@gmail.com"),
                                                  ("password", "Testing012")]),
                         ("jeff.walker", Map.fromList [("firstName", "Jeff"),
                                                       ("lastName", "Walker"),
                                                       ("email", "jeff.walker@gmail.com"),
                                                       ("password", "Testing012")])]

getUserInfo :: B.ByteString -> Map.Map String String
getUserInfo body =
  case Map.lookup (B8.unpack body) userInfo of
    (Just x) -> x
    Nothing  -> Map.empty

app :: Application
app request respond = do
  case rawPathInfo request of
    "/"            -> respond index
    "/api/my-data" -> respond $ myData (getUserInfo (requestBody request))
    _              -> respond notFound

index :: Response
index = responseFile
  status200
  [("Content-Type", "text/html")]
  "../client/index.html"
  Nothing

myData :: IO (Map.Map String String) -> Response
myData user = responseLBS
  status200
  [("Content-Type", "application/json")]
  (encode user)


notFound :: Response
notFound = responseLBS
  status404
  [("Content-Type", "text/plain")]
  "404 - Not Found"

main :: IO ()
main = do
  putStrLn $ "http://localhost:8080/"
  run 8080 $ staticPolicy (addBase "../client/") $ app
这将导致以下错误:

src/Core/Main.hs:32:54:
    Couldn't match expected type ‘B8.ByteString’
                with actual type ‘IO B8.ByteString’
    In the first argument of ‘getUserInfo’, namely
      ‘(requestBody request)’
    In the first argument of ‘myData’, namely
      ‘(getUserInfo (requestBody request))’

我可以很容易地将
getUserInfo
myData
的类型更改为
iobytestring->IO(Map.Map String)
IO(Map.Map String)->Response
,但我最终会遇到更多的类型错误。类型让我头晕目眩。

因为
requestBody
有以下类型:

requestBody :: Request -> IO ByteString
结果表达式不能直接传递给
getUserInfo
,它接受
ByteString

您可以做的是,假设
应用程序
只是
请求->(响应->IO响应已接收)->IO响应已接收
,并且在
应用程序
中,您处于
IO
单子中,使用
do
符号提取
ByteString
,如下所示:

str <- requestBody request 
app :: Application
app request respond = do
  str <- requestBody request
  case rawPathInfo request of
    "/"            -> respond index
    "/api/my-data" -> respond $ myData (getUserInfo str)
    _              -> respond notFound
此时,
myData
可以简单地接受一个
Map

myData :: Map.Map String String -> Response

不过,在深入了解WAI之前,您肯定应该阅读更多有关Monad和IO的信息。

应用程序
只是
请求->(响应->IO响应已接收)->IO响应已接收
。这意味着在
app request respond=..
中,您可以执行IO操作,生成bytestring,然后将该bytestring传递给代码其余部分中的纯函数。将
respond$myData(getUserInfo(requestBody请求))
更改为类似
do的内容{x Response
requestBody
后来被弃用,取而代之的是一个更准确的名称:
getRequestBodyChunk
;因为这是
requestBody
一直在做的事情:返回请求体的一个块,而不是全部。因此,我认为您在这里发布的解决方案行不通。