Rust Web 全棧開發(十):編寫服務器端 Web 應用

Rust Web 全棧開發(十):編寫服務器端 Web 應用

  • Rust Web 全棧開發(十):編寫服務器端 Web 應用
    • 創建成員庫:webapp
    • models
    • handlers
    • routers
    • errors
    • mod
    • svr
    • static
      • teachers.html
      • register.html
      • bootstrap.min.css
      • bootstrap.min.js
      • jquery.min.js
    • 測試

Rust Web 全棧開發(十):編寫服務器端 Web 應用

參考視頻:https://www.bilibili.com/video/BV1RP4y1G7KF

繼續之前的 Actix 項目。

我們已經實現了一個 Web Service,現在我們想創建一個 Web App,在網頁端查看并操作數據庫中的數據。

在這里插入圖片描述

主要的技術是模板引擎,它的作用是以靜態頁面作為模板,把動態的數據渲染進頁面,最后一同返回給用戶。

創建成員庫:webapp

回到頂層的工作空間,在終端執行命令 cargo new webapp,創建一個新的成員庫 webapp。

工作空間的 Cargo.toml 會自動添加這個新成員:

在這里插入圖片描述

修改 webapp 中的 Cargo.toml,添加如下依賴:

[package]
name = "webapp"
version = "0.1.0"
edition = "2021"[dependencies]
actix-files = "0.6.0-beta.16"
actix-web = "4.0.0-rc.2"
awc = "3.0.0-beta.21"
dotenv = "0.15.0"
serde = { version = "1.0.136", features = ["derive"] }
serde_json = "1.0.79"
tera = "1.15.0"

在 webapp 目錄下新建一個 .env 文件,內容如下:

HOST_PORT=127.0.0.1:8080

webapp 目錄一覽:

├── webapp
│   ├── src
│   │   ├── bin
│   │       └── svr.rs
│   │   └── mod.rs
│   │   └── errors.rs
│   │   └── handlers.rs
│   │   └── models.rs
│   │   └── routers.rs
│   ├── static
│   │   ├── css
│   │       └── bootstrap.min.css
│   │   ├── javascript
│   │       └── bootstrap.min.js
│   │       └── jquery.min.js
│   │   └── register.html
│   │   └── teachers.html
│   ├── .env
│   └── Cargo.toml

models

編輯 models.rs:

use serde::{Deserialize, Serialize};/// 教師信息,用于應用注冊
#[derive(Serialize, Deserialize, Debug)]
pub struct TeacherRegisterForm {pub name: String,pub picture_url: String,pub profile: String,
}/// 教師信息,用于數據庫查詢
#[derive(Serialize, Deserialize, Debug)]
pub struct TeacherResponse {pub id: i32,pub name: String,pub picture_url: String,pub profile: String,
}

handlers

編輯 handlers.rs:

use crate::errors::MyError;
use crate::models::{TeacherRegisterForm, TeacherResponse};
use actix_web::{web, Error, HttpResponse, Result};
use serde_json::json;pub async fn get_all_teachers(tmpl: web::Data<tera::Tera>
) -> Result<HttpResponse, Error> {// 創建 HTTP 客戶端let awc_client = awc::Client::default();let res = awc_client.get("http://localhost:3000/teachers/").send().await.unwrap().json::<Vec<TeacherResponse>>().await.unwrap();// 創建一個上下文,可以向 HTML 模板里添加數據let mut ctx = tera::Context::new();// 向上下文中插入數據ctx.insert("error", "");ctx.insert("teachers", &res);// s 是渲染的模板,靜態部分是 teachers.html,動態數據是 ctxlet s = tmpl.render("teachers.html", &ctx).map_err(|_| MyError::TeraError("Template error".to_string()))?;Ok(HttpResponse::Ok().content_type("text/html").body(s))
}pub async fn show_register_form(tmpl: web::Data<tera::Tera>
) -> Result<HttpResponse, Error> {let mut ctx = tera::Context::new();ctx.insert("error", "");ctx.insert("current_name", "");ctx.insert("current_picture_url", "");ctx.insert("current_profile", "");let s = tmpl.render("register.html", &ctx).map_err(|_| MyError::TeraError("Template error".to_string()))?;Ok(HttpResponse::Ok().content_type("text/html").body(s))
}pub async fn handle_register(tmpl: web::Data<tera::Tera>,params: web::Form<TeacherRegisterForm>,
) -> Result<HttpResponse, Error> {let mut ctx = tera::Context::new();let s;if params.name == "Dave" {ctx.insert("error", "Dave already exists!");ctx.insert("current_name", &params.name);ctx.insert("current_picture_url", &params.picture_url);ctx.insert("current_profile", &params.profile);s = tmpl.render("register.html", &ctx).map_err(|err| MyError::TeraError(err.to_string()))?;} else {let new_teacher = json!({"name": &params.name,"picture_url": &params.picture_url,"profile": &params.profile,});let awc_client = awc::Client::default();let res = awc_client.post("http://localhost:3000/teachers/").send_json(&new_teacher).await.unwrap().body().await?;let teacher_response: String =serde_json::from_str(&std::str::from_utf8(&res)?)?;s = format!("Message from Web Server: {}", teacher_response);}Ok(HttpResponse::Ok().content_type("text/html").body(s))
}

