在 WAI 中使用请求参数不会 "IO" 导致问题
Using Request Parameters in WAI Without "IO" Causing Problems
我正在为使用 WAI 获取 API 和 运行 的基础知识而苦苦挣扎。主要问题是处理 IO
感染一切。我相信一旦我更好地理解 Monads,我的问题就会解决,但希望这个问题的答案将是一个很好的起点。
以下是一个简短的示例,它在根目录 url 上提供静态 html 页面,并接受用户名 /api/my-data
的请求 return对应用户的数据。我无法弄清楚如何使用请求的 body
IO Bytestring
进行地图查找、检索数据并将结果发送回编码为 json.
我尝试使用 fmap
提取 Bytestring
然后 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
的类型更改为 IO Bytestring -> IO (Map.Map String String)
和 IO (Map.Map String String) -> Response
,但我最终会遇到更多类型错误。类型让我头晕目眩。
因为 requestBody
具有以下类型:
requestBody :: Request -> IO ByteString
生成的表达式不能直接传递给接受 ByteString
的 getUserInfo
。
你可以做的是,假设 Application
只是 Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived
并且在 app
中你在 IO
monad 中,提取 ByteString
使用 do
这样的表示法:
str <- requestBody request
然后将str
传给getUserInfo
,像这样:
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 之前,您绝对应该阅读更多关于 monads 和 IO 的一般知识。
我正在为使用 WAI 获取 API 和 运行 的基础知识而苦苦挣扎。主要问题是处理 IO
感染一切。我相信一旦我更好地理解 Monads,我的问题就会解决,但希望这个问题的答案将是一个很好的起点。
以下是一个简短的示例,它在根目录 url 上提供静态 html 页面,并接受用户名 /api/my-data
的请求 return对应用户的数据。我无法弄清楚如何使用请求的 body
IO Bytestring
进行地图查找、检索数据并将结果发送回编码为 json.
我尝试使用 fmap
提取 Bytestring
然后 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
的类型更改为 IO Bytestring -> IO (Map.Map String String)
和 IO (Map.Map String String) -> Response
,但我最终会遇到更多类型错误。类型让我头晕目眩。
因为 requestBody
具有以下类型:
requestBody :: Request -> IO ByteString
生成的表达式不能直接传递给接受 ByteString
的 getUserInfo
。
你可以做的是,假设 Application
只是 Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived
并且在 app
中你在 IO
monad 中,提取 ByteString
使用 do
这样的表示法:
str <- requestBody request
然后将str
传给getUserInfo
,像这样:
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 之前,您绝对应该阅读更多关于 monads 和 IO 的一般知识。