- 1 前言
- 2 环境配置
- 2.1 开发环境
- 2.2 配置环境参考博客
- 3 案例介绍
- 3.1 案例需求
- 3.2 案例实现
- 3.2.1 文件结构
- 3.2.2 代码实现
- 3.2.2.1 Main类
- 3.2.2.2 Start类
- 3.2.2.3 MyDB类
- 3.2.2.4 Tools类
- 3.2.2.5 User类
- 3.2.2.6 Note类
- 3.2.2.7 Makefile
- 3.3 案例结果
- 3.3.1 用户注册
- 3.3.2 用户登录
- 3.3.3 发布笔记
- 3.3.4 删除笔记
- 3.3.5 修改笔记
- 3.3.6 查询笔记
- 4 问题总结
- 5 参考博客
- 6 其他
这个案例是一个简单的后端接口实现,含有用户注册、用户登录、发布笔记、删除笔记、修改笔记、查询笔记的功能。
笔者使用的环境如下:
工具 | 版本 | 备注 |
---|---|---|
Ubantu | 20.04 | |
mysql | 5.7.37 | |
apache | 2.4.41 | |
vim | 8.1.3741 | 编辑源文件 |
jsoncpp | 0.5.0 | 解析json数据 |
libmysqlclient-dev | libmysqlclient.so.21.2.28 | *** 作mysql数据库 |
postman | 9.15.10 | 测试接口 |
- mysql
参考作者:“風の住む街~” 的博客
Ubuntu20.04安装Mysql - apache
参考作者:“JstuCheng” 的博客
Ubuntu安装使用apache2 - jsoncpp
参考作者:“Acuity.” 的博客
jsoncpp应用(Linux环境)
参考作者:“肥叔菌” 的博客
JsonCpp入门一:在linux下编译使用JsonCpp - libmysqlclient-dev
参考作者:“Devin_white” 的博客
LINUX下C/C++ *** 作MySql
- 需要建立两张表,用于存储用户信息和笔记信息,分别是users和notes表
- 接口需求
-
user
- 用户注册(register)
url:{yours ip}/cgi-bin/notes/bin/main/user/register request method:POST request body: { "username":"zhangsan", "password":"123456" } response: { "errCode":0, "errInfo":"ok" }
- 用户登录(login)
url:{yours ip}/cgi-bin/notes/bin/main/user/login request method:POST request body: { "username":"zhangsan", "password":"123456" } response: { "errCode":0, "errInfo":"ok" }
-
note
- 发布笔记(add_note)
url:{yours ip}/cgi-bin/notes/bin/main/note/add_note request method:POST request body: { "userId":1, "content":"this is a test", "tag":1 } response: { "errCode":0, "errInfo":"ok" }
- 删除笔记(delete_note)
url:{yours ip}/cgi-bin/notes/bin/main/note/delete_note request method:POST request body: { "userId":1, "noteIds":[1, 2, 3] } response: { "errCode":0, "errInfo":"ok" }
- 修改笔记(update_note)
url:{yours ip}/cgi-bin/notes/bin/main/note/update_note request method:POST request body: { "userId":1, "noteId":1, "content":"this is a text", "tag":2, "type":1 } ps: type = 1, update content; type = 2, update tag; type = 3, update content and tag response: { "errCode":0, "errInfo":"ok" }
- 查询笔记(select_note)
url:{yours ip}/cgi-bin/notes/bin/main/note/select_note request method:POST request body: { "userId":1, "offset":0, "limit":10, "order":1 } ps: order = 1, preorder order = 2, postorder response: { "errCode":0, "errInfo":"ok", "data":[ { "noteId":1, "userId":1, "content":"this is a test", "tag":1, "createTime":"2022-03-27 12:00:00" } ] }
-
// Main.h
#include
#include "Start.h"
int main(int argc, char **argv);
// Main.cpp
#include "../include/Main.h"
int main(int argc, char **argv)
{
Start start;
start.run();
return 0;
}
3.2.2.2 Start类
// Start.h
#include
#include
#include
#include
#include
// Start.cpp
#include "../include/Start.h"
Start::Start()
{
this->db = new MyDB();
this->db->initDB((char *)"localhost", (char *)"root", (char *)"fast1111", (char *)"test");
this->tools = new Tools();
this->note = new Note(db, tools);
this->user = new User(db, tools);
}
Start::~Start()
{
}
void Start::run()
{
Json::Value root;
Json::Value postData;
Json::Value getData;
map allowMethod;
allowMethod["GET"] = 1;
allowMethod["POST"] = 2;
allowMethod["PUT"] = 3;
allowMethod["DELETE"] = 4;
map allowResource;
allowResource["user"] = 1;
allowResource["note"] = 2;
// judeg method
char *requestMethod = getenv("REQUEST_METHOD");
if (!requestMethod || !allowMethod[requestMethod])
{
root["errCode"] = Json::Value(40100);
root["errInfo"] = Json::Value("The request method is not allowed");
this->tools->printHeader(2);
this->tools->printJson(root, 2);
exit(0);
}
switch (allowMethod[requestMethod])
{
case 1:
{
getData = this->tools->getGetData(); break;
}
case 2:
{
postData = this->tools->getPostData();
break;
}
default:
{
break;
}
}
// judge resource
char *requestResource = getenv("PATH_INFO");
if (!requestResource)
{
root["errCode"] = Json::Value(40101);
root["errInfo"] = Json::Value("The request resource is missing");
this->tools->printHeaderAndJson(root, 2, 2);
exit(0);
}
else if (requestResource)
{
vector resource;
char *res = strtok(requestResource, "/");
while (res)
{
resource.push_back(res);
res = strtok(NULL, "/");
}
// match resource and choose execute path
if (resource.size() < 2)
{
root = this->tools->jsonEncode(40102, (char *)"The request resource is missing");
this->tools->printHeaderAndJson(root, 2, 2); exit(0);
}
else
{
// request user
if (!strcmp("user", resource[0].c_str()))
{
if (!strcmp("register", resource[1].c_str()))
{
root = this->user->_register(postData);
}
else if (!strcmp("login", resource[1].c_str()))
{
root = this->user->login(postData);
}
else
{
root = this->tools->jsonEncode(40103, (char*)"Request resource is not allowed");
}
}
// request note
else if (!strcmp("note", resource[0].c_str()))
{
// add note
if (!strcmp("add_note", resource[1].c_str()))
{
root = this->note->addNote(postData);
}
// delete note
else if (!strcmp("delete_note", resource[1].c_str()))
{
root = this->note->deleteNote(postData);
}
// update note
else if (!strcmp("update_note", resource[1].c_str()))
{
root = this->note->updateNote(postData);
}
// select note
else if (!strcmp("select_note", resource[1].c_str()))
{
root = this->note->selectNote(postData);
}
// other
else
{
root = this->tools->jsonEncode(40104, (char *)"Request resource is not allowed");
}
}
}
}
this->tools->printHeaderAndJson(root, 2, 2);
}
3.2.2.3 MyDB类
// MyDB.h
#ifndef _MYDB_H
#define _MYDB_H
#include
#include
#include
#include "json/json.h"
using namespace std;
class MyDB
{
private:
MYSQL* mysql; // connect mysql pointer
MYSQL_RES* result; // query result pointer
MYSQL_ROW row; // result row
public:
MyDB();
~MyDB();
bool initDB(char* host, char* user, char* pwd, char* db_name);
Json::Value execSQL(char* sql);
};
#endif
// MyDB.cpp
#include "../include/MyDB.h"
MyDB::MyDB()
{
mysql = mysql_init(NULL); // initial connect param
if (mysql == NULL)
{
cout << "Error:" << mysql_error(mysql);
exit(1);
}
}
MyDB::~MyDB()
{
if (mysql != NULL)
{
mysql_close(mysql);
}
}
bool MyDB::initDB(char* host, char* user, char* pwd, char* db_name){
mysql = mysql_real_connect(mysql, (const char*)host, (const char*)user, (const char*)pwd, (const char*)db_name, 0, NULL, 0);
if (mysql == NULL)
{
cout << "Error:" << mysql_error(mysql);
exit(1);
}
return true;
}
Json::Value MyDB::execSQL(char* sql)
{
Json::Value root;
// mysql_query() success will return 0
if (mysql_query(mysql, (const char*)sql))
{
root["errCode"] = Json::Value(40000);
root["errInfo"] = Json::Value(mysql_error(mysql));
return root;
}
else // success
{
result = mysql_store_result(mysql);
if (result) // return result set
{
root["errCode"] = Json::Value(0);
root["errInfo"] = Json::Value("ok");
int num_fields = mysql_num_fields(result);
int num_rows = mysql_num_rows(result);
for (int i = 0; i < num_rows; i++)
{
// get next line data
row = mysql_fetch_row(result);
if (row < 0) break;
Json::Value rowData;
char key[10];
for (int j = 0; j < num_fields; j++)
{
sprintf(key, "%d", j);
rowData[key] = Json::Value(row[j]);
}
root["data"].append(rowData);
}
}
else // result == NULL
{
// execute update, insert, delete
if (mysql_field_count(mysql) == 0)
{
root["errCode"] = Json::Value(0);
root["errInfo"] = Json::Value("ok");
int numRows = mysql_affected_rows(mysql);
// root["numRows"] = Json::Value(numRows);
}
else // error
{
root["errCode"] = Json::Value(40001);
root["errInfo"] = Json::Value(mysql_error(mysql));
}
}
}
return root;
}
3.2.2.4 Tools类
// Tools.h
#ifndef _TOOLS_H
#define _TOOLS_H
#include
#include
#include
#include
#include "json/json.h"
using namespace std;
class Tools
{
public:
Tools();
~Tools();
int getDatetime(char *timeStr);
void printHeader(int type);
void printJson(Json::Value root, int type);
Json::Value jsonEncode(int errCode, char* errInfo); void printHeaderAndJson(Json::Value root, int headerType, int jsonType);
Json::Value getPostData();
Json::Value getGetData();
};
#endif
// Tools.cpp
#include "../include/Tools.h"
Tools::Tools()
{
}
Tools::~Tools()
{
}
int Tools::getDatetime(char *timeStr)
{
time_t seconds = time(NULL);
struct tm *curTime = localtime(&seconds);
int timeStrLen = sprintf(timeStr, "%04d-%02d-%02d %02d:%02d:%02d", curTime->tm_year + 1900, curTime->tm_mon + 1, curTime->tm_mday, curTime->tm_hour, curTime->tm_min, curTime->tm_sec);
return timeStrLen;
}
void Tools::printHeader(int type = 1)
{
if (type == 1)
{
cout << "Content-type: text/html\r\n\r\n";
}
else if (type == 2)
{
cout << "Content-type: application/json\r\n\r\n";
}
}
void Tools::printJson(Json::Value root, int type = 1)
{
if (type == 1) // FastWriter
{
Json::FastWriter fastWriter;
cout << fastWriter.write(root);
}
else if (type == 2) // StyledWriter
{
Json::StyledWriter styledWriter;
cout << styledWriter.write(root);
}
}
// make errCode and errInfo
Json::Value Tools::jsonEncode(int errCode, char* errInfo)
{
Json::Value root;
root["errCode"] = Json::Value(errCode);
root["errInfo"] = Json::Value(errInfo);
return root;
}
void Tools::printHeaderAndJson(Json::Value root, int headerType = 1, int jsonType = 1)
{
this->printHeader(headerType);
this->printJson(root, jsonType);
}
Json::Value Tools::getPostData()
{
char *postStr;
// check data size
if (getenv("CONTENT_LENGTH"))
{
int len;
sscanf(getenv("CONTENT_LENGTH"), "%d", &len);
postStr = new char[len * 2];
int tot = 0;
char c;
// format post data
for (int i = 0; i < len; i++)
{
c = getchar();
if (c == '\n' || c == '\r' || c == '\t')
{
continue;
}
postStr[tot++] = c;
}
postStr[tot] = '
';
}
Json::Value root;
Json::Reader reader;
if (!reader.parse(postStr, root))
{
this->printHeaderAndJson(this->jsonEncode(40200, (char*)"Json format error"), 2, 2);
exit(1);
}
return root;
}
Json::Value Tools::getGetData()
{
Json::Value root;
return root;
}
3.2.2.5 User类
// User.h
#include
#include
#include
#include "MyDB.h"
#include "Tools.h"
#include "json/json.h"
using namespace std;
class User
{
private:
MyDB *db;
Tools *tools;
char* sql;
Json::Value root;
public:
User();
User(MyDB* db, Tools* tools);
~User();
Json::Value insert(const char* username, const char* pwd);
Json::Value _delete(int id);
Json::Value update(int id, const char* username,const char* pwd, int type);
Json::Value select(int id);
Json::Value _register(Json::Value postData);
Json::Value login(Json::Value postData);
};
// User.cpp
#include "../include/User.h"
User::User()
{
}
User::User(MyDB* db, Tools* tools)
{
this->db = db;
this->tools = tools;
this->sql = new char[300];
}
User::~User()
{
}
Json::Value User::insert(const char* username, const char* pwd)
{
char curTime[30];
this->tools->getDatetime(curTime);
sprintf(sql, "insert into users(username, password, createTime) values(\"%s\", \"%s\", \"%s\");", username, pwd, curTime);
root = this->db->execSQL(sql);
return root;
}
Json::Value User::_delete(int id)
{
sprintf(sql, "delete from users where id = %d;", id);
root = this->db->execSQL(sql);
return root;
}
Json::Value User::update(int id, const char* username, const char* pwd, int type)
{
if (type == 1) // update username
{
sprintf(sql, "update users set username = \"%s\" where id = %d;", username, id);
}
else if (type == 2) // update password
{
sprintf(sql, "update users set password = \"%s\" where id = %d;", pwd, id);
}
root = this->db->execSQL(sql);
return root;
}
Json::Value User::select(int id)
{
sprintf(sql, "select * from users where id = %d;", id);
root = this->db->execSQL(sql);
return root;
}
Json::Value User::_register(Json::Value postData)
{
Json::Value root;
if (postData.isMember("username") && postData.isMember("password"))
{
sprintf(sql, "select count(1) from users where username = \"%s\";", postData["username"].asString().c_str());
Json::Value result = this->db->execSQL(sql);
int flag;
sscanf(result["data"][0u]["0"].asString().c_str(), "%d", &flag);
if (flag >= 1)
{
root = this->tools->jsonEncode(40303, (char*)"Username is exists");
}
else
{
result = this->insert(postData["username"].asString().c_str(), postData["password"].asString().c_str());
root["errCode"] = result["errCode"];
root["errInfo"] = result["errInfo"];
}
}
else
{
root = this->tools->jsonEncode(40300, (char*)"Params is missing");
}
return root;
}
Json::Value User::login(Json::Value postData)
{
Json::Value root;
if (postData.isMember("username") && postData.isMember("password"))
{
sprintf(sql, "select count(1) from users where username = \"%s\" and password = \"%s\";", postData["username"].asString().c_str(), postData["password"].asString().c_str());
Json::Value result = this->db->execSQL(sql);
int flag;
sscanf(result["data"][0u]["0"].asString().c_str(), "%d", &flag);
if (!flag)
{
root = this->tools->jsonEncode(40301, (char*)"Username or password is wrong");
}
else
{
root = this->tools->jsonEncode(0, (char *)"Ok");
}
// root["0"] = Json::Value(result["data"][0u]["0"].asInt());
}
else
{
root = this->tools->jsonEncode(40302, (char*)"Params is missing");
}
return root;
}
3.2.2.6 Note类
// Note.h
#ifndef _NOTE_H
#define _NOTE_H
#include
#include
#include
#include "MyDB.h"
#include "Tools.h"
#include "json/json.h"
using namespace std;
class Note
{
private:
MyDB *db;
Tools *tools;
char *sql;
Json::Value root;
public:
Note();
Note(MyDB *db, Tools *tools);
~Note();
Json::Value insert(int userId, const char* content, int tag);
Json::Value _delete(int id);
Json::Value update(int id, const char* content);
Json::Value select(int id, int type);
Json::Value addNote(Json::Value postData);
Json::Value deleteNote(Json::Value postData);
Json::Value updateNote(Json::Value postData);
Json::Value selectNote(Json::Value postDate);
};
#endif
< postData["noteIds"].size(); i++)
{
sprintf(sql, "delete from notes where id = %d and userId = %d;", postData["noteIds"][i].asInt(), postData["userId"].asInt());
result = this->// Note.cpp
#include "../include/Note.h"
Note::Note()
{
this->db = new MyDB();
this->db->initDB((char *)"localhost", (char *)"root", (char *)"fast1111", (char *)"test");
this->sql = new char[300];
}
Note::Note(MyDB *db, Tools *tools)
{
this->db = db;
this->tools = tools;
this->sql = new char[300];
}
Note::~Note()
{
}
Json::Value Note::insert(int userId, const char* content, int tag = 1)
{
char curTime[40];
// format values
tools->getDatetime(curTime);
sprintf(sql, "insert into notes(userId, content, tag, createTime) values(%d, \"%s\", %d, \"%s\");",userId, content, tag, curTime);
root = this->db->execSQL(sql);
return root;
}
Json::Value Note::_delete(int id)
{
sprintf(sql, "delete from notes where id = %d;", id);
root = this->db->execSQL(sql);
return root;
}
Json::Value Note::update(int id, const char* content)
{
sprintf(sql, "update notes set content = \"%s\" where id = %d;", content, id);
root = this->db->execSQL(sql);
return root;
}
Json::Value Note::select(int id, int type = 0)
{
if (type == 1) // use note id to select
{
sprintf(sql, "select * from notes where id = %d;", id);
}
else if (type == 2) // use user id to select
{
sprintf(sql, "select * from notes where userId = %d;", id);
}
root = this->db->execSQL(sql);
return root;
}
Json::Value Note::addNote(Json::Value postData)
{
Json::Value root;
char curTime[40];
this->tools->getDatetime(curTime);
sprintf(sql, "insert into notes(userId, content, createTime) values(%d, \"%s\", \"%s\");", postData["userId"].asInt(), postData["content"].asString().c_str(), curTime);
root = this->db->execSQL(sql);
return root;
}
Json::Value Note::deleteNote(Json::Value postData)
{
Json::Value root;
Json::Value result;
// use transaction to aviod some error
memset(sql, 0x00, sizeof(sql));
memcpy(sql, "set autocommit = 0;", 20);
root = this->db->execSQL(sql);
if (root["errCode"].asInt())
{
return root;
}
memcpy(sql, "start transaction;", 20);
root = this->db->execSQL(sql);
if (root["errCode"].asInt())
{
return root;
}
// delete note in the order it was received
for (int i = 0; i
db->execSQL(sql);
if (result["errCode"].asInt())
{
break;
}
}
// hava error, need to rollback
if (result["errCode"].asInt())
{
memcpy(sql, "rollback;", 10);
result = this->db->execSQL(sql);
root = this->tools->jsonEncode(40401, (char *)"Delete notes wrong");
}
// no error, delete all, return Ok
else
{
memcpy(sql, "commit;", 10);
result = this->db->execSQL(sql);
root = this->tools->jsonEncode(0, (char*)"Ok");
}
return root;
}
Json::Value Note::updateNote(Json::Value postData)
{
Json::Value root;
// check necessary params, type and noteId
if (!postData.isMember("type") || !postData.isMember("noteId") || !postData.isMember("userId"))
{
return this->tools->jsonEncode(40402, (char *)"Params is missing");
}
switch (postData["type"].asInt())
{
case 1: // update content
{
if (!postData.isMember("content"))
{
return this->tools->jsonEncode(40402, (char *)"Params is missing");
}
sprintf(sql, "update notes set content = \"%s\" where id = %d and userId = %d;", postData["content"].asString().c_str(), postData["noteId"].asInt(), postData["userId"].asInt()); break;
}
case 2: // update tag
{
if (!postData.isMember("tag"))
{
return this->tools->jsonEncode(40402, (char*)"Params is missing");
}
sprintf(sql, "update notes set tag = %d where id = %d and userId = %d;", postData["tag"].asInt(), postData["noteId"].asInt(), postData["userId"].asInt());
break;
}
case 3: // update content and tag
{
if (!postData.isMember("content") || !postData.isMember("tag"))
{
return this->tools->jsonEncode(40402, (char*)"Params is missing");
}
sprintf(sql, "update notes set content = \"%s\", tag = %d where id = %d and userId = %d;", postData["content"].asString().c_str(), postData["tag"].asInt(), postData["noteId"].asInt(), postData["userId"].asInt());
break;
}
default:
{
return this->tools->jsonEncode(40403, (char*)"Type error");
break;
}
}
root = this->db->execSQL(sql);
return root;
}
Json::Value Note::selectNote(Json::Value postData)
{
Json::Value root;
if (!postData.isMember("userId"))
{
return this->tools->jsonEncode(40404, (char*)"Params is missing");
}
int offset = 0, limit = 10, type = 1;
char* orderStr = (char*)"asc";
if (postData.isMember("offset"))
{
offset = postData["offset"].asInt();
}
if (postData.isMember("limit"))
{
limit = postData["limit"].asInt();
}
if (postData.isMember("type"))
{
type = postData["type"].asInt();
}
if (postData.isMember("order") && postData["order"].asInt() == 2)
{
orderStr = (char*)"desc";
}
switch (type)
{
case 1: // select all fields
{
sprintf(sql, "select * from notes where userId = %d order by createTime %s limit %d, %d;", postData["userId"].asInt(), orderStr, offset, limit);
break;
}
default:
{
return this->tools->jsonEncode(40404, (char*)"Type error");
}
}
root = this->db->execSQL(sql);
return root;
}
3.2.2.7 Makefile
objects = temp/Main.o \
temp/Start.o \
temp/MyDB.o \
temp/Tools.o \
temp/Note.o \
temp/User.o
bin/main: $(objects)
g++ -g -o $@ $^ -Llibs -ljson_linux-gcc-9_libmt -lmysqlclient
temp/%.o: source/%.cpp include/%.h
g++ -o $@ -c $< -g
clean:
rm -rf $(objects)
3.3 案例结果
3.3.1 用户注册
3.3.2 用户登录
3.3.3 发布笔记
3.3.4 删除笔记
3.3.5 修改笔记
3.3.6 查询笔记
ps: 返回集中没有记录的字段名,暂时没找到mysql的哪个函数会提供字段名,就先用列号代替了
-
4 问题总结
-
5 参考博客
6 其他
这篇博客作为学习过程的一个记录,方便以后查看。
目前还有很多内容没有完善,实现的比较粗糙,只是按着自己的想法做的,后面有机会也会再完善完善。
期间遇到了很多问题,参考了很多前辈的博客,远不止上面所列举的博客,学习了不少新知识。
我这边测试的基本功能都可以实现,如发现错误,请不吝赐教。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)