Safe Haskell | Safe-Inferred |
---|---|
Language | GHC2021 |
Module for HTML-based servers
Synopsis
- newtype Server m = Server {}
- class ToText a where
- class ToHtmlResp a where
- toHtmlResp :: a -> Resp
- class FromText a where
- class Monad (ServerMonad a) => ToServer a where
- type ServerMonad a :: Type -> Type
- toServer :: a -> Server (ServerMonad a)
- withServerAction :: Monad m => Server m -> m () -> Server m
- (/.) :: ToServer a => Text -> a -> Server (ServerMonad a)
- newtype Capture a = Capture a
- newtype Query (sym :: Symbol) a = Query a
- newtype Optional (sym :: Symbol) a = Optional (Maybe a)
- newtype Body a = Body a
- newtype RawBody = RawBody ByteString
- newtype Header (sym :: Symbol) = Header (Maybe ByteString)
- newtype RawFormData = RawFormData FormBody
- data FormBody = FormBody {
- params :: [(ByteString, ByteString)]
- files :: [(ByteString, FileInfo ByteString)]
- newtype FormJson a = FormJson a
- newtype PathInfo = PathInfo [Text]
- data AddHeaders a = AddHeaders {
- headers :: ResponseHeaders
- content :: a
- data SetStatus a = SetStatus {}
- setStatus :: Monad m => Status -> Server m -> Server m
- addHeaders :: Monad m => ResponseHeaders -> Server m -> Server m
- data Error a = Error {}
- handleError :: (Exception a, MonadCatch m) => (a -> Server m) -> Server m -> Server m
- class Monad m => HasServer m where
- type ServerResult m :: Type
- renderServer :: Server m -> ServerResult m
- fromReader :: env -> Server (ReaderT env IO) -> IO (Server IO)
- data ServerConfig = ServerConfig {}
- toApplication :: ServerConfig -> Server IO -> Application
- runServer :: Int -> Server IO -> IO ()
- badRequest :: Text -> Resp
- module Network.HTTP.Types.Status
types
Server type. It is a function fron request to response. Some servers does not return valid value. We use it to find right path.
Example:
server :: Server IO server = "api" /. "v1" /. mconcat [ "foo" /. (\(Query @"name" arg) -> Get @Json (handleFoo arg) , "bar" /. Post @Json handleBar ] handleFoo :: Int -> IO Text handleBar :: IO Text
Note that server is monoid and it can be constructed with Monoid functions and
path constructor (/.)
. To pass inputs for handler we can use special newtype wrappers:
Query
- for required query parametersOptional
- for optional query parametersCapture
- for parsing elements of URIBody
- fot JSON-body inputRawBody
- for raw ByteString inputHeader
- for headers
To distinguish by HTTP-method we use corresponding constructors: Get, Post, Put, etc. Let's discuss the structure of the constructor. Let's take Get for example:
newtype Get ty m a = Get (m a)
Let's look at the arguments of he type
ty
- type of the response. it can be: Text, Html, Json, ByteStringm
- underlying server monada
- result type. It should be convertible to the type of the response.
also result can be wrapped to special data types to modify Http-response. we have wrappers:
SetStatus
- to set statusAddHeaders
- to append headersEither (Error err)
- to response with errors
DSL
Values convertible to lazy text
class ToHtmlResp a where Source #
Values convertible to Html
toHtmlResp :: a -> Resp Source #
Instances
ToMarkup a => ToHtmlResp a Source # | |
Defined in Mig toHtmlResp :: a -> Resp Source # | |
ToHtmlResp a => ToHtmlResp (AddHeaders a) Source # | |
Defined in Mig toHtmlResp :: AddHeaders a -> Resp Source # | |
ToHtmlResp a => ToHtmlResp (SetStatus a) Source # | |
(ToJSON err, ToHtmlResp a) => ToHtmlResp (Either (Error err) a) Source # | |
class FromText a where Source #
Aything convertible from text
class Monad (ServerMonad a) => ToServer a where Source #
Class ToServer contains anything convertible to Server m
. We use it for flexuble composition
of servers from functions with arbitrary number of arguments. Arguments can
be of specific types: Query, Body, Optional, Capture, Header, etc.
We use type-level strings to encode query-names.
Example:
"api" /. "foo" /. (\(Query @"argA" argA) (Optional @"argB" argB) (Body jsonRequest) -> Post @Json $ handleFoo argA argB jsonRequest) handleFoo :: Int -> Maybe Text -> FooRequest -> IO FooResponse handleFoo = ...
Note that we can use any amount of arguments. And type of the input is decoded fron newtype wrapper which is used with argument of the handler function.
Also we can return pure errors with Either. Anything which can be returned from function
can be wrapped to Either (Error err)
.
For example in previous case we can use function which returns errors as values:
type ServerError = Error Text handleFoo :: Int -> Maybe Text -> FooRequest -> IO (Either ServerError FooResponse) handleFoo = ...
the result of error response is automatically matched with normal response of the server and standard Error type lets us pass status to response and some details.
type ServerMonad a :: Type -> Type Source #
toServer :: a -> Server (ServerMonad a) Source #
Instances
path and query
(/.) :: ToServer a => Text -> a -> Server (ServerMonad a) infixr 4 Source #
Path constructor (right associative). Example:
server :: Server IO server = "api" /. "v1" /. mconcat [ "foo" /. Get @Json handleFoo , "bar" /. Post @Json handleBar ] handleFoo, handleBar :: IO Text
Capture a |
newtype Query (sym :: Symbol) a Source #
Mandatary query parameter. Name is encoded as type-level string. Example:
"api" /. handleFoo handleFoo :: Query "name" Int -> Server IO handleFoo (Query arg) = ...
Query a |
newtype Optional (sym :: Symbol) a Source #
Optional query parameter. Name is encoded as type-level string. Example:
"api" /. handleFoo handleFoo :: Optional "name" -> Server IO handleFoo (Optional maybeArg) = ...
Reads Json body (lazy). We can limit the body size with server config. Example:
"api" /. "search" /. (\(Body request) -> handleSearch request)
Body a |
Reads raw body as lazy bytestring. We can limit the body size with server config. Example:
"api" /. "upload" /. (\(RawBody content) -> handleUpload content)
newtype Header (sym :: Symbol) Source #
Reads input header. Example:
"api" /. (\(Header @"Trace-Id" traceId) -> Post @Json (handleFoo traceId)) handleFoo :: Maybe ByteString -> IO FooResponse
newtype RawFormData Source #
Parse raw form body. It includes named form arguments and file info. Note that we can not use FormBody and JSON-body at the same time. They occupy the same field in the HTTP-request.
Instances
(ToServer b, MonadIO (ServerMonad b)) => ToServer (RawFormData -> b) Source # | |
Defined in Mig type ServerMonad (RawFormData -> b) :: Type -> Type Source # toServer :: (RawFormData -> b) -> Server (ServerMonad (RawFormData -> b)) Source # | |
type ServerMonad (RawFormData -> b) Source # | |
Defined in Mig |
FormBody | |
|
It reads form as plain JSON-object where name of the form's field becomes a field of JSON-object and every value is Text.
For example if submit a form with fields: name, password, date. We can read it in the data type:
data User = User { name :: Text , passord :: Text , date :: Text }
Note that we can not use FormBody and JSON-body at the same time. They occupy the same field in the HTTP-request.
FormJson a |
Reads current path info
response
data AddHeaders a Source #
Attach headers to response. It can be used inside any ToXxxResp value. Example:
"api" /. handleFoo handleFoo :: Get Text IO (AddHeaders Text) handleFoo = Get $ pure $ AddHeaders headers "Hello foo"
AddHeaders | |
|
Instances
ToHtmlResp a => ToHtmlResp (AddHeaders a) Source # | |
Defined in Mig toHtmlResp :: AddHeaders a -> Resp Source # | |
ToJsonResp a => ToJsonResp (AddHeaders a) Source # | |
Defined in Mig toJsonResp :: AddHeaders a -> Resp Source # | |
ToTextResp a => ToTextResp (AddHeaders a) Source # | |
Defined in Mig toTextResp :: AddHeaders a -> Resp Source # |
Set status to response. It can be ised inside any ToXxxResp value. Example:
"api" /. handleFoo handleFoo :: Get Text IO (SetStatus Text) handleFoo = Get $ pure $ SetStatus status500 "Bad request"
Instances
ToHtmlResp a => ToHtmlResp (SetStatus a) Source # | |
ToJsonResp a => ToJsonResp (SetStatus a) Source # | |
ToTextResp a => ToTextResp (SetStatus a) Source # | |
setStatus :: Monad m => Status -> Server m -> Server m Source #
Sets status for response of the server
addHeaders :: Monad m => ResponseHeaders -> Server m -> Server m Source #
Adds headers for response of the server
Errors
Errors
Instances
(Typeable a, Show a) => Exception (Error a) Source # | |
Defined in Mig.Internal.Types toException :: Error a -> SomeException # fromException :: SomeException -> Maybe (Error a) # displayException :: Error a -> String # | |
Show a => Show (Error a) Source # | |
(Show a, Typeable a) => HasServer (ReaderT env (ExceptT (Error a) IO)) Source # | |
(ToJSON err, ToHtmlResp a) => ToHtmlResp (Either (Error err) a) Source # | |
(ToJSON err, ToJsonResp a) => ToJsonResp (Either (Error err) a) Source # | |
(ToText err, ToTextResp a) => ToTextResp (Either (Error err) a) Source # | |
type ServerResult (ReaderT env (ExceptT (Error a) IO)) Source # | |
handleError :: (Exception a, MonadCatch m) => (a -> Server m) -> Server m -> Server m Source #
Handle errors
Render
class Monad m => HasServer m where Source #
Class contains types which can be converted to IO-based server to run as with WAI-interface.
We can run plain IO-servers and ReaderT over IO based servers. Readers can be wrapped in newtypes.
In that case we can derive automatically HasServer
instance.
type ServerResult m :: Type Source #
renderServer :: Server m -> ServerResult m Source #
fromReader :: env -> Server (ReaderT env IO) -> IO (Server IO) Source #
Render reader server to IO-based server
Run
toApplication :: ServerConfig -> Server IO -> Application Source #
Convert server to WAI-application
utils
badRequest :: Text -> Resp Source #
Bad request response
module Network.HTTP.Types.Status