博客系统 #
本节将使用Rocket构建一个完整的博客系统,包括文章管理、评论系统和用户交互功能。
项目结构 #
text
blog_system/
├── Cargo.toml
├── Rocket.toml
├── src/
│ ├── main.rs
│ ├── models/
│ │ ├── mod.rs
│ │ ├── user.rs
│ │ ├── post.rs
│ │ └── comment.rs
│ ├── routes/
│ │ ├── mod.rs
│ │ ├── auth.rs
│ │ ├── post.rs
│ │ └── comment.rs
│ └── db.rs
├── templates/
│ ├── base.html
│ ├── index.html
│ ├── post.html
│ └── login.html
└── static/
└── css/
└── style.css
数据模型 #
用户模型 #
rust
use serde::{Serialize, Deserialize};
use uuid::Uuid;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct User {
pub id: Uuid,
pub username: String,
pub email: String,
pub password_hash: String,
pub bio: Option<String>,
pub created_at: String,
}
#[derive(Debug, Deserialize)]
pub struct RegisterUser {
pub username: String,
pub email: String,
pub password: String,
}
文章模型 #
rust
use serde::{Serialize, Deserialize};
use uuid::Uuid;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Post {
pub id: Uuid,
pub title: String,
pub content: String,
pub author_id: Uuid,
pub author_name: String,
pub created_at: String,
pub updated_at: String,
pub views: u32,
pub likes: u32,
}
#[derive(Debug, Deserialize)]
pub struct CreatePost {
pub title: String,
pub content: String,
}
#[derive(Debug, Deserialize)]
pub struct UpdatePost {
pub title: Option<String>,
pub content: Option<String>,
}
评论模型 #
rust
use serde::{Serialize, Deserialize};
use uuid::Uuid;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Comment {
pub id: Uuid,
pub post_id: Uuid,
pub author_id: Uuid,
pub author_name: String,
pub content: String,
pub created_at: String,
}
#[derive(Debug, Deserialize)]
pub struct CreateComment {
pub content: String,
}
数据库 #
rust
use std::collections::HashMap;
use std::sync::Mutex;
use uuid::Uuid;
pub struct Db {
pub users: Mutex<HashMap<Uuid, crate::models::user::User>>,
pub posts: Mutex<HashMap<Uuid, crate::models::post::Post>>,
pub comments: Mutex<HashMap<Uuid, crate::models::comment::Comment>>,
}
impl Db {
pub fn new() -> Self {
Self {
users: Mutex::new(HashMap::new()),
posts: Mutex::new(HashMap::new()),
comments: Mutex::new(HashMap::new()),
}
}
}
路由实现 #
文章路由 #
rust
use rocket::serde::json::Json;
use rocket::State;
use rocket_dyn_templates::Template;
use uuid::Uuid;
use crate::models::post::{Post, CreatePost, UpdatePost};
use crate::db::Db;
use crate::guards::AuthUser;
#[get("/posts")]
pub fn list(db: &State<Db>) -> Json<Vec<Post>> {
let posts = db.posts.lock().unwrap();
Json(posts.values().cloned().collect())
}
#[get("/posts/<id>")]
pub fn get(id: Uuid, db: &State<Db>) -> Option<Json<Post>> {
let posts = db.posts.lock().unwrap();
posts.get(&id).cloned().map(Json)
}
#[post("/posts", format = "json", data = "<post>")]
pub fn create(post: Json<CreatePost>, user: AuthUser, db: &State<Db>) -> Json<Post> {
let id = Uuid::new_v4();
let now = chrono::Utc::now().to_rfc3339();
let new_post = Post {
id,
title: post.title.clone(),
content: post.content.clone(),
author_id: Uuid::parse_str(&user.id).unwrap(),
author_name: user.username.clone(),
created_at: now.clone(),
updated_at: now,
views: 0,
likes: 0,
};
db.posts.lock().unwrap().insert(id, new_post.clone());
Json(new_post)
}
#[put("/posts/<id>", format = "json", data = "<post>")]
pub fn update(
id: Uuid,
post: Json<UpdatePost>,
user: AuthUser,
db: &State<Db>,
) -> Option<Json<Post>> {
let mut posts = db.posts.lock().unwrap();
if let Some(existing) = posts.get_mut(&id) {
if existing.author_id.to_string() == user.id {
if let Some(title) = &post.title {
existing.title = title.clone();
}
if let Some(content) = &post.content {
existing.content = content.clone();
}
existing.updated_at = chrono::Utc::now().to_rfc3339();
return Some(Json(existing.clone()));
}
}
None
}
#[delete("/posts/<id>")]
pub fn delete(id: Uuid, user: AuthUser, db: &State<Db>) -> bool {
let mut posts = db.posts.lock().unwrap();
if let Some(post) = posts.get(&id) {
if post.author_id.to_string() == user.id {
posts.remove(&id);
return true;
}
}
false
}
#[post("/posts/<id>/like")]
pub fn like(id: Uuid, db: &State<Db>) -> Option<Json<Post>> {
let mut posts = db.posts.lock().unwrap();
if let Some(post) = posts.get_mut(&id) {
post.likes += 1;
return Some(Json(post.clone()));
}
None
}
评论路由 #
rust
use rocket::serde::json::Json;
use rocket::State;
use uuid::Uuid;
use crate::models::comment::{Comment, CreateComment};
use crate::db::Db;
use crate::guards::AuthUser;
#[get("/posts/<post_id>/comments")]
pub fn list(post_id: Uuid, db: &State<Db>) -> Json<Vec<Comment>> {
let comments = db.comments.lock().unwrap();
let post_comments: Vec<Comment> = comments
.values()
.filter(|c| c.post_id == post_id)
.cloned()
.collect();
Json(post_comments)
}
#[post("/posts/<post_id>/comments", format = "json", data = "<comment>")]
pub fn create(
post_id: Uuid,
comment: Json<CreateComment>,
user: AuthUser,
db: &State<Db>,
) -> Json<Comment> {
let id = Uuid::new_v4();
let new_comment = Comment {
id,
post_id,
author_id: Uuid::parse_str(&user.id).unwrap(),
author_name: user.username.clone(),
content: comment.content.clone(),
created_at: chrono::Utc::now().to_rfc3339(),
};
db.comments.lock().unwrap().insert(id, new_comment.clone());
Json(new_comment)
}
#[delete("/comments/<id>")]
pub fn delete(id: Uuid, user: AuthUser, db: &State<Db>) -> bool {
let mut comments = db.comments.lock().unwrap();
if let Some(comment) = comments.get(&id) {
if comment.author_id.to_string() == user.id {
comments.remove(&id);
return true;
}
}
false
}
模板渲染 #
基础模板 #
html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{% block title %}Blog{% endblock %}</title>
<link rel="stylesheet" href="/static/css/style.css">
</head>
<body>
<header>
<nav>
<a href="/">Home</a>
{% if user %}
<a href="/new-post">New Post</a>
<a href="/logout">Logout ({{ user.username }})</a>
{% else %}
<a href="/login">Login</a>
<a href="/register">Register</a>
{% endif %}
</nav>
</header>
<main>
{% block content %}{% endblock %}
</main>
<footer>
<p>© 2024 My Blog</p>
</footer>
</body>
</html>
首页模板 #
html
{% extends "base.html" %}
{% block title %}Blog - Home{% endblock %}
{% block content %}
<h1>Latest Posts</h1>
{% for post in posts %}
<article class="post-card">
<h2><a href="/posts/{{ post.id }}">{{ post.title }}</a></h2>
<p class="meta">
By {{ post.author_name }} | {{ post.created_at }}
| Views: {{ post.views }} | Likes: {{ post.likes }}
</p>
<p>{{ post.content | truncate(length=200) }}</p>
</article>
{% endfor %}
{% endblock %}
主程序 #
rust
#[macro_use] extern crate rocket;
mod models;
mod routes;
mod db;
mod guards;
use db::Db;
use rocket_dyn_templates::Template;
use rocket::fs::FileServer;
#[launch]
fn rocket() -> _ {
rocket::build()
.manage(Db::new())
.attach(Template::fairing())
.mount("/static", FileServer::from("static/"))
.mount("/api", routes![
routes::post::list,
routes::post::get,
routes::post::create,
routes::post::update,
routes::post::delete,
routes::post::like,
routes::comment::list,
routes::comment::create,
routes::comment::delete,
])
}
API测试 #
bash
# 创建文章
curl -X POST http://localhost:8000/api/posts \
-H "Content-Type: application/json" \
-H "Authorization: Bearer {token}" \
-d '{"title":"My First Post","content":"Hello World!"}'
# 获取文章列表
curl http://localhost:8000/api/posts
# 获取单篇文章
curl http://localhost:8000/api/posts/{id}
# 添加评论
curl -X POST http://localhost:8000/api/posts/{id}/comments \
-H "Content-Type: application/json" \
-H "Authorization: Bearer {token}" \
-d '{"content":"Great post!"}'
# 点赞文章
curl -X POST http://localhost:8000/api/posts/{id}/like
下一步 #
掌握了博客系统后,让我们继续学习 部署上线,了解如何将应用部署到生产环境。
最后更新:2026-03-28