Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/thomasp85/reqres

Powerful classes for http requests and responses
https://github.com/thomasp85/reqres

http http-server request response rstats

Last synced: 15 days ago
JSON representation

Powerful classes for http requests and responses

Awesome Lists containing this project

README

        

---
output: github_document
---

```{r, echo = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>",
fig.path = "README-"
)
```

# reqres

[![Lifecycle: stable](https://img.shields.io/badge/lifecycle-stable-brightgreen.svg)](https://lifecycle.r-lib.org/articles/stages.html#stable)
[![R-CMD-check](https://github.com/thomasp85/reqres/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/thomasp85/reqres/actions/workflows/R-CMD-check.yaml)
[![CRAN_Status_Badge](http://www.r-pkg.org/badges/version-ago/reqres)](https://cran.r-project.org/package=reqres)
[![CRAN_Download_Badge](http://cranlogs.r-pkg.org/badges/reqres)](https://cran.r-project.org/package=reqres)
[![Codecov test coverage](https://codecov.io/gh/thomasp85/reqres/branch/main/graph/badge.svg)](https://app.codecov.io/gh/thomasp85/reqres?branch=main)

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`](https://github.com/jeffreyhorner/Rook/blob/a5e45f751/README.md)
specifications and is thus well suited for
[`httpuv`-based](https://github.com/rstudio/httpuv) webservers.

## Features
`reqres` draws a lot of inspiration from [express.js](https://expressjs.com) 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.

## Installation
reqrescan be installed from CRAN with `install.packages('reqres')` or the
development version can be installed from github:

```{r, eval=FALSE}
# install.packages('devtools')
devtools::install_github('thomasp85/reqres')
```

## Demo
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:

```{r}
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
```

### Request
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.

```{r}
req$host
req$query
req$body
```

The body can easily be parsed though, as long as a parser exists for the
provided content type.

```{r}
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

```{r}
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

```{r}
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

```{r}
req$set_body(NULL)
req$parse(default_parsers)
req$body
```

### Response
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.

```{r}
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.

```{r}
res$set_data('alphabet', letters)
res$get_data('alphabet')
```

Files can be attached and marked for download, setting the relevant headers
automatically

```{r}
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.

```{r}
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.

```{r}
res$format(json = format_json())
res$body
res$get_header('Content-Type')
res$get_header('Content-Encoding')
```

The content negotiation understands wildcards as well

```{r}
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.

```{r}
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

```{r}
res$body <- head(mtcars)
res$format('text/yaml' = yaml::as.yaml, compress = FALSE)
res$body
```

## Code of Conduct
Please note that the 'reqres' project is released with a
[Contributor Code of Conduct](https://reqres.data-imaginist.com/CODE_OF_CONDUCT.html).
By contributing to this project, you agree to abide by its terms.