-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Haskell web framework inspired by Ruby's Sinatra, using WAI and Warp -- -- A Haskell web framework inspired by Ruby's Sinatra, using WAI and -- Warp. -- --
-- {-# LANGUAGE OverloadedStrings #-}
--
-- import Web.Scotty
--
-- main = scotty 3000 $
-- get "/:word" $ do
-- beam <- pathParam "word"
-- html $ mconcat ["<h1>Scotty, ", beam, " me up!</h1>"]
--
--
-- Scotty is the cheap and cheerful way to write RESTful, declarative web
-- applications.
--
--
-- {-# LANGUAGE OverloadedStrings #-}
--
-- import Control.Monad
-- import Data.Monoid
-- import Data.Maybe
-- import qualified Data.Text.Lazy as TL
-- import qualified Data.Text.Lazy.Read as TL (decimal)
-- import Web.Scotty (scotty, html)
-- import Web.Scotty.Cookie (getCookie, setSimpleCookie)
--
-- main :: IO ()
-- main = scotty 3000 $
-- get "/" $ do
-- hits <- liftM (fromMaybe "0") $ getCookie "hits"
-- let hits' =
-- case TL.decimal hits of
-- Right n -> TL.pack . show . (+1) $ (fst n :: Integer)
-- Left _ -> "1"
-- setSimpleCookie "hits" $ TL.toStrict hits'
-- html $ mconcat [ "<html><body>"
-- , hits'
-- , "</body></html>"
-- ]
--
module Web.Scotty.Cookie
-- | Set a cookie, with full access to its options (see SetCookie)
setCookie :: MonadIO m => SetCookie -> ActionT m ()
-- | makeSimpleCookie and setCookie combined.
setSimpleCookie :: MonadIO m => Text -> Text -> ActionT m ()
-- | Lookup one cookie name
getCookie :: Monad m => Text -> ActionT m (Maybe Text)
-- | Returns all cookies
getCookies :: Monad m => ActionT m CookiesText
-- | Browsers don't directly delete a cookie, but setting its expiry to a
-- past date (e.g. the UNIX epoch) ensures that the cookie will be
-- invalidated (whether and when it will be actually deleted by the
-- browser seems to be browser-dependent).
deleteCookie :: MonadIO m => Text -> ActionT m ()
-- | Textual cookies. Functions assume UTF8 encoding.
type CookiesText = [(Text, Text)]
-- | Construct a simple cookie (an UTF-8 string pair with default cookie
-- options)
makeSimpleCookie :: Text -> Text -> SetCookie
-- | Data type representing the key-value pair to use for a cookie, as well
-- as configuration options for it.
--
--
-- import Web.Cookie
-- :set -XOverloadedStrings
-- let cookie = defaultSetCookie { setCookieName = "cookieName", setCookieValue = "cookieValue" }
--
--
-- -- get "/" $ text "beam me up!" ---- -- The path spec can include values starting with a colon, which are -- interpreted as captures. These are parameters that can be -- looked up with pathParam. -- --
-- >>> :{
-- let server = S.get "/foo/:bar" (S.pathParam "bar" >>= S.text)
-- in do
-- withScotty server $ curl "http://localhost:3000/foo/something"
-- :}
-- "something"
--
addroute :: MonadUnliftIO m => StdMethod -> RoutePattern -> ActionT m () -> ScottyT m ()
-- | Add a route that matches regardless of the HTTP verb.
matchAny :: MonadUnliftIO m => RoutePattern -> ActionT m () -> ScottyT m ()
-- | Specify an action to take if nothing else is found. Note: this
-- _always_ matches, so should generally be the last route specified.
notFound :: MonadUnliftIO m => ActionT m () -> ScottyT m ()
-- | Set global size limit for the request body. Requests with body size
-- exceeding the limit will not be processed and an HTTP response 413
-- will be returned to the client. Size limit needs to be greater than 0,
-- otherwise the application will terminate on start.
setMaxRequestBodySize :: Kilobytes -> ScottyT m ()
-- | Standard Sinatra-style route. Named captures are prepended with
-- colons. This is the default route type generated by OverloadedString
-- routes. i.e.
--
-- -- get (capture "/foo/:bar") $ ... ---- -- and -- --
-- {-# LANGUAGE OverloadedStrings #-}
-- ...
-- get "/foo/:bar" $ ...
--
--
-- are equivalent.
capture :: String -> RoutePattern
-- | Match requests using a regular expression. Named captures are not yet
-- supported.
--
--
-- >>> :{
-- let server = S.get (S.regex "^/f(.*)r$") $ do
-- cap <- S.pathParam "1"
-- S.text cap
-- in do
-- withScotty server $ curl "http://localhost:3000/foo/bar"
-- :}
-- "oo/ba"
--
regex :: String -> RoutePattern
-- | Build a route based on a function which can match using the entire
-- Request object. Nothing indicates the route does not
-- match. A Just value indicates a successful match, optionally
-- returning a list of key-value pairs accessible by param.
--
--
-- >>> :{
-- let server = S.get (function $ \req -> Just [("version", T.pack $ show $ W.httpVersion req)]) $ do
-- v <- S.pathParam "version"
-- S.text v
-- in do
-- withScotty server $ curl "http://localhost:3000/"
-- :}
-- "HTTP/1.1"
--
function :: (Request -> Maybe [Param]) -> RoutePattern
-- | Build a route that requires the requested path match exactly, without
-- captures.
literal :: String -> RoutePattern
-- | Get the Request object.
request :: Monad m => ActionT m Request
-- | Get a request header. Header name is case-insensitive.
header :: Monad m => Text -> ActionT m (Maybe Text)
-- | Get all the request headers. Header names are case-insensitive.
headers :: Monad m => ActionT m [(Text, Text)]
-- | Get the request body.
body :: MonadIO m => ActionT m ByteString
-- | Get an IO action that reads body chunks
--
--
bodyReader :: Monad m => ActionT m (IO ByteString)
-- | Parse the request body as a JSON object and return it.
--
-- If the JSON object is malformed, this sets the status to 400 Bad
-- Request, and throws an exception.
--
-- If the JSON fails to parse, this sets the status to 422 Unprocessable
-- Entity.
--
-- These status codes are as per
-- https://www.restapitutorial.com/httpstatuscodes.html.
jsonData :: (FromJSON a, MonadIO m) => ActionT m a
-- | Get list of uploaded files.
files :: Monad m => ActionT m [File]
-- | Get a parameter. First looks in captures, then form data, then query
-- parameters.
--
-- -- redirect "http://www.google.com" ---- -- OR -- --
-- redirect "/foo/bar" --redirect :: Monad m => Text -> ActionT m a text :: MonadIO m => Text -> ActionT m () html :: MonadIO m => Text -> ActionT m () -- | Send a file as the response. Doesn't set the "Content-Type" header, so -- you probably want to do that on your own with setHeader. -- Setting a status code will have no effect because Warp will overwrite -- that to 200 (see sendResponse). file :: MonadIO m => FilePath -> ActionT m () -- | Set the body of the response to the JSON encoding of the given value. -- Also sets "Content-Type" header to "application/json; charset=utf-8" -- if it has not already been set. json :: (ToJSON a, MonadIO m) => a -> ActionT m () -- | Set the body of the response to a Source. Doesn't set the -- "Content-Type" header, so you probably want to do that on your own -- with setHeader. stream :: MonadIO m => StreamingBody -> ActionT m () -- | Set the body of the response to the given ByteString value. -- Doesn't set the "Content-Type" header, so you probably want to do that -- on your own with setHeader. raw :: MonadIO m => ByteString -> ActionT m () -- | Nest a whole WAI application inside a Scotty handler. See Web.Scotty -- for further documentation nested :: MonadIO m => Application -> ActionT m () -- | Access the HTTP headers of the Response -- -- SINCE 0.21 getResponseHeaders :: MonadIO m => ActionT m ResponseHeaders -- | Access the HTTP Status of the Response -- -- SINCE 0.21 getResponseStatus :: MonadIO m => ActionT m Status -- | Access the content of the Response -- -- SINCE 0.21 getResponseContent :: MonadIO m => ActionT m Content -- | Throw a "500 Server Error" StatusError, which can be caught -- with rescue. -- -- Uncaught exceptions turn into HTTP 500 responses. -- | Deprecated: Throw an exception instead raise :: MonadIO m => Text -> ActionT m a -- | Throw a StatusError exception that has an associated HTTP error -- code and can be caught with rescue. -- -- Uncaught exceptions turn into HTTP responses corresponding to the -- given status. -- | Deprecated: Use status, text, and finish instead raiseStatus :: Monad m => Status -> Text -> ActionT m a -- | Throw an exception which can be caught within the scope of the current -- Action with catch. -- -- If the exception is not caught locally, another option is to implement -- a global Handler (with defaultHandler) that defines -- its interpretation and a translation to HTTP error codes. -- -- Uncaught exceptions turn into HTTP 500 responses. throw :: (MonadIO m, Exception e) => e -> ActionT m a -- | Catch an exception e.g. a StatusError or a user-defined -- exception. -- --
-- raise JustKidding `catch` (\msg -> text msg) ---- | Deprecated: Use catch instead rescue :: (MonadUnliftIO m, Exception e) => ActionT m a -> (e -> ActionT m a) -> ActionT m a -- | Abort execution of this action and continue pattern matching routes. -- Like an exception, any code after next is not executed. -- -- NB : Internally, this is implemented with an exception that can only -- be caught by the library, but not by the user. -- -- As an example, these two routes overlap. The only way the second one -- will ever run is if the first one calls next. -- --
-- get "/foo/:bar" $ do -- w :: Text <- pathParam "bar" -- unless (w == "special") next -- text "You made a request to /foo/special" -- -- get "/foo/:baz" $ do -- w <- pathParam "baz" -- text $ "You made a request to: " <> w --next :: Monad m => ActionT m a -- | Finish the execution of the current action. Like throwing an -- uncatchable exception. Any code after the call to finish will not be -- run. -- -- Since: 0.10.3 finish :: Monad m => ActionT m a -- | Global handler for user-defined exceptions. defaultHandler :: Monad m => ErrorHandler m -> ScottyT m () -- | Catch any synchronous IO exceptions -- | Deprecated: Use liftIO instead liftAndCatchIO :: MonadIO m => IO a -> ActionT m a -- | Lift a computation from the IO monad. This allows us to run IO -- computations in any monadic stack, so long as it supports these kinds -- of operations (i.e. IO is the base monad for the stack). -- --
-- import Control.Monad.Trans.State -- from the "transformers" library -- -- printState :: Show s => StateT s IO () -- printState = do -- state <- get -- liftIO $ print state ---- -- Had we omitted liftIO, we would have ended up with -- this error: -- --
-- • Couldn't match type ‘IO’ with ‘StateT s IO’ -- Expected type: StateT s IO () -- Actual type: IO () ---- -- The important part here is the mismatch between StateT s IO -- () and IO (). -- -- Luckily, we know of a function that takes an IO a and -- returns an (m a): liftIO, enabling us to run -- the program and see the expected results: -- --
-- > evalStateT printState "hello" -- "hello" -- -- > evalStateT printState 3 -- 3 --liftIO :: MonadIO m => IO a -> m a -- | Catch a synchronous (but not asynchronous) exception and recover from -- it. -- -- This is parameterized on the exception type. To catch all synchronous -- exceptions, use catchAny. catch :: (MonadUnliftIO m, Exception e) => m a -> (e -> m a) -> m a -- | E.g. when a parameter is not found in a query string (400 Bad Request) -- or when parsing a JSON body fails (422 Unprocessable Entity) -- | Deprecated: If it is supposed to be caught, a proper exception type -- should be defined data StatusError -- | Deprecated: If it is supposed to be caught, a proper exception type -- should be defined StatusError :: Status -> Text -> StatusError -- | Thrown e.g. when a request is too large data ScottyException RequestTooLarge :: ScottyException MalformedJSON :: ByteString -> Text -> ScottyException FailedToParseJSON :: ByteString -> Text -> ScottyException PathParameterNotFound :: Text -> ScottyException QueryParameterNotFound :: Text -> ScottyException FormFieldNotFound :: Text -> ScottyException FailedToParseParameter :: Text -> Text -> Text -> ScottyException type Param = (Text, Text) -- | Minimum implemention: parseParam class Parsable a -- | Take a Text value and parse it as a, or fail with a -- message. parseParam :: Parsable a => Text -> Either Text a -- | Default implementation parses comma-delimited lists. -- --
-- parseParamList t = mapM parseParam (T.split (== ',') t) --parseParamList :: Parsable a => Text -> Either Text [a] -- | Useful for creating Parsable instances for things that already -- implement Read. Ex: -- --
-- instance Parsable Int where parseParam = readEither --readEither :: Read a => Text -> Either Text a data RoutePattern type File = (Text, FileInfo ByteString) data Content ContentBuilder :: Builder -> Content ContentFile :: FilePath -> Content ContentStream :: StreamingBody -> Content ContentResponse :: Response -> Content type Kilobytes = Int -- | Specializes a Handler to the ActionT monad type ErrorHandler m = Handler (ActionT m) () -- | Generalized version of Handler data Handler (m :: Type -> Type) a Handler :: (e -> m a) -> Handler (m :: Type -> Type) a data ScottyT m a data ActionT m a data ScottyState m defaultScottyState :: ScottyState m -- | This module is essentially identical to Trans, except that some -- functions take/return strict Text instead of the lazy ones. -- -- It should be noted that most of the code snippets below depend on the -- OverloadedStrings language pragma. -- -- The functions in this module allow an arbitrary monad to be embedded -- in Scotty's monad transformer stack in order that Scotty be combined -- with other DSLs. -- -- Scotty is set up by default for development mode. For production -- servers, you will likely want to modify settings and the -- defaultHandler. See the comments on each of these functions for -- more information. module Web.Scotty.Trans.Strict -- | Run a scotty application using the warp server. NB: scotty p === -- scottyT p id scottyT :: (Monad m, MonadIO n) => Port -> (m Response -> IO Response) -> ScottyT m () -> n () -- | Turn a scotty application into a WAI Application, which can be -- run with any WAI handler. NB: scottyApp === scottyAppT id scottyAppT :: (Monad m, Monad n) => (m Response -> IO Response) -> ScottyT m () -> n Application -- | Run a scotty application using the warp server, passing extra options. -- NB: scottyOpts opts === scottyOptsT opts id scottyOptsT :: (Monad m, MonadIO n) => Options -> (m Response -> IO Response) -> ScottyT m () -> n () -- | Run a scotty application using the warp server, passing extra options, -- and listening on the provided socket. NB: scottySocket opts sock === -- scottySocketT opts sock id scottySocketT :: (Monad m, MonadIO n) => Options -> Socket -> (m Response -> IO Response) -> ScottyT m () -> n () data Options Options :: Int -> Settings -> Options -- | 0 = silent, 1(def) = startup banner [verbose] :: Options -> Int -- | Warp Settings Note: to work around an issue in warp, the -- default FD cache duration is set to 0 so changes to static files are -- always picked up. This likely has performance implications, so you may -- want to modify this for production servers using -- setFdCacheDuration. [settings] :: Options -> Settings defaultOptions :: Options -- | Use given middleware. Middleware is nested such that the first -- declared is the outermost middleware (it has first dibs on the request -- and last action on the response). Every middleware is run on each -- request. middleware :: Middleware -> ScottyT m () -- | get = addroute GET get :: MonadUnliftIO m => RoutePattern -> ActionT m () -> ScottyT m () -- | post = addroute POST post :: MonadUnliftIO m => RoutePattern -> ActionT m () -> ScottyT m () -- | put = addroute PUT put :: MonadUnliftIO m => RoutePattern -> ActionT m () -> ScottyT m () -- | delete = addroute DELETE delete :: MonadUnliftIO m => RoutePattern -> ActionT m () -> ScottyT m () -- | patch = addroute PATCH patch :: MonadUnliftIO m => RoutePattern -> ActionT m () -> ScottyT m () -- | options = addroute OPTIONS options :: MonadUnliftIO m => RoutePattern -> ActionT m () -> ScottyT m () -- | Define a route with a StdMethod, a route pattern representing -- the path spec, and an Action which may modify the response. -- --
-- get "/" $ text "beam me up!" ---- -- The path spec can include values starting with a colon, which are -- interpreted as captures. These are parameters that can be -- looked up with pathParam. -- --
-- >>> :{
-- let server = S.get "/foo/:bar" (S.pathParam "bar" >>= S.text)
-- in do
-- withScotty server $ curl "http://localhost:3000/foo/something"
-- :}
-- "something"
--
addroute :: MonadUnliftIO m => StdMethod -> RoutePattern -> ActionT m () -> ScottyT m ()
-- | Add a route that matches regardless of the HTTP verb.
matchAny :: MonadUnliftIO m => RoutePattern -> ActionT m () -> ScottyT m ()
-- | Specify an action to take if nothing else is found. Note: this
-- _always_ matches, so should generally be the last route specified.
notFound :: MonadUnliftIO m => ActionT m () -> ScottyT m ()
-- | Set global size limit for the request body. Requests with body size
-- exceeding the limit will not be processed and an HTTP response 413
-- will be returned to the client. Size limit needs to be greater than 0,
-- otherwise the application will terminate on start.
setMaxRequestBodySize :: Kilobytes -> ScottyT m ()
-- | Standard Sinatra-style route. Named captures are prepended with
-- colons. This is the default route type generated by OverloadedString
-- routes. i.e.
--
-- -- get (capture "/foo/:bar") $ ... ---- -- and -- --
-- {-# LANGUAGE OverloadedStrings #-}
-- ...
-- get "/foo/:bar" $ ...
--
--
-- are equivalent.
capture :: String -> RoutePattern
-- | Match requests using a regular expression. Named captures are not yet
-- supported.
--
--
-- >>> :{
-- let server = S.get (S.regex "^/f(.*)r$") $ do
-- cap <- S.pathParam "1"
-- S.text cap
-- in do
-- withScotty server $ curl "http://localhost:3000/foo/bar"
-- :}
-- "oo/ba"
--
regex :: String -> RoutePattern
-- | Build a route based on a function which can match using the entire
-- Request object. Nothing indicates the route does not
-- match. A Just value indicates a successful match, optionally
-- returning a list of key-value pairs accessible by param.
--
--
-- >>> :{
-- let server = S.get (function $ \req -> Just [("version", T.pack $ show $ W.httpVersion req)]) $ do
-- v <- S.pathParam "version"
-- S.text v
-- in do
-- withScotty server $ curl "http://localhost:3000/"
-- :}
-- "HTTP/1.1"
--
function :: (Request -> Maybe [Param]) -> RoutePattern
-- | Build a route that requires the requested path match exactly, without
-- captures.
literal :: String -> RoutePattern
-- | Get the Request object.
request :: Monad m => ActionT m Request
-- | Get a request header. Header name is case-insensitive.
header :: Monad m => Text -> ActionT m (Maybe Text)
-- | Get all the request headers. Header names are case-insensitive.
headers :: Monad m => ActionT m [(Text, Text)]
-- | Get the request body.
body :: MonadIO m => ActionT m ByteString
-- | Get an IO action that reads body chunks
--
--
bodyReader :: Monad m => ActionT m (IO ByteString)
-- | Get a parameter. First looks in captures, then form data, then query
-- parameters.
--
-- -- redirect "http://www.google.com" ---- -- OR -- --
-- redirect "/foo/bar" --redirect :: Monad m => Text -> ActionT m a -- | Set the body of the response to the given Text value. Also sets -- "Content-Type" header to "text/plain; charset=utf-8" if it has not -- already been set. text :: MonadIO m => Text -> ActionT m () -- | Set the body of the response to the given Text value. Also sets -- "Content-Type" header to "text/html; charset=utf-8" if it has not -- already been set. html :: MonadIO m => Text -> ActionT m () -- | Send a file as the response. Doesn't set the "Content-Type" header, so -- you probably want to do that on your own with setHeader. -- Setting a status code will have no effect because Warp will overwrite -- that to 200 (see sendResponse). file :: MonadIO m => FilePath -> ActionT m () -- | Set the body of the response to the JSON encoding of the given value. -- Also sets "Content-Type" header to "application/json; charset=utf-8" -- if it has not already been set. json :: (ToJSON a, MonadIO m) => a -> ActionT m () -- | Set the body of the response to a Source. Doesn't set the -- "Content-Type" header, so you probably want to do that on your own -- with setHeader. stream :: MonadIO m => StreamingBody -> ActionT m () -- | Set the body of the response to the given ByteString value. -- Doesn't set the "Content-Type" header, so you probably want to do that -- on your own with setHeader. raw :: MonadIO m => ByteString -> ActionT m () -- | Nest a whole WAI application inside a Scotty handler. See Web.Scotty -- for further documentation nested :: MonadIO m => Application -> ActionT m () -- | Set the body of the response to the given Text value. Also sets -- "Content-Type" header to "text/plain; charset=utf-8" if it has not -- already been set. textLazy :: MonadIO m => Text -> ActionT m () -- | Set the body of the response to the given Text value. Also sets -- "Content-Type" header to "text/html; charset=utf-8" if it has not -- already been set. htmlLazy :: MonadIO m => Text -> ActionT m () -- | Access the HTTP headers of the Response -- -- SINCE 0.21 getResponseHeaders :: MonadIO m => ActionT m ResponseHeaders -- | Access the HTTP Status of the Response -- -- SINCE 0.21 getResponseStatus :: MonadIO m => ActionT m Status -- | Access the content of the Response -- -- SINCE 0.21 getResponseContent :: MonadIO m => ActionT m Content -- | Throw a "500 Server Error" StatusError, which can be caught -- with catch. -- -- Uncaught exceptions turn into HTTP 500 responses. -- | Deprecated: Throw an exception instead raise :: MonadIO m => Text -> ActionT m a -- | Throw a StatusError exception that has an associated HTTP error -- code and can be caught with catch. -- -- Uncaught exceptions turn into HTTP responses corresponding to the -- given status. -- | Deprecated: Use status, text, and finish instead raiseStatus :: Monad m => Status -> Text -> ActionT m a -- | Throw an exception which can be caught within the scope of the current -- Action with catch. -- -- If the exception is not caught locally, another option is to implement -- a global Handler (with defaultHandler) that defines -- its interpretation and a translation to HTTP error codes. -- -- Uncaught exceptions turn into HTTP 500 responses. throw :: (MonadIO m, Exception e) => e -> ActionT m a -- | Catch an exception e.g. a StatusError or a user-defined -- exception. -- --
-- raise JustKidding `catch` (\msg -> text msg) ---- | Deprecated: Use catch instead rescue :: (MonadUnliftIO m, Exception e) => ActionT m a -> (e -> ActionT m a) -> ActionT m a -- | Abort execution of this action and continue pattern matching routes. -- Like an exception, any code after next is not executed. -- -- NB : Internally, this is implemented with an exception that can only -- be caught by the library, but not by the user. -- -- As an example, these two routes overlap. The only way the second one -- will ever run is if the first one calls next. -- --
-- get "/foo/:bar" $ do -- w :: Text <- pathParam "bar" -- unless (w == "special") next -- text "You made a request to /foo/special" -- -- get "/foo/:baz" $ do -- w <- pathParam "baz" -- text $ "You made a request to: " <> w --next :: Monad m => ActionT m a -- | Finish the execution of the current action. Like throwing an -- uncatchable exception. Any code after the call to finish will not be -- run. -- -- Since: 0.10.3 finish :: Monad m => ActionT m a -- | Global handler for user-defined exceptions. defaultHandler :: Monad m => ErrorHandler m -> ScottyT m () -- | Catch any synchronous IO exceptions -- | Deprecated: Use liftIO instead liftAndCatchIO :: MonadIO m => IO a -> ActionT m a -- | E.g. when a parameter is not found in a query string (400 Bad Request) -- or when parsing a JSON body fails (422 Unprocessable Entity) -- | Deprecated: If it is supposed to be caught, a proper exception type -- should be defined data StatusError -- | Deprecated: If it is supposed to be caught, a proper exception type -- should be defined StatusError :: Status -> Text -> StatusError -- | Thrown e.g. when a request is too large data ScottyException RequestTooLarge :: ScottyException MalformedJSON :: ByteString -> Text -> ScottyException FailedToParseJSON :: ByteString -> Text -> ScottyException PathParameterNotFound :: Text -> ScottyException QueryParameterNotFound :: Text -> ScottyException FormFieldNotFound :: Text -> ScottyException FailedToParseParameter :: Text -> Text -> Text -> ScottyException type Param = (Text, Text) -- | Minimum implemention: parseParam class Parsable a -- | Take a Text value and parse it as a, or fail with a -- message. parseParam :: Parsable a => Text -> Either Text a -- | Default implementation parses comma-delimited lists. -- --
-- parseParamList t = mapM parseParam (T.split (== ',') t) --parseParamList :: Parsable a => Text -> Either Text [a] -- | Useful for creating Parsable instances for things that already -- implement Read. Ex: -- --
-- instance Parsable Int where parseParam = readEither --readEither :: Read a => Text -> Either Text a data RoutePattern type File = (Text, FileInfo ByteString) data Content ContentBuilder :: Builder -> Content ContentFile :: FilePath -> Content ContentStream :: StreamingBody -> Content ContentResponse :: Response -> Content type Kilobytes = Int -- | Specializes a Handler to the ActionT monad type ErrorHandler m = Handler (ActionT m) () -- | Generalized version of Handler data Handler (m :: Type -> Type) a Handler :: (e -> m a) -> Handler (m :: Type -> Type) a data ScottyT m a data ActionT m a data ScottyState m defaultScottyState :: ScottyState m -- | It should be noted that most of the code snippets below depend on the -- OverloadedStrings language pragma. -- -- Scotty is set up by default for development mode. For production -- servers, you will likely want to modify settings and the -- defaultHandler. See the comments on each of these functions for -- more information. -- -- Please refer to the examples directory and the spec -- test suite for concrete use cases, e.g. constructing responses, -- exception handling and useful implementation details. module Web.Scotty -- | Run a scotty application using the warp server. scotty :: Port -> ScottyM () -> IO () -- | Run a scotty application using the warp server, passing extra options. scottyOpts :: Options -> ScottyM () -> IO () -- | Run a scotty application using the warp server, passing extra options, -- and listening on the provided socket. This allows the user to provide, -- for example, a Unix named socket, which can be used when reverse HTTP -- proxying into your application. scottySocket :: Options -> Socket -> ScottyM () -> IO () data Options Options :: Int -> Settings -> Options -- | 0 = silent, 1(def) = startup banner [verbose] :: Options -> Int -- | Warp Settings Note: to work around an issue in warp, the -- default FD cache duration is set to 0 so changes to static files are -- always picked up. This likely has performance implications, so you may -- want to modify this for production servers using -- setFdCacheDuration. [settings] :: Options -> Settings defaultOptions :: Options -- | Turn a scotty application into a WAI Application, which can be -- run with any WAI handler. scottyApp :: ScottyM () -> IO Application -- | Use given middleware. Middleware is nested such that the first -- declared is the outermost middleware (it has first dibs on the request -- and last action on the response). Every middleware is run on each -- request. middleware :: Middleware -> ScottyM () -- | get = addroute GET get :: RoutePattern -> ActionM () -> ScottyM () -- | post = addroute POST post :: RoutePattern -> ActionM () -> ScottyM () -- | put = addroute PUT put :: RoutePattern -> ActionM () -> ScottyM () -- | delete = addroute DELETE delete :: RoutePattern -> ActionM () -> ScottyM () -- | patch = addroute PATCH patch :: RoutePattern -> ActionM () -> ScottyM () -- | options = addroute OPTIONS options :: RoutePattern -> ActionM () -> ScottyM () -- | Define a route with a StdMethod, a route pattern representing -- the path spec, and an Action which may modify the response. -- --
-- get "/" $ text "beam me up!" ---- -- The path spec can include values starting with a colon, which are -- interpreted as captures. These are parameters that can be -- looked up with pathParam. -- --
-- >>> :{
-- let server = S.get "/foo/:bar" (S.pathParam "bar" >>= S.text)
-- in do
-- withScotty server $ curl "http://localhost:3000/foo/something"
-- :}
-- "something"
--
addroute :: StdMethod -> RoutePattern -> ActionM () -> ScottyM ()
-- | Add a route that matches regardless of the HTTP verb.
matchAny :: RoutePattern -> ActionM () -> ScottyM ()
-- | Specify an action to take if nothing else is found. Note: this
-- _always_ matches, so should generally be the last route specified.
notFound :: ActionM () -> ScottyM ()
-- | Nest a whole WAI application inside a Scotty handler. Note: You will
-- want to ensure that this route fully handles the response, as there is
-- no easy delegation as per normal Scotty actions. Also, you will have
-- to carefully ensure that you are expecting the correct routes, this
-- could require stripping the current prefix, or adding the prefix to
-- your application's handlers if it depends on them. One potential
-- use-case for this is hosting a web-socket handler under a specific
-- route.
nested :: Application -> ActionM ()
-- | Set global size limit for the request body. Requests with body size
-- exceeding the limit will not be processed and an HTTP response 413
-- will be returned to the client. Size limit needs to be greater than 0,
-- otherwise the application will terminate on start.
setMaxRequestBodySize :: Kilobytes -> ScottyM ()
-- | Standard Sinatra-style route. Named captures are prepended with
-- colons. This is the default route type generated by OverloadedString
-- routes. i.e.
--
-- -- get (capture "/foo/:bar") $ ... ---- -- and -- --
-- {-# LANGUAGE OverloadedStrings #-}
-- ...
-- get "/foo/:bar" $ ...
--
--
-- are equivalent.
capture :: String -> RoutePattern
-- | Match requests using a regular expression. Named captures are not yet
-- supported.
--
--
-- >>> :{
-- let server = S.get (S.regex "^/f(.*)r$") $ do
-- cap <- S.pathParam "1"
-- S.text cap
-- in do
-- withScotty server $ curl "http://localhost:3000/foo/bar"
-- :}
-- "oo/ba"
--
regex :: String -> RoutePattern
-- | Build a route based on a function which can match using the entire
-- Request object. Nothing indicates the route does not
-- match. A Just value indicates a successful match, optionally
-- returning a list of key-value pairs accessible by param.
--
--
-- >>> :{
-- let server = S.get (function $ \req -> Just [("version", T.pack $ show $ W.httpVersion req)]) $ do
-- v <- S.pathParam "version"
-- S.text v
-- in do
-- withScotty server $ curl "http://localhost:3000/"
-- :}
-- "HTTP/1.1"
--
function :: (Request -> Maybe [Param]) -> RoutePattern
-- | Build a route that requires the requested path match exactly, without
-- captures.
literal :: String -> RoutePattern
-- | Get the Request object.
request :: ActionM Request
-- | Get a request header. Header name is case-insensitive.
header :: Text -> ActionM (Maybe Text)
-- | Get all the request headers. Header names are case-insensitive.
headers :: ActionM [(Text, Text)]
-- | Get the request body.
body :: ActionM ByteString
-- | Get an IO action that reads body chunks
--
--
bodyReader :: ActionM (IO ByteString)
-- | Parse the request body as a JSON object and return it. Raises an
-- exception if parse is unsuccessful.
jsonData :: FromJSON a => ActionM a
-- | Get list of uploaded files.
files :: ActionM [File]
-- | Get a parameter. First looks in captures, then form data, then query
-- parameters.
--
-- -- redirect "http://www.google.com" ---- -- OR -- --
-- redirect "/foo/bar" --redirect :: Text -> ActionM a -- | Set the body of the response to the given Text value. Also sets -- "Content-Type" header to "text/plain; charset=utf-8" if it has not -- already been set. text :: Text -> ActionM () -- | Set the body of the response to the given Text value. Also sets -- "Content-Type" header to "text/html; charset=utf-8" if it has not -- already been set. html :: Text -> ActionM () -- | Send a file as the response. Doesn't set the "Content-Type" header, so -- you probably want to do that on your own with setHeader. file :: FilePath -> ActionM () -- | Set the body of the response to the JSON encoding of the given value. -- Also sets "Content-Type" header to "application/json; charset=utf-8" -- if it has not already been set. json :: ToJSON a => a -> ActionM () -- | Set the body of the response to a StreamingBody. Doesn't set the -- "Content-Type" header, so you probably want to do that on your own -- with setHeader. stream :: StreamingBody -> ActionM () -- | Set the body of the response to the given ByteString value. -- Doesn't set the "Content-Type" header, so you probably want to do that -- on your own with setHeader. raw :: ByteString -> ActionM () -- | Access the HTTP headers of the Response -- -- Since: 0.21 getResponseHeaders :: ActionM ResponseHeaders -- | Access the HTTP Status of the Response -- -- Since: 0.21 getResponseStatus :: ActionM Status -- | Access the content of the Response -- -- Since: 0.21 getResponseContent :: ActionM Content -- | Throw a "500 Server Error" StatusError, which can be caught -- with catch. -- -- Uncaught exceptions turn into HTTP 500 responses. -- | Deprecated: Throw an exception instead raise :: Text -> ActionM a -- | Throw a StatusError exception that has an associated HTTP error -- code and can be caught with catch. -- -- Uncaught exceptions turn into HTTP responses corresponding to the -- given status. -- | Deprecated: Use status, text, and finish instead raiseStatus :: Status -> Text -> ActionM a -- | Throw an exception which can be caught within the scope of the current -- Action with catch. -- -- If the exception is not caught locally, another option is to implement -- a global Handler (with defaultHandler) that defines its -- interpretation and a translation to HTTP error codes. -- -- Uncaught exceptions turn into HTTP 500 responses. throw :: Exception e => e -> ActionM a -- | Catch an exception e.g. a StatusError or a user-defined -- exception. -- --
-- raise JustKidding `catch` (\msg -> text msg) ---- | Deprecated: Use catch instead rescue :: Exception e => ActionM a -> (e -> ActionM a) -> ActionM a -- | Abort execution of this action and continue pattern matching routes. -- Like an exception, any code after next is not executed. -- -- NB : Internally, this is implemented with an exception that can only -- be caught by the library, but not by the user. -- -- As an example, these two routes overlap. The only way the second one -- will ever run is if the first one calls next. -- --
-- get "/foo/:bar" $ do -- w :: Text <- pathParam "bar" -- unless (w == "special") next -- text "You made a request to /foo/special" -- -- get "/foo/:baz" $ do -- w <- pathParam "baz" -- text $ "You made a request to: " <> w --next :: ActionM () -- | Abort execution of this action. Like an exception, any code after -- finish is not executed. -- -- As an example only requests to /foo/special will include in -- the response content the text message. -- --
-- get "/foo/:bar" $ do -- w :: Text <- pathParam "bar" -- unless (w == "special") finish -- text "You made a request to /foo/special" ---- -- Since: 0.10.3 finish :: ActionM a -- | Global handler for user-defined exceptions. defaultHandler :: ErrorHandler IO -> ScottyM () -- | Like liftIO, but catch any IO exceptions and turn them into -- Scotty exceptions. -- | Deprecated: Use liftIO instead liftAndCatchIO :: IO a -> ActionM a -- | Lift a computation from the IO monad. This allows us to run IO -- computations in any monadic stack, so long as it supports these kinds -- of operations (i.e. IO is the base monad for the stack). -- --
-- import Control.Monad.Trans.State -- from the "transformers" library -- -- printState :: Show s => StateT s IO () -- printState = do -- state <- get -- liftIO $ print state ---- -- Had we omitted liftIO, we would have ended up with -- this error: -- --
-- • Couldn't match type ‘IO’ with ‘StateT s IO’ -- Expected type: StateT s IO () -- Actual type: IO () ---- -- The important part here is the mismatch between StateT s IO -- () and IO (). -- -- Luckily, we know of a function that takes an IO a and -- returns an (m a): liftIO, enabling us to run -- the program and see the expected results: -- --
-- > evalStateT printState "hello" -- "hello" -- -- > evalStateT printState 3 -- 3 --liftIO :: MonadIO m => IO a -> m a -- | Catch a synchronous (but not asynchronous) exception and recover from -- it. -- -- This is parameterized on the exception type. To catch all synchronous -- exceptions, use catchAny. catch :: (MonadUnliftIO m, Exception e) => m a -> (e -> m a) -> m a -- | E.g. when a parameter is not found in a query string (400 Bad Request) -- or when parsing a JSON body fails (422 Unprocessable Entity) -- | Deprecated: If it is supposed to be caught, a proper exception type -- should be defined data StatusError -- | Deprecated: If it is supposed to be caught, a proper exception type -- should be defined StatusError :: Status -> Text -> StatusError -- | Thrown e.g. when a request is too large data ScottyException RequestTooLarge :: ScottyException MalformedJSON :: ByteString -> Text -> ScottyException FailedToParseJSON :: ByteString -> Text -> ScottyException PathParameterNotFound :: Text -> ScottyException QueryParameterNotFound :: Text -> ScottyException FormFieldNotFound :: Text -> ScottyException FailedToParseParameter :: Text -> Text -> Text -> ScottyException type Param = (Text, Text) -- | Minimum implemention: parseParam class Parsable a -- | Take a Text value and parse it as a, or fail with a -- message. parseParam :: Parsable a => Text -> Either Text a -- | Default implementation parses comma-delimited lists. -- --
-- parseParamList t = mapM parseParam (T.split (== ',') t) --parseParamList :: Parsable a => Text -> Either Text [a] -- | Useful for creating Parsable instances for things that already -- implement Read. Ex: -- --
-- instance Parsable Int where parseParam = readEither --readEither :: Read a => Text -> Either Text a type ScottyM = ScottyT IO type ActionM = ActionT IO data RoutePattern type File = (Text, FileInfo ByteString) data Content ContentBuilder :: Builder -> Content ContentFile :: FilePath -> Content ContentStream :: StreamingBody -> Content ContentResponse :: Response -> Content type Kilobytes = Int -- | Specializes a Handler to the ActionT monad type ErrorHandler m = Handler (ActionT m) () -- | Generalized version of Handler data Handler (m :: Type -> Type) a Handler :: (e -> m a) -> Handler (m :: Type -> Type) a data ScottyState m defaultScottyState :: ScottyState m