get_all_teachers 函數向 http://localhost:3000/teachers/ 發生 GET 請求,將得到的 Vec<TeacherResponse> 渲染到 teachers.html,最終返回一個包含渲染網頁的 HTTP Response。

show_register_form 函數創建一個包含教師信息(name、picture_url、profile)的上下文 ctx,渲染到 register.html,最終返回一個包含渲染網頁的 HTTP Response。

handle_register 函數判斷傳入的教師的 name 是否是 Dave 來做出不同的響應。如果是,則將 Dave already exists! 錯誤信息渲染到 register.html,作為 s。如果不是,則向 http://localhost:3000/teachers/ 發送 json 化的新教師信息,將服務器的響應作為 s。最終返回一個包含 s 的 HTTP Response。

routers

編輯 routers.rs:

use crate::handlers::{get_all_teachers, handle_register, show_register_form};
use actix_files;
use actix_web::web;pub fn app_config(config: &mut web::ServiceConfig) {config.service(web::scope("").service(actix_files::Files::new("/static", "./static/").show_files_listing()).service(web::resource("/").route(web::get().to(get_all_teachers))).service(web::resource("/register").route(web::get().to(show_register_form))).service(web::resource("/register-post").route(web::post().to(handle_register))));
}

errors

編輯 errors.rs:

use actix_web::{error, http::StatusCode, HttpResponse, Result};
use serde::Serialize;
use std::fmt;#[derive(Debug, Serialize)]
pub enum MyError {ActixError(String),#[allow(dead_code)]NotFound(String),TeraError(String),
}#[derive(Debug, Serialize)]
pub struct MyErrorResponse {error_message: String,
}impl std::error::Error for MyError {}impl MyError {fn error_response(&self) -> String {match self {MyError::ActixError(msg) => {println!("Server error occurred: {:?}", msg);"Internal server error".into()}MyError::TeraError(msg) => {println!("Error in rendering the template: {:?}", msg);msg.into()}MyError::NotFound(msg) => {println!("Not found error occurred: {:?}", msg);msg.into()}}}
}impl error::ResponseError for MyError {fn status_code(&self) -> StatusCode {match self {MyError::ActixError(_msg) | MyError::TeraError(_msg) => StatusCode::INTERNAL_SERVER_ERROR,MyError::NotFound(_msg) => StatusCode::NOT_FOUND}}fn error_response(&self) -> HttpResponse {HttpResponse::build(self.status_code()).json(MyErrorResponse {error_message: self.error_response(),})}
}impl fmt::Display for MyError {fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {write!(f, "{:?}", self)}
}impl From<actix_web::error::Error> for MyError {fn from(err: actix_web::error::Error) -> Self {MyError::ActixError(err.to_string())}
}

mod

mod.rs:

pub mod models;
pub mod handlers;
pub mod routers;
pub mod errors;

svr

bin/svr.rs 類似于 teacher_service.rs。

