Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

gdis5251/WeBlog

Folders and files

NameName
Last commit message
Last commit date

Latest commit

History

29 Commits

Repository files navigation

WeBlog

描述:

制作一个网页版的博客管理系统,用户可以添加、查看、修改、删除博客。还可以对博客进行标签设定。当然标签也是可以添加、查看和删除的。

对于一个匿名用户进入网页后只能看到博客的标题和具体内容,不能对其进行修改和删除。当用户进行登录操作并且成功后,会自动进行博客管理页面,可以对博客进行修改和删除。

环境

  • 编程语言:C++
  • 开发平台:Linux CentOS 7.0
  • 开发工具:vim 代码编辑器,gcc 编译器,makefile 工程管理工具,gdb 调试器

需求:

实现一个简单的博客系统。

  1. 只支持单个用户。
  2. 实现针对文章的 CURD 。
  3. 实现针对标签的 CURD (分类)。
  4. 用户登录

模块划分

在这里插入图片描述

网页端的具体展现形式

  1. 展现博客列表页面
  2. 展现博客详情页面
  3. 管理博客页面
  4. 登录页面

当用户在网页上执行某个操作的时候,比如新增博客,此时就会由客户端给服务器发送 HTTP 请求, 请求中就包含了用户行为,服务器再根据这个行为来完成对数据的操作(对数据库的操作)。

设计思路

  1. 先设计数据库(表结构)。
  2. 设计前后端交互接口。
  3. 实现服务器端和客户端的逻辑。

数据库设计 -- MySQL 关系型数据库

  1. 创建一个数据库
  2. 创建一个博客表
    • blog_id int primary key
    • title varchar(50)
    • content text
    • create_time varchar(50)
    • tag_id int comment '博客属于的标签'
  3. 创建一个标签表
    • tag_id int
    • tag_name varchar(50)
  4. 创建用户表
    • user_id
    • user_name
    • user_password

使用 C 语言 MySQL API 来完成数据库操作

MySQL 的官方文档

  • 头文件 #include <mysql/mysql.h>
  • MYSQL *mysql_init(MYSQL *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

  • int mysql_set_character_set(MYSQL *mysql, const char *csname)
    • 设置字符集

    • 例如: mysql_set_character_set(connect_fd, "utf8");

      • 设置默认字符集为 utf8
  • int mysql_query(MYSQL *mysql, const char *stmt_str)
    • 执行一个 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()

      释放查询结果集合

  • void mysql_close(MYSQL *mysql)
    • 将刚才连接关闭,因为在某种情况下,就算程序退出,连接也不一定会一定关闭,所以一定要手动关闭。
  • 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() 的大小

设计前后端的交互 API

基于 HTTP 协议进行扩展,创建自定制协议。

一、博客管理

  1. restful 风格的 API 设计方式:使用不用的 HTTP 方法来表达不同的语义

  2. 使用 path 来表示要操作的资源

  3. 使用 JSON 来组织 body 中的数据

  4. 新增博客

    使用 POST 方法表示新增
    例如:
    POST /blog
    {
     	"title": "xxxx",
     	"content": "xxxx",
     	"create_time": "xxxx",
     	"tag_id":	"xxxx"
    } 
    HTTP/1.1 200 OK
    {
     	ok: true,
     	reason: "xxxx"
    }
  5. 获取博客列表

    查看所有博客(标题列表)
    使用 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	
     	},
     {
     
     }
    ]
  6. 获取某个博客的详细内容

    查看某个博客
    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	
    }
  7. 修改博客

    使用 PUT 方法表示修改
    PUT /blog/:blog_id
    {
    	"title": "修改之后的标题",
    	"content": "修改之后的正文",
    	"tag_id": "修改之后的 tag_id"
    }
    HTTP/1.1 200 OK
    {
    	ok: true
    }
  8. 删除博客

    使用 DELETE 表示删除
    DELETE /blog/:blog_id
    HTTP/1.1 200 OK
    {
    	ok: true
    } 

二、标签管理

  1. 新增标签

    POST /tag
    {
     	"tag_name": "新增的标签名",
    }
    HTTP/1.1 200 OK
    {
    	ok: true
    }
  2. 删除标签

    DELETE /tag/:tag_id
    HTTP/1.1 200 OK
    {
    	ok: true
    }
  3. 查看所有标签

    GET /tag
     
    HTTP/1.1 200 OK
    [
    	{
     		"tag_id": 1,
     	"tag_name": "c++"
    	},
    	{},
    	{}
    ] 

三、用户管理

  1. 用户登录

    POST /login
    {
    	"user_name": "xxx",
    	"user_password": "xxx"
    }
    HTTP/1.1 200 OK
    {
    	ok: "true"
    }
  2. 用户注册

    POST /sign_in
    {
    	"user_name": "xxx",
    	"user_password": "xxx"
    }
    HTTP/1.1 200 OK
    {
    	ok: "true"
    }

编码

1. 数据库部分

Json 作为函数参数,可以很方便的进行数据库命令拼接。Json 中的键值对,可以很方便的解析出我们所需要的东西。

通过 sprintf 的方式来拼接 SQL 语句。

借助:mysql_real_escape_string 进行一个正文内容的转义。防止正文中出现特殊符号干扰数据库命令。

小操作:

将 string 类型转换为整数:stoi(string)

将 C 风格字符串转换为整数:atoi(char*)

将数字类型转换为 string 类型:to_string(数字类型)

使用 gtest 测试各个模块

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

在这里插入图片描述

2. HTTP 服务器

基于 TCP 服务器,在 HTTP 协议格式的基础上来完成字符串的解析和拼装。

使用 cpp-httplib 第三方库来管理连接和路由选择。

Postman HTTP 测试工具

测试各个接口是否正常可用:

新增博客

在这里插入图片描述

查看所有博客

在这里插入图片描述

查看某个具体的博客

在这里插入图片描述

修改某个具体的博客

在这里插入图片描述

删除某个具体的博客

在这里插入图片描述

新增标签

在这里插入图片描述

查看所有标签

在这里插入图片描述

删除某个具体的标签

在这里插入图片描述

3. 客户端部分

实现一组网页,通过网页来和服务器进行交互。主要是找一个免费模板进行修改。

使用 Vue.js:开源社区的框架

小操作

ln -s 源目录 目标目录

创建一个链接文件,关联到源目录上

使用 Editor.md 第三方 JS 的库,完成 markdown 和 html 相互转换,以及 markdown 在线编辑器

主要实现的页面

  • 博客列表页:主要显示所有博客的大体信息。
  • 博客管理页:实现博客的删除和更新操作。
  • 新增博客页:实现对新增博客的编辑和提交。

改进方面

  1. 迁移博客==》实现一个爬虫程序(HTTP 客户端,cpp-httplib), 把曾经的博客抓取过来然后插入到博客中

    存在问题:目前我的博客显示页面是加载所有博客和标题,只是选择性显示。所以如果博客数量增多会导致进入页面的时候加载非常慢。

  2. 实现一个图床服务器(HTTP 服务器,专门用于存图片)

  3. 支持多用户(对数据库的表结构进行重新设计)cookie session

  4. 分页展示

  5. 搜索博客功能(简单的话用数据库:Like。用的复杂的话就是 倒排索引)

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

Contributors

AltStyle によって変換されたページ (->オリジナル) /