Let’s consider an example. We develop an application which calculates factorial
of a number:
library(RestRserve)
backend = BackendRserve$new()
application = Application$new()
application$add_get(path = "/factorial", function(req, res) {
x = req$get_param_query("x")
x = as.integer(x)
res$set_body(factorial(x))
})
Here is how request will be processed:
request = Request$new(
path = "/factorial",
method = "GET",
parameters_query = list(x = 10)
)
response = application$process_request(request)
response
#> <RestRserve Response>
#> status code: 200 OK
#> content-type: text/plain
#> <Headers>
#> Server: RestRserve/0.3.0
Let’s take a closer look to the response
object and its body
property:
As we can see it is a numeric value. HTTP response body however can’t be an arbitrary R object. It should be something that external systems can understand - either character
vector or raw
vector. Fortunately application
helps to avoid writing boilerplate code to encode the body. Based on the content_type
property it can find encode
function which will be used to transform body
into a http body.
Two immediate questions can arise:
content_type
is equal to text/plain
?
content_type
in Application
constructor. It is text/plain
by default, which means all the responses by default will have text/plain
content type.text/plain
? Can it encode any arbitrary content type?
ContentHandlers
property. Out of the box it supports two content types - text/plain
and application/json
.For instance app1
and app2
are equal:
encode_decode_middleware = EncodeDecodeMiddleware$new()
app1 = Application$new(middleware = list())
app1$append_middleware(encode_decode_middleware)
app2 = Application$new()
Here is example on how you can get the actual function used for application/json
encoding:
FUN = encode_decode_middleware$ContentHandlers$get_encode('application/json')
FUN
#> function(x, unbox = TRUE) {
#> res = jsonlite::toJSON(x, dataframe = 'columns', auto_unbox = unbox, null = 'null', na = 'null')
#> unclass(res)
#> }
#> <bytecode: 0x468bc00>
#> <environment: namespace:RestRserve>
We can manually override application default content-type:
application$add_get(path = "/factorial-json", function(req, res) {
x = as.integer(req$get_param_query("x"))
result = factorial(x)
res$set_body(list(result = result))
res$set_content_type("application/json")
})
request = Request$new(
path = "/factorial-json",
method = "GET",
parameters_query = list(x = 10)
)
response = application$process_request(request)
And here is a little bit more complex example where we store a binary object in the body. We will use R’s native serialization, but one can use protobuf
, messagepack
, etc.
application$add_get(path = "/factorial-rds", function(req, res) {
x = as.integer(req$get_param_query("x"))
result = factorial(x)
body_rds = serialize(list(result = result), connection = NULL)
res$set_body(body_rds)
res$set_content_type("application/x-rds")
})
However function above won’t work correctly. Out of the box ContentHndlers
doesn’t know anything about application/x-rds
:
request = Request$new(
path = "/factorial-rds",
method = "GET",
parameters_query = list(x = 10)
)
response = application$process_request(request)
response$body
#> [1] "500 Internal Server Error: can't encode body with content_type = 'application/x-rds'"
In order to resolve problem above we would need to either register application/x-rds
content handler with ContentHandlers$set_encode()
or manually specify encode
function (identity
in our case):
application$add_get(path = "/factorial-rds2", function(req, res) {
x = as.integer(req$get_param_query("x"))
result = factorial(x)
body_rds = serialize(list(result = result), connection = NULL)
res$set_body(body_rds)
res$set_content_type("application/x-rds")
res$encode = identity
})
Now the answer is valid:
RestRserve facilitates with parsing incoming request body as well. Consider a service which expects JSON POST requests:
application = Application$new(content_type = "application/json")
application$add_post("/echo", function(req, res) {
res$set_body(req$body)
})
request = Request$new(path = "/echo", method = "POST", body = '{"hello":"world"}', content_type = "application/json")
response = application$process_request(request)
response$body
#> [1] "{\"hello\":\"world\"}"
The logic behind decoding is also controlled by [EncodeDecodeMiddleware] and its ContentHandlers
property.