Created
October 27, 2019 20:00
-
-
Save TrueMemer/05dba5e9cf00a97cc1860b931467d899 to your computer and use it in GitHub Desktop.
Rust hyper web api
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
extern crate futures; | |
extern crate hyper; | |
extern crate pretty_env_logger; | |
#[macro_use] extern crate log; | |
extern crate serde; | |
extern crate serde_json; | |
extern crate serde_with; | |
extern crate usher; | |
use futures::{future, Future}; | |
use hyper::{Body, Request, Server, StatusCode, Client}; | |
use hyper::client::HttpConnector; | |
use hyper::service::service_fn; | |
use usher::capture::find_capture; | |
use serde::{Serialize, Deserialize}; | |
use serde_with::skip_serializing_none; | |
type GenericError = Box<dyn std::error::Error + Send + Sync>; | |
type ResponseFuture = Box<dyn Future<Item = hyper::Response<Body>, Error = GenericError> + Send>; | |
type Handler = Box<dyn Fn(Request<Body>, Vec<(&str, (usize, usize))>, &Client<HttpConnector>) -> ResponseFuture + Send + Sync>; | |
#[skip_serializing_none] | |
#[derive(Serialize, Deserialize)] | |
struct Response<T> { | |
status: u16, | |
data: Option<T>, | |
error: Option<String> | |
} | |
impl<T> Response<T> { | |
fn new() -> Self { | |
Response { status: 200, data: None, error: None } | |
} | |
fn status(mut self, s: u16) -> Response<T> { | |
self.status = s; | |
self | |
} | |
fn data(mut self, d: T) -> Response<T> { | |
self.data = Some(d); | |
self | |
} | |
fn error(mut self, e: String) -> Response<T> { | |
self.error = Some(e); | |
self | |
} | |
} | |
type StringResponse = Response<String>; | |
impl StringResponse { | |
fn to_json_string(&self) -> String { | |
serde_json::to_string(self).unwrap() | |
} | |
fn to_body(&self) -> Body { | |
Body::from(self.to_json_string()) | |
} | |
} | |
fn not_found_handler() -> ResponseFuture { | |
let resp: StringResponse = | |
Response::new() | |
.status(404) | |
.error(String::from("Not Found")); | |
let body = resp.to_body(); | |
Box::new(future::ok( | |
hyper::Response::builder() | |
.status(StatusCode::from_u16(resp.status).unwrap()) | |
.body(body) | |
.unwrap() | |
)) | |
} | |
fn hello(req: Request<Body>, params: Vec<(&str, (usize, usize))>, _client: &Client<HttpConnector>) -> ResponseFuture { | |
let path = req.uri().path(); | |
let name = find_capture(&path, ¶ms, "name").unwrap(); | |
let body = Body::from(String::from(format!("Hello, {}", name))); | |
let resp = hyper::Response::new(body); | |
Box::new(future::ok(resp)) | |
} | |
fn main() { | |
pretty_env_logger::init(); | |
hyper::rt::run(future::lazy(move || { | |
let addr: std::net::SocketAddr = ([127, 0, 0, 1], 3000).into(); | |
use usher::http::HttpRouter; | |
use usher::prelude::{StaticParser, DynamicParser}; | |
let mut router: HttpRouter<Handler> = | |
HttpRouter::new(vec![Box::new(DynamicParser), Box::new(StaticParser)]); | |
router.get("/:name", Box::new(hello)); | |
let router = std::sync::Arc::new(router); | |
let http_client = Client::new(); | |
let service = move || { | |
let client = http_client.clone(); | |
let router = router.clone(); | |
service_fn(move |req: Request<Body>| { | |
let method = req.method(); | |
let path = req.uri().path(); | |
match router.handler(method, path) { | |
Some((handler, captures)) => handler(req, captures, &client), | |
None => not_found_handler() | |
} | |
}) | |
}; | |
let server = Server::bind(&addr) | |
.serve(service) | |
.map_err(|e| eprintln!("error {}", e)); | |
info!("Listening on {}...", addr); | |
server | |
})); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment