Spock-core
Safe HaskellNone
LanguageHaskell2010

Web.Spock.Action

Description

Read requests and produce responses inside route handlers. This module is part of Spock-core and is also reexported by Web.Spock. Its actions work in both core and full Spock applications.

Choose a request reader

  • param returns an optional typed query/form value; param' returns status 400 when a required value is missing or cannot be parsed.
  • paramsGet and paramsPost distinguish query and form parameters.
  • jsonBody returns optional parsed JSON; jsonBody' rejects invalid JSON with status 400. body gives the cached raw bytes.
  • header decodes a header as text; rawHeader preserves its bytes.
  • filesMulti groups uploaded files by field name. Process them during the action because their temporary files are removed after the request.

Send a response

Set status and headers with setStatus and setHeader, then call text, html, json, file, or another response helper. Sending a response finishes the action. lazyBytes requires you to choose the content type.

For HTML templates, see the Blaze and Lucid rendering tutorial.

Synopsis

Action types

type ActionT = ActionCtxT () Source #

A request action with context (). m supplies the application's base effects and a is the result. For a core application m can be IO or a custom transformer stack; full Spock uses its WebStateM base monad.

data ActionCtxT ctx (m :: Type -> Type) a Source #

A per-request computation: it reads the request and typed hook context ctx, accumulates response metadata, and can finish with a response or continue routing. m is the base monad and a is the result.

getContext reads the value supplied by a prehook. lift runs a computation in m; liftIO runs IO when m supports it. Neither changes the hook context. Response helpers finish the action, so statements after text, json, or another response helper are not executed.

Instances

Instances details
MFunctor (ActionCtxT ctx :: (Type -> Type) -> Type -> Type) Source # 
Instance details

Defined in Web.Spock.Internal.Wire

Methods

hoist :: Monad m => (forall a. m a -> n a) -> ActionCtxT ctx m b -> ActionCtxT ctx n b

MonadBaseControl b m => MonadBaseControl b (ActionCtxT ctx m) Source # 
Instance details

Defined in Web.Spock.Internal.Wire

Methods

liftBaseWith :: (RunInBase (ActionCtxT ctx m) b -> b a) -> ActionCtxT ctx m a

restoreM :: StM (ActionCtxT ctx m) a -> ActionCtxT ctx m a

MonadBase b m => MonadBase b (ActionCtxT ctx m) Source # 
Instance details

Defined in Web.Spock.Internal.Wire

Methods

liftBase :: b α -> ActionCtxT ctx m α

MonadTransControl (ActionCtxT ctx) Source # 
Instance details

Defined in Web.Spock.Internal.Wire

Methods

liftWith :: Monad m => (Run (ActionCtxT ctx) -> m a) -> ActionCtxT ctx m a

restoreT :: Monad m => m (StT (ActionCtxT ctx) a) -> ActionCtxT ctx m a

MonadTrans (ActionCtxT ctx) Source # 
Instance details

Defined in Web.Spock.Internal.Wire

Methods

lift :: Monad m => m a -> ActionCtxT ctx m a

Monad m => Alternative (ActionCtxT ctx m) Source # 
Instance details

Defined in Web.Spock.Internal.Wire

Methods

empty :: ActionCtxT ctx m a

(<|>) :: ActionCtxT ctx m a -> ActionCtxT ctx m a -> ActionCtxT ctx m a

some :: ActionCtxT ctx m a -> ActionCtxT ctx m [a]

many :: ActionCtxT ctx m a -> ActionCtxT ctx m [a]

Monad m => Applicative (ActionCtxT ctx m) Source # 
Instance details

Defined in Web.Spock.Internal.Wire

Methods

pure :: a -> ActionCtxT ctx m a

(<*>) :: ActionCtxT ctx m (a -> b) -> ActionCtxT ctx m a -> ActionCtxT ctx m b

liftA2 :: (a -> b -> c) -> ActionCtxT ctx m a -> ActionCtxT ctx m b -> ActionCtxT ctx m c

(*>) :: ActionCtxT ctx m a -> ActionCtxT ctx m b -> ActionCtxT ctx m b

(<*) :: ActionCtxT ctx m a -> ActionCtxT ctx m b -> ActionCtxT ctx m a

Functor m => Functor (ActionCtxT ctx m) Source # 
Instance details

Defined in Web.Spock.Internal.Wire

Methods

