制作一个网页版的博客管理系统,用户可以添加、查看、修改、删除博客。还可以对博客进行标签设定。当然标签也是可以添加、查看和删除的。
对于一个匿名用户进入网页后只能看到博客的标题和具体内容,不能对其进行修改和删除。当用户进行登录操作并且成功后,会自动进行博客管理页面,可以对博客进行修改和删除。
- 编程语言:C++
- 开发平台:Linux CentOS 7.0
- 开发工具:vim 代码编辑器,gcc 编译器,makefile 工程管理工具,gdb 调试器
实现一个简单的博客系统。
- 只支持单个用户。
- 实现针对文章的 CURD 。
- 实现针对标签的 CURD (分类)。
- 用户登录
- 展现博客列表页面
- 展现博客详情页面
- 管理博客页面
- 登录页面
当用户在网页上执行某个操作的时候,比如新增博客,此时就会由客户端给服务器发送 HTTP 请求, 请求中就包含了用户行为,服务器再根据这个行为来完成对数据的操作(对数据库的操作)。
- 先设计数据库(表结构)。
- 设计前后端交互接口。
- 实现服务器端和客户端的逻辑。
- 创建一个数据库
- 创建一个博客表
- blog_id int primary key
- title varchar(50)
- content text
- create_time varchar(50)
- tag_id int comment '博客属于的标签'
- 创建一个标签表
- tag_id int
- tag_name varchar(50)
- 创建用户表
- user_id
- user_name
- user_password
MySQL 的官方文档
-
- 注意这里参数如果填空,就返回一个句柄。空需要 NULL 不能写成 nullptr。
-
MYSQL *mysql_real_connect(MYSQL *mysql, const char *host, const char *user, const char *passwd, const char *db, unsigned int port, const char *unix_socket, unsigned long client_flag) (8 个参数!!!!)
-
参数
-
mysql: 刚才创建的句柄
-
host:要连接的服务器 ip
-
user:数据库用户名
-
passwd:数据库对应用户名的密码
-
db:database 名
-
port:数据库服务器端口名,默认 3306
-
unix_socket:NULL
-
client_flag:0
-
返回值
-
如果成功返回跟刚才一样的句柄,如果失败返回 NULL
-
-
-
设置字符集
-
例如: mysql_set_character_set(connect_fd, "utf8");
- 设置默认字符集为 utf8
-
-
- 执行一个 sql 语句
- 这里主要使用拼接 sql 语句的方式写入到 stmt_str,然后在把 stmt_str 传进去
-
-
MYSQL_RES *mysql_store_result(MYSQL *mysql)
将刚才 mysql 句柄查询的结果放进 MYSQL_RES 这个结构体中。
-
my_ulonglong mysql_num_rows(MYSQL_RES *result)
获取结果集合的行数
-
unsigned int mysql_num_fields(MYSQL_RES *result)
获取结果集合的列数
-
MYSQL_ROW mysql_fetch_row(MYSQL_RES *result)
一次获取一行数据
-
mysql_free_result()
释放查询结果集合
-
-
- 将刚才连接关闭,因为在某种情况下,就算程序退出,连接也不一定会一定关闭,所以一定要手动关闭。
-
unsigned long mysql_real_escape_string(MYSQL *mysql, char *to, const char from, unsigned long length)
- 将正文进行转义,防止正文出现特殊符号从而导致 SQL 语句出错
- mysql 句柄
- to 是动态开辟的一个空间,是 from.size() * 2 + 1
- from 是正文地址
- length 是 from.size() 的大小
基于 HTTP 协议进行扩展,创建自定制协议。
-
restful 风格的 API 设计方式:使用不用的 HTTP 方法来表达不同的语义
-
使用 path 来表示要操作的资源
-
使用 JSON 来组织 body 中的数据
-
使用 POST 方法表示新增 例如: POST /blog { "title": "xxxx", "content": "xxxx", "create_time": "xxxx", "tag_id": "xxxx" } HTTP/1.1 200 OK { ok: true, reason: "xxxx" }
-
查看所有博客(标题列表) 使用 GET 方法表示查看 GET /blog 获取所有 GET /blog?tag_id=1 按照标签来筛选 HTTP/1.1 200 OK [ { "blog_id": 1, "title": "My Carrer", "create_time": "2019/07/27", "tag_id": 1 }, { } ]
-
查看某个博客 GET /blog/:blog_id // :blog_id 会将 blog_id 替换成真正的 id; 类似 /blog/1 HTTP/1.1 200 OK { "blog_id": 1, "title": "My Carrer", "content": "博客正文", "create_time": "2019/07/27", "tag_id": 1 }
-
使用 PUT 方法表示修改 PUT /blog/:blog_id { "title": "修改之后的标题", "content": "修改之后的正文", "tag_id": "修改之后的 tag_id" } HTTP/1.1 200 OK { ok: true }
-
使用 DELETE 表示删除 DELETE /blog/:blog_id HTTP/1.1 200 OK { ok: true }
-
POST /tag { "tag_name": "新增的标签名", } HTTP/1.1 200 OK { ok: true }
-
DELETE /tag/:tag_id HTTP/1.1 200 OK { ok: true }
-
GET /tag HTTP/1.1 200 OK [ { "tag_id": 1, "tag_name": "c++" }, {}, {} ]
-
POST /login { "user_name": "xxx", "user_password": "xxx" } HTTP/1.1 200 OK { ok: "true" }
-
POST /sign_in { "user_name": "xxx", "user_password": "xxx" } HTTP/1.1 200 OK { ok: "true" }
Json 作为函数参数,可以很方便的进行数据库命令拼接。Json 中的键值对,可以很方便的解析出我们所需要的东西。
通过 sprintf 的方式来拼接 SQL 语句。
借助:mysql_real_escape_string 进行一个正文内容的转义。防止正文中出现特殊符号干扰数据库命令。
小操作:
将 string 类型转换为整数:stoi(string)
将 C 风格字符串转换为整数:atoi(char*)
将数字类型转换为 string 类型:to_string(数字类型)
gtest 测试博客表各个模块
TEST(event, test)
{
MYSQL* mysql = blog_system::MySQLInit();
blog_system::BlogTable blog_table(mysql);
Json::StyledWriter writer;
Json::Value blog;
// 单元测试 gtest google 提供的一个单元测试框架
// 测试插入
blog["title"] = "My Carrer!";
blog["content"] = "我要拿1000W年薪!";
blog["tag_id"] = 1;
blog["create_time"] = "2019/07/27";
EXPECT_EQ(blog_table.Insert(blog), true);
// 测试查找
Json::Value blogs;
EXPECT_EQ(blog_table.SelectAll(&blogs), true);
// 测试查找单个博客
EXPECT_EQ(blog_table.SelectOne(3, &blog), true);
// 测试修改博客
blog["blog_id"] = 3;
blog["title"] = "我的 Offers!";
blog["content"] = "100W\n 100W 100W 100W'''''!!!100000000W'''";
EXPECT_EQ(blog_table.Update(blog), true);
// 测试删除
EXPECT_EQ(blog_table.Delete(3), true);
blog_system::MySQLRelease(mysql);
}
int main()
{
// TestBlogTbale();
// TestTagTable();
testing::InitGoogleTest();
return RUN_ALL_TESTS();
}
gtest 测试标签表各个模块
TEST(test, tag_table)
{
MYSQL* mysql = blog_system::MySQLInit();
blog_system::TagTable tag_table(mysql);
Json::StyledWriter writer;
Json::Value tag;
// 测试插入
tag["tag_name"] = "C语言";
EXPECT_EQ(tag_table.Insert(tag), true);
// 测试查找
Json::Value tags;
EXPECT_EQ(tag_table.SelectAll(&tags), true);
// 测试删除
EXPECT_EQ(tag_table.Delete(1), true);
}
int main()
{
// TestBlogTbale();
// TestTagTable();
testing::InitGoogleTest();
return RUN_ALL_TESTS();
}
基于 TCP 服务器,在 HTTP 协议格式的基础上来完成字符串的解析和拼装。
使用 cpp-httplib 第三方库来管理连接和路由选择。
测试各个接口是否正常可用:
新增博客
查看所有博客
查看某个具体的博客
修改某个具体的博客
删除某个具体的博客
新增标签
查看所有标签
删除某个具体的标签
小操作
ln -s 源目录 目标目录
创建一个链接文件,关联到源目录上
- 博客列表页:主要显示所有博客的大体信息。
- 博客管理页:实现博客的删除和更新操作。
- 新增博客页:实现对新增博客的编辑和提交。
-
迁移博客==》实现一个爬虫程序(HTTP 客户端,cpp-httplib), 把曾经的博客抓取过来然后插入到博客中
存在问题:目前我的博客显示页面是加载所有博客和标题,只是选择性显示。所以如果博客数量增多会导致进入页面的时候加载非常慢。
-
实现一个图床服务器(HTTP 服务器,专门用于存图片)
-
支持多用户(对数据库的表结构进行重新设计)cookie session
-
分页展示
-
搜索博客功能(简单的话用数据库:Like。用的复杂的话就是 倒排索引)