#[path = "../mod.rs"]
mod webapp;use actix_web::{web, App, HttpServer};
use dotenv::dotenv;
use routers::app_config;
use std::env;
use webapp::{errors, handlers, models, routers};
use tera::Tera;#[actix_web::main]
async fn main() -> std::io::Result<()> {// 檢測并讀取 .env 文件中的內容,若不存在也會跳過異常dotenv().ok();let host_port = env::var("HOST_PORT").expect("HOST_PORT is not set in .env file");println!("Listening on {}", &host_port);let app = move || {let tera = Tera::new(concat!(env!("CARGO_MANIFEST_DIR"), "/static/**/*")).unwrap();App::new().app_data(web::Data::new(tera)).configure(app_config)};HttpServer::new(app).bind(&host_port)?.run().await
}

CARGO_MANIFEST_DIR 就是 webapp 目錄的地址,這一句代碼將該地址與 /static 連接起來,告訴 app 這些靜態文件的位置。

static

這個目錄下都是網頁相關的靜態文件。

├── static
│   ├── css
│       └── bootstrap.min.css
│   ├── javascript
│       └── bootstrap.min.js
│       └── jquery.min.js
│   └── register.html
│   └── teachers.html

teachers.html

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><title>Teachers</title><link rel="stylesheet" href="/static/css/bootstrap.min.css">
</head><body><div class="container"><h1 class="mt-4 mb-4">Teacher List</h1><ul class="list-group">{% for t in teachers %}<li class="list-group-item"><div class="d-flex w-100 justify-content-between"><h5 class="mb-1">{{t.name}}</h5></div><p class="mb-1">{{t.profile}}</p></li>{% endfor %}</ul><div class="mt-4"><a href="/register" class="btn btn-primary">Register a Teacher</a></div></div><script src="/static/javascript/jquery.min.js"></script><script src="/static/javascript/bootstrap.min.js"></script>
</body></html>

register.html

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><title>Teacher registration</title><link rel="stylesheet" href="/static/css/bootstrap.min.css">
</head><body style="background-color: #f2f9fd;"><div class="container"><h2 class="header text-center mb-4 pt-4">Teacher Registration</h2><div class="row justify-content-center"><div class="col-md-6"><form action="/register-post" method="POST" class="p-4 bg-white rounded shadow-sm"><div class="form-group"><label for="name">Teacher Name</label><input type="text" name="name" id="name" value="{{current_name}}" class="form-control"></div><div class="form-group"><label for="picture_url">Teacher Picture URL</label><input type="text" name="picture_url" id="picture_url" value="{{current_picture_url}}"class="form-control"></div><div class="form-group"><label for="profile">Teacher Profile</label><input type="text" name="profile" id="profile" value="{{current_profile}}" class="form-control"></div><div><p style="color: red">{{error}}</p></div><br/><button type="submit" id="button1" class="btn btn-primary">Register</button></form></div></div></div><script src="/static/javascript/jquery.min.js"></script><script src="/static/javascript/bootstrap.min.js"></script>
</body></html>

bootstrap.min.css

文件太長,見于:UestcXiye/Actix-Workspace/webapp/static/css/bootstrap.min.css

bootstrap.min.js

文件太長,見于:UestcXiye/Actix-Workspace/webapp/static/javascript/bootstrap.min.js

jquery.min.js

文件太長,見于:UestcXiye/Actix-Workspace/webapp/static/javascript/jquery.min.js

測試

因為路由中配置了 /static,訪問 http://localhost:8080/static 會給出項目 static 文件夾的目錄:

在這里插入圖片描述

數據庫中 teacher 表:

在這里插入圖片描述

在一個終端 cd 到 webservice,執行命令 cargo run。

在另一個終端 cd 到 webservice,執行命令 cargo run。

在瀏覽器訪問 http://localhost:8080,頁面如下:

在這里插入圖片描述

webapp 終端輸出 Listening on 127.0.0.1:8080。

點擊 Register a Teacher 按鈕,跳轉到 http://localhost:8080/register,可以填寫表單,注冊新的教師信息。

在這里插入圖片描述

我們先測試無法新增的情況,也就是 Teacher Name 是 Dave 的情況:

在這里插入圖片描述

再測試可以插入新教師信息的情況:

在這里插入圖片描述

插入成功,跳轉到 http://localhost:8080/register-post 頁面:

在這里插入圖片描述

數據庫中 teacher 表新增一條教師信息:

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/90023.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/90023.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/90023.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

每日面試題11:JVM

