mirror of
https://github.com/TrueLayer/reqwest-middleware.git
synced 2024-12-25 18:36:30 +00:00
Include README in crate doctests and improve documentation (#8)
* chore: Improve docs and include some examples in doctests
This commit is contained in:
parent
e0b01383d6
commit
b8645f81eb
10 changed files with 124 additions and 71 deletions
56
README.md
56
README.md
|
@ -8,11 +8,29 @@ to allow for client middleware chains.
|
|||
[![CI](https://github.com/TrueLayer/reqwest-middleware/workflows/CI/badge.svg)](https://github.com/TrueLayer/reqwest-middleware/actions)
|
||||
[![Coverage Status](https://coveralls.io/repos/github/TrueLayer/reqwest-middleware/badge.svg?branch=main&t=YKhONc)](https://coveralls.io/github/TrueLayer/reqwest-middleware?branch=main)
|
||||
|
||||
This crate provides functionality for building and running middleware but no middleware
|
||||
implementations. This repository also contains a couple of useful concrete middleware crates:
|
||||
|
||||
* [`reqwest-retry`](https://crates.io/crates/reqwest-retry): retry failed requests.
|
||||
* [`reqwest-trcing`](https://crates.io/crates/reqwest-tracing):
|
||||
[`tracing`](https://crates.io/crates/tracing) integration, optional opentelemetry support.
|
||||
|
||||
## Overview
|
||||
|
||||
The `reqwest-middleware` client exposes the same interface as a plain `reqwest` client, but
|
||||
`ClientBuilder` exposes functionality to attach middleware:
|
||||
|
||||
```toml
|
||||
# Cargo.toml
|
||||
# ...
|
||||
[dependencies]
|
||||
reqwest = "0.11"
|
||||
reqwest-middleware = "0.1.1"
|
||||
reqwest-retry = "0.1.1"
|
||||
reqwest-tracing = "0.1.2"
|
||||
tokio = { version = "1.12.0", features = ["macros", "rt-multi-thread"] }
|
||||
```
|
||||
|
||||
```rust
|
||||
use reqwest_middleware::{ClientBuilder, ClientWithMiddleware};
|
||||
use reqwest_retry::{RetryTransientMiddleware, policies::ExponentialBackoff};
|
||||
|
@ -20,35 +38,27 @@ use reqwest_tracing::TracingMiddleware;
|
|||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let retry_policy = ExponentialBackoff::builder().build_with_max_retries(3);
|
||||
let client = ClientBuilder::new(reqwest::Client::new())
|
||||
.with(TracingMiddleware)
|
||||
.with(RetryTransientMiddleware::new_with_policy(retry_policy))
|
||||
.build();
|
||||
run(client).await;
|
||||
// Retry up to 3 times with increasing intervals between attempts.
|
||||
let retry_policy = ExponentialBackoff::builder().build_with_max_retries(3);
|
||||
let client = ClientBuilder::new(reqwest::Client::new())
|
||||
// Trace HTTP requests. See the tracing crate to make use of these traces.
|
||||
.with(TracingMiddleware)
|
||||
// Retry failed requests.
|
||||
.with(RetryTransientMiddleware::new_with_policy(retry_policy))
|
||||
.build();
|
||||
run(client).await;
|
||||
}
|
||||
|
||||
async fn run(client: ClientWithMiddleware) {
|
||||
// free retries!
|
||||
client
|
||||
.get("https://some-external-service.com")
|
||||
.header("foo", "bar")
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
client
|
||||
.get("https://truelayer.com")
|
||||
.header("foo", "bar")
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
```
|
||||
|
||||
## How to install
|
||||
|
||||
Add `reqwest-middleware` to your dependencies
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
# ...
|
||||
reqwest-middleware = "0.1.0"
|
||||
```
|
||||
|
||||
#### License
|
||||
|
||||
<sup>
|
||||
|
|
|
@ -21,5 +21,8 @@ thiserror = "1"
|
|||
truelayer-extensions = "0.1"
|
||||
|
||||
[dev-dependencies]
|
||||
reqwest = "0.11"
|
||||
reqwest-retry = { path = "../reqwest-retry" }
|
||||
reqwest-tracing = { path = "../reqwest-tracing" }
|
||||
wiremock = "0.5"
|
||||
tokio = { version = "1", features = ["macros"] }
|
||||
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
|
||||
|
|
|
@ -13,7 +13,7 @@ use crate::middleware::{Middleware, Next};
|
|||
|
||||
/// A `ClientBuilder` is used to build a [`ClientWithMiddleware`].
|
||||
///
|
||||
/// [`ClientWithMiddleware`]: struct.ClientWithMiddleware.html
|
||||
/// [`ClientWithMiddleware`]: crate::ClientWithMiddleware
|
||||
pub struct ClientBuilder {
|
||||
client: Client,
|
||||
middleware_stack: Vec<Arc<dyn Middleware>>,
|
||||
|
@ -31,7 +31,7 @@ impl ClientBuilder {
|
|||
///
|
||||
/// If you need to keep a reference to the middleware after attaching, use [`with_arc`].
|
||||
///
|
||||
/// [`with_arc`]: #method.with_arc
|
||||
/// [`with_arc`]: Self::with_arc
|
||||
pub fn with<M>(self, middleware: M) -> Self
|
||||
where
|
||||
M: Middleware,
|
||||
|
@ -41,7 +41,7 @@ impl ClientBuilder {
|
|||
|
||||
/// Add middleware to the chain. [`with`] is more ergonomic if you don't need the `Arc`.
|
||||
///
|
||||
/// [`with`]: #method.with
|
||||
/// [`with`]: Self::with
|
||||
pub fn with_arc(mut self, middleware: Arc<dyn Middleware>) -> Self {
|
||||
self.middleware_stack.push(middleware);
|
||||
self
|
||||
|
@ -55,8 +55,6 @@ impl ClientBuilder {
|
|||
|
||||
/// `ClientWithMiddleware` is a wrapper around [`reqwest::Client`] which runs middleware on every
|
||||
/// request.
|
||||
///
|
||||
/// [`reqwest::Client`]: https://docs.rs/reqwest/0.10.8/reqwest/struct.Client.html
|
||||
#[derive(Clone)]
|
||||
pub struct ClientWithMiddleware {
|
||||
inner: reqwest::Client,
|
||||
|
@ -65,8 +63,6 @@ pub struct ClientWithMiddleware {
|
|||
|
||||
impl ClientWithMiddleware {
|
||||
/// See [`ClientBuilder`] for a more ergonomic way to build `ClientWithMiddleware` instances.
|
||||
///
|
||||
/// [`ClientBuilder`]: struct.ClientBuilder.html
|
||||
pub fn new<T>(client: Client, middleware_stack: T) -> Self
|
||||
where
|
||||
T: Into<Box<[Arc<dyn Middleware>]>>,
|
||||
|
@ -77,40 +73,37 @@ impl ClientWithMiddleware {
|
|||
}
|
||||
}
|
||||
|
||||
/// See [`Client::get`](https://docs.rs/reqwest/latest/reqwest/struct.Client.html#method.get)
|
||||
/// See [`Client::get`]
|
||||
pub fn get<U: IntoUrl>(&self, url: U) -> RequestBuilder {
|
||||
self.request(Method::GET, url)
|
||||
}
|
||||
|
||||
/// See [`Client::post`](https://docs.rs/reqwest/latest/reqwest/struct.Client.html#method.post)
|
||||
/// See [`Client::post`]
|
||||
pub fn post<U: IntoUrl>(&self, url: U) -> RequestBuilder {
|
||||
self.request(Method::POST, url)
|
||||
}
|
||||
|
||||
/// See [`Client::put`](https://docs.rs/reqwest/latest/reqwest/struct.Client.html#method.put)
|
||||
/// See [`Client::put`]
|
||||
pub fn put<U: IntoUrl>(&self, url: U) -> RequestBuilder {
|
||||
self.request(Method::PUT, url)
|
||||
}
|
||||
|
||||
/// See
|
||||
/// [`Client::patch`](https://docs.rs/reqwest/latest/reqwest/struct.Client.html#method.patch)
|
||||
/// See [`Client::patch`]
|
||||
pub fn patch<U: IntoUrl>(&self, url: U) -> RequestBuilder {
|
||||
self.request(Method::PATCH, url)
|
||||
}
|
||||
|
||||
/// See
|
||||
/// [`Client::delete`](https://docs.rs/reqwest/latest/reqwest/struct.Client.html#method.delete)
|
||||
/// See [`Client::delete`]
|
||||
pub fn delete<U: IntoUrl>(&self, url: U) -> RequestBuilder {
|
||||
self.request(Method::DELETE, url)
|
||||
}
|
||||
|
||||
/// See [`Client::head`](https://docs.rs/reqwest/latest/reqwest/struct.Client.html#method.head)
|
||||
/// See [`Client::head`]
|
||||
pub fn head<U: IntoUrl>(&self, url: U) -> RequestBuilder {
|
||||
self.request(Method::HEAD, url)
|
||||
}
|
||||
|
||||
/// See
|
||||
/// [`Client::request`](https://docs.rs/reqwest/latest/reqwest/struct.Client.html#method.request)
|
||||
/// See [`Client::request`]
|
||||
pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder {
|
||||
RequestBuilder {
|
||||
inner: self.inner.request(method, url),
|
||||
|
@ -118,8 +111,7 @@ impl ClientWithMiddleware {
|
|||
}
|
||||
}
|
||||
|
||||
/// See
|
||||
/// [`Client::execute`](https://docs.rs/reqwest/latest/reqwest/struct.Client.html#method.execute)
|
||||
/// See [`Client::execute`]
|
||||
pub async fn execute(&self, req: Request) -> Result<Response> {
|
||||
let mut ext = Extensions::new();
|
||||
self.execute_with_extensions(req, &mut ext).await
|
||||
|
@ -147,8 +139,6 @@ impl From<Client> for ClientWithMiddleware {
|
|||
}
|
||||
|
||||
/// This is a wrapper around [`reqwest::RequestBuilder`] exposing the same API.
|
||||
///
|
||||
/// [`reqwest::RequestBuilder`]: https://docs.rs/reqwest/0.10.8/reqwest/struct.RequestBuilder.html
|
||||
#[must_use = "RequestBuilder does nothing until you 'send' it"]
|
||||
pub struct RequestBuilder {
|
||||
inner: reqwest::RequestBuilder,
|
||||
|
|
|
@ -37,11 +37,18 @@
|
|||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! [`build`]: struct.ClientBuilder.html#method.build
|
||||
//! [`ClientBuilder`]: struct.ClientBuilder.html
|
||||
//! [`ClientWithMiddleware`]: struct.ClientWithMiddleware.html
|
||||
//! [`reqwest::Client`]: https://docs.rs/reqwest/0.10.8/reqwest/struct.Client.html
|
||||
//! [`with`]: struct.ClientBuilder.html#method.with
|
||||
//! [`build`]: ClientBuilder::build
|
||||
//! [`ClientBuilder`]: ClientBuilder
|
||||
//! [`ClientWithMiddleware`]: ClientWithMiddleware
|
||||
//! [`with`]: ClientBuilder::with
|
||||
|
||||
// Test README examples without overriding module docs.
|
||||
// We want to keep the in-code docs separate as those allow for automatic linking to crate
|
||||
// documentation.
|
||||
#[doc = include_str!("../../README.md")]
|
||||
#[cfg(doctest)]
|
||||
pub struct ReadmeDoctests;
|
||||
|
||||
mod client;
|
||||
mod error;
|
||||
mod middleware;
|
||||
|
|
|
@ -30,9 +30,8 @@ use crate::error::{Error, Result};
|
|||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// [`ClientWithMiddleware`]: struct.ClientWithMiddleware.html
|
||||
/// [`Extensions`]: TODO
|
||||
/// [`with`]: struct.ClientBuilder.html#method.with
|
||||
/// [`ClientWithMiddleware`]: crate::ClientWithMiddleware
|
||||
/// [`with`]: crate::ClientBuilder::with
|
||||
#[async_trait::async_trait]
|
||||
pub trait Middleware: 'static + Send + Sync {
|
||||
/// Invoked with a request before sending it. If you want to continue processing the request,
|
||||
|
@ -69,8 +68,8 @@ where
|
|||
/// Next encapsulates the remaining middleware chain to run in [`Middleware::handle`]. You can
|
||||
/// forward the request down the chain with [`run`].
|
||||
///
|
||||
/// [`Middleware::handle`]: trait.Middleware.html#tymethod.handle
|
||||
/// [`run`]: #method.run
|
||||
/// [`Middleware::handle`]: Middleware::handle
|
||||
/// [`run`]: Self::run
|
||||
#[derive(Clone)]
|
||||
pub struct Next<'a> {
|
||||
client: &'a Client,
|
||||
|
|
|
@ -5,7 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
- Re-export `RetryPolicy` from the crate root.
|
||||
### Changed
|
||||
- Disabled default features on `reqwest`
|
||||
|
||||
|
|
|
@ -15,15 +15,7 @@ Build `RetryTransientMiddleware` from a `RetryPolicy`, then attach it to a
|
|||
[`retry-policies::policies`](https://crates.io/crates/retry-policies) is reexported under
|
||||
`reqwest_retry::policies` for convenience.
|
||||
|
||||
## How to install
|
||||
|
||||
Add `reqwest-retry` to your dependencies
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
# ...
|
||||
reqwest-retry = "0.1.0"
|
||||
```
|
||||
See [`reqwest_middleware`](https://docs.rs/reqwest_middleware) for usage with reqwest.
|
||||
|
||||
#### License
|
||||
|
||||
|
|
|
@ -1,7 +1,34 @@
|
|||
//! Middleware to retry failed HTTP requests built on [`reqwest_middleware`].
|
||||
//!
|
||||
//! Use [`RetryTransientMiddleware`] to retry failed HTTP requests. Retry control flow is managed
|
||||
//! by a [`RetryPolicy`].
|
||||
//!
|
||||
//! ## Example
|
||||
//!
|
||||
//! ```
|
||||
//! use reqwest_middleware::{ClientBuilder, ClientWithMiddleware};
|
||||
//! use reqwest_retry::{RetryTransientMiddleware, policies::ExponentialBackoff};
|
||||
//!
|
||||
//! async fn run_retries() {
|
||||
//! // Retry up to 3 times with increasing intervals between attempts.
|
||||
//! let retry_policy = ExponentialBackoff::builder().build_with_max_retries(3);
|
||||
//! let client = ClientBuilder::new(reqwest::Client::new())
|
||||
//! .with(RetryTransientMiddleware::new_with_policy(retry_policy))
|
||||
//! .build();
|
||||
//!
|
||||
//! client
|
||||
//! .get("https://truelayer.com")
|
||||
//! .header("foo", "bar")
|
||||
//! .send()
|
||||
//! .await
|
||||
//! .unwrap();
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
mod middleware;
|
||||
mod retryable;
|
||||
|
||||
pub use retry_policies::policies;
|
||||
pub use retry_policies::{policies, RetryPolicy};
|
||||
|
||||
pub use middleware::RetryTransientMiddleware;
|
||||
pub use retryable::Retryable;
|
||||
|
|
|
@ -12,8 +12,23 @@ Opentracing middleware implementation for
|
|||
|
||||
Attach `TracingMiddleware` to your client to automatically trace HTTP requests:
|
||||
|
||||
```rust
|
||||
use opentelemetry::exporter::trace::stdout;
|
||||
```toml
|
||||
# Cargo.toml
|
||||
# ...
|
||||
[dependencies]
|
||||
opentelemetry = "0.16"
|
||||
reqwest = "0.11"
|
||||
reqwest-middleware = "0.1.1"
|
||||
reqwest-retry = "0.1.1"
|
||||
reqwest-tracing = { version = "0.1.2", features = ["opentelemetry_0_16"] }
|
||||
tokio = { version = "1.12.0", features = ["macros", "rt-multi-thread"] }
|
||||
tracing = "0.1"
|
||||
tracing-opentelemetry = "0.15"
|
||||
tracing-subscriber = "0.2"
|
||||
```
|
||||
|
||||
```rust,skip
|
||||
use opentelemetry::sdk::export::trace::stdout;
|
||||
use reqwest_middleware::ClientBuilder;
|
||||
use reqwest_tracing::TracingMiddleware;
|
||||
use tracing_subscriber::layer::SubscriberExt;
|
||||
|
@ -21,7 +36,7 @@ use tracing_subscriber::Registry;
|
|||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let (tracer, _) = stdout::new_pipeline().install();
|
||||
let tracer = stdout::new_pipeline().install_simple();
|
||||
let telemetry = tracing_opentelemetry::layer().with_tracer(tracer);
|
||||
let subscriber = Registry::default().with(telemetry);
|
||||
tracing::subscriber::set_global_default(subscriber).unwrap();
|
||||
|
@ -29,15 +44,20 @@ async fn main() {
|
|||
run().await;
|
||||
}
|
||||
|
||||
async fun run() {
|
||||
async fn run() {
|
||||
let client = ClientBuilder::new(reqwest::Client::new())
|
||||
.with(TracingMiddleware)
|
||||
.build();`
|
||||
.build();
|
||||
|
||||
client.get("https://truelayer.com").send().await.unwrap();
|
||||
}
|
||||
```
|
||||
|
||||
```terminal
|
||||
$ cargo run
|
||||
SpanData { span_context: SpanContext { trace_id: ...
|
||||
```
|
||||
|
||||
See the [`tracing`](https://crates.io/crates/tracing) crate for more information on how to set up a
|
||||
tracing subscriber to make use of the spans.
|
||||
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
//! Opentracing middleware implementation for [`reqwest-middleware`].
|
||||
//!
|
||||
//! Attach [`TracingMiddleware`] to your client to automatically trace HTTP requests.
|
||||
|
||||
mod middleware;
|
||||
#[cfg(any(
|
||||
feature = "opentelemetry_0_13",
|
||||
|
|
Loading…
Reference in a new issue