FeignHttp is a declarative HTTP client. Based on rust macros.

Overview

FeignHttp

crates.io Documentation MIT licensed

FeignHttp is a declarative HTTP client. Based on rust macros.

Features

  • Easy to use
  • Asynchronous request
  • Configurable timeout settings
  • Supports plain text and json
  • Selectable HTTP backends (reqwest or isahc)

Usage

FeignHttp mark macros on asynchronous functions, you need a runtime for support async/await. You can use async-std or tokio.

async-std:

[dependencies]
async-std = { version = "1", features = ["attributes", "tokio1"] }

The feature tokio1 is need when use reqwest as the HTTP backend.

tokio:

[dependencies]
tokio = { version = "1", features = ["full"] }

Add feignhttp in your Cargo.toml and use default feature:

feignhttp = { version = "0.2" }

Then add the following code:

use feignhttp::get;

#[get("https://api.github.com")]
async fn github() -> feignhttp::Result<String> {}

#[async_std::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let r = github().await?;
    println!("result: {}", r);

    Ok(())
}

The get attribute macro specifies get request, feignhttp::Result specifies the return result. It will send get request to https://api.github.com and receive a plain text body.

Using non-default HTTP backend:

feignhttp = { version = "0.2", default-features = false, features = ["isahc-client"] }

The default-features = false option disable default reqwest.

Making a POST request

For a post request, you should use the post attribute macro to specify request method and use a body attribute to specify a request body.

use feignhttp::post;

#[post(url = "https://httpbin.org/anything")]
async fn post_data(#[body] text: String) -> feignhttp::Result<String> {}

The #[body] mark a request body. Function parameter text is a String type, it will put in the request body as plain text. String and &str will be put as plain text into the request body.

Paths

Using path to specify path value:

use feignhttp::get;

#[get("https://api.github.com/repos/{owner}/{repo}")]
async fn repository(
    #[path] owner: &str,
    #[path] repo: String,
) -> feignhttp::Result<String> {}

#[async_std::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let r = repository("dxx", "feignhttp".to_string()).await?;
    println!("repository result: {}", r);

    Ok(())
}

dxx will replace {owner} and feignhttp will replace {repo} , the url to be send will be https://api.github.com/repos/dxx/feignhttp. You can specify a path name like #[path("owner")].

Query Parameters

Using param to specify query parameter:

use feignhttp::get;

#[get("https://api.github.com/repos/{owner}/{repo}/contributors")]
async fn contributors(
    #[path("owner")] user: &str,
    #[path] repo: &str,
    #[param] page: u32,
) -> feignhttp::Result<String> {}

#[async_std::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let r = contributors (
        "dxx",
        "feignhttp",
        1,
    ).await?;
    println!("contributors result: {}", r);

    Ok(())
}

The page parameter will as query parameter in the url. An url which will be send is https://api.github.com/repos/dxx/feignhttp?page=1.

Note: A function parameter without param attribute will as a query parameter by default.

Headers

Using header to specify request header:

use feignhttp::get;

#[get("https://api.github.com/repos/{owner}/{repo}/commits")]
async fn commits(
    #[header] accept: &str,
    #[path] owner: &str,
    #[path] repo: &str,
    #[param] page: u32,
    #[param] per_page: u32,
) -> feignhttp::Result<String> {}

#[async_std::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let r = commits(
        "application/vnd.github.v3+json",
        "dxx",
        "feignhttp",
        1,
        5,
    )
    .await?;
    println!("commits result: {}", r);

    Ok(())
}

A header accept:application/vnd.github.v3+json will be send.

URL constant

We can use constant to maintain all urls of request:

use feignhttp::get;

const GITHUB_URL: &str = "https://api.github.com";

#[get(GITHUB_URL, path = "/repos/{owner}/{repo}/languages")]
async fn languages(
    #[path] owner: &str,
    #[path] repo: &str,
) -> feignhttp::Result<String> {}

Url constant must be the first metadata in get attribute macro. You also can specify metadata key:

#[get(url = GITHUB_URL, path = "/repos/{owner}/{repo}/languages")]
async fn languages(
    #[path] owner: &str,
    #[path] repo: &str,
) -> feignhttp::Result<String> {}

JSON

Serde is a framework for serializing and deserializing Rust data structures. When use json, you should add serde in Cargo.toml:

[dependencies]
serde = { version = "1", features = ["derive"] }

Here is an example of getting json:

use feignhttp::get;
use serde::Deserialize;

// Deserialize: Specifies deserialization
#[derive(Debug, Deserialize)]
struct IssueItem {
    pub id: u32,
    pub number: u32,
    pub title: String,
    pub url: String,
    pub repository_url: String,
    pub state: String,
    pub body: Option<String>,
}


const GITHUB_URL: &str = "https://api.github.com";

#[get(url = GITHUB_URL, path = "/repos/{owner}/{repo}/issues")]
async fn issues(
    #[path] owner: &str,
    #[path] repo: &str,
    page: u32,
    per_page: u32,
) -> feignhttp::Result<Vec> {}


