7 Request Path
By now you understand that this is how to attach your request handlers to the web server instance:
app$get(
path = "/",
handler = function(req, res) {}
)Let’s talk about the path argument. Each HTTP method of the app instance takes it as the first parameter.
But what values are allowed for the path? Strings, obviously.
7.1 Strict Matching
Matching to deterministic paths is probably what you have been doing.
app$get("/", home_get)
app$get("/about", about_get)
app$get("/contact", contact_get)An exact path that matches to a particular page. Any person who visits that page will see the same content the world over.
7.2 Dynamic Matching
Sometimes you need a little bit more than what strict matching offers.
7.2.1 Request Parameters
Assume for a second that your app has 100 users (This is a huge assumption, everyone knows you’re the only one who uses your application.)
Each user will have their own profile page. Going by the user ID:
| user ID | profile page |
|---|---|
| abc | /profile/abc |
| jkl | /profile/jkl |
| xyz | /profile/xyz |
Now the thing is that you cannot build individual pages for each user. It will be too much work and the ROI isn’t exactly worth it.
What you need is a generic path that will match to a generic handler for all user profile requests.
The path will be of this format:
app$get(path = "/profile/:user_id", handler = ...)Anytime Ambiorix encounters a path component starting with a colon (:), it knows that you want to specify a varying parameter (user_id in this case), and once the request is parsed you can access that parameter by its name in the handler.
In our case that’d be:
app$get(
path = "/profile/:user_id",
handler = function(req, res) {
id <- req$params$user_id
}
)It’s that simple: The req object is a list which contains everything you need to know about the incoming HTTP request. Request parameters are stored in the params element by name.
Here is a simple program to drive that point home:
library(ambiorix)
library(htmltools)
#' Handle GET at '/'
#'
#' @export
home_get <- function(req, res) {
html <- tagList(
tags$h1("Request Parameters"),
tags$p(
"Requests to /profile/<user-id> are dynamically handled.",
),
tags$p(
"Visit",
tags$a(
href = "/profile/100",
"/profile/100"
),
"."
),
tags$p(
"In the browser URL, change the 100 to another value. What happens?"
)
)
res$send(html)
}
#' Handle GET at '/profile/:user_id'
#'
#' @export
profile_get <- function(req, res) {
id <- req$params$user_id
if (is.null(id) || identical(id, "")) {
res$status <- 400L
return(
res$send("400. Missing user ID.")
)
}
html <- tags$div(
tags$h1("Profile"),
tags$p(
"User",
tags$strong(id)
),
tags$p("Here's your profile:"),
tags$ul(
tags$li("Detail 1"),
tags$li("Detail 2"),
tags$li("Detail 3")
),
tags$a(
href = "/",
"Go back home"
)
)
res$send(html)
}
app <- Ambiorix$new()
app$get("/", home_get)
app$get("/profile/:user_id", profile_get)
app$start(port = 3000L)7.2.2 Regular Expressions
Request path matching is just pattern matching. Therefore, a request path can be a simple regular expression.
Say we have this character vector:
x <- c("a=10", "b=22", "c=300")To only retain c("a", "b", "c"), you can use greedy matching in gsub():
gsub(pattern = "=.*", replacement = "", x = x)
# [1] "a" "b" "c"The pattern "=.*" matches everything from the equal sign to the end.
This can become important if you decide to build a Single Page Application (SPA) using Ambiorix. To be honest, I don’t know why you might want to do that. But who knows?
In an SPA, you want every route to be handled by a single handler.
Here’s an application to demonstrate one of the ways you can implement that:
library(ambiorix)
library(htmltools)
#' Handle GET at '/'
#'
#' @export
home_get <- function(req, res) {
html <- tagList(
tags$h1("Single Page Application"),
tags$p("The greedily matched page."),
tags$ul(
tags$li("One page to rule them."),
tags$li("One page to find them."),
tags$li("One page to bring them all."),
tags$li("And in the darkness bind them.")
)
)
res$send(html)
}
app <- Ambiorix$new()
app$get(path = "/.*", handler = home_get)
app$start(port = 3000L)