Turn a Result into a Request. Useful with andThen
. Turns Err
into a skipped request handler (non-matching request),
and Ok
values into a succeed
(matching request).
This is a Request.Parser that will never match an HTTP request. Similar to Json.Decode.fail
.
Why would you want it to always fail? It's helpful for building custom Server.Request.Parser
. For example, let's say
you wanted to define a custom Server.Request.Parser
to use an XML Decoding package on the request body.
You could define a custom function like this
import Server.Request as Request
expectXmlBody : XmlDecoder value -> Request.Parser value
expectXmlBody xmlDecoder =
Request.expectBody
|> Request.andThen
(\bodyAsString ->
case runXmlDecoder xmlDecoder bodyAsString of
Ok decodedXml ->
Request.succeed decodedXml
Err error ->
Request.skip ("XML could not be decoded " ++ xmlErrorToString error)
)
Note that when we said Request.skip
, remaining Request Parsers will run (for example if you use Server.Request.oneOf
).
You could build this with different semantics if you wanted to handle any valid XML body. This Request Parser will not
handle any valid XML body. It will only handle requests that can match the XmlDecoder that is passed in.
So when you define your Server.Request.Parser
s, think carefully about whether you want to handle invalid cases and give an
error, or fall through to other Parsers. There's no universal right answer, it's just something to decide for your use case.
expectXmlBody : Request.Parser value
expectXmlBody =
Request.map2
acceptContentTypes
Request.expectBody
|> Request.andThen
(\bodyAsString ->
case runXmlDecoder xmlDecoder bodyAsString of
Ok decodedXml ->
Request.succeed decodedXml
Err error ->
Request.skip ("XML could not be decoded " ++ xmlErrorToString error)
)
Decode an argument and provide it to a function in a decoder.
decoder : Decoder String
decoder =
succeed (String.repeat)
|> andMap (field "count" int)
|> andMap (field "val" string)
""" { "val": "hi", "count": 3 } """
|> decodeString decoder
--> Success "hihihi"
Same as rawBody
, but will only match when a body is present in the HTTP request.
Gets the HTTP Method as a String, like 'GET', 'PUT', etc.
TODO internal only
TODO internal only
A
Server.Request.Parser
lets you send aServer.Response.Response
based on an incoming HTTP request. For example, using aServer.Request.Parser
, you could check a session cookie to decide whether to respond by rendering a page for the logged-in user, or else respond with an HTTP redirect response (see theServer.Response
docs).You can access the incoming HTTP request's:
method
requestTime
(as aTime.Posix
)Note that this data is not available for pre-rendered pages or pre-rendered API Routes, only for server-rendered pages. This is because when a page is pre-rendered, there is no incoming HTTP request to respond to, it is rendered before a user requests the page and then the pre-rendered page is served as a plain file (without running your Route Module).
That's why
RouteBuilder.preRender
hasdata : RouteParams -> DataSource Data
:import DataSource exposing (DataSource) import RouteBuilder exposing (StatelessRoute) type alias Data = {} data : RouteParams -> DataSource Data data routeParams = DataSource.succeed Data route : StatelessRoute RouteParams Data ActionData route = RouteBuilder.preRender { data = data , head = head , pages = pages } |> RouteBuilder.buildNoState { view = view }
A server-rendered Route Module does have access to a user's incoming HTTP request because it runs every time the page is loaded. That's why
data
is aRequest.Parser
in server-rendered Route Modules. Since you have an incoming HTTP request for server-rendered routes,RouteBuilder.serverRender
hasdata : RouteParams -> Request.Parser (DataSource (Response Data))
. That means that you can use the incoming HTTP request data to choose how to respond. For example, you could check for a dark-mode preference cookie and render a light- or dark-themed page and render a different page.That's a mouthful, so let's unpack what it means.
Request.Parser
means you can pull outdata from the request payload using a Server Request Parser.
import DataSource exposing (DataSource) import RouteBuilder exposing (StatelessRoute) import Server.Request as Request exposing (Request) import Server.Response as Response exposing (Response) type alias Data = {} data : RouteParams -> Request.Parser (DataSource (Response Data)) data routeParams = {} |> Server.Response.render |> DataSource.succeed |> Request.succeed route : StatelessRoute RouteParams Data ActionData route = RouteBuilder.serverRender { head = head , data = data } |> RouteBuilder.buildNoState { view = view }