#[async_std::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let r = issues("octocat", "hello-world", 1, 2).await?;
    println!("issues: {:#?}", r);

    Ok(())
}

This issues function return Vec, it is deserialized according to the content of the response.

Send a json request:

use feignhttp::post;
use serde::Serialize;

#[derive(Debug, Serialize)]
struct User {
    id: i32,
    name: String,
}

#[post(url = "https://httpbin.org/anything")]
async fn post_user(#[body] user: User) -> feignhttp::Result<String> {}

#[async_std::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let user = User {
        id: 1,
        name: "jack".to_string(),
    };
    let r = post_user(user).await?;
    println!("{}", r);

    Ok(())
}

See here for a complete example.

Using structure

Structure is a good way to manage requests. Define a structure and then define a large number of request methods:

use feignhttp::feign;

const GITHUB_URL: &str = "https://api.github.com";

struct Github;

#[feign(url = GITHUB_URL)]
impl Github {
    #[get]
    async fn home() -> feignhttp::Result<String> {}

    #[get("/repos/{owner}/{repo}")]
    async fn repository(
        #[path] owner: &str,
        #[path] repo: &str,
    ) -> feignhttp::Result<String> {}

    // ...
    
    // Structure method still send request
    #[get(path = "/repos/{owner}/{repo}/languages")]
    async fn languages(
        &self,
        #[path] owner: &str,
        #[path] repo: &str,
    ) -> feignhttp::Result<String> {}
}

See here for a complete example.

Timeout configuration

If you need to configure the timeout, use connect_timeout and timeout to specify connect timeout and read timeout.

Connect timeout:

#[get(url = "http://xxx.com", connect_timeout = 3000)]
async fn connect_timeout() -> feignhttp::Result<String> {}

Read timeout:

#[get(url = "https://httpbin.org/delay/5", timeout = 3000)]
async fn timeout() -> feignhttp::Result<String> {}

License

FeignHttp is provided under the MIT license. See LICENSE.

You might also like...
πŸ±β€πŸ‘€ Drop-in HTTP replacement module for Garry's Mod

πŸ±β€πŸ‘€ gmsv_reqwest This module is a drop-in replacement for Garry's Mod's HTTP function, inspired by gmsv_chttp created by timschumi. The module uses

Multi-stream HTTP downloader using range requests

chooch - An Amazing Project Downloads files faster than wget/curl (in theory) using multiple connections. Chooch recycles the slowest connection and r

xh is a friendly and fast tool for sending HTTP requests. It reimplements as much as possible of HTTPie's excellent design, with a focus on improved performance.
xh is a friendly and fast tool for sending HTTP requests. It reimplements as much as possible of HTTPie's excellent design, with a focus on improved performance.

xh is a friendly and fast tool for sending HTTP requests. It reimplements as much as possible of HTTPie's excellent design, with a focus on improved performance

Wrapper around reqwest to allow for client middleware chains.

reqwest-middleware A crate implementing a wrapper around reqwest to allow for client middleware chains. Overview The reqwest-middleware client exposes

Rust bindings to libcurl

curl-rust libcurl bindings for Rust Quick Start use std::io::{stdout, Write}; use curl::easy::Easy; // Print a web page onto stdout fn main() {

A GraphQL server library implemented in Rust

A GraphQL server library implemented in Rust Async-graphql is a high-performance server-side library that supports all GraphQL specifications. Feature

Typed, correct GraphQL requests and responses in Rust

graphql_client A typed GraphQL client library for Rust. Features Precise types for query variables and responses. Supports GraphQL fragments, objects,

A rule34 scraper made in rust this time

rust-34-scraper A rule34 scraper made in rust this time Building Clone the repository Execute cargo build --release --release is an optimized build pr

Pretend is a macros-based declarative Rust HTTP client

pretend is a modular, Feign-inspired HTTP, client based on macros. It's goal is to decouple the definition of a REST API from it's implementation.

What if we could check declarative macros before using them?
What if we could check declarative macros before using them?

expandable An opinionated attribute-macro based macro_rules! expansion checker. Textbook example rustc treats macro definitions as some opaque piece o

An experimental HTTP server in Rust that supports HTTP/1.1, HTTP/2, and HTTP/3 over QUIC.

πŸš€ H123 An experimental HTTP server in Rust that supports HTTP/1.1, HTTP/2, and HTTP/3 over QUIC. Warning This is an experimental project and not inte

Js-macros - Quickly prototype Rust procedural macros using JavaScript or TypeScript!

js-macros Quickly prototype Rust procedural macros using JavaScript or TypeScript! Have you ever thought "this would be a great use case for a procedu

A more modern http framework benchmarker supporting HTTP/1 and HTTP/2 benchmarks.

rewrk A more modern http framework benchmark utility.

A library for building declarative text-based user interfaces
A library for building declarative text-based user interfaces

Intuitive docs.rs Documentation Intuitive is a component-based library for creating text-based user interfaces (TUIs) easily. It is heavily inspired b