fmap :: (a -> b) -> ActionCtxT ctx m a -> ActionCtxT ctx m b

(<$) :: a -> ActionCtxT ctx m b -> ActionCtxT ctx m a

Monad m => Monad (ActionCtxT ctx m) Source # 
Instance details

Defined in Web.Spock.Internal.Wire

Methods

(>>=) :: ActionCtxT ctx m a -> (a -> ActionCtxT ctx m b) -> ActionCtxT ctx m b

(>>) :: ActionCtxT ctx m a -> ActionCtxT ctx m b -> ActionCtxT ctx m b

return :: a -> ActionCtxT ctx m a

MonadIO m => MonadIO (ActionCtxT ctx m) Source # 
Instance details

Defined in Web.Spock.Internal.Wire

Methods

liftIO :: IO a -> ActionCtxT ctx m a

type StT (ActionCtxT ctx) a Source # 
Instance details

Defined in Web.Spock.Internal.Wire

type StT (ActionCtxT ctx) a
type StM (ActionCtxT ctx m) a Source # 
Instance details

Defined in Web.Spock.Internal.Wire

type StM (ActionCtxT ctx m) a = ComposeSt (ActionCtxT ctx) m a

Handling requests

request :: forall (m :: Type -> Type) ctx. MonadIO m => ActionCtxT ctx m Request Source #

Get the original Wai Request object

header :: forall (m :: Type -> Type) ctx. MonadIO m => Text -> ActionCtxT ctx m (Maybe Text) Source #

Read a header

rawHeader :: forall (m :: Type -> Type) ctx. MonadIO m => HeaderName -> ActionCtxT ctx m (Maybe ByteString) Source #

Read a header without converting it to text

cookies :: forall (m :: Type -> Type) ctx. MonadIO m => ActionCtxT ctx m [(Text, Text)] Source #

Read all cookies. The cookie value will already be urldecoded.

getRequestId :: forall (m :: Type -> Type) ctx. MonadIO m => ActionCtxT ctx m (Maybe Text) Source #

Request ID, when structured logging has been enabled.

logMessage :: forall (m :: Type -> Type) ctx. MonadIO m => LogLevel -> Text -> [(Text, Value)] -> ActionCtxT ctx m () Source #

Emit a message with structured fields through the configured request logger. With logging disabled this is a no-op. Fields are nested in JSON under fields so they cannot overwrite the request ID or other metadata.

cookie :: forall (m :: Type -> Type) ctx. MonadIO m => Text -> ActionCtxT ctx m (Maybe Text) Source #

Read a cookie. The cookie value will already be urldecoded. Note that it is more efficient to use cookies if you need do access many cookies during a request handler.

reqMethod :: forall (m :: Type -> Type) ctx. MonadIO m => ActionCtxT ctx m SpockMethod Source #

Returns the current request method, e.g. GET

preferredFormat :: forall (m :: Type -> Type) ctx. MonadIO m => ActionCtxT ctx m ClientPreferredFormat Source #

Tries to dected the preferred format of the response using the Accept header

body :: forall (m :: Type -> Type) ctx. MonadIO m => ActionCtxT ctx m ByteString Source #

Get the raw request body

jsonBody :: forall (m :: Type -> Type) a ctx. (MonadIO m, FromJSON a) => ActionCtxT ctx m (Maybe a) Source #

Parse the request body as json

jsonBody' :: forall (m :: Type -> Type) a ctx. (MonadIO m, FromJSON a) => ActionCtxT ctx m a Source #

Parse the request body as json and fails with 400 status code on error

files :: forall (m :: Type -> Type) ctx. MonadIO m => ActionCtxT ctx m (HashMap Text UploadedFile) Source #

Get the last uploaded file for each form field name. Use filesMulti to retrieve every file when a field has multiple uploads.

filesMulti :: forall (m :: Type -> Type) ctx. MonadIO m => ActionCtxT ctx m (HashMap Text [UploadedFile]) Source #

Get every uploaded file grouped by form field name, in upload order.

data UploadedFile Source #

Instances

Instances details
Show UploadedFile Source # 
Instance details

Defined in Web.Spock.Internal.Wire

Methods

showsPrec :: Int -> UploadedFile -> ShowS

show :: UploadedFile -> String

showList :: [UploadedFile] -> ShowS

params :: forall (m :: Type -> Type) ctx. MonadIO m => ActionCtxT ctx m [(Text, Text)] Source #

