Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JSON mode #227

Open
ad-si opened this issue Feb 25, 2019 · 6 comments
Open

JSON mode #227

ad-si opened this issue Feb 25, 2019 · 6 comments
Labels
info needed More information is needed new feature R&D

Comments

@ad-si
Copy link

ad-si commented Feb 25, 2019

In the default configuration Scotty returns always HTML. E.g.

Content-Type: text/html; charset=utf-8
Date: Mon, 25 Feb 2019 14:12:54 GMT
Server: Warp/3.2.26
Transfer-Encoding: chunked

<h1>500 Internal Server Error</h1>

There should be an option to run Scotty in a JSON mode where all default responses are JSON. E.g.:

Content-Type: application/json; charset=utf-8
Date: Mon, 25 Feb 2019 14:19:36 GMT
Server: Warp/3.2.26
Transfer-Encoding: chunked

{
    "status": 500
    "description": "There was an error while accessing the database"
}
@ip1981
Copy link

ip1981 commented Jul 25, 2019

A custom handler should do that. IOW, there is an option.

@magthe
Copy link

magthe commented Dec 23, 2020

AFAICS a custom handler will lose the raised status, e.g. when lacking a body jsonData will result in a 400, but when installing a custom handler I get a 500.

@ocramz

This comment was marked as outdated.

@ocramz
Copy link
Collaborator

ocramz commented Dec 17, 2023

#121 is also relevant for this

@ocramz
Copy link
Collaborator

ocramz commented Dec 28, 2023

@ip1981 @magthe In our current design (as of 0.21 let's say :

scotty/Web/Scotty/Action.hs

Lines 113 to 176 in 66d60f7

ok <- flip runReaderT env $ runAM $ tryNext $ action `catches` concat
[ [actionErrorHandler]
, maybeToList mh
, [statusErrorHandler, scottyExceptionHandler, someExceptionHandler]
]
res <- getResponse env
return $ bool Nothing (Just $ mkResponse res) ok
-- | Catches 'StatusError' and produces an appropriate HTTP response.
statusErrorHandler :: MonadIO m => ErrorHandler m
statusErrorHandler = Handler $ \case
StatusError s e -> do
status s
let code = T.pack $ show $ statusCode s
let msg = decodeUtf8Lenient $ statusMessage s
html $ mconcat ["<h1>", code, " ", msg, "</h1>", e]
-- | Exception handler in charge of 'ActionError'. Rethrowing 'Next' here is caught by 'tryNext'.
-- All other cases of 'ActionError' are converted to HTTP responses.
actionErrorHandler :: MonadIO m => ErrorHandler m
actionErrorHandler = Handler $ \case
AERedirect url -> do
status status302
setHeader "Location" url
AENext -> next
AEFinish -> return ()
-- | Default handler for exceptions from scotty
scottyExceptionHandler :: MonadIO m => ErrorHandler m
scottyExceptionHandler = Handler $ \case
RequestTooLarge -> do
status status413
text "Request body is too large"
MalformedJSON bs err -> do
status status400
raw $ BL.unlines
[ "jsonData: malformed"
, "Body: " <> bs
, "Error: " <> BL.fromStrict (encodeUtf8 err)
]
FailedToParseJSON bs err -> do
status status422
raw $ BL.unlines
[ "jsonData: failed to parse"
, "Body: " <> bs
, "Error: " <> BL.fromStrict (encodeUtf8 err)
]
PathParameterNotFound k -> do
status status500
text $ T.unwords [ "Path parameter", k, "not found"]
QueryParameterNotFound k -> do
status status400
text $ T.unwords [ "Query parameter", k, "not found"]
FormFieldNotFound k -> do
status status400
text $ T.unwords [ "Query parameter", k, "not found"]
FailedToParseParameter k v e -> do
status status400
text $ T.unwords [ "Failed to parse parameter", k, v, ":", e]
-- | Uncaught exceptions turn into HTTP 500 Server Error codes
someExceptionHandler :: MonadIO m => ErrorHandler m
someExceptionHandler = Handler $ \case
(_ :: E.SomeException) -> status status500
) we have a cascade of handlers, which is where the exception to HTTP status translation takes place.

In turn runAction is called within addroute

addroute method pat action = ScottyT $ MS.modify $ \s -> addRoute (route (routeOptions s) (handler s) (Just method) pat action) s
, which is where we could configure the output rendering (JSON, HTML etc.)

@ocramz ocramz added R&D info needed More information is needed and removed easy fix labels Mar 9, 2024
@ocramz
Copy link
Collaborator

ocramz commented Mar 9, 2024

I've just realized that currently our exception handling and server response generation are very coupled.

E.g. if a user specifies a custom handler, should they have to re-implement the response generation part?

@ocramz ocramz added this to the Improved runtime Options milestone Mar 10, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
info needed More information is needed new feature R&D
Projects
None yet
Development

No branches or pull requests

4 participants