simple-0.11.2: A minimalist web framework for the WAI server interface

Safe Haskell | Trustworthy
Language | Haskell2010

Web.Simple.Controller

Contents

- Example
- Common Routes
- Inspecting query
- Redirection via referrer
- Exception handling
- Low-level utilities

Description

`Controller` provides a convenient syntax for writting `Application` code as a Monadic action with access to an HTTP request as well as app specific data (e.g. a database connection pool, app configuration etc.) This module also defines some helper functions that leverage this feature. For example, `redirectBack` reads the underlying request to extract the referer and returns a redirect response:

       myController = do
         ...
         if badLogin then
           redirectBack
           else
             ...

Synopsis

- type Controller s = ControllerT s IO
- newtype ControllerT s m a = ControllerT {
  - runController :: s -> Request -> m (Either Response a, s)
  }
- controllerApp :: s -> Controller s a -> Application
- controllerState :: Controller s s
- putState :: s -> Controller s ()
- request :: Controller s Request
- localRequest :: (Request -> Request) -> Controller s a -> Controller s a
- respond :: Response -> Controller s a
- requestHeader :: HeaderName -> Controller s (Maybe ByteString)
- routeHost :: ByteString -> Controller s a -> Controller s ()
- routeTop :: Controller s a -> Controller s ()
- routeMethod :: StdMethod -> Controller s a -> Controller s ()
- routeAccept :: ByteString -> Controller s a -> Controller s ()
- routePattern :: Text -> Controller s a -> Controller s ()
- routeName :: Text -> Controller s a -> Controller s ()
- routeVar :: Text -> Controller s a -> Controller s ()
- class Parseable a
- queryParam :: Parseable a => ByteString -> Controller s (Maybe a)
- queryParam' :: Parseable a => ByteString -> Controller s a
- queryParams :: Parseable a => ByteString -> Controller s [a]
- readQueryParam :: Read a => ByteString -> Controller s (Maybe a)
- readQueryParam' :: Read a => ByteString -> Controller s a
- readQueryParams :: Read a => ByteString -> Controller s [a]
- parseForm :: Controller s ([Param], [(ByteString, FileInfo ByteString)])
- redirectBack :: Controller s a
- redirectBackOr :: Response -> Controller s a
- data ControllerException
- body :: Controller s ByteString
- hoistEither :: Either Response a -> Controller s a

# Example

The most basic `Routeable` types are `Application` and `Response`. Reaching either of these types marks a termination in the routing lookup. This module exposes a monadic type `Route` which makes it easy to create routing logic in a DSL-like fashion.

`Route`s are concatenated using the `>>` operator (or using do-notation). In the end, any `Routeable`, including a `Route` is converted to an `Application` and passed to the server using `mkRoute`:

      mainAction :: Controller () ()
      mainAction = ...

      signinForm :: Controller () ()
      signinForm req = ...

      login :: Controller () ()
      login = ...

      updateProfile :: Controller () ()
      updateProfile = ...

      main :: IO ()
      main = run 3000 $ controllerApp () $ do
        routeTop mainAction
        routeName "sessions" $ do
          routeMethod GET signinForm
          routeMethod POST login
        routeMethod PUT $ routePattern "users/:id" updateProfile
        routeAll $ responseLBS status404 [] "Are you in the right place?"

type Controller s = ControllerT s IO Source #

The Controller Monad is both a State-like monad which, when run, computes either a `Response` or a result. Within the Controller Monad, the remainder of the computation can be short-circuited by `respond`ing with a `Response`.

newtype ControllerT s m a Source #

The ControllerT Monad is both a State-like monad which, when run, computes either a `Response` or a result. Within the ControllerT Monad, the remainder of the computation can be short-circuited by `respond`ing with a `Response`.

Constructors

ControllerT |

Fields

- runController :: s -> Request -> m (Either Response a, s)

Instances

(Applicative m, Monad m, MonadBase m m) => MonadBase m (ControllerT s m) Source # |
Methods liftBase :: m α -> ControllerT s m α #
MonadBaseControl m m => MonadBaseControl m (ControllerT s m) Source # |
Associated Types type StM (ControllerT s m :: * -> *) a :: * # Methods liftBaseWith :: (RunInBase (ControllerT s m) m -> m a) -> ControllerT s m a # restoreM :: StM (ControllerT s m) a -> ControllerT s m a #
Monad m => MonadReader Request (ControllerT s m) Source # |
Methods ask :: ControllerT s m Request # local :: (Request -> Request) -> ControllerT s m a -> ControllerT s m a # reader :: (Request -> a) -> ControllerT s m a #
Monad m => MonadState s (ControllerT s m) Source # |
Methods get :: ControllerT s m s # put :: s -> ControllerT s m () # state :: (s -> (a, s)) -> ControllerT s m a #
MonadTrans (ControllerT s) Source # |
Methods lift :: Monad m => m a -> ControllerT s m a #
Monad m => Monad (ControllerT s m) Source # |
Methods (>>=) :: ControllerT s m a -> (a -> ControllerT s m b) -> ControllerT s m b # (>>) :: ControllerT s m a -> ControllerT s m b -> ControllerT s m b # return :: a -> ControllerT s m a # fail :: String -> ControllerT s m a #
Functor m => Functor (ControllerT s m) Source # |
Methods fmap :: (a -> b) -> ControllerT s m a -> ControllerT s m b # (<$) :: a -> ControllerT s m b -> ControllerT s m a #
(Monad m, Functor m) => Applicative (ControllerT s m) Source # |
Methods pure :: a -> ControllerT s m a # (<*>) :: ControllerT s m (a -> b) -> ControllerT s m a -> ControllerT s m b # (*>) :: ControllerT s m a -> ControllerT s m b -> ControllerT s m b # (<*) :: ControllerT s m a -> ControllerT s m b -> ControllerT s m a #
MonadIO m => MonadIO (ControllerT s m) Source # |
Methods liftIO :: IO a -> ControllerT s m a #
(Functor m, Monad m) => Alternative (ControllerT s m) Source # |
Methods empty :: ControllerT s m a # (<|>) :: ControllerT s m a -> ControllerT s m a -> ControllerT s m a # some :: ControllerT s m a -> ControllerT s m [a] # many :: ControllerT s m a -> ControllerT s m [a] #
Monad m => MonadPlus (ControllerT s m) Source # |
Methods mzero :: ControllerT s m a # mplus :: ControllerT s m a -> ControllerT s m a -> ControllerT s m a #
type StM (ControllerT s m) a Source # |
type StM (ControllerT s m) a = (Either Response a, s)

