Request parameters and payloads
Handling data sent over HTTP requests is a fundamental aspect of web development. This guide covers how to efficiently manage everything from GET query parameters to POST data and file uploads using Genie's built-in utilities.
Handling GET query params
Genie makes it easy to access query params, which are values sent as part of the URL over GET requests
(ex: mywebsite.com/index?foo=1&bar=2 foo
and bar
are query params corresponding to the variables foo = 1
and bar = 2
).
All these values are automatically collected by Genie and exposed in the params()
collection (which is part of the Router
module).
Example
using Genie
route("/hi") do
name = params(:name, "Anon")
"Hello $name"
end
If you access http://127.0.0.1:8000/hi
the app will respond with "Hello Anon" since we're not passing any query params.
However, requesting http://127.0.0.1:8000/hi?name=Adrian
will in turn display "Hello Adrian" as we're passing the
name
query variable with the value Adrian
. This variable is exposed by Genie as params(:name)
.
Genie, however, provides utility methods for accessing these values in the Requests
module.
The Requests
module
Genie provides a set of utilities for working with requests data within the Requests
module. You can use the
getpayload
method to retrieve the query params as a Dict{Symbol,Any}
. We can rewrite the previous route to take
advantage of the Requests
utilities.
Example
using Genie, Genie.Requests
route("/hi") do
"Hello $(getpayload(:name, "Anon"))"
end
The getpayload
function has a few specializations, and one of them accepts the key and a default value. The default
value is returned if the key
variable is not defined. You can see the various implementations for getpayload
using
the API docs or Julia's help>
mode.
Reading POST payloads
Genie makes it easy to work with POST
data. First, we need to register a dedicated route to handle POST
requests.
Then, when a POST
request is received, Genie will automatically extract the payload, making it accessible throughout
the Requests.postpayload
method -- and appending it to the Router.params(:POST)
collection.
Handling form-data
payloads
The following snippet registers two routes in the root of the app, one for GET
and the other for POST
requests.
The GET
route displays a form that submits over POST
to the other route. Finally, upon receiving the data,
we display a message.
Example
using Genie, Genie.Renderer.Html, Genie.Requests
form = """
<form action="/" method="POST" enctype="multipart/form-data">
<input type="text" name="name" value="" placeholder="What's your name?" />
<input type="submit" value="Greet" />
</form>
"""
route("/") do
html(form)
end
route("/", method = POST) do
"Hello $(postpayload(:name, "Anon"))"
end
up()
The postpayload
function has a few specializations, and one of them accepts the key and the default value. The default
value is returned if the key
variable is not defined. You can see the various implementations for postpayload
using
the API docs or Julia's help>
mode.
Using JSON payloads
A very common design pattern, especially when developing REST APIs, is to accept JSON payloads sent as application/json
data over POST
requests. Genie efficiently handles this use case through the utility function Requests.jsonpayload
.
Under the cover, Genie will process the POST
request and will attempt to parse the JSON text payload. If this fails,
we can still access the raw data (the text payload is not converted to JSON) by using the Requests.rawpayload
method.
Example
using Genie, Genie.Requests, Genie.Renderer.Json
route("/jsonpayload", method = POST) do
@show jsonpayload()
@show rawpayload()
json("Hello $(jsonpayload()["name"])")
end
up()
Next we make a POST
request using the HTTP
package:
using HTTP
HTTP.request("POST", "http://localhost:8000/jsonpayload", [("Content-Type", "application/json")], """{"name":"Adrian"}""")
We will get the following output:
jsonpayload() = Dict{String,Any}("name"=>"Adrian")
rawpayload() = "{\"name\":\"Adrian\"}"
INFO:Main: /jsonpayload 200
HTTP.Messages.Response:
"""
HTTP/1.1 200 OK
Content-Type: application/json
Transfer-Encoding: chunked
"Hello Adrian""""
First, for the two @show
calls, notice how jsonpayload
had successfully converted the POST
data to a Dict
.
While the rawpayload
returns the POST
data as a String
, exactly as received. Finally, our route handler returns a
JSON response, greeting the user by extracting the name from within the jsonpayload
Dict
.
Handling file uploads
Genie has built-in support for working with file uploads. The collection of uploaded files (as POST
variables) can be
accessed through the Requests.filespayload
method. Or, we can retrieve the data corresponding to a given file form
input by using Requests.filespayload(key)
-- where key
is the name of the file input in the form.
In the following snippet, we configure two routes in the root of the app (/
): the first route, handling GET
requests,
displays an upload form. The second route, handling POST
requests, processes the uploads, generating a file from the
uploaded data, saving it, and displaying the file stats.
HEADS UP
Notice that we can define multiple routes at the same URL if they have different methods, in our case GET
and POST
.
Example
using Genie, Genie.Router, Genie.Renderer.Html, Genie.Requests
form = """
<form action="/" method="POST" enctype="multipart/form-data">
<input type="file" name="yourfile" /><br/>
<input type="submit" value="Submit" />
</form>
"""
route("/") do
html(form)
end
route("/", method = POST) do
if infilespayload(:yourfile)
write(filespayload(:yourfile))
stat(filename(filespayload(:yourfile)))
else
"No file uploaded"
end
end
up()
Upon uploading a file and submitting the form, our app will display the file's stats.