Safe Haskell | None |
---|---|
Language | Haskell2010 |
This module provides a WAI Middleware
for routing requests
to handlers (i.e. Application
s) based on the request path,
thereby optionally capturing variables.
The basic functionality is provided by route
and routePrefix
.
The RoutingTrie
s for the middleware can be constructed directly
or by compiling Route
s via the construction of Path
s,
which offers enhanced type-safety.
Handlers may furthermore parse parameters from query strings,
via the construction of Query
s. Some additional functions
for working with HTTP methods and request headers are also
provided.
The sources contain some examples.
Strictness: This module uses -XStrictData
.
Synopsis
- type App m = Request -> (Response -> m ResponseReceived) -> m ResponseReceived
- type Handler m = Seq (Capture Text) -> App m
- type RoutingTrie m = Trie Text (Handler m)
- route :: Monad m => RoutingTrie m -> App m -> App m
- routePrefix :: Monad m => RoutingTrie m -> App m -> App m
- data Route m = Route {
- routePath :: Path vars
- routeHandler :: Params vars -> App m
- routeInvalidParam :: InvalidParam -> App m
- defRoute :: Monad m => Path vars -> (Params vars -> App m) -> Route m
- compileRoute :: Monad m => Route m -> (Pattern Text, Handler m)
- compileRoutes :: Monad m => [Route m] -> RoutingTrie m
- data Params :: Vars -> Type where
- type ParamName = String
- data InvalidParam = InvalidParam {}
- data Path :: Vars -> Type
- type Vars = [(Symbol, Type)]
- type Var s a = '(s, a)
- type Some a = Var "" a
- (=~=) :: Path vars -> Path vars' -> Bool
- str :: Text -> Path vars -> Path vars
- var :: (KnownSymbol s, Eq a, FromHttpApiData a) => Path vars -> Path (Var s a ': vars)
- some :: (Eq a, FromHttpApiData a) => Path vars -> Path (Some a ': vars)
- (./) :: (Path vars -> Path vars') -> Path vars -> Path vars'
- end :: Path '[]
- pathVarsLen :: Path vars -> Int
- pathPattern :: Path vars -> Pattern Text
- parsePath :: Path vars -> Seq (Capture Text) -> Either PathError (Params vars)
- data PathError
- data SomePath = SomePath (Path vars)
- data Query :: Vars -> Type
- qreq :: (KnownSymbol s, FromHttpApiData a, Eq a) => Query '[Var s a]
- qdef :: (KnownSymbol s, FromHttpApiData a, Eq a) => a -> Query '[Var s a]
- qopt :: (KnownSymbol s, FromHttpApiData a, Eq a) => Query '[Var s (Maybe a)]
- (.&.) :: (Eq a, KnownSymbol s, FromHttpApiData a) => Query '[Var s a] -> Query (Var s' a' ': vars) -> Query (Var s a ': (Var s' a' ': vars))
- withQuery :: Query vars -> (QueryError -> App m) -> (Params vars -> App m) -> App m
- parseQuery :: Query vars -> [QueryItem] -> Either QueryError (Params vars)
- data QueryError
- type family VarsLen vars :: Nat where ...
- knownVarsLen :: forall proxy vars. KnownNat (VarsLen vars) => proxy vars -> Integer
- getMethod :: Request -> Either ByteString StdMethod
- byMethod :: (StdMethod -> App m) -> App m
- withMethod :: Monad m => StdMethod -> App m -> App m
- getQueryParam :: FromHttpApiData a => Request -> ByteString -> Maybe (Either InvalidParam a)
- getQueryParam' :: FromHttpApiData a => Request -> Text -> Maybe (Either InvalidParam a)
- getHeader :: FromHttpApiData a => Request -> HeaderName -> Maybe (Either InvalidHeader a)
- data InvalidHeader = InvalidHeader {}
- appInvalidParam :: InvalidParam -> App m
- appMissingParam :: ParamName -> App m
- appQueryError :: QueryError -> App m
- app400 :: App m
- app404 :: App m
- app405 :: App m
- data Trie s a
- type Pattern s = Seq (Matcher s)
- data Matcher s
- newtype Capture s = Capture {
- captured :: s
- class FromHttpApiData a
Documentation
The following extensions are used for all inline examples.
>>>
:set -XDataKinds
>>>
:set -XOverloadedStrings
>>>
:set -XTypeApplications
More extensive examples can be found in the examples
directory of the source distribution.
Middleware
type App m = Request -> (Response -> m ResponseReceived) -> m ResponseReceived Source #
An App
is a WAI Application
generalised to any type
of kind * -> *
, and thus in particular any monad, i.e.
App IO ~ Application
.
routePrefix :: Monad m => RoutingTrie m -> App m -> App m Source #
Routes requests to Handler
s via a routing trie, passing along
captured path parameters. A prefix of the request path must match a route in
order for the associated handler function to be called. Thereby the route
for the longest matching prefix is chosen. If no route matches a
prefix of the request path, the request is forward to the App
lication
given as second argument.
Note: With prefix routing, the pathInfo
of the Request
passed to
a handler contains only the (unmatched) suffix of the request path, enabling
nested / chained routing with multiple routing tries. An example for
the composition of routing tries can be seen
here.
Routes
A route whose handler is run if the path is a match for a request path.
The handler function is thereby given the captured and parsed Params
.
Route | |
|
defRoute :: Monad m => Path vars -> (Params vars -> App m) -> Route m Source #
Define a Route
with the given path and handler, using default
values for other arguments.
compileRoute :: Monad m => Route m -> (Pattern Text, Handler m) Source #
Compile a Route
into a pair of a Pattern
and a Handler
,
suitable for insertion into a RoutingTrie
.
compileRoutes :: Monad m => [Route m] -> RoutingTrie m Source #
Compile a list of Route
s into a RoutingTrie
.
Parameters
data InvalidParam Source #
A parameter could not be parsed correctly.
Instances
Eq InvalidParam Source # | |
Defined in Network.Wai.Route (==) :: InvalidParam -> InvalidParam -> Bool # (/=) :: InvalidParam -> InvalidParam -> Bool # | |
Read InvalidParam Source # | |
Defined in Network.Wai.Route readsPrec :: Int -> ReadS InvalidParam # readList :: ReadS [InvalidParam] # | |
Show InvalidParam Source # | |
Defined in Network.Wai.Route showsPrec :: Int -> InvalidParam -> ShowS # show :: InvalidParam -> String # showList :: [InvalidParam] -> ShowS # |
Paths
data Path :: Vars -> Type Source #
A path of a Route
, indexed by the names and types of
the captured variables.
Paths are constructed using str
, var
, some
, glued
together by ./
and terminated by end
, e.g.
>>>
let p = str "a" ./ var @"b" @Int ./ str "c" ./ some @Text ./ end
>>>
:t p
p :: Path '[Var "b" Int, Some Text]
Two different paths are overlapping iff their underlying Pattern
s,
as given by pathPattern
, are overlapping. The preference given to
routes based on overlapping paths is given by the preference between
the overlapping patterns (see the documentation for Pattern
s).
Instances
Eq (Path vars) Source # | Equality for paths indexed by the same |
Show (Path vars) Source # | Shows paths with a leading "/", whereby
|
(=~=) :: Path vars -> Path vars' -> Bool Source #
Structural equality of paths.
p =~= p
\(\iff\) pathPattern
p == pathPattern p
str :: Text -> Path vars -> Path vars Source #
Match a fixed string, capturing nothing, e.g.
>>>
let p = str "tmp" ./ end
>>>
:t p
p :: Path '[]
var :: (KnownSymbol s, Eq a, FromHttpApiData a) => Path vars -> Path (Var s a ': vars) Source #
Capture a parameter as a named variable, e.g.
>>>
let p = var @"name" @Text ./ end
>>>
:t p
p :: Path '[Var "name" Text]
The type-level variable name can serve at least two purposes:
- It allows to disambiguate multiple parameters of the same type in the path for extra type safety (i.e. against mixing up the order of parameters).
- The name is made available at runtime when parsing of a parameter
fails in the form of an
InvalidParam
error, enabling its use in error responses, logs, etc.
some :: (Eq a, FromHttpApiData a) => Path vars -> Path (Some a ': vars) Source #
Capture a parameter as an unnamed variable, e.g.
>>>
let p = some @Text ./ end
>>>
:t p
p :: Path '[Some Text]
(./) :: (Path vars -> Path vars') -> Path vars -> Path vars' infixr 5 Source #
Right-associative infix operator for constructing Path
s:
>>>
let p = str "a" ./ some @Int ./ var @"y" @Int ./ end
>>>
:t p
p :: Path '[Some Int, Var "y" Int]
pathVarsLen :: Path vars -> Int Source #
Compute the length of the list of variables in a Path
, at runtime.
\(\mathcal{O}(n)\), where n
is the total length of the path.
>>>
let p = str "a" ./ some @Text ./ end
>>>
pathVarsLen p
1
See also knownVarsLen
for lengths known at compile-time.
An error during parsing of the parameters of a Path
.
PathMissingParams | The path contains more variables than the number of captures given. |
PathInvalidParam InvalidParam | A parameter failed to parse into the type expected by the corresponding variable of the path. |
A path with existentially quantified variables.
Query Strings
qreq :: (KnownSymbol s, FromHttpApiData a, Eq a) => Query '[Var s a] Source #
A required query parameter.
qdef :: (KnownSymbol s, FromHttpApiData a, Eq a) => a -> Query '[Var s a] Source #
A query parameter with a default value. The default only applies when the parameter is absent or has no value, not if there is a value that fails to parse.
qopt :: (KnownSymbol s, FromHttpApiData a, Eq a) => Query '[Var s (Maybe a)] Source #
An optional query parameter. The parameter value is Nothing
only if
the parameter is absent or has no value, not if there is a value
that fails to parse.
(.&.) :: (Eq a, KnownSymbol s, FromHttpApiData a) => Query '[Var s a] -> Query (Var s' a' ': vars) -> Query (Var s a ': (Var s' a' ': vars)) infixr 5 Source #
Combine a query parameter with one or more other query parameters.
parseQuery :: Query vars -> [QueryItem] -> Either QueryError (Params vars) Source #
data QueryError Source #
An error during parsing of the parameters for a Query
.
QueryMissingParam ParamName | A query parameter is missing. |
QueryInvalidParam InvalidParam | A query parameter failed to parse correctly. |
Instances
Eq QueryError Source # | |
Defined in Network.Wai.Route (==) :: QueryError -> QueryError -> Bool # (/=) :: QueryError -> QueryError -> Bool # | |
Read QueryError Source # | |
Defined in Network.Wai.Route readsPrec :: Int -> ReadS QueryError # readList :: ReadS [QueryError] # readPrec :: ReadPrec QueryError # readListPrec :: ReadPrec [QueryError] # | |
Show QueryError Source # | |
Defined in Network.Wai.Route showsPrec :: Int -> QueryError -> ShowS # show :: QueryError -> String # showList :: [QueryError] -> ShowS # |
Utilities
HTTP Methods
getMethod :: Request -> Either ByteString StdMethod Source #
Get and parse the request method as a StdMethod
.
byMethod :: (StdMethod -> App m) -> App m Source #
Dispatch a request to an application based on the standardised
HTTP request methods (verbs). If the request method is not a
standard method, app405
is called.
HTTP Query Parameters
getQueryParam :: FromHttpApiData a => Request -> ByteString -> Maybe (Either InvalidParam a) Source #
Get and parse a query parameter by its name, assuming UTF-8
encoding of the value. If the query parameter is not present in the
request or has an empty value, Nothing
is returned.
If the parameter name is known to contain only ASCII characters (the most
common case), this function is more efficient than 'getQueryParam\'', since
query parameter names are plain ByteString
s in the WAI.
getQueryParam' :: FromHttpApiData a => Request -> Text -> Maybe (Either InvalidParam a) Source #
Like getQueryParam
but supports UTF-8 encoded names of query parameters.
HTTP Headers
getHeader :: FromHttpApiData a => Request -> HeaderName -> Maybe (Either InvalidHeader a) Source #
Get an parse the value of an HTTP header.
data InvalidHeader Source #
A header that failed to parse.
Instances
Eq InvalidHeader Source # | |
Defined in Network.Wai.Route (==) :: InvalidHeader -> InvalidHeader -> Bool # (/=) :: InvalidHeader -> InvalidHeader -> Bool # | |
Read InvalidHeader Source # | |
Defined in Network.Wai.Route readsPrec :: Int -> ReadS InvalidHeader # readList :: ReadS [InvalidHeader] # | |
Show InvalidHeader Source # | |
Defined in Network.Wai.Route showsPrec :: Int -> InvalidHeader -> ShowS # show :: InvalidHeader -> String # showList :: [InvalidHeader] -> ShowS # |
Standard Applications
appInvalidParam :: InvalidParam -> App m Source #
An application that always yields a 400 Bad Request plaintext response for an invalid parameter.
appMissingParam :: ParamName -> App m Source #
An application that always yields a 400 Bad Request plaintext response for a missing parameter.
appQueryError :: QueryError -> App m Source #
An application that always yields a 400 Bad Request plaintext response for an invalid or missing query parameter.
Re-exports
An unordered map from Pattern
s of strings of type s
to values
of type a
.
Instances
Functor (Trie s) | |
Foldable (Trie s) | |
Defined in Data.Trie.Pattern fold :: Monoid m => Trie s m -> m # foldMap :: Monoid m => (a -> m) -> Trie s a -> m # foldr :: (a -> b -> b) -> b -> Trie s a -> b # foldr' :: (a -> b -> b) -> b -> Trie s a -> b # foldl :: (b -> a -> b) -> b -> Trie s a -> b # foldl' :: (b -> a -> b) -> b -> Trie s a -> b # foldr1 :: (a -> a -> a) -> Trie s a -> a # foldl1 :: (a -> a -> a) -> Trie s a -> a # elem :: Eq a => a -> Trie s a -> Bool # maximum :: Ord a => Trie s a -> a # minimum :: Ord a => Trie s a -> a # | |
Traversable (Trie s) | |
(Eq s, Eq a) => Eq (Trie s a) | |
(Eq s, Hashable s, Read s, Read a) => Read (Trie s a) | |
(Show s, Show a) => Show (Trie s a) | |
Generic (Trie s a) | |
(Eq s, Hashable s) => Semigroup (Trie s a) | Note (left preference): If two tries have a value attached to
the same |
(Eq s, Hashable s) => Monoid (Trie s a) | Note: |
(NFData s, NFData a) => NFData (Trie s a) | |
Defined in Data.Trie.Pattern | |
type Rep (Trie s a) | |
Defined in Data.Trie.Pattern type Rep (Trie s a) = D1 (MetaData "Trie" "Data.Trie.Pattern" "pattern-trie-0.1.0-EO2ub3nKhTEUToUvE7Jyd" False) (C1 (MetaCons "Trie" PrefixI True) (S1 (MetaSel (Just "strtries") NoSourceUnpackedness SourceStrict DecidedStrict) (Rec0 (HashMap s (Trie s a))) :*: (S1 (MetaSel (Just "vartrie") NoSourceUnpackedness SourceStrict DecidedStrict) (Rec0 (Maybe (Trie s a))) :*: S1 (MetaSel (Just "value") NoSourceUnpackedness SourceStrict DecidedStrict) (Rec0 (Maybe a))))) |
type Pattern s = Seq (Matcher s) #
A pattern is a sequence of Matcher
s and serves as a key in a pattern
trie.
If two patterns are overlapping for an input string, the preference for
a match
is given by the partial order EqStr > AnyStr
on the competing
matchers, i.e. towards the more specific pattern. This partial order is
witnessed and subsumed by the total order MatchOrd
.
The preference for a prefix match is reversed, i.e. for an input string where
only a proper prefix is a match for overlapping patterns, the preference
is given by the partial order AnyStr > EqStr
, i.e. towards the more general
pattern. This partial order is witnessed and subsumed by the total order
PrefixMatchOrd
.
A Matcher
is applied on a single chunk of an input Str
ing
while looking for a match
and either succeeds or fails. If it succeeds,
it may Capture
the chunk.
AnyStr | Match and capture an arbitrary chunk of an input string. |
EqStr !s | Match a chunk of an input string exactly, capturing nothing. |
Instances
Eq s => Eq (Matcher s) | |
Read s => Read (Matcher s) | |
Show s => Show (Matcher s) | |
Generic (Matcher s) | |
NFData s => NFData (Matcher s) | |
Defined in Data.Trie.Pattern | |
type Rep (Matcher s) | |
Defined in Data.Trie.Pattern type Rep (Matcher s) = D1 (MetaData "Matcher" "Data.Trie.Pattern" "pattern-trie-0.1.0-EO2ub3nKhTEUToUvE7Jyd" False) (C1 (MetaCons "AnyStr" PrefixI False) (U1 :: Type -> Type) :+: C1 (MetaCons "EqStr" PrefixI False) (S1 (MetaSel (Nothing :: Maybe Symbol) NoSourceUnpackedness SourceStrict DecidedStrict) (Rec0 s))) |
A captured chunk of an input Str
ing.
class FromHttpApiData a #
Parse value from HTTP API data.
WARNING: Do not derive this using DeriveAnyClass
as the generated
instance will loop indefinitely.