knitr::opts_chunk$set( collapse = TRUE, comment = "#>", fig.path = "README-" )
While the http protocol is rather basic in essence, it can be a pain to work
with. reqres
is here to soothe the pain somewhat by providing two powerful
classes for handling all parts of request and response handling during a http
exchange. This is not a web server, instead it focuses on making life easier
for developers of web servers by extracting the complexity of cookies, headers,
content negotiation, and the likes into neat little classes. reqres
builds
upon the rook
specifications and is thus well suited for
httpuv
-based webservers.
reqres
draws a lot of inspiration from express.js and
the Request
and Response
classes is aiming for feature parity with those
from express. The Request
class provides automatic parsing of the query string
along with parsing of the body based on the Content-Type
header (with
decompression if Content-Encoding
is provided). Further, it provides content
negotiation based on the Accept(-*)
headers. The Response
class allows you
to set headers and cookies easily, assign arbitrary data for later use, and
automatically format the body based on content negotiation with the Request
object that it is responding to (again, it will compress automatically if the
Accept-Encoding
header allows it). If any part of the content negotiation
fails the correct response status code will be set, making the response ready to
send.
reqres
comes with a range of parsers and formatters making it work out of the
box with json, xml, html, csv, tab, multipart, and www-form-urlencoded payloads.
It is easy to either modify these or provide your own parsers and formatters if
needed - reqres
will take care of the content negotiation and simply call your
custom parser/formatter if chosen.
reqrescan be installed from CRAN with install.packages('reqres')
or the
development version can be installed from github:
# install.packages('devtools') devtools::install_github('thomasp85/reqres')
Below is a quick demo of some of the features in reqres
. It uses the
fake_request()
in fiery
to mock a rook request so it can be used without
setting up a webserver:
library(reqres) # We start by mocking our request rook <- fiery::fake_request( url = 'http://www.example.com/summary?id=2347&user=Thomas+Lin+Pedersen', content = '{"name":["Thomas Lin Pedersen"],"age":[31],"homepage":["www.data-imaginist.com","www.github.com/thomasp85"]}', headers = list( Content_Type = 'application/json', Accept = 'application/json, application/xml; q=0.5, text/*; q=0.3', Accept_Encoding = 'gzip, br' ) ) # A Request object can now be created req <- Request$new(rook) req # ... along with a response res <- req$respond() res
A lot of information is already available, such as the query and other parts of the url, but the body is not filled in automatically.
req$host req$query req$body
The body can easily be parsed though, as long as a parser exists for the provided content type.
req$is('json') req$parse(json = parse_json()) req$body
Instead of inspecting it manually you can simply provide a range of parsers and let the object choose the correct one itself
req$set_body(NULL) req$parse( txt = parse_plain(), html = parse_html(), json = parse_json() ) req$body
In the case that none of the provided parsers fits the content type, the response will automatically get updated with the correct error code
req$set_body(NULL) req$parse(txt = parse_plain()) res
To facilitate all this reqres
comes with a mapping of standard mime types to
the provided parsers. This can simply be supplied to the parse method
req$set_body(NULL) req$parse(default_parsers) req$body
While the request is mainly intended to be read from, the response should be
written to. The Response
class contains a slew of methods to easily set
headers, cookies, etc.
res$set_header('Date', to_http_date(Sys.time())) res$get_header('Date') res$set_cookie('user', req$query$id, max_age = 9000L) res$has_cookie('user')
Furthermore, it contains its own data store where arbitrary information can be stored so as to pass it between middleware etc. This data will never be part of the actual response.
res$set_data('alphabet', letters) res$get_data('alphabet')
Files can be attached and marked for download, setting the relevant headers automatically
res$attach(system.file('NEWS.md', package = 'reqres')) res$get_header('Content-Type') res$get_header('Content-Disposition')
Often we need to provide a payload in the form of a body. This can be any type of R object until the response is handed off to the server, where it should be either a string or a raw vector.
res$remove_header('Content-Disposition') res$body <- head(mtcars) res$body
Based on the Accept
header in the request it can be formatted correctly thus
making it ready to send back to the client. As this request contains an
Accept-Encoding
header it will be compressed as well.
res$format(json = format_json()) res$body res$get_header('Content-Type') res$get_header('Content-Encoding')
The content negotiation understands wildcards as well
res$body <- head(mtcars) req$get_header('Accept') res$format(csv = format_table(sep = ','), compress = FALSE) res$body res$get_header('Content-Type')
A default formatter mapping exists in parallel to default_parsers
for the
Request$format()
method.
res$body <- head(mtcars) res$format(default_formatters, compress = FALSE) res$body
It is easy to define your own formatters and add them along the defaults
res$body <- head(mtcars) res$format('text/yaml' = yaml::as.yaml, compress = FALSE) res$body
Please note that the 'reqres' project is released with a Contributor Code of Conduct. By contributing to this project, you agree to abide by its terms.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.