当前位置: 首页 > 工具软件 > rust-jsonnet > 使用案例 >

Rust Web 全栈开发 - 4 HTTP Server

梁学真
2023-12-01

4. HTTP Server

4.1 构建server模块

因为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();
}

4.2 构建router模块

我们继续在下列文件中编写代码

/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模块

4.3 构建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

上面的命令均能够得到预期的页面

 类似资料: