newobj跨平台开发框架:https://github.com/Liuccysdgg/newobj
本片着重介绍 MYSQL连接池、HTTP静态文件响应、部分JS等。
效果演示
一、MYSQL连接池
如果每次业务请求进来时去创建mysql连接并处理,其中会每次消耗几十到几百毫秒的连接耗时,如果第一次连接后不将连接断开并在下一次需要获取连接时从池内获取,是不是可以减少连接消耗呢?
二、用户控制器
既然本章需要处理用户登录,那么就要考虑到权限问题 ---- 已登录 | 未登录
已登录用户会有几点操作:
① 退出登录
② 获取资料【性别、年龄】
③设置资料【性别、年龄】
未登录用户会有以下操作:
① 登录
现在结构及权限清晰,我们将开始构建代码,即需要 一个处理类、一个拦截器函数
1、处理类用来处理已登录用户的操作
2、拦截器函数用来拦截需要已登录用户才能操作的接口目录进行鉴权。
新建 src/user_ctl.h
#pragma once
#include "network/http/http_controller.h"
class user_ctl :public network::http::controller
{
public:
// 登录
network::http::response_type login();
// 退出登录
network::http::response_type outlogin();
// 取个人资料
network::http::response_type get_info();
};
新建 src/user_ctl.cpp
#include "user_ctl.h"
#include "util/json.h"
#include "mysql/mysql_plus.h"
#include "public/environment.h"
#include "define.h"
network::http::response_type user_ctl::get_info()
{
auto session = request()->session("cppsession");
newobj::json rep;
rep["username"] = (*session)["username"].to<nstring>();
rep["sex"] = (*session)["sex"].to<bool>();
rep["age"] = (*session)["age"].to<int32>();
response()->send(rep);
return RT_OK;
}
network::http::response_type user_ctl::login()
{
nstring username = request()->parser()->json()["username"].to<nstring>();
nstring password = request()->parser()->json()["password"].to<nstring>();
// 回复JSON
newobj::json rep;
// 查询是否已登录
auto session = request()->session("cppsession");
if((*session)["username"].to<nstring>().empty() == false){
rep["result"] = true;
rep["msg"] = "已登录";
response()->send(rep);
return RT_OK;
}
// 获取可自动释放的MYSQL连接
conn_autofree<mysql_plus::conn> conn(MYSQL_POOL->get());
auto ppst = conn->setsql("SELECT id,sex,age FROM users WHERE username = ? and password = ? LIMIT 1");
ppst->set_string(1,username);
ppst->set_string(2,password);
auto result = ppst->query();
if(result->row_count() == 0){
// 没有记录,则账号密码错误
rep["result"] = false;
rep["msg"] = "账号或密码错误";
response()->send(rep);
return RT_OK;
}
result->next();
// 找到记录,账号密码正确,登录成功
//
// 写入session
(*session)["username"] = username;
(*session)["age"] = result->get_int32("age");
(*session)["sex"] = result->get_boolean("sex");
rep["result"] = true;
rep["msg"] = "登录成功";
response()->send(rep);
return RT_OK;
}
network::http::response_type user_ctl::outlogin()
{
auto session = request()->session("cppsession");
session->destory();
response()->send((nstring)"已注销");
return RT_OK;
}
新建 www/index.html
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<h1>账号:</h1>
<input id="username" type="text" />
<h1>密码:</h1>
<input id="password" type="text" />
<br>
<input id="login" type="button" value="登录" />
<input id="outlogin" type="button" value="注销" />
<input id="get_info" type="button" value="获取资料" />
<script src="jquery.min.js"></script>
<script>
$(document).ready(function(){
$("#outlogin").click(function(){
$.ajax({
type: "get",
url: "/user/outlogin",
success: function(data, status) {
alert(data.msg)
}
})
})
$("#login").click(function(){
var data = {};
data.username = $("#username").val();
data.password = $("#password").val();
$.ajax({
type: "post",
url: "/user/login",
contentType: "application/json",
data: JSON.stringify(data),
dataType: "json",
success: function(data, status) {
alert(data.msg)
}
})
})
$("#get_info").click(function(){
var data = {};
$.ajax({
type: "get",
url: "/user/get_info",
dataType: "json",
success: function(data, status) {
alert(JSON.stringify(data))
}
})
})
})
</script>
</body>
</html>
main.cpp
#include <iostream>
#include <regex>
#include "public/environment.h"
#include "util/system.h"
#include "util/time.h"
#include "network/http/http_client_plus.h"
#include "network/http/http_center.h"
#include "network/http/http_request.h"
#include "network/http/http_response.h"
#include "network/http/http_reqpack.h"
#include "network/http/http_router.h"
#include "network/http/http_interceptor.h"
#include "network/http/http_website.h"
#include "user_ctl.h"
#include <tuple>
#include "define.h"
#include "mysql/mysql_plus.h"
int main()
{
// 配置文件
newobj::json config;
config.parse_file("./res/config.json");
/*初始化MYSQL连接池*/
{
mysql_plus::mysql_conn_info info;
// JSON配置写入结构体
info.charset = "utf8mb4";
info.database = config["mysql"]["database"].to<nstring>();
info.ipaddress = config["mysql"]["ipaddress"].to<nstring>();
info.password = config["mysql"]["password"].to<nstring>();
info.username = config["mysql"]["username"].to<nstring>();
info.port = config["mysql"]["port"].to<uint32>();
// 创建连接池指针
mysql_plus::pool* pool = new mysql_plus::pool();
if(pool->start(info,10/*连接最大数量*/) == false){
newobj::log->fatal(pool->last_error());
return 0;
}
// 设置为全局环境变量
newobj::env->set<mysql_plus::pool>("mysql_pool",pool);
}
// 创建控制中心
auto center = new network::http::center;
center->create(config);
// 通过域名获取 website 站点对象指针
auto website = center->website("0.0.0.0");
/************************ 添加控制器\拦截器\静态文件处理 **********************************/
// 获取路由
auto router = website->router();
// 获取拦截器
auto interceptor = router->interceptor();
// 拦截所有 /user/ 目录下请求,该目录为管理器权限
interceptor->add("/user/.*",[](network::http::reqpack* reqpack/*请求包*/)->bool{
auto session = reqpack->request()->session("cppsession");
std::cout<<session->session_id().c_str()<<std::endl;
if(session->session_id().empty()){
session->init("");
reqpack->response()->headers()->emplace("Set-Cookie","cppsession="+session->session_id());
}
// 判断是否为登录请求,登录请求不用拦截。
if(reqpack->filepath() == "/user/login"){
return true;
}
if(reqpack->filepath() == "/user/outlogin"){
return true;
}
if((*session)["username"].to<nstring>().empty()){
reqpack->response()->send((nstring)"Please Login");
// 未登录
return false;
}
// 已登录
return true;
});
// 增加请求处理 (lamaba 方式)
router->subscribe("/hello",network::http::GET,[](network::http::request* request,network::http::response* response){
// 发送 文本
response->send((nstring)"<h1>Hello!,This is newobj Web Server</h1>");
});
// 其它处理
router->other([](network::http::request* request,network::http::response* response){
auto filepath = request->filepath();
if(filepath == "/")
filepath = "/index.html";
if(response->send_file(filepath) == false){
response->send((nstring)"404",404,"Not Found");
}
return;
});
// 增加请求处理集 (对象方式)
SUBSCRIBE(router,user_ctl,get_info,"/user/get_info",network::http::GET);
SUBSCRIBE(router,user_ctl,outlogin,"/user/outlogin",network::http::GET);
SUBSCRIBE(router,user_ctl,login,"/user/login",network::http::POST);
// 启动控制中心
if (center->start() == false)
{
newobj::log->fatal(center->last_error());
return 0;
}
newobj::log->info("start success");
while (true)
{
system::sleep_msec(1000);
}
return 0;
}
config.json
{
"website":[
{
"host":[
{
"host":"0.0.0.0:6699",
"ssl":false
}
],
"router":{
"threadpool":{
"queuemax":10000,
"size":5
}
},
"rootdir":"/home/ubuntu/project/webserver/www"
}
],
"mysql":{
"ipaddress":"127.0.0.1",
"password":"xeMjCwGLKsdwT6A7",
"port":3306,
"username":"blog",
"database":"blog"
}
}
最终效果如上,实现登录、获取信息、注销登录三部操作,屏幕前的你可以尝试补全更新资料功能哦~