深入理解JVM&#xff1a;Java的“心臟”如何驅動程序運行&#xff1f;為什么需要JVM&#xff1f;你是否想過&#xff0c;為什么用Java寫的程序&#xff0c;能在Windows、Linux、macOS上“無縫運行”&#xff1f;為什么開發者無需為不同操作系統重寫代碼&#xff1f;這背后的核心…

Linux網絡信息(含ssh服務和rsync)

73.telnet&#xff1a;測試端口連通性用法&#xff1a;telnet 主機名或IP 端口號測試目標主機的指定端口是否開放&#xff0c;檢查網絡服務連通性。eg&#xff1a;telnet www.baidu.com 80# 說明&#xff1a;# - 如果連接成功&#xff0c;顯示 "Connected to ..."。…

【PTA數據結構 | C語言版】我愛背單詞

本專欄持續輸出數據結構題目集&#xff0c;歡迎訂閱。 文章目錄題目代碼題目 作為一個勤奮的學生&#xff0c;你在閱讀一段英文文章時&#xff0c;是否希望有個程序能自動幫你把沒有背過的生詞列出來&#xff1f;本題就請你實現這個程序。 輸入格式&#xff1a; 輸入第 1 行給…

如何使用電腦連接小米耳機(紅米 redmi耳機)

如何使用電腦連接小米&#xff08;紅米 redmi&#xff09;耳機Redmi耳機連接電腦的具體步驟如下注意事項和常見問題解決方法&#xff1a;Redmi耳機連接電腦的具體步驟如下 打開耳機倉蓋&#xff1a; 首先&#xff0c;打開Redmi耳機的充電倉蓋&#xff0c;但不需要取出耳機。進…

排序算法—交換排序(冒泡、快速)(動圖演示)

目錄 十大排序算法分類?編輯 冒泡排序 算法步驟&#xff1a; 動圖演示&#xff1a; 性能分析&#xff1a; 代碼實現&#xff08;Java&#xff09;&#xff1a; 快速排序&#xff08;挖坑法&#xff09; 算法步驟&#xff1a; 動圖演示&#xff1a; 性能分析&#xff1…

2023 年 5 月青少年軟編等考 C 語言八級真題解析

目錄 T1. 道路 思路分析 T2. Rainbow 的商店 思路分析 T3. 冰闊落 I 思路分析 T4. 青蛙的約會 思路分析 T1. 道路 題目鏈接:SOJ D1216 N N N 個以 1 ~ N 1 \sim N 1~N 標號的城市通過單向的道路相連,每條道路包含兩個參數:道路的長度和需要為該路付的通行費(以金幣的數…

【vue-4】深入理解 Vue 3 中的 v-for 指令

Vue.js 作為現代前端框架的代表之一&#xff0c;其模板指令系統提供了強大的數據綁定和渲染能力。其中&#xff0c;v-for 指令是 Vue 中最常用且最重要的指令之一&#xff0c;它允許我們基于數據源循環渲染元素或組件。在 Vue 3 中&#xff0c;v-for 保留了一貫的簡潔語法&…

《R for Data Science (2e)》免費中文翻譯 (第1章) --- Data visualization(1)

寫在前面 本系列推文為《R for Data Science (2)》的中文翻譯版本。所有內容都通過開源免費的方式上傳至Github&#xff0c;歡迎大家參與貢獻&#xff0c;詳細信息見&#xff1a; Books-zh-cn 項目介紹&#xff1a; Books-zh-cn&#xff1a;開源免費的中文書籍社區 r4ds-zh-cn …

界面組件DevExpress WPF中文教程:Grid - 如何完成節點排序和移動?

DevExpress WPF擁有120個控件和庫&#xff0c;將幫助您交付滿足甚至超出企業需求的高性能業務應用程序。通過DevExpress WPF能創建有著強大互動功能的XAML基礎應用程序&#xff0c;這些應用程序專注于當代客戶的需求和構建未來新一代支持觸摸的解決方案。 無論是Office辦公軟件…

【Prometheus+Grafana篇】監控通過Keepalived實現的MySQL HA高可用架構

&#x1f4ab;《博主主頁》&#xff1a;    &#x1f50e; CSDN主頁__奈斯DB    &#x1f50e; IF Club社區主頁__奈斯、 &#x1f525;《擅長領域》&#xff1a;擅長阿里云AnalyticDB for MySQL(分布式數據倉庫)、Oracle、MySQL、Linux、prometheus監控&#xff1b;并對…

k8s:利用kubectl部署postgis:17-3.5

1.離線環境CPU:Hygon C86 7285 32-core Processor 操作系統&#xff1a;麒麟操作系統 containerd&#xff1a;1.7.27 Kubernetes:1.26.12 KubeSphere:4.1.2 kubekey&#xff1a;3.1.10 Harbor:2.13.1 Postgis:17-3.52.創建并執行postgresql-headless.yaml2.1創建apiVersion: v1…

Mysql(存儲過程)

目錄 介紹 特點 存儲過程創建 系統變量(不重要) 用戶變量 局部變量 if 判斷 參數&#xff08;in, out, inout) case while repeat loop 游標和條件處理程序-handler 存儲函數 為了防止以后忘記&#xff0c;反復去看視頻浪費時間&#xff0c;特寫一篇 介紹 存儲過程…

Effective Python 第14條: 用sort方法的key參數來表示復雜的排序邏輯

一、引言&#xff1a;Python排序功能的重要性 在Python開發中&#xff0c;排序功能是一個常見的需求。無論是處理數據、優化算法&#xff0c;還是提升用戶體驗&#xff0c;排序都是不可或缺的一部分。Python的列表內置了sort方法&#xff0c;提供了靈活的排序功能。然而&#…

react+antd 可拖拽模態框組件

DraggableModal 可拖拽模態框組件使用說明 概述 DraggableModal 是一個基于 dnd-kit/core 實現的可拖拽模態框組件&#xff0c;允許用戶通過拖拽標題欄來移動模態框位置。該組件具有智能邊界檢測功能&#xff0c;確保模態框始終保持在可視區域內。 功能特性 ? 可拖拽移動&…

MySQL的基本操作及相關python代碼

下面為你介紹 MySQL 的基本操作,以及對應的 Python 代碼實現。我會先介紹 SQL 基本操作,再展示如何用 Python 連接 MySQL 并執行這些操作。 一、MySQL 基本操作(SQL 語句) 1. 連接數據庫 bash mysql -u root -p2. 創建數據庫 sql CREATE DATABASE testdb;3. 使用數據…

Armbian(斐訊N1)安裝xfce桌面以及遠程環境

安裝xfce桌面以及vncserver(遠程連接) 安裝xfce桌面 apt-get install xfce4 xfce4-goodies xorg dbus-x11 x11-xserver-utils ubuntu的安裝gdm3&#xff0c; apt install gdm3 debian安裝lightdm。 apt install lightdm 安裝vnc server apt-get install tightvncserver 中文字體…

【Oracle】Oracle 11g打補丁時遇到opatch apply命令無法識別

?? 1. 使用完整路徑執行命令 問題原因&#xff1a;若未將$ORACLE_HOME/OPatch加入系統PATH環境變量&#xff0c;直接輸入opatch apply會因系統無法定位命令而報錯。 解決方案&#xff1a; 改用絕對路徑執行&#xff1a; $ORACLE_HOME/OPatch/opatch apply例如&#xff1a; /u…

單例模式詳細講解

一.定義單例模式是一種創建型設計模式&#xff0c;確保一個類只有一個實例&#xff0c;并提供一個全局訪問點特點&#xff1a;1.構造函數和析構函數私有化2.禁用拷貝構造函數和賦值運算符重載&#xff08;delete&#xff09;3.利用靜態成員函數和靜態成員變量來給外界提供訪問二…

KORGym:評估大語言模型推理能力的動態游戲平臺

KORGym&#xff1a;評估大語言模型推理能力的動態游戲平臺 現有評估基準多受領域限制或 pretraining 數據影響&#xff0c;難以精準測LLMs內在推理能力。KORGym平臺應運而生&#xff0c;含50余款游戲&#xff0c;多維度評估&#xff0c;本文將深入解析其設計、框架、實驗及發現…

ISPDiffuser文章翻譯理解

ISPDiffuser: Learning RAW-to-sRGB Mappings with Texture-Aware Diffusion Models and Histogram-Guided Color Consistency翻譯 Type: Conference paper Author: Yang Ren1,4, Hai Jiang1,4, Menglong Yang1,2,?, Wei Li1,2, Shuaicheng Liu3,4,? Select: ???????…