The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.
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/1.2.4; Rserve/1.8.13Let’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: 0x7f93f3b58c40>
#> <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.
Here is an example which demonstrates on how to extend ?EncodeDecodeMiddleware to handle additional content types:
encode_decode_middleware = EncodeDecodeMiddleware$new()
encode_decode_middleware$ContentHandlers$set_encode(
"text/csv",
function(x) {
con = rawConnection(raw(0), "w")
on.exit(close(con))
write.csv(x, con, row.names = FALSE)
rawConnectionValue(con)
}
)
encode_decode_middleware$ContentHandlers$set_decode(
"text/csv",
function(x) {
res = try({
con = textConnection(rawToChar(x), open = "r")
on.exit(close(con))
read.csv(con)
}, silent = TRUE)
if (inherits(res, "try-error")) {
raise(HTTPError$bad_request(body = attributes(res)$condition$message))
}
return(res)
}
)Extended middleware needs to be provided to the application constructor:
Now let’s test it:
app$add_get("/iris", FUN = function(.req, .res) {
.res$set_content_type("text/csv")
.res$set_body(iris)
})
req = Request$new(path = "/iris", method = "GET")
res = app$process_request(req)
iris_out = read.csv(textConnection(rawToChar(res$body)))
head(iris_out)
#> Sepal.Length Sepal.Width Petal.Length Petal.Width Species
#> 1 5.1 3.5 1.4 0.2 setosa
#> 2 4.9 3.0 1.4 0.2 setosa
#> 3 4.7 3.2 1.3 0.2 setosa
#> 4 4.6 3.1 1.5 0.2 setosa
#> 5 5.0 3.6 1.4 0.2 setosa
#> 6 5.4 3.9 1.7 0.4 setosaapp$add_post("/in", FUN = function(.req, .res) {
str(.req$body)
})
req = Request$new(path = "/in", method = "POST", body = res$body, content_type = "text/csv")
app$process_request(req)
#> 'data.frame': 150 obs. of 5 variables:
#> $ Sepal.Length: num 5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
#> $ Sepal.Width : num 3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
#> $ Petal.Length: num 1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
#> $ Petal.Width : num 0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
#> $ Species : chr "setosa" "setosa" "setosa" "setosa" ...
#> <RestRserve Response>
#> status code: 200 OK
#> content-type: text/plain
#> <Headers>
#> Server: RestRserve/1.2.4; Rserve/1.8.13These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.