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.