Server-rendered Route modules and server-rendered API Routes give you access to a Server.Request.Request
argument.
Internal.Request.Request
A value that lets you access data from the incoming HTTP request.
requestTime : Request -> Time.Posix
Get the Time.Posix
when the incoming HTTP request was received.
header : String -> Request -> Maybe String
Get a header from the request. The header name is case-insensitive.
Header: Accept-Language: en-US,en;q=0.5
request |> Request.header "Accept-Language"
-- Just "Accept-Language: en-US,en;q=0.5"
headers : Request -> Dict String String
method : Request -> Method
The HTTP request method of the incoming request.
Note that Route modules data
is run for GET
requests, and action
is run for other request methods (including POST
, PUT
, DELETE
).
So you don't need to check the method
in your Route Module's data
function, though you can choose to do so in its action
.
An Incoming HTTP Request Method.
methodToString : Method -> String
Gets the HTTP Method as an uppercase String.
Examples:
Get
|> methodToString
-- "GET"
body : Request -> Maybe String
The Request body, if present (or Nothing
if there is no request body).
jsonBody : Json.Decode.Decoder value -> Request -> Maybe (Result Json.Decode.Error value)
If the request has a body and its Content-Type
matches JSON, then
try running a JSON decoder on the body of the request. Otherwise, return Nothing
.
Example:
Body: { "name": "John" }
Headers:
Content-Type: application/json
request |> jsonBody (Json.Decode.field "name" Json.Decode.string)
-- Just (Ok "John")
Body: { "name": "John" }
No Headers
jsonBody (Json.Decode.field "name" Json.Decode.string) request
-- Nothing
No Body
No Headers
jsonBody (Json.Decode.field "name" Json.Decode.string) request
-- Nothing
formData : Form.Handler.Handler error combined -> Request -> Maybe ( Form.ServerResponse error, Form.Validated error combined )
Takes a Form.Handler.Handler
and
parses the raw form data into a Form.Validated
value.
This is the standard pattern for dealing with form data in elm-pages
. You can share your code for your Form
definitions between your client and server code, using this function to parse the raw form data into a Form.Validated
value for the backend,
and Pages.Form
to render the Form
on the client.
Since we are sharing the Form
definition between frontend and backend, we get to re-use the same validation logic so we gain confidence that
the validation errors that the user sees on the client are protected on our backend, and vice versa.
import BackendTask exposing (BackendTask)
import FatalError exposing (FatalError)
import Form
import Server.Request as Request exposing (Request)
import Server.Response as Response exposing (Response)
type Action
= Delete
| CreateOrUpdate Post
formHandlers : Form.Handler.Handler String Action
formHandlers =
deleteForm
|> Form.Handler.init (\() -> Delete)
|> Form.Handler.with CreateOrUpdate createOrUpdateForm
deleteForm : Form.HtmlForm String () input msg
createOrUpdateForm : Form.HtmlForm String Post Post msg
action :
RouteParams
-> Request
-> BackendTask FatalError (Response ActionData ErrorPage)
action routeParams request =
case request |> Server.Request.formData formHandlers of
Nothing ->
BackendTask.fail (FatalError.fromString "Missing form data")
Just ( formResponse, parsedForm ) ->
case parsedForm of
Form.Valid Delete ->
deletePostBySlug routeParams.slug
|> BackendTask.map
(\() -> Route.redirectTo Route.Index)
Form.Valid (CreateOrUpdate post) ->
let
createPost : Bool
createPost =
okForm.slug == "new"
in
createOrUpdatePost post
|> BackendTask.map
(\() ->
Route.redirectTo
(Route.Admin__Slug_ { slug = okForm.slug })
)
Form.Invalid _ invalidForm ->
BackendTask.succeed
(Server.Response.render
{ errors = formResponse }
)
You can handle form submissions as either GET or POST requests. Note that for security reasons, it's important to performing mutations with care from GET requests,
since a GET request can be performed from an outside origin by embedding an image that points to the given URL. So a logout submission should be protected by
using POST
to ensure that you can't log users out by embedding an image with a logout URL in it.
If the request has HTTP method GET
, the form data will come from the query parameters.
If the request has the HTTP method POST
and the Content-Type
is application/x-www-form-urlencoded
, it will return the
decoded form data from the body of the request.
Otherwise, this Parser
will not match.
Note that in server-rendered Route modules, your data
function will handle GET
requests (and will not receive any POST
requests),
while your action
will receive POST (and other non-GET) requests.
By default, [Form
]'s are rendered with a POST
method, and you can configure them to submit GET
requests using withGetMethod
.
So you will want to handle any Form
's rendered using withGetMethod
in your Route's data
function, or otherwise handle forms in action
.
formDataWithServerValidation : Pages.Form.Handler error combined -> Request -> Maybe (BackendTask FatalError (Result (Form.ServerResponse error) ( Form.ServerResponse error, combined )))
rawFormData : Request -> Maybe (List ( String, String ))
Get the raw key-value pairs from a form submission.
If the request has the HTTP method GET
, it will return the query parameters.
If the request has the HTTP method POST
and the Content-Type
is application/x-www-form-urlencoded
, it will return the
decoded form data from the body of the request.
Otherwise, this Parser
will not match.
Note that in server-rendered Route modules, your data
function will handle GET
requests (and will not receive any POST
requests),
while your action
will receive POST (and other non-GET) requests.
By default, [Form
]'s are rendered with a POST
method, and you can configure them to submit GET
requests using withGetMethod
.
So you will want to handle any Form
's rendered using withGetMethod
in your Route's data
function, or otherwise handle forms in action
.
rawUrl : Request -> String
The full URL of the incoming HTTP request, including the query params.
Note that the fragment is not included because this is client-only (not sent to the server).
rawUrl request
-- url: http://example.com?coupon=abc
-- parses into: "http://example.com?coupon=abc"
rawUrl request
-- url: https://example.com?coupon=abc&coupon=xyz
-- parses into: "https://example.com?coupon=abc&coupon=xyz"
queryParam : String -> Request -> Maybe String
Get Nothing
if the query param with the given name is missing, or Just
the value if it is present.
If there are multiple query params with the same name, the first one is returned.
queryParam "coupon"
-- url: http://example.com?coupon=abc
-- parses into: Just "abc"
queryParam "coupon"
-- url: http://example.com?coupon=abc&coupon=xyz
-- parses into: Just "abc"
queryParam "coupon"
-- url: http://example.com
-- parses into: Nothing
See also queryParams
, or rawUrl
if you need something more low-level.
queryParams : Request -> Dict String (List String)
Gives all query params from the URL.
queryParam "coupon"
-- url: http://example.com?coupon=abc
-- parses into: Dict.fromList [("coupon", ["abc"])]
queryParam "coupon"
-- url: http://example.com?coupon=abc&coupon=xyz
-- parses into: Dict.fromList [("coupon", ["abc", "xyz"])]
matchesContentType : String -> Request -> Basics.Bool
True if the content-type
header is present AND matches the given argument.
Examples:
Content-Type: application/json; charset=utf-8
request |> matchesContentType "application/json"
-- True
Content-Type: application/json
request |> matchesContentType "application/json"
-- True
Content-Type: application/json
request |> matchesContentType "application/xml"
-- False
cookie : String -> Request -> Maybe String
Get a cookie from the request. For a more high-level API, see Server.Session
.
cookies : Request -> Dict String String
Get all of the cookies from the incoming HTTP request. For a more high-level API, see Server.Session
.