因为HTTP Server需要引用HTTP Package,因此我们先在如下文件中添加依赖
/Users/qinjianquan/s1/httpserver/Cargo.toml
[dependencies]
http = {path = "../http"}
然后在如下文件夹中创建三个mod
/Users/qinjianquan/s1/httpserver/src
handler.rs, router.rs, server.rs
调用顺序:main.rs - server.rs - router.rs - handler.rs
我们先来写server
use super::router::Router;
use super::request::Request;
use std::io::prelude::*;
use std::net::TcpListener;
use std::str;
//define a server struct(object)
pub struct Server<'a> {
socket_addr: <&'a str>, //reference type
}
//implement methods for struct above
impl<'a> Server<'a> {
//create new server
pub fn new(socket_addr: &'a str) -> Server<'a> {
Server { socket_addr }
}
//run server
pub fn run(&self) {
let connection = TcpListener::bind(self.socket_addr).unwrap(); //bind to address and listen connection
println!("Running on {}",self.socket_addr);
for stream in connection.incoming { //use a for loop to read incoming connection into a vector
let mut stream = stream.unwrap();
println!("connection established");
let mut read_buffer = [0;200];
stream.read(&mut read_buffer).unwrap;
let req: HttpRequest = String::from_utf-8(read_buffer.to_vec()).unwrap();
Router::route()
}
}
}
然后在main.rs中添加模块,并且我们初始化一个Server并且运行它
/Users/qinjianquan/s1/httpserver/src/main.rs
mod server;
mod router;
mod handler;
use server::Server;
fn main() {
let server = Server::new("localhost:3000");
server.run();
}
我们继续在下列文件中编写代码
/Users/qinjianquan/s1/httpserver/src/router.rs
use super::handler::{Handler,PageNotFoundHandler,StaticPageHandler,WriteBodyHandler};//not construct
use http::{httprequest,httprequest::HttpRequest,httpresponse::HttpResponse};
use std::io::prelude::*;
//define a struct(object)
pub struct Router;
//implement methods for struct above
impl Router {
pub fn route(req: HttpRequest,stream:&mut impl Write) -> () {
match req.method {
httprequest::Method::Get => match &req.resource {
httprequest::Resource::Path(s) => {
let route: Vec<&str> = s.split("/").collect();
match route[1] {
"api" => {
let resp : HttpResponse = WebServiceHandler::handle(&req);
let _ = resp.send_response(stream);
}
_ => {
let resp : HttpResponse = StaticPageHandler::handle(&req);
let _ = resp.send_response(stream);
}
}
}
},
_ => {
let resp : HttpResponse = PageNotFoundHandler::handle(&req);
let _ = resp.send_response(stream);
}
}
}
}
现在我们的router模块已经构建好,下面我们接着构建handler模块
我们现在cargo.toml文件中添加依赖(两个crate(package))
/Users/qinjianquan/s1/httpserver/Cargo.toml
[dependencies]
http = {path = "../http"}
# use to serialize and deserialize data
serde = {version = "1.0.131",features = ["derive"]}
# use to handle json file
serde_json = "1.0.72"
然后我们编写handler
/Users/qinjianquan/s1/httpserver/src/handler.rs
use http::{httprequest::HttpRequest,httpresponse::HttpResponse};
use serde::{Deserialize,Serialize};
use std::collections::HashMap;
use std::env;
use std::fs;
use std::path;
pub trait Handler {
//handle web request
fn handle(req: &HttpRequest) -> HttpResponse;
//load file (static html file)
fn load_file(file_name: &str) -> Option<String>{
let default_path = format!("{}/public",env!("CARGO_MANIFEST_DIR"));
let public_path = env::var("PUBLIC_PATH").unwrap_or(default_path);
let full_path = format!("{}/{}",public_path,file_name);
let contents = fs::read_to_string(full_path);
contents.ok()
}
}
pub struct StaticPageHandler;
pub struct PageNotFoundHandler;
pub struct WebServiceHandler;
#[derive(Serialize,Deserialize)]
pub struct OrderStatus {
order_id: i32,
order_date: String,
order_status: String
}
impl Handler for PageNotFoundHandler {
fn handle(_req: &HttpRequest) -> HttpResponse {
HttpResponse::new("404",None,Self::load_file("404.html"))
}
}
impl Handler for StaticPageHandler {
fn handle(req:&HttpRequest) -> HttpResponse {
let http::httprequest::Resource::Path(s) = &req.resource;
let route: Vec<&str> = s.split("/").collect();
match route[1] {
"" => HttpResponse::new("200",None,Self::load_file("index.html")),
"health" => HttpResponse::new("200",None,Self::load_file("health.html")),
path => match Self::load_file(path) {
Some(contents) => {
let mut map: HashMap<&str,&str> = HashMap::new();
if path.ends_with(".css") {
map.insert("Content-Type","text/css");
}else if path.ends_with (".js"){
map.insert("Content-Type","text/javascript");
}else {
map.insert("Content-Type","text/html");
}
HttpResponse::new("200",Some(map),Some(contents))
}
None => HttpResponse::new("404",None,Self::load_file("404.html")),
}
}
}
}
impl WebServiceHandler {
fn load_json() -> Vec<OrderStatus> {
let default_path = format!("{}/data",env!("CARGO_MANIFEST_DIR"));
let data_path = env::var("DATA_PATH").unwrap_or(default_path);
let full_path = format!("{}/{}",data_path,"orders.json");
let json_contents = fs::read_to_string(full_path);
let orders: Vec<OrderStatus> =
serde_json::from_str(json_contents.unwrap().as_str()).unwrap();
orders
}
}
impl Handler for WebServiceHandler {
fn handle(req: &HttpRequest) -> HttpResponse {
let http::httprequest::Resource::Path(s) = &req.resource;
let route: Vec<&str> = s.split("/").collect();
match route[2] {
"shipping" if route.len() > 2 && route[3] == "orders" => {
let body = Some(serde_json::to_string(&Self::load_json()).unwrap());
let mut headers :HashMap<&str,&str> = HashMap::new();
headers.insert("Content-Type","application/json");
HttpResponse::new("200", Some(headers),body)
}
_ => HttpResponse::new("404",None,Self::load_file("404.html")),
}
}
}
最后我们再将一些文件添加
/Users/qinjianquan/s1/httpserver/data/{} orders.json
[
{
"order_id": 1,
"order_date": "22 Jan 2022",
"order_status":"Delivered"
},
{
"order_id": 2,
"order_date": "2 Feb 2022",
"order_status":"Pending"
}
]
/Users/qinjianquan/s1/httpserver/public/# styles.css
h1 {
color: red;
margin-left: 25px;
}
/Users/qinjianquan/s1/httpserver/public/<> health.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Health!</title>
</head>
<body>
<h1>Hello,welcome to health page!</h1>
<p>This site is perfectly fine</p>
</body>
</html>
/Users/qinjianquan/s1/httpserver/public/<> index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="stylesheet" href="style.css">
<title>Index</title>
</head>
<body>
<h1>Hello,welcome to home page</h1>
<p>This is the index page for the web site</p>
</body>
</html>
/Users/qinjianquan/s1/httpserver/public/<> 404.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" /> <title>Not Found!</title>
</head>
<body>
<h1>404 Error</h1>
<p>Sorry! The requested page does not exist</p>
</body>
</html>
运行如下命令,在浏览器中访问
cargo run -p httpserver
localhost:3000
localhost:3000/health
localhost:3000/404
localhost:3000/health/orders
上面的命令均能够得到预期的页面