Get all request (POST + GET) params

paramsGet :: forall (m :: Type -> Type) ctx. MonadIO m => ActionCtxT ctx m [(Text, Text)] Source #

Get all request GET params

paramsPost :: forall (m :: Type -> Type) ctx. MonadIO m => ActionCtxT ctx m [(Text, Text)] Source #

Get all request POST params

param :: forall p (m :: Type -> Type) ctx. (FromHttpApiData p, MonadIO m) => Text -> ActionCtxT ctx m (Maybe p) Source #

Read a request parameter, searching query (GET) values before form (POST) values. Use paramsPost or paramsGet when the source matters.

param' :: forall p (m :: Type -> Type) ctx. (FromHttpApiData p, MonadIO m) => Text -> ActionCtxT ctx m p Source #

Like param, but outputs an error when a param is missing

Working with context

getContext :: forall (m :: Type -> Type) ctx. MonadIO m => ActionCtxT ctx m ctx Source #

Get the context of the current request

runInContext :: forall (m :: Type -> Type) ctx' a ctx. MonadIO m => ctx' -> ActionCtxT ctx' m a -> ActionCtxT ctx m a Source #

Run an Action in a different context

Sending responses

setStatus :: forall (m :: Type -> Type) ctx. MonadIO m => Status -> ActionCtxT ctx m () Source #

Set a response status

setHeader :: forall (m :: Type -> Type) ctx. MonadIO m => Text -> Text -> ActionCtxT ctx m () Source #

Set a response header. If the response header is allowed to occur multiple times (as in RFC 2616), it will be appended. Otherwise the previous value is overwritten. See setMultiHeader.

redirect :: forall (m :: Type -> Type) ctx a. MonadIO m => Text -> ActionCtxT ctx m a Source #

Redirect to a given url

jumpNext :: forall (m :: Type -> Type) ctx a. MonadIO m => ActionCtxT ctx m a Source #

Abort the current action and jump the next one matching the route

data CookieSettings Source #

Cookie settings

Constructors

CookieSettings 

Fields

data SameSite Source #

Instances

Instances details
Eq SameSite Source # 
Instance details

Defined in Web.Spock.Internal.Cookies

Methods

(==) :: SameSite -> SameSite -> Bool

(/=) :: SameSite -> SameSite -> Bool

Show SameSite Source # 
Instance details

Defined in Web.Spock.Internal.Cookies

Methods

showsPrec :: Int -> SameSite -> ShowS

show :: SameSite -> String

showList :: [SameSite] -> ShowS

defaultCookieSettings :: CookieSettings Source #

Default cookie settings, equals

CookieSettings
  { cs_EOL      = CookieValidForSession
  , cs_HTTPOnly = False
  , cs_secure   = False
  , cs_sameSite = Nothing
  , cs_domain   = Nothing
  , cs_path     = Just "/"
  }

data CookieEOL Source #

Setting cookie expiration

Constructors

CookieValidUntil UTCTime

a point in time in UTC until the cookie is valid

CookieValidFor NominalDiffTime

a period (in seconds) for which the cookie is valid

CookieValidForSession

the cookie expires with the browser session

CookieValidForever

the cookie will have an expiration date in the far future

setCookie :: forall (m :: Type -> Type) ctx. MonadIO m => Text -> Text -> CookieSettings -> ActionCtxT ctx m () Source #

Set a cookie. The cookie value will be urlencoded.

deleteCookie :: forall (m :: Type -> Type) ctx. MonadIO m => Text -> ActionCtxT ctx m () Source #

Delete a cookie

bytes :: forall (m :: Type -> Type) ctx a. MonadIO m => ByteString -> ActionCtxT ctx m a Source #

Send a ByteString as response body. Provide your own "Content-Type"

lazyBytes :: forall (m :: Type -> Type) ctx a. MonadIO m => ByteString -> ActionCtxT ctx m a Source #

Send a lazy ByteString as response body. Provide your own "Content-Type"

setRawMultiHeader :: forall (m :: Type -> Type) ctx. MonadIO m => MultiHeader -> ByteString -> ActionCtxT ctx m () Source #

Set a response header that can occur multiple times. (eg: Cache-Control)

data MultiHeader Source #

Instances

Instances details
Eq MultiHeader Source # 
Instance details

Defined in Web.Spock.Internal.Wire

Methods

(==) :: MultiHeader -> MultiHeader -> Bool

(/=) :: MultiHeader -> MultiHeader -> Bool

Bounded MultiHeader Source # 
Instance details

Defined in Web.Spock.Internal.Wire

Methods

minBound :: MultiHeader

maxBound :: MultiHeader

Enum MultiHeader Source # 
Instance details

Defined in Web.Spock.Internal.Wire

Methods

succ :: MultiHeader -> MultiHeader

pred :: MultiHeader -> MultiHeader

toEnum :: Int -> MultiHeader

fromEnum :: MultiHeader -> Int

enumFrom :: MultiHeader -> [MultiHeader]

enumFromThen :: MultiHeader -> MultiHeader -> [MultiHeader]

enumFromTo :: MultiHeader -> MultiHeader -> [MultiHeader]

enumFromThenTo :: MultiHeader -> MultiHeader -> MultiHeader -> [MultiHeader]

Generic MultiHeader Source # 
Instance details

Defined in Web.Spock.Internal.Wire

Associated Types

type Rep MultiHeader 
Instance details

Defined in Web.Spock.Internal.Wire

type Rep MultiHeader = D1 ('MetaData "MultiHeader" "Web.Spock.Internal.Wire" "Spock-core-0.16.1.0-inplace" 'False) (((C1 ('MetaCons "MultiHeaderCacheControl" 'PrefixI 'False) (U1 :: Type -> Type) :+: (C1 ('MetaCons "MultiHeaderConnection" 'PrefixI 'False) (U1 :: Type -> Type) :+: C1 ('MetaCons "MultiHeaderContentEncoding" 'PrefixI 'False) (U1 :: Type -> Type))) :+: (C1 ('MetaCons "MultiHeaderContentLanguage" 'PrefixI 'False) (U1 :: Type -> Type) :+: (C1 ('MetaCons "MultiHeaderPragma" 'PrefixI 'False) (U1 :: Type -> Type) :+: C1 ('MetaCons "MultiHeaderProxyAuthenticate" 'PrefixI 'False) (U1 :: Type -> Type)))) :+: ((C1 ('MetaCons "MultiHeaderTrailer" 'PrefixI 'False) (U1 :: Type -> Type) :+: (C1 ('MetaCons "MultiHeaderTransferEncoding" 'PrefixI 'False) (U1 :: Type -> Type) :+: C1 ('MetaCons "MultiHeaderUpgrade" 'PrefixI 'False) (U1 :: Type -> Type))) :+: ((C1 ('MetaCons "MultiHeaderVia" 'PrefixI 'False) (U1 :: Type -> Type) :+: C1 ('MetaCons "MultiHeaderWarning" 'PrefixI 'False) (U1 :: Type -> Type)) :+: (C1 ('MetaCons "MultiHeaderWWWAuth" 'PrefixI 'False) (U1 :: Type -> Type) :+: C1 ('MetaCons "MultiHeaderSetCookie" 'PrefixI 'False) (U1 :: Type -> Type)))))

Methods

from :: MultiHeader -> Rep MultiHeader x

to :: Rep MultiHeader x -> MultiHeader

Show MultiHeader Source # 
Instance details

Defined in Web.Spock.Internal.Wire

Methods

showsPrec :: Int -> MultiHeader -> ShowS

show :: MultiHeader -> String

showList :: [MultiHeader] -> ShowS

Hashable MultiHeader Source # 
Instance details

Defined in Web.Spock.Internal.Wire

Methods

hashWithSalt :: Int -> MultiHeader -> Int

hash :: MultiHeader -> Int

type Rep MultiHeader Source # 
Instance details

Defined in Web.Spock.Internal.Wire

type Rep MultiHeader = D1 ('MetaData "MultiHeader" "Web.Spock.Internal.Wire" "Spock-core-0.16.1.0-inplace" 'False) (((C1 ('MetaCons "MultiHeaderCacheControl" 'PrefixI 'False) (U1 :: Type -> Type) :+: (C1 ('MetaCons "MultiHeaderConnection" 'PrefixI 'False) (U1 :: Type -> Type) :+: C1 ('MetaCons "MultiHeaderContentEncoding" 'PrefixI 'False) (U1 :: Type -> Type))) :+: (C1 ('MetaCons "MultiHeaderContentLanguage" 'PrefixI 'False) (U1 :: Type -> Type) :+: (C1 ('MetaCons "MultiHeaderPragma" 'PrefixI 'False) (U1 :: Type -> Type) :+: C1 ('MetaCons "MultiHeaderProxyAuthenticate" 'PrefixI 'False) (U1 :: Type -> Type)))) :+: ((C1 ('MetaCons "MultiHeaderTrailer" 'PrefixI 'False) (U1 :: Type -> Type) :+: (C1 ('MetaCons "MultiHeaderTransferEncoding" 'PrefixI 'False) (U1 :: Type -> Type) :+: C1 ('MetaCons "MultiHeaderUpgrade" 'PrefixI 'False) (U1 :: Type -> Type))) :+: ((C1 ('MetaCons "MultiHeaderVia" 'PrefixI 'False) (U1 :: Type -> Type) :+: C1 ('MetaCons "MultiHeaderWarning" 'PrefixI 'False) (U1 :: Type -> Type)) :+: (C1 ('MetaCons "MultiHeaderWWWAuth" 'PrefixI 'False) (U1 :: Type -> Type) :+: C1 ('MetaCons "MultiHeaderSetCookie" 'PrefixI 'False) (U1 :: Type -> Type)))))

text :: forall (m :: Type -> Type) ctx a. MonadIO m => Text -> ActionCtxT ctx m a Source #

Send text as a response body. Content-Type will be "text/plain"

html :: forall (m :: Type -> Type) ctx a. MonadIO m => Text -> ActionCtxT ctx m a Source #

Send a text as response body. Content-Type will be "text/html"

file :: forall (m :: Type -> Type) ctx a. MonadIO m => Text -> FilePath -> ActionCtxT ctx m a Source #

Send a file as response

json :: forall a (m :: Type -> Type) ctx b. (ToJSON a, MonadIO m) => a -> ActionCtxT ctx m b Source #

Send json as response. Content-Type will be "application/json"

stream :: forall (m :: Type -> Type) ctx a. MonadIO m => StreamingBody -> ActionCtxT ctx m a Source #

Use a WAI streaming body to generate a response.

response :: forall (m :: Type -> Type) ctx a. MonadIO m => (Status -> ResponseHeaders -> Response) -> ActionCtxT ctx m a Source #

Use a custom WAI response generator as response body.

respondApp :: forall (m :: Type -> Type) ctx a. Monad m => Application -> ActionCtxT ctx m a Source #

Respond to the request by running a WAI application. This is usefull in combination with wildcard routes. This can not be used in combination with other request consuming combinators like jsonBody, body, paramsPost, ...

respondMiddleware :: forall (m :: Type -> Type) ctx a. Monad m => Middleware -> ActionCtxT ctx m a Source #

Respond to the request by running WAI middleware. This is usefull in combination with wildcard routes. This can not be used in combination with other request consuming combinators like jsonBody, body, paramsPost, ...

Middleware helpers

middlewarePass :: forall (m :: Type -> Type) ctx a. MonadIO m => ActionCtxT ctx m a Source #

If the Spock application is used as a middleware, you can use this to pass request handling to the underlying application. If Spock is not uses as a middleware, or there is no underlying application this will result in 404 error.

modifyVault :: forall (m :: Type -> Type) ctx. MonadIO m => (Vault -> Vault) -> ActionCtxT ctx m () Source #

Modify the vault (useful for sharing data between middleware and app)

queryVault :: forall (m :: Type -> Type) a ctx. MonadIO m => Key a -> ActionCtxT ctx m (Maybe a) Source #

Query the vault

Basic HTTP-Auth

requireBasicAuth :: forall (m :: Type -> Type) ctx b a. MonadIO m => Text -> (Text -> Text -> ActionCtxT ctx m b) -> (b -> ActionCtxT ctx m a) -> ActionCtxT ctx m a Source #

Convenience Basic authentification provide a title for the prompt and a function to validate user and password. Usage example:

get ("auth" <//> var <//> var) $ \user pass ->
      let checker user' pass' =
              unless (user == user' && pass == pass') $
              do setStatus status401
                 text "err"
      in requireBasicAuth "Foo" checker $ \() -> text "ok"

withBasicAuthData :: forall (m :: Type -> Type) ctx a. MonadIO m => (Maybe (Text, Text) -> ActionCtxT ctx m a) -> ActionCtxT ctx m a Source #

"Lower level" basic authentification handeling. Does not set any headers that will promt browser users, only looks for an Authorization header in the request and breaks it into username and passwort component if present