I would like to create and modify a Request object independently from a Client.
Actually, the API kind of enforce the creation of a RequestBuilder through a Client instance.
Is there any reason to this? Is it planned to update the API of RequestBuilder to offer a way to construct Request like:
let req = Request::builder()
.header("User-Agent", "Some-Agent")
.method(Method::Get).body(())
.timeout(Duration::from_sec(10))
.build();
It's possible to make and use a Request without a Client, and then eventually call client.execute(req).
I read my first comment and I realised that I was not really clear.
I would like to be able to create a Request whose components would be provided
little by little:
// API Host and method are known here
let req_uri = reqwest::Url::parse("https://www.some.com/api/").unwrap();
let mut req = reqwest::Request::new(reqwest::Method::POST, req_uri);
// A test is done to determine if a body needs to be attached
if let Some(body) = potential_body() {
let bytes = serde_json::ser::to_vec(&body).unwrap();
*req.body_mut() = Some(reqwest::Body::from(bytes));
};
if is_protected() {
req.headers_mut()
.insert("Header", "Some Value".parse().unwrap());
}
It is doable but playing with mutable references does not sound really nice.
A RequestBuilder struct exists. As its name imply, I expect it to be able to use it to create
my Request object as proposed in my first comment.
Currently, there is two ways to obtain one RequestBuilder instance:
RequestBuilder::new method:RequestBuilder::new(client: Client, request: ::Result<Request>)
Client::request or one of its derived methods:pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder
I would expect to be able to create a instance of the builder without requiring any dependencies on Client or Request:
// Or RequestBuilder::new()
let req = Request::builder()
.method(Method::Get)
.uri("http://some.com/api")
.header("User-Agent", "Some-Agent")
.timeout(Duration::from_sec(10))
.body(()) // Or .body((())).build()
Related: would it be possible to directly support the types in the http crate so as to consume Requests created using that crate?
I'd also be interested in something like this, or another pattern for "specify everything I need to know to execute a request" that I can then hand to a Client to be executed and handled in some uniform way. Is this something y'all would be open to, or are there reasons to keep RequestBuilder instantiation via a Client?
I've been thinking about this and I have a potential solution.
There are currently two ways to build and send a request:
rust
client.get()
// customize...
.send();
Client.rust
let mut request = Request::new(Method::GET, url);
request.headers_mut().insert(...);
request.body_mut() = ...;
client.execute(&request);
Request "template" without Client. That is, I can Request::try_clone and modify for individual requests. That way, my request defaults are not tied to the Client (see ClientBuilder::default_headers).request.to_builder(). I can't use any shared logic that uses RequestBuilder.The docs for Client::execute state
You should prefer to use the
RequestBuilderandRequestBuilder::send().
This is understandable when weighing the two options above. But it's unfortunate that I can't have the best of both worlds.
I think the crux of the problem is that RequestBuilder contains a reference to Client.
RequestBuilder without ClientRequest::to_builderOptional here: RequestBuilder { client: Optional<Client> }. New functionality described below will not use that field.Request::<httpmethod>() -> RequestBuilder methods and Request::builder() (defaults to GET).Request::to_builder.Client::execute.RequestBuilder::send work without a Client when it is not available, similar to the get convenience function. This would have not affect on existing code and avoids panicing on new code.Client::<httpmethod>() -> RequestBuilderRequestBuilder::send. Or maybe not? It could be left in as a convenience to avoid building a Client.Client::execute to Client::send. (purely aesthetic)Request::new and all Request::*_mut methods, making the struct immutable and encouraging use of RequestBuilder.Unfortunately this is quite invasive. But I think it is worth it as it creates a more flexible and consistent API.
Note: I am taking some inspiration from Java's java.net.http package (for example see HttpRequest). This was introduced with Java 11 (so it's kinda new) and I think it is a good comparison, very similar goals. A further consideration from that API is the way the request builder accepts the body with the http method such as HttpRequest.Builder#POST(body).
Most helpful comment
Related: would it be possible to directly support the types in the
httpcrate so as to consumeRequests created using that crate?