Creates Application object. Application provides an interface for building high-performance REST API by registering R functions as handlers http requests.
There are several advanced options to control how HTTP headers are processed:
options("RestRserve.headers.server")
controls response "Server"
header
options("RestRserve.headers.split")
controls which header values
split by comma during parsing. See
https://en.wikipedia.org/wiki/List_of_HTTP_header_fields,
https://stackoverflow.com/a/29550711/3048453
There is also an option to switch-off runtime types validation in
the Request/Response handlers. This might provide some performance gains,
but ultimately leads to less robust applications. Use at your own risk!
See options("RestRserve.runtime.asserts")
logger
Logger object which records events during request processing.
Alternatively user can use loggers from lgr package as a drop-in
replacement - Logger
methods and loggers created by lgr
share
function signatures.
content_type
Default response body content type.
HTTPError
Class which raises HTTP errors.
Global HTTPError is used by default. In theory user can replace it with
his own class (see RestRserve:::HTTPErrorFactory
). However we believe
in the majority of the cases using HTTPError will be enough.
endpoints
Prints all the registered routes with allowed methods.
new()
Creates Application object.
Application$new(
middleware = list(EncodeDecodeMiddleware$new()),
content_type = "text/plain",
...
)
middleware
List of Middleware objects.
content_type
Default response body content (media) type. "text/plain"
by default.
...
Not used at the moment.
add_route()
Adds endpoint and register user-supplied R function as a handler.
Application$add_route(
path,
method,
FUN,
match = c("exact", "partial", "regex"),
...
)
path
Endpoint path.
method
HTTP method. Allowed methods at the moment:
GET
, HEAD
, POST
, PUT
, DELETE
, OPTIONS
, PATCH
.
FUN
User function to handle requests. FUN
must take two arguments:
first is request
(Request) and second is response
(Response).
The goal of the user function is to modify response
or throw
exception (call raise()
or stop()
).
Both response
and request
objects modified in-place and internally
passed further to RestRserve execution pipeline.
match
Defines how route will be processed. Allowed values:
exact
- match route as is. Returns 404 if route is not matched.
partial
- match route as prefix. Returns 404 if prefix are not matched.
regex
- match route as template. Returns 404 if template pattern not matched.
...
Not used.
add_get()
Shorthand to Application$add_route()
with GET
method.
Application$add_get(
path,
FUN,
match = c("exact", "partial", "regex"),
...,
add_head = TRUE
)
path
Endpoint path.
FUN
User function to handle requests. FUN
must take two arguments:
first is request
(Request) and second is response
(Response).
The goal of the user function is to modify response
or throw
exception (call raise()
or stop()
).
Both response
and request
objects modified in-place and internally
passed further to RestRserve execution pipeline.
match
Defines how route will be processed. Allowed values:
exact
- match route as is. Returns 404 if route is not matched.
partial
- match route as prefix. Returns 404 if prefix are not matched.
regex
- match route as template. Returns 404 if template pattern not matched.
...
Not used.
add_head
Adds HEAD method.
add_post()
Shorthand to Application$add_route()
with POST
method.
Application$add_post(path, FUN, match = c("exact", "partial", "regex"), ...)
path
Endpoint path.
FUN
User function to handle requests. FUN
must take two arguments:
first is request
(Request) and second is response
(Response).
The goal of the user function is to modify response
or throw
exception (call raise()
or stop()
).
Both response
and request
objects modified in-place and internally
passed further to RestRserve execution pipeline.
match
Defines how route will be processed. Allowed values:
exact
- match route as is. Returns 404 if route is not matched.
partial
- match route as prefix. Returns 404 if prefix are not matched.
regex
- match route as template. Returns 404 if template pattern not matched.
...
Not used.
add_static()
Adds GET
method to serve file or directory at file_path
.
path
Endpoint path.
file_path
Path file or directory.
content_type
MIME-type for the content.
If content_type = NULL
then MIME code content_type
will be inferred
automatically (from file extension).
If it will be impossible to guess about file type then content_type
will
be set to application/octet-stream
.
...
Not used.
add_openapi()
Adds endpoint to serve OpenAPI description of available methods.
add_swagger_ui()
Adds endpoint to show Swagger UI.
process_request()
Process incoming request and generate Response object.
request
Request object.
Useful for tests your handlers before deploy application.
# init logger
app_logger = Logger$new()
# set log level for the middleware
app_logger$set_log_level("debug")
# set logger name
app_logger$set_name("MW Logger")
# init middleware to logging
mw = Middleware$new(
process_request = function(rq, rs) {
app_logger$info(sprintf("Incomming request (id %s): %s", rq$id, rq$path))
},
process_response = function(rq, rs) {
app_logger$info(sprintf("Outgoing response (id %s): %s", rq$id, rs$status))
},
id = "awesome-app-logger"
)
# init application
app = Application$new(middleware = list(mw))
# set internal log level
app$logger$set_log_level("error")
# define simply request handler
status_handler = function(rq, rs) {
rs$set_body("OK")
rs$set_content_type("text/plain")
rs$set_status_code(200L)
}
# add route
app$add_get("/status", status_handler, "exact")
# add static file handler
desc_file = system.file("DESCRIPTION", package = "RestRserve")
# add route
app$add_static("/desc", desc_file, "text/plain")
# define say message handler
say_handler = function(rq, rs) {
who = rq$parameters_path[["user"]]
msg = rq$parameters_query[["message"]]
if (is.null(msg)) msg = "Hello"
rs$set_body(paste(who, "say", dQuote(msg)))
rs$set_content_type("text/plain")
rs$set_status_code(200L)
}
# add route
app$add_get("/say/{user}", say_handler, "regex")
# print application info
app
#> <RestRserve Application>
#> <Middlewares>
#> [request][response]: awesome-app-logger
#> <Endpoints>
#> HEAD [exact]: /status
#> HEAD [regex]: /say/{user}
#> GET [exact]: /status
#> GET [exact]: /desc
#> GET [regex]: /say/{user}
# test app
# simulate requests
not_found_rq = Request$new(path = "/no")
status_rq = Request$new(path = "/status")
desc_rq = Request$new(path = "/desc")
say_rq = Request$new(path = "/say/anonym", parameters_query = list("message" = "Hola"))
# process prepared requests
app$process_request(not_found_rq)
#> {"timestamp":"2024-04-18 01:54:44.752187","level":"INFO","name":"MW Logger","pid":3468,"msg":"Incomming request (id a107796a-fd26-11ee-98b2-0050569e8e3c): /no"}
#> {"timestamp":"2024-04-18 01:54:44.755027","level":"INFO","name":"MW Logger","pid":3468,"msg":"Outgoing response (id a107796a-fd26-11ee-98b2-0050569e8e3c): 404 Not Found"}
#> <RestRserve Response>
#> status code: 404 Not Found
#> content-type: text/plain
#> <Headers>
#> Server: RestRserve/1.2.2; Rserve/1.8.13
app$process_request(status_rq)
#> {"timestamp":"2024-04-18 01:54:44.793391","level":"INFO","name":"MW Logger","pid":3468,"msg":"Incomming request (id a107c0c8-fd26-11ee-98b2-0050569e8e3c): /status"}
#> {"timestamp":"2024-04-18 01:54:44.843766","level":"INFO","name":"MW Logger","pid":3468,"msg":"Outgoing response (id a107c0c8-fd26-11ee-98b2-0050569e8e3c): 200 OK"}
#> <RestRserve Response>
#> status code: 200 OK
#> content-type: text/plain
#> <Headers>
#> Server: RestRserve/1.2.2; Rserve/1.8.13
app$process_request(desc_rq)
#> {"timestamp":"2024-04-18 01:54:44.869942","level":"INFO","name":"MW Logger","pid":3468,"msg":"Incomming request (id a108015a-fd26-11ee-98b2-0050569e8e3c): /desc"}
#> {"timestamp":"2024-04-18 01:54:44.871164","level":"INFO","name":"MW Logger","pid":3468,"msg":"Outgoing response (id a108015a-fd26-11ee-98b2-0050569e8e3c): 200 OK"}
#> <RestRserve Response>
#> status code: 200 OK
#> content-type: text/plain
#> <Headers>
#> Server: RestRserve/1.2.2; Rserve/1.8.13
app$process_request(say_rq)
#> {"timestamp":"2024-04-18 01:54:44.874052","level":"INFO","name":"MW Logger","pid":3468,"msg":"Incomming request (id a1084002-fd26-11ee-98b2-0050569e8e3c): /say/anonym"}
#> {"timestamp":"2024-04-18 01:54:44.875389","level":"INFO","name":"MW Logger","pid":3468,"msg":"Outgoing response (id a1084002-fd26-11ee-98b2-0050569e8e3c): 200 OK"}
#> <RestRserve Response>
#> status code: 200 OK
#> content-type: text/plain
#> <Headers>
#> Server: RestRserve/1.2.2; Rserve/1.8.13
# run app
backend = BackendRserve$new()
# \donttest{
if (interactive()) {
backend$start(app, 8080)
}
# }