我将servant
用于简单的JSON api,它使您可以创建名称必须唯一的用户。这是由SQLite中的唯一约束强制执行的。我有一个函数DB.saveUser :: UserReq -> IO Int
(毫不奇怪)将用户保存到SQLite并返回生成的ID。如果名称已被使用,它将抛出SQLError ErrorConstraint _ _
。如果发生这种情况,我想返回HTTP响应代码409。所以我的问题是,有没有办法在Handler Monad中捕获SQLError
?如果没有,实现我所寻找的最干净的方法是什么?我曾考虑过让DB.saveUser
返回Maybe Int
,但我认为必须有一个更好的解决方案。
createUser :: UserReq -> Handler (Headers '[Header "Location" Text] NoContent)
createUser ur = do id <- liftIO (DB.saveUser ur)
return . addHeader (T.pack ("/user/" ++ show id)) $ NoContent
saveUser (UR.UserReq name) = withConnection database $ \conn ->
do executeNamed conn "INSERT INTO users (name) VALUES (:name)" [":name" := name]
fromIntegral <$> lastInsertRowId conn
答案 0 :(得分:2)
仆人的Handler
是新型包装器:
newtype Handler a = Handler { runHandler' :: ExceptT ServantErr IO a }
内部有一个ExceptT ServantErr
,其中ServantErr
是:
data ServantErr = ServantErr { errHTTPCode :: Int
, errReasonPhrase :: String
, errBody :: LBS.ByteString
, errHeaders :: [HTTP.Header]
} deriving (Show, Eq, Read, Typeable)
因此,您可以在try
中运行数据库操作,或者处理bracket
的释放资源,并将数据库异常映射到ServantErr
以获得您的HTTP响应代码想要。
如何在捕获数据库异常后抛出ServantErr
:https://haskell-servant.readthedocs.io/en/stable/tutorial/Server.html#failing-through-servanterr