What is the pattern to encapsulate a HTTP client base on reqwest?

Hi, Im new to Rust world and Im a frontend programmer familiar with Javascript and Typescript. Now, I want to encapsulate a HTTP client base on reqwest like what I did before. But the one by one type errors really drives me crasy. This is my code:

use std::error::Error;
use reqwest::{Url, blocking};
use crate::types::{JsonRpcResponse, JsonRpcRequest};
use serde::{Serialize, Deserialize};

pub struct RawHttpRpcClient {
    base_url: Url,
    client: blocking::Client,
}

impl RawHttpRpcClient {
    pub fn new(base_url: Url) -> RawHttpRpcClient {
        RawHttpRpcClient {
            base_url,
            client: blocking::Client::new(),
        }
    }

   // I hope this function can accept lots of serde_json serializable struct, and return another serde_json serializable struct as return.
   // So here I write params as `Option<impl Serialize + Deserialize>`
    pub fn post(&self, method: &str, params: Option<impl Serialize + Deserialize>) -> Result<String, reqwest::Error> {
        let data = JsonRpcRequest {
            id: 1,
            jsonrpc: String::from("2.0"),
            method: String::from(method),
            params,
        };
        let body = serde_json::to_string(&data).expect("Can not parse post data to json string.");

        let response = self.client
            .post(&self.base_url.into())
            .body(body)
            .send()?
            .text()?;

        println!("{}", response);

        Ok(response)
    }
}

Q1: Why the compiler tolds me the following error? I though T is not reference so it do not need any lifetime specifier.

error[E0106]: missing lifetime specifier
 --> ckb-indexer/src/types.rs:4:30
  |
4 | pub struct JsonRpcRequest<T: Deserialize + Serialize> {
  |                              ^^^^^^^^^^^ expected named lifetime parameter
  |
help: consider introducing a named lifetime parameter
  |
4 | pub struct JsonRpcRequest<'a, T: Deserialize<'a> + Serialize> {
  |                           ^^^    ^^^^^^^^^^^^^^^

Q2: The params some times is expected to be None. But I found that I can not call it like this xxx.post("get_something", None), so, Im wandering am I totally wrong with this? Is there any better pattern so solve this situation?

THX for any help.

I recommend not putting trait bounds on structs. You can put them on the impl blocks without putting them on the structs.

To answer your question, you should use DeserializeOwned when you don't care about deserialization into types that contain references into the original data.

So, my design is ok, and the problem is Im using wrong type? And could you please make explain more about You can put them on the impl blocks without putting them on the structs. ? I dont get what impl blocks exactly means.

yes, the issue was using the wrong trait.

This is an impl block:

impl RawHttpRpcClient {
    ...
}

:sweat_smile: Oh! I see, thanks a lot! That's makes me feel better now, Im really afraid of my script language programming experience misleading me on wrong direction.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.