Skip to content
This repository has been archived by the owner on Oct 29, 2021. It is now read-only.

Commit

Permalink
Merge pull request #34 from obsidian-rs/feature/custom_header
Browse files Browse the repository at this point in the history
Feature/custom header
  • Loading branch information
jk-gan authored Feb 3, 2020
2 parents aa46ecb + bc68323 commit c5fbe93
Show file tree
Hide file tree
Showing 8 changed files with 519 additions and 259 deletions.
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ path = 'examples/hello.rs'
name = 'hello_handler'
path = 'examples/hello_handler.rs'

[[example]]
name = 'json'
path = 'examples/json.rs'

[package]
name = 'obsidian'
version = '0.1.0-alpha.1'
Expand Down
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,41 @@ async fn main() {
}
```

## JSON Response
```rust
use obsidian::{
context::Context,
router::{Responder, Response},
App,
};
use serde::*;

async fn get_user(_ctx: Context) -> impl Responder {
#[derive(Serialize, Deserialize)]
struct User {
name: String,
};

let user = User {
name: String::from("Obsidian"),
};
Response::ok().json(user)
}

#[tokio::main]
async fn main() {
let mut app = App::new();
let addr = ([127, 0, 0, 1], 3000).into();

app.get("/user", get_user);

app.listen(&addr, || {
println!("server is listening to {}", &addr);
})
.await;
}
```

## Example Files

Example are located in `example/main.rs`.
Expand Down
31 changes: 31 additions & 0 deletions examples/json.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use obsidian::{
context::Context,
router::{Responder, Response},
App,
};
use serde::*;

async fn get_user(_ctx: Context) -> impl Responder {
#[derive(Serialize, Deserialize)]
struct User {
name: String,
};

let user = User {
name: String::from("Obsidian"),
};
Response::ok().json(user)
}

#[tokio::main]
async fn main() {
let mut app = App::new();
let addr = ([127, 0, 0, 1], 3000).into();

app.get("/user", get_user);

app.listen(&addr, || {
println!("server is listening to {}", &addr);
})
.await;
}
153 changes: 104 additions & 49 deletions examples/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use std::{fmt, fmt::Display};
use obsidian::{
context::Context,
middleware::logger::Logger,
router::{response, Responder, Router},
router::{header, Responder, Response, Router},
App, StatusCode,
};

Expand All @@ -30,41 +30,107 @@ struct ParamTest {
test2: String,
}

#[derive(Serialize, Deserialize, Debug)]
struct User {
name: String,
age: i8,
}

impl Display for JsonTest {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{{title: {}, content: {}}}", self.title, self.content)
}
}

async fn responder_obsidian_error(mut ctx: Context) -> impl Responder {
let json: JsonTest = ctx.json().await?;
println!("{}", json);
Ok(response::json(json, StatusCode::OK))
}
// async fn responder_json(mut ctx: Context) -> impl Responder {
// let person: Person = ctx.json().await?;

// person.age += 1;

// Ok(response::json(person))
// }

// async fn responder_obsidian_error(mut ctx: Context) -> impl Responder {
// let json: JsonTest = ctx.json().await?;
// println!("{}", json);
// Ok(response::json(json, StatusCode::OK))
// }

// fn responder_with_header(_ctx: Context) -> impl Responder {
// let headers = vec![
// ("X-Custom-Header-4", "custom-value-4"),
// ("X-Custom-Header-5", "custom-value-5"),
// ];

// "here"
// .header("Content-Type", "application/json")
// .header("X-Custom-Header", "custom-value")
// .header("X-Custom-Header-2", "custom-value-2")
// .header("X-Custom-Header-3", "custom-value-3")
// .set_headers(headers)
// .status(StatusCode::CREATED)
// }

#[tokio::main]
async fn main() {
let mut app = App::new();
let addr = ([127, 0, 0, 1], 3000).into();

app.get("/", |_ctx| async {
"<!DOCTYPE html><html><head><link rel=\"shotcut icon\" href=\"favicon.ico\" type=\"image/x-icon\" sizes=\"32x32\" /></head> <h1>Hello Obsidian</h1></html>"
Response::ok().html("<!DOCTYPE html><html><head><link rel=\"shotcut icon\" href=\"favicon.ico\" type=\"image/x-icon\" sizes=\"32x32\" /></head> <h1>Hello Obsidian</h1></html>")
});

app.get("/json", |_ctx| async {
let point = Point { x: 1, y: 2 };

Response::created()
.set_header(header::AUTHORIZATION, "token")
.set_header_str("X-Custom-Header", "Custom header value")
.json(point)
});

app.get("/json", |_ctx| {
async {
let point = Point { x: 1, y: 2 };
app.get("/json-with-headers", |_ctx| async {
let point = Point { x: 1, y: 2 };

let custom_headers = vec![
("X-Custom-Header-1", "Custom header 1"),
("X-Custom-Header-2", "Custom header 2"),
("X-Custom-Header-3", "Custom header 3"),
];

let standard_headers = vec![
(header::AUTHORIZATION, "token"),
(header::ACCEPT_CHARSET, "utf-8"),
];

Response::created()
.with_headers(standard_headers)
.with_headers_str(custom_headers)
.json(point)
});

response::json(point, StatusCode::OK)
// res.header(header::CONTENT_TYPE, "application/json")
// .status(StatusCode::OK)
// .json(point)
}
app.get("/string-with-headers", |_ctx| async {
let custom_headers = vec![
("X-Custom-Header-1", "Custom header 1"),
("X-Custom-Header-2", "Custom header 2"),
("X-Custom-Header-3", "Custom header 3"),
];

let standard_headers = vec![
(header::AUTHORIZATION, "token"),
(header::ACCEPT_CHARSET, "utf-8"),
];

"Hello World"
.with_headers(standard_headers)
.with_headers_str(custom_headers)
});

app.get("/empty-body", |_ctx| async { StatusCode::OK });

app.get("/vec", |_ctx| async { vec![1, 2, 3] });
app.get("/vec", |_ctx| async {
vec![1, 2, 3].with_status(StatusCode::CREATED)
});

app.get("/String", |_ctx| async {
"<h1>This is a String</h1>".to_string()
Expand All @@ -74,32 +140,20 @@ async fn main() {
"<h1>Test radix</h1>".to_string()
});

app.get("/team/radix", |_ctx| async {
"<h1>Team radix</h1>".to_string()
});
app.get("/team/radix", |_ctx| async { "Team radix".to_string() });

app.get("/test/radix2", |_ctx| async {
"<h1>Test radix2</h1>".to_string()
});

app.get("/jsontest", |_ctx| async {
response::file("./testjson.html").await
Response::ok().file("./testjson.html").await
});

app.get("/jsan", |_ctx: Context| async {
"<h1>jsan</h1>".to_string()
});

app.post("/jsontestapi", |mut ctx: Context| async move {
let json: serde_json::Value = ctx.json().await?;

println!("{}", json);

Ok(response::json(json, StatusCode::OK))
});

app.post("/jsonteststructapi", responder_obsidian_error);

app.get("/test/wildcard/*", |ctx: Context| async move {
format!(
"{}<br>{}",
Expand Down Expand Up @@ -151,38 +205,39 @@ async fn main() {

let mut form_router = Router::new();

form_router.get("/formtest", |_ctx| response::file("./test.html"));
form_router.get("/formtest", |_ctx| Response::ok().file("./test.html"));

form_router.post("/formtest", |mut ctx: Context| async move {
let param_test: ParamTest = ctx.form().await?;
// form_router.post("/formtest", |mut ctx: Context| async move{
// let param_test: ParamTest = ctx.form().await?;

dbg!(&param_test);
// dbg!(&param_test);

Ok(response::json(param_test, StatusCode::OK))
});
// Ok(response::json(param_test, StatusCode::OK))
// });

let mut param_router = Router::new();
let logger = Logger::new();
app.use_service(logger);

let logger_example = middleware::logger_example::LoggerExample::new();
app.use_service(logger_example);
// param_router.get("/paramtest/:id", |ctx: Context| async move {
// let param_test: i32 = ctx.param("id")?;

param_router.get("/paramtest/:id", |ctx: Context| async move {
let param_test: i32 = ctx.param("id")?;
// dbg!(&param_test);

dbg!(&param_test);
// Ok(response::json(param_test, StatusCode::OK))
// });
//
// param_router.get("/paramtest/:id/test", |ctx: Context| async move {
// let mut param_test: i32 = ctx.param("id").unwrap();
// param_test = param_test * 10;

Ok(response::json(param_test, StatusCode::OK))
});
param_router.get("/paramtest/:id/test", |ctx: Context| async move {
let mut param_test: i32 = ctx.param("id").unwrap();
param_test *= 10;
// dbg!(&param_test);

dbg!(&param_test);
// Ok(response::json(param_test, StatusCode::OK))
// });

Ok(response::json(param_test, StatusCode::OK))
});
let logger_example = middleware::logger_example::LoggerExample::new();
app.use_service(logger_example);

param_router.get("/test-next-wild/*", |_ctx| async {
"<h1>test next wild</h1>".to_string()
Expand Down
10 changes: 6 additions & 4 deletions src/router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ mod handler;
mod req_deserializer;
mod resource;
mod responder;
pub mod response;
mod response;
mod response_body;
mod route;
mod route_trie;
Expand All @@ -11,11 +11,13 @@ use self::route_trie::RouteTrie;
use crate::context::Context;
use crate::middleware::Middleware;
use crate::Method;
pub use hyper::header;

pub use self::handler::Handler;
pub use self::req_deserializer::{from_cow_map, Error as FormError};
pub use self::resource::Resource;
pub use self::responder::{Responder, ResponseResult};
pub use self::responder::Responder;
pub use self::response::Response;
pub use self::response_body::ResponseBody;
pub use self::route::Route;

Expand Down Expand Up @@ -131,7 +133,7 @@ impl Router {

dir_path.append(&mut relative_path);

Box::pin(async move { response::file(&dir_path.join("/")).await })
Box::pin(async move { Response::ok().file(&dir_path.join("/")).await })
}
}

Expand All @@ -144,7 +146,7 @@ impl Router {
.map(|x| x.to_string())
.collect::<Vec<String>>();

response::file(&relative_path.join("/")).await
Response::ok().file(&relative_path.join("/")).await
}
}

Expand Down
20 changes: 17 additions & 3 deletions src/router/handler.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use super::{Responder, ResponseResult};
use super::Responder;
use crate::context::Context;

use std::future::Future;
use async_trait::async_trait;
use hyper::{header, Body, Response};
use std::future::Future;

pub type ResponseResult = http::Result<Response<Body>>;

#[async_trait]
pub trait Handler: Send + Sync + 'static {
Expand All @@ -17,6 +20,17 @@ where
F::Output: Responder,
{
async fn call(&self, ctx: Context) -> ResponseResult {
(self)(ctx).await.respond_to()
let response = (self)(ctx).await.respond_to();

let mut res = Response::builder();
if let Some(headers) = response.headers() {
if let Some(response_headers) = res.headers_mut() {
headers.iter().for_each(move |(key, value)| {
response_headers.insert(key, header::HeaderValue::from_static(value));
});
}
}

res.status(response.status()).body(response.body())
}
}
Loading

0 comments on commit c5fbe93

Please sign in to comment.