controllerApp :: s -> Controller s a -> Application Source #

Convert the controller into an `Application`

controllerState :: Controller s s Source #

Extract the application-specific state

putState :: s -> Controller s () Source #

request :: Controller s Request Source #

Extract the request

localRequest :: (Request -> Request) -> Controller s a -> Controller s a Source #

Modify the request for the given computation

respond :: Response -> Controller s a Source #

Provide a response

    respond r >>= f === respond r

requestHeader :: HeaderName -> Controller s (Maybe ByteString) Source #

Returns the value of the given request header or `Nothing` if it is not present in the HTTP request.

# Common Routes

routeHost :: ByteString -> Controller s a -> Controller s () Source #

Matches on the hostname from the `Request`. The route only succeeds on exact matches.

routeTop :: Controller s a -> Controller s () Source #

Matches if the path is empty.

Note that this route checks that `pathInfo` is empty, so it works as expected in nested contexts that have popped components from the `pathInfo` list.

routeMethod :: StdMethod -> Controller s a -> Controller s () Source #

Matches on the HTTP request method (e.g. `GET`, `POST`, `PUT`)

routeAccept :: ByteString -> Controller s a -> Controller s () Source #

Matches if the request's Content-Type exactly matches the given string

routePattern :: Text -> Controller s a -> Controller s () Source #

Routes the given URL pattern. Patterns can include directories as well as variable patterns (prefixed with `:`) to be added to `queryString` (see `routeVar`)

- /posts/:id
- /posts/:id/new
- /:date/posts/:category/new

routeName :: Text -> Controller s a -> Controller s () Source #

Matches if the first directory in the path matches the given `ByteString`

routeVar :: Text -> Controller s a -> Controller s () Source #

Always matches if there is at least one directory in `pathInfo` but and adds a parameter to `queryString` where the key is the first parameter and the value is the directory consumed from the path.

# Inspecting query

class Parseable a Source #

The class of types into which query parameters may be converted

Minimal complete definition

parse

Instances

Parseable ByteString Source # |
Methods parse :: ByteString -> ByteString Source #
Parseable String Source # |
Methods parse :: ByteString -> String Source #
Parseable Text Source # |
Methods parse :: ByteString -> Text Source #

queryParam Source #

Arguments

:: Parseable a |
=> ByteString | Parameter name
-> Controller s (Maybe a) |

Looks up the parameter name in the request's query string and returns the `Parseable` value or `Nothing`.

For example, for a request with query string: "?foo=bar&baz=7", `queryParam "foo"` would return `Just "bar"`, but `queryParam "zap"` would return `Nothing`.

queryParam' :: Parseable a => ByteString -> Controller s a Source #

Like `queryParam`, but throws an exception if the parameter is not present.

queryParams :: Parseable a => ByteString -> Controller s [a] Source #

Selects all values with the given parameter name

readQueryParam Source #

Arguments

:: Read a |
=> ByteString | Parameter name
-> Controller s (Maybe a) |

Like `queryParam`, but further processes the parameter value with `read`. If that conversion fails, an exception is thrown.

readQueryParam' Source #

Arguments

:: Read a |
=> ByteString | Parameter name
-> Controller s a |

Like `readQueryParam`, but throws an exception if the parameter is not present.

readQueryParams Source #

Arguments

:: Read a |
=> ByteString | Parameter name
-> Controller s [a] |

Like `queryParams`, but further processes the parameter values with `read`. If any read-conversion fails, an exception is thrown.

parseForm :: Controller s ([Param], [(ByteString, FileInfo ByteString)]) Source #

Parses a HTML form from the request body. It returns a list of `Param`s as well as a list of `File`s, which are pairs mapping the name of a file form field to a `FileInfo` pointing to a temporary file with the contents of the upload.

      myControllerT = do
        (prms, files) <- parseForm
        let mPicFile = lookup "profile_pic" files
        case mPicFile of
          Just (picFile) -> do
            sourceFile (fileContent picFile) $$
              sinkFile ("images/" ++ (fileName picFile))
            respond $ redirectTo "/"
          Nothing -> redirectBack

# Redirection via referrer

redirectBack :: Controller s a Source #

Redirect back to the referer. If the referer header is not present redirect to root (i.e., `/`).

redirectBackOr Source #

Arguments

:: Response | Fallback response
-> Controller s a |

Redirect back to the referer. If the referer header is not present fallback on the given `Response`.

# Exception handling

data ControllerException Source #

Instances

Show ControllerException Source # |
Methods showsPrec :: Int -> ControllerException -> ShowS # show :: ControllerException -> String # showList :: [ControllerException] -> ShowS #
Exception ControllerException Source # |
Methods toException :: ControllerException -> SomeException # fromException :: SomeException -> Maybe ControllerException # displayException :: ControllerException -> String #

# Low-level utilities

body :: Controller s ByteString Source #

Reads and returns the body of the HTTP request.

hoistEither :: Either Response a -> Controller s a Source #