Skip to content

Commit 02c1562

Browse files
committed
Merge branch 'release/v1.0.0'
2 parents e828b7f + 49f8c7a commit 02c1562

10 files changed

+485
-124
lines changed

Cargo.toml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22
name = "json-response"
33
version = "1.0.0"
4-
description = "A utility library to send JSON response."
4+
description = "A utility library to send JSON response for Routerify and hyper.rs apps."
55
homepage = "https://github.com/routerify/json-response"
66
repository = "https://github.com/routerify/json-response"
77
keywords = ["routerify", "hyper-rs", "json", "response"]
@@ -12,8 +12,10 @@ license = "MIT"
1212
edition = "2018"
1313

1414
[dependencies]
15-
routerify = "1.0"
1615
hyper = "0.13"
16+
routerify = "1.0"
17+
serde = { version = "1.0", features = ["derive"] }
18+
serde_json = "1.0"
1719

1820
[dev-dependencies]
1921
tokio = { version = "0.2", features = ["full"] }

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2020 Boilerplato
3+
Copyright (c) 2020 Rousan Ali
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

README.md

Lines changed: 77 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,28 +5,97 @@
55

66
# json-response
77

8-
A utility library to send JSON response.
8+
A utility library to send JSON response for [`Routerify`](https://github.com/routerify/routerify) and the Rust HTTP library [`hyper.rs`](https://hyper.rs/) apps.
9+
10+
In `Success` case, It generates JSON response in the following format:
11+
12+
```json
13+
{
14+
"status": "success",
15+
"code": "<status_code>",
16+
"data": "<data>"
17+
}
18+
```
19+
20+
In `Failed` case, It generates JSON response in the following format:
21+
22+
```json
23+
{
24+
"status": "failed",
25+
"code": "<status_code>",
26+
"message": "<error_message>"
27+
}
28+
```
929

1030
[Docs](https://docs.rs/json-response)
1131

12-
## Usage
32+
## Install
1333

14-
First add this to your `Cargo.toml`:
34+
Add this to your `Cargo.toml`:
1535

1636
```toml
1737
[dependencies]
38+
routerify = "1.0"
1839
json-response = "1.0"
1940
```
2041

21-
An example:
42+
## Example
43+
2244
```rust
23-
use json_response;
45+
use hyper::{Body, Request, Response, Server, StatusCode};
46+
// Import required json_response methods.
47+
use json_response::{json_failed_resp_with_message, json_success_resp};
48+
use routerify::{Router, RouterService};
49+
use std::net::SocketAddr;
50+
51+
async fn list_users_handler(_: Request<Body>) -> Result<Response<Body>, routerify::Error> {
52+
// Fetch response data from somewhere.
53+
let users = ["Alice", "John"];
54+
55+
// Generate a success JSON response with the data in the following format:
56+
// { "status": "success", code: 200, data: ["Alice", "John"] }
57+
json_success_resp(&users)
58+
}
59+
60+
async fn list_books_handler(_: Request<Body>) -> Result<Response<Body>, routerify::Error> {
61+
// Generate a failed JSON response in the following format:
62+
// { "status": "failed", code: 500, data: "Internal Server Error: Couldn't fetch book list from database" }
63+
json_failed_resp_with_message(
64+
StatusCode::INTERNAL_SERVER_ERROR,
65+
"Couldn't fetch book list from database",
66+
)
67+
}
68+
69+
// Create a router.
70+
fn router() -> Router<Body, routerify::Error> {
71+
Router::builder()
72+
// Attach the handlers.
73+
.get("/users", list_users_handler)
74+
.get("/books", list_books_handler)
75+
.build()
76+
.unwrap()
77+
}
78+
79+
#[tokio::main]
80+
async fn main() {
81+
let router = router();
82+
83+
// Create a Service from the router above to handle incoming requests.
84+
let service = RouterService::new(router);
85+
86+
// The address on which the server will be listening.
87+
let addr = SocketAddr::from(([127, 0, 0, 1], 3001));
88+
89+
// Create a server by passing the created service to `.serve` method.
90+
let server = Server::bind(&addr).serve(service);
2491

25-
fn main() {
26-
println!("{}", json_response::add(2, 3));
92+
println!("App is running on: {}", addr);
93+
if let Err(err) = server.await {
94+
eprintln!("Server error: {}", err);
95+
}
2796
}
2897
```
2998

30-
## Contributing
99+
## Contributing
31100

32101
Your PRs and suggestions are always welcome.

examples/example.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
use hyper::{Body, Request, Response, Server, StatusCode};
2+
// Import required json_response methods.
3+
use json_response::{json_failed_resp_with_message, json_success_resp};
4+
use routerify::{Router, RouterService};
5+
use std::net::SocketAddr;
6+
7+
async fn list_users_handler(_: Request<Body>) -> Result<Response<Body>, routerify::Error> {
8+
// Fetch response data from somewhere.
9+
let users = ["Alice", "John"];
10+
11+
// Generate a success JSON response with the data in the following format:
12+
// { "status": "success", code: 200, data: ["Alice", "John"] }
13+
json_success_resp(&users)
14+
}
15+
16+
async fn list_books_handler(_: Request<Body>) -> Result<Response<Body>, routerify::Error> {
17+
// Generate a failed JSON response in the following format:
18+
// { "status": "failed", code: 500, data: "Internal Server Error: Couldn't fetch book list from database" }
19+
json_failed_resp_with_message(
20+
StatusCode::INTERNAL_SERVER_ERROR,
21+
"Couldn't fetch book list from database",
22+
)
23+
}
24+
25+
// Create a router.
26+
fn router() -> Router<Body, routerify::Error> {
27+
Router::builder()
28+
// Attach the handlers.
29+
.get("/users", list_users_handler)
30+
.get("/books", list_books_handler)
31+
.build()
32+
.unwrap()
33+
}
34+
35+
#[tokio::main]
36+
async fn main() {
37+
let router = router();
38+
39+
// Create a Service from the router above to handle incoming requests.
40+
let service = RouterService::new(router);
41+
42+
// The address on which the server will be listening.
43+
let addr = SocketAddr::from(([127, 0, 0, 1], 3001));
44+
45+
// Create a server by passing the created service to `.serve` method.
46+
let server = Server::bind(&addr).serve(service);
47+
48+
println!("App is running on: {}", addr);
49+
if let Err(err) = server.await {
50+
eprintln!("Server error: {}", err);
51+
}
52+
}

examples/test.rs

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,28 @@
1-
use json_response;
1+
use hyper::{Body, Request, Response, Server, StatusCode};
2+
use json_response::{json_failed_resp, json_failed_resp_with_message, json_success_resp, json_success_resp_with_code};
3+
use routerify::{Router, RouterService};
4+
use std::{convert::Infallible, net::SocketAddr};
25

3-
fn main() {
4-
println!("{}", json_response::add(2, 3));
6+
async fn home_handler(_: Request<Body>) -> Result<Response<Body>, Infallible> {
7+
Ok(json_success_resp(&["Alice", "John"]).unwrap())
8+
}
9+
10+
fn router() -> Router<Body, Infallible> {
11+
Router::builder().get("/", home_handler).build().unwrap()
12+
}
13+
14+
#[tokio::main]
15+
async fn main() {
16+
let router = router();
17+
18+
let service = RouterService::new(router);
19+
20+
let addr = SocketAddr::from(([127, 0, 0, 1], 3001));
21+
22+
let server = Server::bind(&addr).serve(service);
23+
24+
println!("App is running on: {}", addr);
25+
if let Err(err) = server.await {
26+
eprintln!("Server error: {}", err);
27+
}
528
}

examples/test_stream_body.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
use hyper::{Body as HyperBody, Request, Response, Server, StatusCode};
2+
use json_response::{json_failed_resp, json_failed_resp_with_message, json_success_resp, json_success_resp_with_code};
3+
use routerify::{Router, RouterService};
4+
use std::{convert::Infallible, net::SocketAddr};
5+
use stream_body::StreamBody;
6+
7+
async fn home_handler(_: Request<HyperBody>) -> Result<Response<StreamBody>, Infallible> {
8+
Ok(json_success_resp_with_code(StatusCode::ACCEPTED, &["Alice", "John"]).unwrap())
9+
}
10+
11+
fn router() -> Router<StreamBody, Infallible> {
12+
Router::builder().get("/", home_handler).build().unwrap()
13+
}
14+
15+
#[tokio::main]
16+
async fn main() {
17+
let router = router();
18+
19+
let service = RouterService::new(router);
20+
21+
let addr = SocketAddr::from(([127, 0, 0, 1], 3001));
22+
23+
let server = Server::bind(&addr).serve(service);
24+
25+
println!("App is running on: {}", addr);
26+
if let Err(err) = server.await {
27+
eprintln!("Server error: {}", err);
28+
}
29+
}

src/failed_resp.rs

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
use crate::gen_resp::gen_response;
2+
use hyper::{body::HttpBody, Response, StatusCode};
3+
use serde::Serialize;
4+
5+
const STATUS_FAILED: &'static str = "failed";
6+
7+
#[derive(Serialize, Debug, Clone)]
8+
#[serde(rename_all = "camelCase")]
9+
struct FailedResp {
10+
status: &'static str,
11+
code: u16,
12+
message: String,
13+
}
14+
15+
/// Generates a failed JSON response with the provided message and status code.
16+
///
17+
/// It generates JSON response in the following JSON format:
18+
///
19+
/// ```json
20+
/// {
21+
/// "status": "failed",
22+
/// "code": "<status_code>",
23+
/// "message": "<error_message>"
24+
/// }
25+
///```
26+
///
27+
/// # Examples
28+
///
29+
/// ```
30+
/// use hyper::{Body, Request, Response, StatusCode};
31+
/// use json_response::{json_failed_resp_with_message};
32+
///
33+
/// async fn list_books_handler(_: Request<Body>) -> Result<Response<Body>, routerify::Error> {
34+
/// // Generate a failed JSON response in the following format:
35+
/// // { "status": "failed", code: 500, data: "Internal Server Error: Couldn't fetch book list from database" }
36+
/// json_failed_resp_with_message(
37+
/// StatusCode::INTERNAL_SERVER_ERROR,
38+
/// "Couldn't fetch book list from database",
39+
/// )
40+
/// }
41+
/// ```
42+
pub fn json_failed_resp_with_message<B, M>(code: StatusCode, message: M) -> routerify::Result<Response<B>>
43+
where
44+
B: HttpBody + From<Vec<u8>> + Send + Sync + Unpin + 'static,
45+
M: Into<String>,
46+
{
47+
let resp_data = FailedResp {
48+
status: STATUS_FAILED,
49+
code: code.as_u16(),
50+
message: format!("{}: {}", code.canonical_reason().unwrap(), message.into()),
51+
};
52+
53+
gen_response(code, &resp_data)
54+
}
55+
56+
/// Generates a failed JSON response with the status code specific message and status code.
57+
///
58+
/// It generates JSON response in the following JSON format:
59+
///
60+
/// ```json
61+
/// {
62+
/// "status": "failed",
63+
/// "code": "<status_code>",
64+
/// "message": "<status_code_message>"
65+
/// }
66+
///```
67+
///
68+
/// # Examples
69+
///
70+
/// ```
71+
/// use hyper::{Body, Request, Response, StatusCode};
72+
/// use json_response::{json_failed_resp};
73+
///
74+
/// async fn list_books_handler(_: Request<Body>) -> Result<Response<Body>, routerify::Error> {
75+
/// // Generate a failed JSON response in the following format:
76+
/// // { "status": "failed", code: 500, data: "Internal Server Error" }
77+
/// json_failed_resp(StatusCode::INTERNAL_SERVER_ERROR)
78+
/// }
79+
/// ```
80+
pub fn json_failed_resp<B>(code: StatusCode) -> routerify::Result<Response<B>>
81+
where
82+
B: HttpBody + From<Vec<u8>> + Send + Sync + Unpin + 'static,
83+
{
84+
let resp_data = FailedResp {
85+
status: STATUS_FAILED,
86+
code: code.as_u16(),
87+
message: code.canonical_reason().unwrap().to_string(),
88+
};
89+
90+
gen_response(code, &resp_data)
91+
}

src/gen_resp.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
use hyper::{body::HttpBody, header, Response, StatusCode};
2+
use serde::Serialize;
3+
4+
pub(crate) fn gen_response<B, D>(code: StatusCode, resp_data: &D) -> routerify::Result<Response<B>>
5+
where
6+
B: HttpBody + From<Vec<u8>> + Send + Sync + Unpin + 'static,
7+
D: Serialize + Send + Sync + Unpin,
8+
{
9+
let json_resp_data = match serde_json::to_vec(&resp_data) {
10+
Ok(json_data) => json_data,
11+
Err(err) => {
12+
return Err(routerify::Error::new(format!(
13+
"json-response: Failed to convert the response data as JSON: {}",
14+
err
15+
)));
16+
}
17+
};
18+
19+
let content_ln = json_resp_data.len();
20+
let body = B::from(json_resp_data);
21+
22+
let resp = Response::builder()
23+
.status(code)
24+
.header(header::CONTENT_LENGTH, content_ln.to_string())
25+
.header(header::CONTENT_TYPE, "application/json; charset=utf-8")
26+
.body(body);
27+
28+
match resp {
29+
Ok(resp) => Ok(resp),
30+
Err(err) => Err(routerify::Error::new(format!(
31+
"json-response: Failed to create response: {}",
32+
err
33+
))),
34+
}
35+
}

0 commit comments

Comments
 (0)