3

我正在为使用 WAI 启动和运行 API 的基础知识而苦苦挣扎。主要问题是处理IO感染一切。我相信一旦我更好地理解 Monads,我的问题就会解决,但希望这个问题的答案将是一个很好的起点。

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

我尝试使用fmap提取Bytestring然后unpack将其转换为字符串以进行查找,但无论我做什么,我最终都会追逐与该死的IOmonad 相关的类型错误。

无论如何,这是相关的代码:

{-# 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))’

我可以轻松地更改和的类型,getUserInfomyData最终IO Bytestring -> IO (Map.Map String String)IO (Map.Map String String) -> Response出现更多类型错误。类型让我头晕目眩。

4

1 回答 1

2

由于requestBody具有以下类型:

requestBody :: Request -> IO ByteString

结果表达式不能直接传递给getUserInfo接受 a 的ByteString.

您可以做的是,鉴于这Application很简单Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived,并且app您在monad 中,使用如下符号IO提取:ByteStringdo

str <- requestBody request 

然后传递strgetUserInfo,像这样:

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 的信息。

于 2015-04-19T10:45:46.917 回答