在仆人中捕获 IO 异常

catching IO exceptions in servant

我使用 servant 作为一个简单的 JSON api,它允许您创建名称必须唯一的用户。这是由 SQLite 中的唯一约束强制执行的。我有一个函数 DB.saveUser :: UserReq -> IO Int 可以(不出所料)将用户保存到 SQLite 并 returns 生成的 id。如果该名称已被使用,它将抛出 SQLError ErrorConstraint _ _。如果发生这种情况,我想 return HTTP 响应代码 409。所以我的问题是,有什么方法可以在 Handler Monad 中捕获 SQLError 吗?如果没有,实现我正在寻找的最干净的方法是什么?我考虑过将 DB.saveUser return 设为 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

Servant 的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 响应代码。

捕获数据库异常后如何抛出 ServantErrhttps://haskell-servant.readthedocs.io/en/stable/tutorial/Server.html#failing-through-servanterr