您现在的位置是:主页 > news > 西安北郊网站建设/发广告平台有哪些免费
西安北郊网站建设/发广告平台有哪些免费
admin2025/4/28 23:10:47【news】
简介西安北郊网站建设,发广告平台有哪些免费,义乌小程序装修公司,沈阳有资质做网站的公司有哪些项目名称:博客系统 项目功能:实现一个web服务器,使用户通过浏览器访问,实现博客的展示,以及博客的增删改查管理 框架设计:mvc框架 mvc框架,前端界面模块需要获取数据进行展示,向业…
项目名称:博客系统
项目功能:实现一个web服务器,使用户通过浏览器访问,实现博客的展示,以及博客的增删改查管理
框架设计:mvc框架
mvc框架,前端界面模块需要获取数据进行展示,向业务处理模块发送请求,业务处理模块从数据管理模块获取到数据,组织后响应给前端界面模块,mvc框架是一种数据,控制,页面分离的开发模式,耦合度低
model数据管理模块,使用mysql数据库,封装数据库访问类,实现博客的增删改查功能
controller业务处理模块,使用http协议搭建http服务器,针对浏览器客户端的不同请求进行对应的业务处理,博客页面的获取/博客数据的增删改查
view前端界面模块,使用html+css+js实现前端界面的展示
详细设计
model数据管理模块,使用mysql数据库,封装数据库访问类,实现博客的增删改查功能
设计博客信息表和标签表管理博客和标签信息
blog博客信息表,博客id,博客标题,博客正文,标签id,创建时间
tag标签表,标签id,标签名称
db.sql
create database if not exists db_blog;use db_blog;drop table if exists tb_tag;create table if not exists tb_tag(id int primary key auto_increment comment '标签ID',name varchar(32) comment '标签名称');drop table if exists tb_blog;create table if not exists tb_blog(id int primary key auto_increment comment '博客ID',tag_id int comment '所属标签的ID',title varchar(255) comment '博客标题',content text comment '博客内容',ctime datetime comment '博客的创建时间');insert tb_tag values(null,'C++'),(null,'Java'),(null,'Mysql'),(null,'Linux');insert tb_blog values(null,1,'这是一个C++博客','C++是最好的语言',now());
封装数据库表的访问类,一个类实现对一个表的访问
db.hpp
#include<iostream>#include<mutex>#include<mysql/mysql.h>#include<jsoncpp/json/json.h>using namespace std;//参数定义为宏,方便后续更改#define MYSQL_HOST "127.0.0.1"#define MYSQL_USER "root"#define MYSQL_PSWD ""#define MYSQL_DB "db_blog"//设计一个blog_system的命名空间-防止命名冲突//包含TableBlog和TableTag两个类namespace blog_system{//static修饰全局变量以及函数,作用是修改作用域,表示这个变量或函数,仅在本文件内生效static std::mutex g_mutex;//加锁保证线程安全,使每一个操作都是原子操作//向外提供接口返回初始化完成的mysql句柄(连接服务器,选择数据库,设置字符集)MYSQL*MysqlInit(){MYSQL*mysql;//初始化mysql = mysql_init(NULL);if (mysql == NULL){printf("init mysql error\n");return NULL;}//连接服务器//数据库名称NULL默认选择if (mysql_real_connect(mysql, MYSQL_HOST, MYSQL_USER, MYSQL_PSWD, NULL, 0, NULL, 0) == NULL){printf("connect mysql server error:%s\n", mysql_error(mysql));mysql_close(mysql);return NULL;}//设置字符集if (mysql_set_character_set(mysql, "utf8") != 0){printf("set client character error:%s\n", mysql_error(mysql));mysql_close(mysql);return NULL;}//选择数据库if (mysql_select_db(mysql, MYSQL_DB) != 0){printf("select db error:%s\n", mysql_error(mysql));mysql_close(mysql);return NULL;}return mysql;}//销毁句柄void MysqlRelease(MYSQL*mysql){if (mysql){mysql_close(mysql);}return;}//执行语句的共有接口bool MysqlQuery(MYSQL*mysql, const char*sql){int ret = mysql_query(mysql, sql);if (ret != 0){printf("query sql:[%s] failed:%s\n", sql, mysql_error(mysql));return false;}return true;}class TableBlog{public:TableBlog(MYSQL*mysql) :_mysql(mysql){}//构造函数//从blog中取出博客信息,组织sql语句,将数据插入数据库;bool Insert(Json::Value &blog){//id tag_id title content ctime#define INSERT_BLOG "insert tb_blog values(null,'%d','%s','%s',now());"//因为博客正文长度不定,可能会很长,因此如果直接定义tmp空间的长度固定,有可能会越界访问int len = blog["content"].asString().size() + 4096;char *tmp = (char*)malloc(len);//int sprintf(char *str, const char *format, ...)//发送格式化输出到str所指向的字符串sprintf(tmp, INSERT_BLOG, blog["tag_id"].asInt(),blog["title"].asCString(),blog["content"].asCString());bool ret = MysqlQuery(_mysql, tmp);free(tmp);return ret;}//根据博客id删除博客bool Delete(int blog_id){ #define DELETE_BLOG "delete from tb_blog where id=%d;"char tmp[1024] = { 0 };sprintf(tmp, DELETE_BLOG, blog_id);bool ret = MysqlQuery(_mysql, tmp);return ret;}//从blog中取出博客信息,组织sql语句,更新数据库中的数据bool Update(Json::Value &blog){//id tag_id title content ctime//id和ctime不可修改#define UPDATE_BLOG "update tb_blog set tag_id=%d,title='%s',content='%s'where id=%d;"int len = blog["content"].asString().size() + 4096;char*tmp = (char*)malloc(len);sprintf(tmp, UPDATE_BLOG, blog["tag_id"].asInt(),blog["title"].asCString(),blog["content"].asCString(),blog["id"].asInt());bool ret = MysqlQuery(_mysql, tmp);free(tmp);return ret;}//通过blog返回所有的博客信息(因为返回列表展示,因此不包含正文)bool GetAll(Json::Value *blogs){ #define GETALL_BLOG "select id,tag_id,title,ctime from tb_blog;"//执行查询语句g_mutex.lock();//加锁bool ret = MysqlQuery(_mysql, GETALL_BLOG);if (ret == false){g_mutex.unlock();//解锁return false;}//保存结果集MYSQL_RES*res = mysql_store_result(_mysql);g_mutex.unlock();//解锁if (res == NULL){printf("store all blog result failed:%s\n", mysql_error(_mysql));return false;}//遍历结果集int row_num = mysql_num_rows(res);for (int i = 0; i < row_num; i++){MYSQL_ROW row = mysql_fetch_row(res);Json::Value blog;blog["id"] = std::stoi(row[0]);blog["tag_id"] = stoi(row[1]);blog["title"] = row[2];blog["ctime"] = row[3];blogs->append(blog);//添加json数组元素,每一个博客都属于所有博客数组中的一个元素}mysql_free_result(res);return true;}//返回单个博客信息,包含正文bool GetOne(Json::Value *blog){ #define GETONE_BLOG "select tag_id,title,content,ctime from tb_blog where id=%d;"char tmp[1024] = { 0 };sprintf(tmp, GETONE_BLOG, (*blog)["id"].asInt());g_mutex.lock();//加锁bool ret = MysqlQuery(_mysql, tmp);if (ret == false){g_mutex.unlock();//解锁return false;}//保存结果集MYSQL_RES*res = mysql_store_result(_mysql);g_mutex.unlock();//解锁if (res == NULL){printf("store once blog result failed:%s\n", mysql_error(_mysql));return false;}int row_num = mysql_num_rows(res);if (row_num != 1){printf("get one blog result error\n");mysql_free_result(res);return false;}//遍历结果集MYSQL_ROW row = mysql_fetch_row(res);//blog指针需要解引用(*blog)["tag_id"] = stoi(row[0]);(*blog)["title"] = row[1];(*blog)["content"] = row[2];(*blog)["ctime"] = row[3];mysql_free_result(res);return true;}private:MYSQL*_mysql;//操作句柄};class TableTag{public:TableTag(MYSQL*mysql) :_mysql(mysql){}bool Insert(Json::Value &tag){#define INSERT_TAG "insert tb_tag values(null,'%s');"char tmp[1024] = { 0 };sprintf(tmp, INSERT_TAG, tag["name"].asCString());return MysqlQuery(_mysql, tmp);}bool Delete(int tag_id){#define DELETE_TAG "delete from tb_tag where id=%d;"char tmp[1024] = { 0 };sprintf(tmp, DELETE_TAG, tag_id);return MysqlQuery(_mysql, tmp);}bool Update(Json::Value &tag){#define UPDATE_TAG "update tb_tag set name='%s' where id=%d;"char tmp[1024] = { 0 };sprintf(tmp, UPDATE_TAG, tag["name"].asCString(), tag["id"].asInt());return MysqlQuery(_mysql, tmp);}bool GetAll(Json::Value *tags){#define GETALL_TAG "select id,name from tb_tag;"g_mutex.lock();//加锁bool ret = MysqlQuery(_mysql, GETALL_TAG);if (ret == false){g_mutex.unlock();//解锁return false;}//保存结果集到本地MYSQL_RES *res = mysql_store_result(_mysql);g_mutex.unlock();//解锁if (res == NULL){printf("store all tag result failed:%s\n", mysql_error(_mysql));return false;}//遍历结果集int row_num = mysql_num_rows(res);for (int i = 0; i < row_num; i++){MYSQL_ROW row = mysql_fetch_row(res);Json::Value tag;tag["id"] = stoi(row[0]);tag["name"] = row[1];tags->append(tag);}mysql_free_result(res);return true;}bool GetOne(Json::Value *tag){#define GETONE_TAG "select name from tb_tag where id=%d;"g_mutex.lock();//加锁char tmp[1024] = { 0 };sprintf(tmp, GETONE_TAG, (*tag)["id"].asInt());bool ret = MysqlQuery(_mysql, tmp);if (ret == false){g_mutex.unlock();//解锁return false;}MYSQL_RES *res = mysql_store_result(_mysql);g_mutex.unlock();//解锁if (res == NULL){printf("store one tag result failed:%s\n", mysql_error(_mysql));return false;}int row_num = mysql_num_rows(res);if (row_num != 1){printf("get one tag result error\n");mysql_free_result(res);return false;}MYSQL_ROW row = mysql_fetch_row(res);(*tag)["name"] = row[0];mysql_free_result(res);return true;}private:MYSQL *_mysql;};}
controller业务处理模块,使用http协议搭建http服务器,针对浏览器客户端的不同请求进行对应的业务处理,博客页面的获取/博客数据的增删改查
在业务逻辑模块中,通过数据管理模块的初始化接口,获取到一个数据库操作句柄,实例化一个数据库对象,通过这个表对象的接口,实现对数据库中数据的访问,一个实例化的对象对应一张表的操作
一个服务器对应多个客户端,多个客户端有可能同时发起操作,如果每次来一个请求都实例化一个对象,新建一个mysql句柄,效率会比较低,因此使用同一个对象同一个句柄,如果多个使用了同一个对象同一个句柄,会造成死锁,因此加锁保证线程安全
网络通信模块,实现客户端与服务器网络通信功
业务逻辑模块,针对不同的请求进行对应的业务处理,博客页面的获取/博客数据的增删改查
借助httplib库搭建http服务器,http服务器本质是一个tcp服务器,等待浏览器客户端的请求,解析请求,得到http协议格式中的各个要素,然后根据不同的请求进行对应的业务处理,完成业务处理后,组织http响应数据,进行响应
网络通信接口采用Restful风格的接口设计,什么样的请求对应什么样的服务,基于http通信,http正文使用json对数据进行序列化和反序列化,动作,POST新增/DELETE删除/PUT更新/GER获取
main.cpp
#include "db.hpp"#include"httplib.h"using namespace httplib;using namespace std;//因为接口里都涉及到数据库的操作-定义两个指针table_blog和table_tagblog_system::TableBlog*table_blog;blog_system::TableTag*table_tag;//具体对应业务的处理//从请求中取出正文-正文就是提交的博客信息,以json格式的字符串形式组织的//将json格式的字符串进行反序列化,得到各个博客信息//将得到的博客信息插入到数据库中void InsertBlog(const Request &req,Response &rsp){printf("InsertBlog\n");Json::Reader reader;Json::Value blog;Json::FastWriter writer;//和StyleWrite一个功能,使用稍有差别Json::Value errmsg;bool ret=reader.parse(req.body,blog);//将请求正文进行json反序列化-因为正文就是json格式的博客信息if(ret==false){printf("InsertBlog parse blog json failed!\n");rsp.status=400;//客户端格式错误errmsg["ok"]=false;errmsg["reason"]="parse blog json failed!";rsp.set_content(writer.write(errmsg),"application/json");//添加正文到rsp.body中return;}ret=table_blog->Insert(blog);//db.hpp中的TableBlog的接口Insertif(ret==false){printf("InsertBlog insert into database failed!\n");rsp.status=500;//服务器内部错误return;}rsp.status=200;//200okreturn;}//删除博客操作void DeleteBlog(const Request&req,Response &rsp){// /blog/123 /blog(\d+) req.matches[0]=/blog/123 req/matches[1]=123int blog_id=stoi(req.matches[1]);//blog_id是放在URL中path中bool ret=table_blog->Delete(blog_id);//Delete接口if(ret==false){printf("DeleteBlog delete into database failed!\n");rsp.status=500;return;}rsp.status=200;return;}void UpdateBlog(const Request&req,Response &rsp){int blog_id=stoi(req.matches[1]);//获取博客IDJson::Value blog;//对正文进行反序列化Json::Reader reader;bool ret=reader.parse(req.body,blog);if(ret==false){printf("UpdateBlog parse json failed!\n");rsp.status=400;return;}blog["id"]=blog_id;ret=table_blog->Update(blog);if(ret==false){printf("UpdateBlog update into database failed!\n");rsp.status=500;return;}rsp.status=200;return;}//获取所有博客信息//从数据库中取出博客列表数据void GetAllBlog(const Request&req,Response &rsp){Json::Value blogs;bool ret=table_blog->GetAll(&blogs);if(ret==false){printf("GetAllBlog select from database failed!\n");rsp.status=500;return;}//将数据进行json序列化,添加到rsp正文中Json::FastWriter writer;rsp.set_content(writer.write(blogs),"application/json");rsp.status=200;return;}void GetOneBlog(const Request&req,Response &rsp){int blog_id = stoi(req.matches[1]);Json::Value blog;blog["id"] = blog_id;bool ret=table_blog->GetOne(&blog);if(ret==false){printf("GetOneBlog select from database failed!\n");rsp.status=500;return;}Json::FastWriter writer;rsp.set_content(writer.write(blog),"application/json");rsp.status=200;return;}void InsertTag(const Request&req,Response &rsp){Json::Reader reader;Json::Value tag;bool ret=reader.parse(req.body,tag);if(ret==false){printf("InsertTag parse tag json failed!\n");rsp.status=400;return;}ret=table_tag->Insert(tag);if(ret==false){printf("InsertTag insert into database failed!\n");rsp.status=500;return;}rsp.status=200;return;}void DeleteTag(const Request&req,Response &rsp){int tag_id=stoi(req.matches[1]);bool ret=table_tag->Delete(tag_id);if(ret==false){printf("DeleteTag delete into database failed!\n");rsp.status=500;return;}rsp.status = 200;return;}void UpdateTag(const Request&req,Response &rsp){int tag_id=std::stoi(req.matches[1]);Json::Value tag;Json::Reader reader;bool ret=reader.parse(req.body,tag);if(ret==false){printf("UpdateTag parse json failed!\n");rsp.status=400;return;}tag["id"]=tag_id;ret=table_tag->Update(tag);if(ret==false){printf("UpdateTag update into database failed!\n");rsp.status=500;return;}rsp.status=200;return;}void GetAllTag(const Request&req,Response &rsp){Json::Value tags;bool ret=table_tag->GetAll(&tags);if(ret==false){printf("GetAllTag select from database failed!\n");rsp.status=500;return;}Json::FastWriter writer;rsp.set_content(writer.write(tags),"application/json");rsp.status=200;return;}void GetOneTag(const Request&req,Response &rsp){int tag_id = stoi(req.matches[1]);Json::Value tag;tag["id"] = tag_id;bool ret=table_tag->GetOne(&tag);if(ret==false){printf("GetOneTag select from database failed!\n");rsp.status=500;return;}Json::FastWriter writer;rsp.set_content(writer.write(tag),"application/json");rsp.status=200;return;}int main(){//table_blog和table_tag就是数据库访问的操作对象,通过这两个对象完成对数据库的操作MYSQL*mysql=blog_system::MysqlInit();(mysql);table_blog=new blog_system::TableBlog(mysql);table_tag=new blog_system::TableTag(mysql);//业务处理模块Server server;// /index.html->./www/index.html//设置相对根目录的目的:当客户端请求静态文件资源时,httplib会直接根据路径读取文件数据进行响应server.set_base_dir("./www");//设置URL中的相对根目录,并且在请求/的时候,自动添加index.html//博客信息的增删改查//正则表达式-一种字符串的匹配规则//\d-匹配数字字符//+-表示匹配前边的字符一次或多次//()-为了临时保存匹配数据///blog/(\d+)-表示匹配以/blog/开头,后边跟了一个数字的字符串格式,并且临时保存后边的数字//R"()"取出括号中所有字符的特殊含义-c++11server.Post("/blog",InsertBlog);//不是函数调用,传递的是函数的地址server.Delete(R"(/blog/(\d+))",DeleteBlog);server.Put(R"(/blog/(\d+))",UpdateBlog);server.Get("/blog",GetAllBlog);server.Get(R"(/blog/(\d+))",GetOneBlog);//标签信息的增删改查server.Post("/tag",InsertTag);server.Delete(R"(/tag/(\d+))",InsertTag);server.Put(R"(/tag/(\d+))",UpdateTag);server.Get("/tag",GetAllTag);server.Get(R"(/tag/(\d+))",GetOneTag);server.listen("0.0.0.0",9000);return 0;}//#include "db.hpp"//using namespace std;//int main(){// test();// return 0;//}//void test(){// MYSQL*mysql = blog_system::MysqlInit();// blog_system::TableBlog table_blog(mysql);// Json::Value blog;// //测试增加// blog["tag_id"] = 4;// blog["title"] = "PHP";// blog["content"] = "PHPBEST";// table_blog.Insert(blog);// //测试删除// table_blog.Delete(4);// //测试修改// blog["id"] = 3;// blog["tag_id"] = 4;// blog["title"] = "PHP";// blog["content"] = "PHPBEST";// table_blog.Update(blog);// //测试查询getall// table_blog.GetAll(&blog);// Json::StyledWriter writer;// cout << writer.write(blog) << endl;// //测试查询getone// blog["id"] = 1;// table_blog.GetOne(&blog);// Json::StyledWriter writer;// cout << writer.write(blog) << endl;// //测试标签表的增删改查// blog_system::TableTag table_tag(mysql);// Json::Value tag;// //测试插入删除// tag["name"] = "C";// table_tag.Insert(tag);// table_tag.Delete(5);// //测试修改// tag["name"] = "C#";// tag["id"] = 1;// table_tag.Update(tag);// //测试查询getall// table_tag.GetAll(&tag);// Json::StyledWriter writer;// cout << writer.write(tag) << endl;// //测试查询getone// tag["id"] = 1;// table_tag.GetOne(&tag);// Json::StyledWriter writer;// cout << writer.write(tag) << endl;//}
Makefile
blog_system:main.cpp db.hppg++ -std=c++11 $^ -o $@ -L/usr/lib64/mysql -lmysqlclient -ljsoncpp -lpthread
前端界面模块
html,超文本标签语言,通过浏览器渲染成为一个界面
css,样式语言,针对html控件使用特定样式进行修饰
js,javascript,脚本语言,动态脚本
index.html
<script src="assets/js/jquery.min.js"></script><script src="assets/js/amazeui.min.js"></script><script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script><script src="editor/editormd.min.js"></script><script src="editor/lib/marked.min.js"></script><script src="editor/lib/prettify.min.js"></script><script src="editor/lib/raphael.min.js"></script><script src="editor/lib/underscore.min.js"></script><script src="editor/lib/sequence-diagram.min.js"></script><script src="editor/lib/flowchart.min.js"></script><script src="editor/lib/jquery.flowchart.min.js"></script><script src="editor/editormd.js"></script><script>var app = new Vue({el: '#app',data: {author: 'suoyicong',status: 'blog_list',//blog_dit,blog_new,blog_admintag_list: [],//标签信息 id nameblog_list: [],//博客信息 id tag_id title content ctimeshow_blog: {id:null,tag_id:null,title:null,content:null,ctime:null}//要编辑的博客数据},methods: {get_blog_list: function () {$.ajax({url: "/blog",//请求中的path路径type: "get",//请求中的请求方法context: this,//设置请求成功后回调函数中的this指针-当前复制的this是vue对象success: function (result) {this.blog_list = result;//成功后将响应正文的json串赋值}})},get_tag_list: function () {$.ajax({url: "/tag",//请求中的path路径type: "get",//请求中的请求方法context: this,//设置请求成功后回调函数中的this指针-当前复制的this是vue对象success: function (result) {//result就是响应数据中的正文this.tag_list = result;//成功后将响应正文的json串赋值}})},//通过tag_id来获取标签名称//idx就是一个下标get_tagname_by_tag_id: function (tag_id) {for (idx in this.tag_list) {if (this.tag_list[idx].id == tag_id) {return this.tag_list[idx].name;}}return "未知";},into_admin_status: function () {this.status = 'blog_admin';//将当前页面状态置为管理状态},blog_delete: function (blog_id) {$.ajax({url: "/blog/" + blog_id,type: "delete",context: this,success: function (result) {this.get_blog_list();}})},blog_edit: function (blog_id) {$.ajax({url: "/blog/" + blog_id,type: "get",context: this,success: function (result) {this.show_blog = result;this.status = 'blog_edit';//显示一个博客的编辑页面,其中具有博客的内容testEditor = editormd({id: "test-editormd",width: "100%",height: 640,path: "editor/lib/"});}})},blog_update:function(){this.show_blog.content=testEditor.getMarkdown();$.ajax({url: "/blog/" + this.show_blog.id,type: "put",context: this,data:JSON.stringify(this.show_blog),success: function (result) {this.get_blog_list();this.status='blog_list';}})},blog_view:function(blog_id){$.ajax({url: "/blog/" + blog_id,type: "get",context: this,success: function (result) {this.show_blog=result;this.status='blog_edit';testEditormdView = editormd.markdownToHTML("test-editormd", {markdown : this.show_blog.content,htmlDecode : "style,script,iframe", tocm : true, emoji : true,taskList : true,tex : true, // 默认不解析flowChart : true, // 默认不解析sequenceDiagram : true, // 默认不解析});}})},new_blog:function(){this.status="blog_new";newEditor = editormd({id: "new-editormd",width: "100%",height: 640,path: "editor/lib/"});},blog_insert:function(){this.show_blog.content=newEditor.getMarkdown();var new_blog_info={tag_id:1,title:this.show_blog.title,content:this.show_blog.content,}alert(JSON.stringify(new_blog_info));$.ajax({url: "/blog",type: "post",context: this,data:JSON.stringify(new_blog_info),success: function (result) {this.get_blog_list();this.status='blog_list';}})}}})//这两个是异步操作app.get_tag_list();//浏览器获取到界面之后,就会调用这两个函数,向后台发送请求app.get_blog_list();</script></body></html>