When using servant, I\'d like to return all errors as JSON. Currently, if a request fails to parse, I see an error message like this, returned as plain text
Taking inspiration from @codedmart I also use a middleware, but it does not construct the json, it only changes the content type of the response when there is an error, and keep the original error message.
startApp :: IO ()
startApp = run 8081 . (modifyResponse errorHeadersToJson) $ serve api server
errorHeadersToJson :: Response -> Response
errorHeadersToJson r
| responseStatus r == status200 = r
| otherwise = mapResponseHeaders text2json r
text2json :: ResponseHeaders -> ResponseHeaders
text2json h = Map.assocs (Map.fromList [("Content-Type", "application/json")] `Map.union` Map.fromList h)
The json is built beforehand with a function overriding the Servant throwError function.
data ServerError = ServerError
{ statusCode :: Int
, error :: String
, message :: String
} deriving (Eq, Show)
$(deriveJSON defaultOptions ''ServerError)
throwJsonError :: ServantErr -> String -> Servant.Handler b
throwJsonError err "" = throwError $ err { errBody = encode $ ServerError (errHTTPCode err) ("Server error"::String) (show $ errBody err) }
throwJsonError err message = throwError $ err { errBody = encode $ ServerError (errHTTPCode err) ("Server error"::String) message }
then I can throw any error with a custom message, it will be served as a json with the correct content-type :
throwJsonError err500 "Oh no !"