A easy and declarative way to test JSON input in Rust.

assert_json A easy and declarative way to test JSON input in Rust. assert_json is a Rust macro heavily inspired by serde json macro. Instead of creati

Declarative Testing Framework

Demonstrate allows tests to be written without as a much repetitive code within the demonstrate! macro, which will generate the corresponding full tests.

Continuous Delivery for Declarative Kubernetes, Serverless and Infrastructure Applications
Continuous Delivery for Declarative Kubernetes, Serverless and Infrastructure Applications

Continuous Delivery for Declarative Kubernetes, Serverless and Infrastructure Applications Explore PipeCD docs Β» Overview PipeCD provides a unified co

`boxy` - declarative box-drawing characters

boxy - declarative box-drawing characters Box-drawing characters are used extensively in text user interfaces software for drawing lines, boxes, and o

The Declarative Data Generator
The Declarative Data Generator

The Declarative Data Generator Synth is a tool for generating realistic data using a declarative data model. Synth is database agnostic and can scale

Comments
  • Add parameters inside FeignClient

    Add parameters inside FeignClient

    Add parameters inside struct types to handle dynamic URL's betters, example:

    #[derive(Feign)]
    #[feign(headers = "Accept: {accept}")]
    struct Github {
        #[feign_path("owner")]
        user: &'static str,
        #[feign_path]
        repo: &'static str,
        #[param]
        accept: &'static str,
    }
    
    #[feign(url = "https://api.github.com/repos/{owner}/{repo}")]
    impl Github {
        #[get]
        async fn home(&self) -> feignhttp::Result<String> {}
    
        #[get("/repos/{owner}/{repo}", headers = "accept: application/json")]
        async fn repository(&self) -> feignhttp::Result<String> {}
    }
    

    This makes it easier to have a Github instance that always sends requests with the same headers to the same dynamic URL.

    Fixes #6.

    Changed:

    • Add FeignClient trait + Derive
    • Add #[path] #[query] #[param] #[header] parameters inside FeignClients

    PS: The code is not the greatest and new tests are missing, but if you feel like merging this PR I can easily add those.

    opened by ajuvercr 6
  • Extending struct to use struct fields

    Extending struct to use struct fields

    Hi, I was wondering how difficult it would be to enable templates like #[get("https://api.github.com/repos/{self.path}")]. This can be useful to not store URL's as const like the GITHUB example.

    My usecase would be a bit more advanced, where a struct stores AUTH JWT's and adds the JWT as a header, which is stored in self.

    What are your thoughts?

    opened by ajuvercr 0
Releases(v0.5.0)
Owner
Java β˜• developer. A lover of Rust πŸ¦€. Good at full stack development.
null
A more modern http framework benchmarker supporting HTTP/1 and HTTP/2 benchmarks.

rewrk A more modern http framework benchmark utility.

Harrison Burt 273 Dec 27, 2022
An easy and powerful Rust HTTP Client

reqwest An ergonomic, batteries-included HTTP Client for Rust. Plain bodies, JSON, urlencoded, multipart Customizable redirect policy HTTP Proxies HTT

Sean McArthur 6.8k Dec 31, 2022
Minimal Rust HTTP client for both native and WASM

ehttp: a minimal Rust HTTP client for both native and WASM If you want to do HTTP requests and are targetting both native and web (WASM), then this is

Emil Ernerfeldt 105 Dec 25, 2022
HTTPie: human-friendly CLI HTTP client for the API era

HTTPie: human-friendly CLI HTTP client for the API era HTTPie (pronounced aitch-tee-tee-pie) is a command-line HTTP client. Its goal is to make CLI in

null 25.4k Dec 30, 2022
rh: user-friendly command-line HTTP client

Rust HTTP Cli The command name in your terminal is rh. rh: user-friendly command-line HTTP client rh is a user-friendly, lightweight and performant co

null 8 Nov 30, 2022
An HTTP library for Rust

hyper A fast and correct HTTP implementation for Rust. HTTP/1 and HTTP/2 Asynchronous design Leading in performance Tested and correct Extensive produ

null 11k Jan 8, 2023
Pyre - A fast python HTTP server inspired by japronto written in rust.

Pyre - A fast python HTTP server inspired by japronto written in rust.

null 135 Nov 26, 2022
Fast and friendly HTTP server framework for async Rust

Tide Serve the web API Docs | Contributing | Chat Tide is a minimal and pragmatic Rust web application framework built for rapid development. It comes

http-rs 4.1k Jan 2, 2023
ratpack: a simpleton's HTTP framework (for rust-lang)

ratpack: a simpleton's HTTP framework (for rust-lang) ratpack is idealized in the simplicity of the sinatra (ruby) framework in its goal, and attempts

ZeroTier, Inc. 5 Jun 29, 2022
A backend providing a HTTP REST like interface for uploading files written in rust.

UploadServer A backend providing a HTTP REST like interface for uploading files written in rust. API Documentation License This project is licensed un

null 4 Nov 20, 2022