Avoiding IO Errors When Talking to a Database Inside a WAI Handler

I am writing a web service in haskell using warp, wai and acid-state. At the moment, I have two handler functions that require interaction with the database, the last of which causes me problems.

The first is registration:

registerUser :: AcidState UserDatabase -> Maybe (Map.Map String String) -> Response
registerUser db maybeUserMap =
  case maybeUserMap of
    (Just u) -> let _ = fmap (\id -> update db (StoreUser (toString id) u)) (nextRandom)
                in resPlain status200 "User Created."
    Nothing  -> resPlain status401 "Invalid user JSON."

As you can see, I manage to avoid IOgetting the response infected by updating to let _ = ...

In the login function (which currently only returns the user card), I cannot escape IObecause I need to send a response in response:

loginUser :: AcidState UserDatabase -> String -> Response
loginUser db username = do
  maybeUserMap <- (query db (FetchUser username))
  case maybeUserMap of
    (Just u) -> resJSON u
    Nothing  -> resPlain status401 "Invalid username."

This results in the following error:

src/Main.hs:40:3:
    Couldn't match type ‘IO b0’ with ‘Response’
    Expected type: IO (EventResult FetchUser)
                   -> (EventResult FetchUser -> IO b0) -> Response
      Actual type: IO (EventResult FetchUser)
                   -> (EventResult FetchUser -> IO b0) -> IO b0
    In a stmt of a 'do' block:
      maybeUserMap <- (query db (FetchUser username))
    In the expression:
      do { maybeUserMap <- (query db (FetchUser username));
           case maybeUserMap of {
             (Just u) -> resJSON u
             Nothing -> resPlain status401 "Invalid username." } }
    In an equation for ‘loginUser’:
        loginUser db username
          = do { maybeUserMap <- (query db (FetchUser username));
                 case maybeUserMap of {
                   (Just u) -> resJSON u
                   Nothing -> resPlain status401 "Invalid username." } }

src/Main.hs:42:17:
    Couldn't match expected type ‘IO b0’ with actual type ‘Response’
    In the expression: resJSON u
    In a case alternative: (Just u) -> resJSON u

src/Main.hs:43:17:
    Couldn't match expected type ‘IO b0’ with actual type ‘Response’
    In the expression: resPlain status401 "Invalid username."
    In a case alternative:
        Nothing -> resPlain status401 "Invalid username."

, db, IO. , Response IO Response, , Response, IO Response.

registerUser :

registerUser :: AcidState UserDatabase -> Maybe (Map.Map String String) -> Response
registerUser db maybeUserMap =
  case maybeUserMap of
    (Just u) -> do uuid <- (nextRandom)
                   update db (StoreUser (toString uuid) u)
                   resPlain status200 (toString uuid)
    Nothing  -> resPlain status401 "Invalid user JSON."

.

, , registerUser loginUser:

authRoutes :: AcidState UserDatabase -> Request -> [Text.Text] -> String -> Response
authRoutes db request path body =
  case path of
    ("register":rest) -> registerUser db (decode (LB.pack body) :: Maybe (Map.Map String String))
    ("login":rest) -> loginUser db body
    ("access":rest) -> resPlain status404 "Not implemented."
    _ -> resPlain status404 "Not Found."

-?

+4
2

, , - Haskell. warp, wai acid-state. , .

, , , , IO, , IO. IO, . . GHCI :

> let myStrangeId x = let _ = print "Haskell is fun!" in x

:

>:t myStrangeId
myStrangeId :: a -> a

:

> myStrangeId "Hello"
"Hello"

, , . , let, , . registerUser.

, , , IO, IO . , , , IO, . haskell, IO .

Application Wai, , :

type Application = Request -> IO Response

, , . , Response IO .

, authRoutes. :

authRoutes :: AcidState UserDatabase -> Request -> [Text.Text] -> String -> Response

, , Response IO Response:

authRoutes :: AcidState UserDatabase -> Request -> [Text.Text] -> String -> IO Response

- IO . IO , return :: a -> IO a . , return = . , loginUser registerUser IO Response, . :

authRoutes :: AcidState UserDatabase -> Request -> [Text.Text] -> String -> IO Response
authRoutes db request path body =
  case path of
    ("register":rest) -> registerUser db (decode (LB.pack body) :: Maybe (Map.Map String String))
    ("login":rest)    -> loginUser db body
    ("access":rest)   -> return $ resPlain status404 "Not implemented."
    _                 -> return $ resPlain status404 "Not Found."

, return resPlain, IO.

registerUser. , . , nextRandom , : nextRandom :: IO something, :

registerUser :: AcidState UserDatabase -> Maybe (Map.Map String String) -> IO Response
registerUser db maybeUserMap =
  case maybeUserMap of
    (Just u) -> do
        uuid <- nextRandom 
        update db (StoreUser (toString uuid) u)
        return $ resPlain status200 (toString uuid)
    Nothing  -> return $ resPlain status401 "Invalid user JSON."

loginUser :

loginUser :: AcidState UserDatabase -> String -> IO Response
loginUser db username = do
  maybeUserMap <- query db (FetchUser username)
  case maybeUserMap of
    (Just u) -> return $ resJSON u
    Nothing  -> return $ resPlain status401 "Invalid username."

, , IO , IO. -IO IO. IO , . IO , , return . , loginUser IO, , .

Edit:

, , Wai :

type Application = Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived

, .

IO Response , :

myApp :: Application
myApp request respond = do
    response <- authRoutes db request path body
    respond response
+5

, IO (* → ) value(). "" . unsafePerformIO. ( "" ). IO , liftIO -.

-1

Source: https://habr.com/ru/post/1583893/


All Articles