/** * * @file config.hpp * @author zxw * Copyright 2024, yjrobotics. All rights reserved. * * robotics * */ #pragma once //asio2 #include //robotics #include "utils.hpp" #include "json.hpp" #include "CImg.h" namespace robotics { namespace v3 { namespace mvc { class web_reflect { public: /** * @brief * @param obj */ static std::map get_object_value(rttr::instance obj) { std::map result; auto propertys = obj.get_type().get_properties(); for (auto& property : propertys) { std::string propName(property.get_name()); rttr::variant propValue = property.get_value(obj); rttr::type propType = property.get_type(); if (propType == rttr::type::get()) { result[propName] = std::to_string(propValue.get_value()); } else if (propType == rttr::type::get()) { result[propName] = std::to_string(propValue.get_value()); } else if (propType == rttr::type::get()) { result[propName] = v3::utils::format("{:.2f}", propValue.get_value()); } else if (propType == rttr::type::get()) { result[propName] = v3::utils::format("{:.2f}", propValue.get_value()); } else if (propType == rttr::type::get()) { result[propName] = std::to_string(propValue.get_value()); } else if (propType == rttr::type::get()) { result[propName] = propValue.get_value() ? "true" : "false"; } else if (propType == rttr::type::get()) { result[propName] = propValue.get_value(); } else if (propType == rttr::type::get()) { result[propName] = propValue.get_value().to_string(); } else if (propType == rttr::type::get>()) { auto vecValue = propValue.get_value>(); result[propName] = v3::utils::join(vecValue, ","); } else if (propType == rttr::type::get>()) { auto vecValue = propValue.get_value>(); result[propName] = v3::utils::join(vecValue, ","); } else if (propType == rttr::type::get>()) { auto vecValue = propValue.get_value>(); result[propName] = v3::utils::join(vecValue, ","); } else if (propType == rttr::type::get>()) { auto vecValue = propValue.get_value>(); result[propName] = v3::utils::join(vecValue, ","); } else if (propType == rttr::type::get>()) { auto vecValue = propValue.get_value>(); result[propName] = v3::utils::join(vecValue, ","); } else if (propType == rttr::type::get>()) { auto vecValue = propValue.get_value>(); result[propName] = v3::utils::join(vecValue, ","); } else if (propType == rttr::type::get>()) { auto vecValue = propValue.get_value>(); result[propName] = v3::utils::join(vecValue, ","); } else if (propType == rttr::type::get>()) { auto vecValue = propValue.get_value>(); result[propName] = v3::utils::join(vecValue, ","); } } return result; } /** * @brief * @param obj * @param values */ static void set_object_value(rttr::instance obj, std::mapconst& values) { rttr::instance obj1 = obj.get_type().get_raw_type().is_wrapper() ? obj.get_wrapped_instance() : obj; const auto propertys = obj1.get_derived_type().get_properties(); for (auto& property : propertys) { auto name = std::string(property.get_name()); auto type = property.get_type(); if (!values.contains(name)) continue; std::string value = values.find(name)->second; if (type == rttr::type::get()) { property.set_value(obj1, atoi(value.c_str())); } else if (type == rttr::type::get()) { property.set_value(obj, std::int64_t(std::atoll(value.c_str()))); } else if (type == rttr::type::get()) { property.set_value(obj, float(std::atof(value.c_str()))); } else if (type == rttr::type::get()) { property.set_value(obj, double(std::atof(value.c_str()))); } else if (type == rttr::type::get()) { property.set_value(obj, long(std::atol(value.c_str()))); } else if (type == rttr::type::get()) { if (value == "1" || value == "true" || value == "TRUE" || value == "True") { property.set_value(obj, true); } else { property.set_value(obj, false); } } else if (type == rttr::type::get()) { property.set_value(obj, value); } else if (type == rttr::type::get()) { property.set_value(obj, v3::datetime(value)); } else if (type == rttr::type::get>()) { std::vector vec = v3::utils::split(value, ","); std::vector vecValue; for (auto vecIt : vec) { vecValue.push_back(std::atoi(vecIt.c_str())); } property.set_value(obj, vecValue); } else if (type == rttr::type::get>()) { std::vector vec = v3::utils::split(value, ","); std::vector vecValue; for (auto vecIt : vec) { vecValue.push_back(std::atoll(vecIt.c_str())); } property.set_value(obj, vecValue); } else if (type == rttr::type::get>()) { std::vector vec = v3::utils::split(value, ","); std::vector vecValue; for (auto vecIt : vec) { vecValue.push_back(std::atof(vecIt.c_str())); } property.set_value(obj, vecValue); } else if (type == rttr::type::get>()) { std::vector vec = v3::utils::split(value, ","); std::vector vecValue; for (auto vecIt : vec) { vecValue.push_back(std::atof(vecIt.c_str())); } property.set_value(obj, vecValue); } else if (type == rttr::type::get>()) { std::vector vec = v3::utils::split(value, ","); std::vector vecValue; for (auto vecIt : vec) { vecValue.push_back(std::atol(vecIt.c_str())); } property.set_value(obj, vecValue); } else if (type == rttr::type::get>()) { std::vector vec = v3::utils::split(value, ","); std::vector vecValue; for (auto vecIt : vec) { vecValue.push_back(vecIt == "1" || vecIt == "true" || vecIt == "TRUE" || vecIt == "True"); } property.set_value(obj, vecValue); } else if (type == rttr::type::get>()) { std::vector vecValue = v3::utils::split(value, ","); property.set_value(obj, vecValue); } else if (type == rttr::type::get>()) { std::vector vec = v3::utils::split(value, ","); std::vector vecValue; for (auto vecIt : vec) { vecValue.push_back(v3::datetime(vecIt)); } property.set_value(obj, vecValue); } } } }; class cookie_id { public: static std::string get_id() { static std::string g_cookie_id; if (g_cookie_id.empty()) { g_cookie_id = v3::utils::uuid(); } return g_cookie_id; } }; class cookie { public: cookie() { //name = "SessionId"; name = cookie_id::get_id(); value = v3::utils::base64_encode(v3::utils::uuid()); path = "path=/"; time = 30 * 60 * 1000; last_expire_time = v3::datetime::current_time_stamp() + time; } /** * @brief cookie名称 */ std::string name; /** * @brief cookie值 */ std::string value; /** * @brief cookie路径 */ std::string path; /** * @brief cookie时间 */ time_t time = 0; /** * @brief 上次刷新时间 */ time_t last_expire_time = 0; /** * @brief 对比 * @param cookie * @param remove * @return */ bool compare(std::shared_ptr cookie, bool& remove) { bool result = false; remove = false; if (cookie) { result = (name == cookie->name && value == cookie->value); if (result) { time_t current_time = v3::datetime::current_time_stamp(); if (last_expire_time < current_time) { remove = true; result = false; } else { last_expire_time = v3::datetime::current_time_stamp() + time; } } } return result; } }; class cookies { public: /** * @brief 解析 * @param cookie_str * @return */ static std::shared_ptr analysis(std::string const& cookie_str) { std::shared_ptr result; std::vector cookies_tmp = v3::utils::split(cookie_str, ";"); for (auto& item : cookies_tmp) { int index = item.find('='); if (index == std::string::npos) continue; std::shared_ptr add(new cookie); add->name = item.substr(0, index); add->value = item.substr(index + 1); add->name = v3::utils::trim(add->name); add->value = v3::utils::trim(add->value); if (add->name != "path") { if (!result) { result.reset(new cookies); } result->cookies_.push_back(add); } } return result; } /** * @brief 对比 * @param value * @return */ bool compare(std::shared_ptr value) { bool result = false; if (!value) return result; for (auto& value_elem : value->cookies_) { for (int i = 0; i < cookies_.size(); ++i) { bool is_remove = false; bool exist = cookies_[i]->compare(value_elem, is_remove); if (is_remove) { cookies_.erase(cookies_.begin() + i); --i; continue; } if (exist) { result = true; } } } return result; } /** * @brief 数量 * @return */ int count() { return int(cookies_.size()); } /** * @brief 添加 * @param cookie */ void add(std::shared_ptr cookie) { cookies_.push_back(cookie); } private: std::vector> cookies_; }; struct session { /** * @brief 关闭 */ bool close = false; /** * @brief */ bool authen = false; /** * @brief */ std::shared_ptr cookies_; /** * @brief */ std::map user_data; }; class sessions { public: /** * @brief */ static void add_session(std::shared_ptr session) { std::lock_guard locker(mutex_()); sessions_().push_back(session); } /** * @brief */ static std::shared_ptr get_session(std::string const& cookie_str) { std::shared_ptr result; std::shared_ptr cookies = cookies::analysis(cookie_str); { std::lock_guard locker(mutex_()); for (int i = 0; i < sessions_().size(); ++i) { if (sessions_()[i]->cookies_->compare(cookies) && !sessions_()[i]->close) { result = sessions_()[i]; } else if (sessions_()[i]->cookies_->count() <= 0 || sessions_()[i]->close) { sessions_().erase(sessions_().begin() + i); i--; } } } return result; } private: static std::vector>& sessions_() { static std::vector> g_sessions; return g_sessions; } static std::mutex& mutex_() { static std::mutex g_mutex; return g_mutex; } }; enum class captcha_type { /** * @brief 成功 */ success, /** * @brief 失败 */ fail, /** * @brief 没有验证码 */ no }; struct controller_action; class http_context { public: class curr { public: /** * @brief 当前context * @return */ static std::shared_ptr current() { return http_context::current(); } /** * @brief 是否有权限 * @return */ static bool authen() { return current()->authen(); } /** * @brief 设置权限 * @param value */ static void set_authen(bool value = true) { current()->set_authen(value); } /** * @brief 设置session * @param key * @param value */ static void add_session(std::string const& key, std::any const& value) { current()->add_session(key, value); } /** * @brief 添加session并设置权限 * @param key * @param value */ static void add_authen_session(std::string const& key, std::any const& value) { add_session(key, value); set_authen(); } /** * @brief 获取session * @param key * @return */ static std::any& session(std::string const& key) { return current()->session(key); } /** * @brief 获取session * @tparam _Ret * @param key * @param succ * @return */ template static bool get_session(std::string const& key, _Ret& result) { std::any any_value = current()->session(key); if (any_value.type() != typeid(_Ret)) { return false; } result = std::any_cast<_Ret>(any_value); return true; } /** * @brief 添加cookie * @param timeout */ static void add_cookie(int timeout = 30) { current()->add_cookie(timeout); } /** * @brief 删除cookie */ static void remove_cookie() { current()->remove_cookie(); } /** * @brief 重置cookie * @param timeout */ static void reset_cookie(int timeout = 30) { remove_cookie(); add_cookie(timeout); } /** * @brief 验证码比较 * @param key * @param captcha_code * @return */ static captcha_type captcha_compare(std::string const& key, std::string const& captcha_code) { std::string value; if (!get_session(key, value)) return mvc::captcha_type::no; else if (v3::utils::case_insensitive_compare(value, captcha_code)) return mvc::captcha_type::success; else return mvc::captcha_type::fail; } /** * @brief 验证码比较 * @param key * @param captcha_code * @param success * @param message * @return */ static bool captcha_compare(std::string const& key, std::string const& captcha_code, bool& success, std::string& message) { auto result = captcha_compare(key, captcha_code); if (mvc::captcha_type::no == result) { success = false; message = "验证码不存在!"; return false; } else if (mvc::captcha_type::fail == result) { success = false; message = "验证码错误!"; return false; } else { success = true; message = "成功!"; return true; } } }; /** * @brief */ static void new_curr_http_context(http::web_request& req, http::web_response& rep) { rep.clear(); new http_context(req, rep); } /** * @brief */ static void remove_curr_http_context() { std::lock_guard locker(mutex_()); http_context_list_().erase(std::this_thread::get_id()); } /** * @brief * @param route * @return */ static std::shared_ptr current() { std::lock_guard locker(mutex_()); return http_context_list_()[std::this_thread::get_id()]; } /** * @brief */ bool authen() { if (current_session_) return current_session_->authen; return false; } /** * @brief */ void set_authen(bool value = true) { if (current_session_) current_session_->authen = value; } /** * @brief */ void add_session(std::string const& key, std::any const& value) { if (!current_session_) throw std::runtime_error("not cookie!"); current_session_->user_data[key] = value; } /** * @brief */ std::any& session(std::string const& key) { static std::any g_any; if (current_session_) { if (current_session_->user_data.find(key) != current_session_->user_data.end()) { return current_session_->user_data[key]; } } g_any = std::any(); return g_any; } /** * @brief */ void add_cookie(int timeout = 30) { std::shared_ptr item(new cookie); item->time = timeout * (1000 * 60); item->last_expire_time = v3::datetime::current_time_stamp() + item->time; if (current_session_ && !current_session_->close) { if (!current_session_->cookies_) current_session_->cookies_.reset(new cookies); current_session_->cookies_->add(item); } else { current_session_.reset(new mvc::session); current_session_->cookies_.reset(new cookies); current_session_->cookies_->add(item); mvc::sessions::add_session(current_session_); } std::string set_cookie_str; set_cookie_str += item->name; set_cookie_str += "="; set_cookie_str += item->value; set_cookie_str += ";"; set_cookie_str += item->path; response.set(http::field::set_cookie, set_cookie_str); } /** * @brief */ void remove_cookie() { if (current_session_) { current_session_->close = true; } response.set(http::field::set_cookie, "SessionId=deleted; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT"); } /** * @brief */ void set_controller_action(std::shared_ptr value) { controller_action_ = value; } /** * @brief */ std::shared_ptr get_controller_action() { return controller_action_; } /** * @brief */ http::web_request& request; /** * @brief */ http::web_response& response; /** * @brief */ std::filesystem::path root_directory; private: http_context(http::web_request& req, http::web_response& rep) : request(req), response(rep), root_directory(rep.get_root_directory()) { if (req.find(http::field::cookie) != req.end()) { current_session_ = sessions::get_session(std::string(req.find(http::field::cookie)->value().begin(), req.find(http::field::cookie)->value().end())); } std::lock_guard locker(mutex_()); http_context_list_()[std::this_thread::get_id()].reset(this); } private: std::shared_ptr current_session_; std::shared_ptr controller_action_; private: static std::mutex& mutex_() { static std::mutex g_mutex; return g_mutex; } static std::map>& http_context_list_() { static std::map> g_http_context_list; return g_http_context_list; } }; class http_context_gc { public: http_context_gc(http::web_request& req, http::web_response& rep) { http_context::new_curr_http_context(req, rep); } virtual ~http_context_gc() { http_context::remove_curr_http_context(); } }; enum class pseudo_type { NON = 0, MASK, MODEL, FOR, IF, SWITCH, FILE }; struct class_value { bool is_list = false; std::string class_name; std::vector> class_value; }; struct ipseudo_label { ipseudo_label(std::pair> const& value_type) : type(value_type.first), label(value_type.second) { } /** * @brief 类型 */ pseudo_type type; /** * @brief 原始代码 */ std::string html_code; /** * @brief 临时原始代码 */ std::string html_code_tmp; /** * @brief 类名 */ std::string class_name; /** * @brief 标签 */ std::pair label; /** * @brief 实体类数据列表 */ std::vector> class_value_list; /** * @brief 子标签 */ std::vector> childs; /** * @brief 解析返回html */ virtual std::string analysis() = 0; /** * @brief 设置属性值 */ void set_class_value(std::vector>const& value) { for (auto it : childs) { it->set_class_value(value); } for (auto it : value) { if (class_name != it->class_name) continue; if (it->is_list && type == pseudo_type::FOR) { class_value_list = it->class_value; break; } else if (!it->is_list) { class_value_list = it->class_value; break; } } } /** * @brief 解析基础信息 */ virtual void analysis_baseinfo() = 0; protected: std::string extract(std::string const& value, std::pair const& pair, int& index) { std::string result; bool ok = false; int count = 0; for (size_t i = 0; i < value.size(); ++i) { if (i + 1 >= pair.first.size() && value[i] == pair.first[pair.first.size() - 1] && value.substr(i - pair.first.size() + 1, pair.first.size()) == pair.first) { count++; if (count == 1) { ok = true; continue; } } if (ok && value.size() >= i + pair.second.size() && value[i] == pair.second[0] && value.substr(i, pair.second.size()) == pair.second) { count--; if (count == 0) { index = i + 1; return result; } } if (ok) { result += value[i]; } } index = -1; return result; } std::vector extractList(std::string const& value, std::pair const& pair) { std::vector result; int index = 0; int pos = 0; while (index > -1) { pos += index; std::string ret = extract(value.substr(pos, value.size() - pos), pair, index); if (index != -1) result.push_back(ret); } return result; } }; struct pseudo_mask_label : public ipseudo_label { pseudo_mask_label(std::pair>const& value_type) : ipseudo_label(value_type) { } std::string mask_name; virtual std::string analysis()override { for (auto child : childs) { std::string childValue = child->analysis(); html_code_tmp = v3::utils::replace(html_code_tmp, child->html_code, childValue); } int index = 0; std::string header = extract(html_code_tmp, { "${mask","}" }, index); header = "${mask" + header + "}"; std::string tmp = extract(html_code_tmp, { header,"{/mask}" }, index); return tmp; } virtual void analysis_baseinfo() override { html_code_tmp = html_code; for (auto child : childs) { child->analysis_baseinfo(); } //获取mask名称 int index = 0; mask_name = extract(html_code, { "name='","'" }, index); } }; struct pseudo_model_label : public ipseudo_label { pseudo_model_label(std::pair>const& value_type) : ipseudo_label(value_type) { } virtual std::string analysis()override { for (auto child : childs) { std::string childValue = child->analysis(); html_code_tmp = v3::utils::replace(html_code_tmp, child->html_code, childValue); } std::string propName; std::string propValue; std::map mapPropValue; { int index = 0; std::string tmp = extract(html_code_tmp, { "name='","'" }, index); std::string::size_type pos = tmp.find('.'); if (pos != std::string::npos) { propName = tmp.substr(pos + 1, tmp.size() - pos - 1); } } //获取值 { int index = 0; std::string tmp = extract(html_code_tmp.substr(index, html_code_tmp.size() - index), { "value='[","]'" }, index); //属性值 if (tmp == "#value") { propValue = tmp; } else { auto values = v3::utils::split(tmp, ","); for (auto value : values) { auto valueItem = v3::utils::split(value, ":"); if (valueItem.size() == 2) { if (valueItem[0].size() >= 2 && valueItem[1].size() >= 2 && valueItem[0][0] == '\'' && valueItem[0][valueItem[0].size() - 1] == '\'' && valueItem[1][0] == '\'' && valueItem[1][valueItem[1].size() - 1] == '\'') { valueItem[0].erase(valueItem[0].begin()); valueItem[0].erase(valueItem[0].begin() + valueItem[0].size() - 1); valueItem[1].erase(valueItem[1].begin()); valueItem[1].erase(valueItem[1].begin() + valueItem[1].size() - 1); mapPropValue[valueItem[0]] = valueItem[1]; } } } } } if (propName.empty()) { return ""; } //属性值 if (!propValue.empty() && !class_value_list.empty()) { if (class_value_list[0].find(propName) != class_value_list[0].end()) { return class_value_list[0][propName]; } } else if (!mapPropValue.empty() && !class_value_list.empty()) { if (class_value_list[0].find(propName) != class_value_list[0].end()) { std::string value = class_value_list[0][propName]; //选择值 if (mapPropValue.find(value) != mapPropValue.end()) { return mapPropValue[value]; } //默认值 else if (mapPropValue.find("default") != mapPropValue.end()) { if (mapPropValue["default"] == "#value") { return class_value_list[0][propName]; } else { return mapPropValue["default"]; } } } } return ""; } virtual void analysis_baseinfo() override { html_code_tmp = html_code; for (auto child : childs) { child->analysis_baseinfo(); } //获取类名属性名 int index = 0; std::string tmp = extract(html_code, { "name='","'" }, index); std::string::size_type pos = tmp.find('.'); if (pos != std::string::npos) { class_name = tmp.substr(0, pos); } } }; struct pseudo_for_label : public ipseudo_label { pseudo_for_label(std::pair> const& value_type) : ipseudo_label(value_type) { } virtual std::string analysis()override { for (auto child : childs) { std::string childValue = child->analysis(); html_code_tmp = v3::utils::replace(html_code_tmp, child->html_code, childValue); } std::string resultValue; std::string itemName; std::string forLabel; std::vector forItemLabelList; std::map>> forItemLabelValueList; //获取ITEM名称 { int index = 0; itemName = extract(html_code_tmp, { "item='","'" }, index); if (itemName.empty()) return ""; } //提取for标签值 { int index = 0; std::string header = extract(html_code_tmp, { "${for","}" }, index); header = "${for" + header + "}"; forLabel = extract(html_code_tmp, { header,"{/for}" }, index); } //提取foritem标签 { forItemLabelList = extractList(forLabel, { "{item","/}" }); for (auto& it : forItemLabelList) { it = "{item" + it + "/}"; } if (forItemLabelList.empty()) return forLabel; } //提取foritem参数 { int index = 0; std::string header = "name='" + itemName + "."; for (auto it : forItemLabelList) { std::string propName = extract(it, { header,"'" }, index); if (propName.empty()) continue; std::string value = extract(it, { "value='[","]'" }, index); if (value.empty()) continue; if (value == "#value") { forItemLabelValueList[it][propName][value] = value; } else { auto values = v3::utils::split(value, ","); for (auto valueIt : values) { auto valueItem = v3::utils::split(valueIt, "':'"); if (valueItem.size() == 2) { valueItem[0] += "'"; valueItem[1] = "'" + valueItem[1]; if (valueItem[0].size() >= 2 && valueItem[1].size() >= 2 && valueItem[0][0] == '\'' && valueItem[0][valueItem[0].size() - 1] == '\'' && valueItem[1][0] == '\'' && valueItem[1][valueItem[1].size() - 1] == '\'') { valueItem[0].erase(valueItem[0].begin()); valueItem[0].erase(valueItem[0].begin() + valueItem[0].size() - 1); valueItem[1].erase(valueItem[1].begin()); valueItem[1].erase(valueItem[1].begin() + valueItem[1].size() - 1); forItemLabelValueList[it][propName][valueItem[0]] = valueItem[1]; } } } } } } //遍历值 { int index = 0; for (auto item : class_value_list) { index++; std::string itemValue = forLabel; for (auto forItem : forItemLabelList) { //没有查找到值赋值为空 if (forItemLabelValueList.find(forItem) == forItemLabelValueList.end()) { itemValue = v3::utils::replace(itemValue, forItem, ""); continue; } //查找属性 if (forItemLabelValueList[forItem].find("for-index") != forItemLabelValueList[forItem].end()) { //默认值 if (forItemLabelValueList[forItem]["for-index"].find("#value") != forItemLabelValueList[forItem]["for-index"].end()) { itemValue = v3::utils::replace(itemValue, forItem, std::to_string(index)); } else { if (forItemLabelValueList[forItem]["for-index"].find(std::to_string(index)) != forItemLabelValueList[forItem]["for-index"].end()) { itemValue = v3::utils::replace(itemValue, forItem, forItemLabelValueList[forItem]["for-index"][std::to_string(index)]); } else if (forItemLabelValueList[forItem]["for-index"].find("default") != forItemLabelValueList[forItem]["for-index"].end()) { itemValue = v3::utils::replace(itemValue, forItem, forItemLabelValueList[forItem]["for-index"]["default"]); } else { itemValue = v3::utils::replace(itemValue, forItem, ""); } } continue; } else { std::string propNameItem; std::map propValueItem; auto childItem = forItemLabelValueList[forItem]; for (auto childItemIt : childItem) { propNameItem = childItemIt.first; propValueItem = childItemIt.second; } if (propNameItem.empty()) { itemValue = v3::utils::replace(itemValue, forItem, ""); continue; } if (item.find(propNameItem) != item.end()) { if (item.find(propNameItem) != item.end()) { if (propValueItem.find("#value") != propValueItem.end()) { itemValue = v3::utils::replace(itemValue, forItem, item[propNameItem]); } else if (propValueItem.find(item[propNameItem]) != propValueItem.end()) { itemValue = v3::utils::replace(itemValue, forItem, propValueItem[item[propNameItem]]); } else if (propValueItem.find("default") != propValueItem.end()) { if (propValueItem["default"] == "#value") { itemValue = v3::utils::replace(itemValue, forItem, item[propNameItem]); } else { itemValue = v3::utils::replace(itemValue, forItem, propValueItem["default"]); } } else { itemValue = v3::utils::replace(itemValue, forItem, ""); } } else { itemValue = v3::utils::replace(itemValue, forItem, ""); } } else { itemValue = v3::utils::replace(itemValue, forItem, ""); } } } resultValue += itemValue; } } return resultValue; } virtual void analysis_baseinfo() override { html_code_tmp = html_code; for (auto child : childs) { child->analysis_baseinfo(); } //获取类名属性名 int index = 0; class_name = extract(html_code, { "list='","'" }, index); } }; struct pseudo_if_label : public ipseudo_label { pseudo_if_label(std::pair> const& value_type) : ipseudo_label(value_type) { } virtual std::string analysis()override { for (auto child : childs) { std::string childValue = child->analysis(); html_code_tmp = v3::utils::replace(html_code_tmp, child->html_code, childValue); } std::string propName; std::string propValue; std::string symbol; std::vector labelValue; //提取标签内容 { labelValue = extractList(html_code_tmp, { "}","{" }); if (labelValue.empty()) return ""; } //判断结构体是否有值 { if (class_value_list.empty()) { if (labelValue.size() == 2) { return labelValue[1]; } return ""; } } //获取类名属性名 { int index = 0; std::string tmp = extract(html_code_tmp, { "name='","'" }, index); std::string::size_type pos = tmp.find('.'); if (pos != std::string::npos) { propName = tmp.substr(pos + 1, tmp.size() - pos - 1); } } //判断类名 { if (propName.empty()) { if (labelValue.size() == 2) { return labelValue[1]; } return ""; } } //判断属性名称是否存在 { if (class_value_list[0].find(propName) == class_value_list[0].end()) { if (labelValue.size() == 2) { return labelValue[1]; } return ""; } } //提取操作符 逻辑对比值 { int index = 0; propValue = extract(html_code_tmp, { "value=='","'" }, index); //== if (propValue.empty()) { propValue = extract(html_code_tmp, { "value!='","'" }, index); //!= if (propValue.empty()) { propValue = extract(html_code_tmp, { "value>='","'" }, index); //>= if (propValue.empty()) { propValue = extract(html_code_tmp, { "value<='","'" }, index); //<= if (propValue.empty()) { if (labelValue.size() == 2) { return labelValue[1]; } return ""; } else symbol = "<="; } else symbol = ">="; } else symbol = "!="; } else symbol = "=="; } //判断 { if (symbol == "==" && propValue.compare(class_value_list[0][propName]) == 0) { return labelValue[0]; } else if (symbol == "!=" && propValue.compare(class_value_list[0][propName]) != 0) { return labelValue[0]; } else if (symbol == ">=" && propValue.compare(class_value_list[0][propName]) >= 0) { return labelValue[0]; } else if (symbol == "<=" && propValue.compare(class_value_list[0][propName]) <= 0) { return labelValue[0]; } else { if (labelValue.size() == 2) return labelValue[1]; } } return ""; } virtual void analysis_baseinfo() override { html_code_tmp = html_code; for (auto child : childs) { child->analysis_baseinfo(); } //获取类名属性名 int index = 0; std::string tmp = extract(html_code, { "name='","'" }, index); std::string::size_type pos = tmp.find('.'); if (pos != std::string::npos) { class_name = tmp.substr(0, pos); } } }; struct pseudo_switch_label : public ipseudo_label { pseudo_switch_label(std::pair> const& value_type) : ipseudo_label(value_type) { } virtual std::string analysis()override { for (auto child : childs) { std::string childValue = child->analysis(); html_code_tmp = v3::utils::replace(html_code_tmp, child->html_code, childValue); } std::string propName; std::string labelValueDefault; std::vector labelValue; //获取类名属性名 { int index = 0; std::string tmp = extract(html_code_tmp, { "name='","'" }, index); std::string::size_type pos = tmp.find('.'); if (pos != std::string::npos) { propName = tmp.substr(pos + 1, tmp.size() - pos - 1); } } //提取标签内容 { int index = 0; labelValue = extractList(html_code_tmp, { "{case","/case}" }); labelValueDefault = extract(html_code_tmp, { "{default}","{/default}" }, index); if (labelValue.empty() && labelValueDefault.empty()) { return ""; } else if (labelValue.empty() && !labelValueDefault.empty()) { if (labelValueDefault.find("#value") != std::string::npos) { if (class_value_list.empty()) return ""; if (propName.empty()) return ""; if (class_value_list[0].find(propName) == class_value_list[0].end()) return ""; return class_value_list[0][propName]; } return labelValueDefault; } } //判断属性值 { if (class_value_list.empty()) { if (labelValueDefault.empty()) return ""; if (labelValueDefault.find("#value") != std::string::npos) return ""; return labelValueDefault; } if (propName.empty()) { if (labelValueDefault.empty()) return ""; if (labelValueDefault.find("#value") != std::string::npos) return ""; return labelValueDefault; } if (class_value_list[0].find(propName) == class_value_list[0].end()) { if (labelValueDefault.empty()) return ""; if (labelValueDefault.find("#value") != std::string::npos) return ""; return labelValueDefault; } } //遍历值 { for (auto it : labelValue) { int index = 0; std::string propValue = extract(it, { "value='","'" }, index); if (class_value_list[0][propName] == propValue) { return extract(it, { "}","{" }, index); } } } //判断默认值 { if (labelValueDefault.empty()) return ""; if (labelValueDefault.find("#value") != std::string::npos) { return v3::utils::replace(labelValueDefault, "#value", class_value_list[0][propName]); } return labelValueDefault; } } virtual void analysis_baseinfo() override { html_code_tmp = html_code; for (auto child : childs) { child->analysis_baseinfo(); } //获取类名属性名 int index = 0; std::string tmp = extract(html_code, { "name='","'" }, index); std::string::size_type pos = tmp.find('.'); if (pos != std::string::npos) { class_name = tmp.substr(0, pos); } } }; struct pseudo_file_label : public ipseudo_label { pseudo_file_label(std::pair> const& value_type) : ipseudo_label(value_type) { } virtual std::string analysis()override { for (auto child : childs) { std::string childValue = child->analysis(); html_code_tmp = v3::utils::replace(html_code_tmp, child->html_code, childValue); } int index = 0; std::string path_tmp = extract(html_code_tmp, { "path='","'" }, index); std::filesystem::path path = path_tmp; std::filesystem::path filepath; filepath = http_context::current()->response.get_root_directory(); filepath.make_preferred(); filepath /= path.make_preferred().relative_path(); if (filepath.empty()) return ""; if (std::filesystem::exists(filepath)) { std::ifstream file(filepath, std::ios::in); if (file.is_open()) { return std::string((std::istreambuf_iterator(file)), std::istreambuf_iterator()); } } return ""; } virtual void analysis_baseinfo() override { html_code_tmp = html_code; for (auto child : childs) { child->analysis_baseinfo(); } } }; struct pseudo_extract_class_value { template static std::vector> get_class_value(_Args&&...args) { std::vector> result; auto tuple = std::forward_as_tuple(std::forward<_Args>(args)...); v3::utils::for_each(tuple, [&](auto& it) { auto value = serialize(it); result.push_back(value); }); return result; } private: static std::string get_prop_value(rttr::variant const& value) { std::string result; if (value.get_type() == rttr::type::get()) { result = std::to_string(value.get_value()); } else if (value.get_type() == rttr::type::get()) { result = std::to_string(value.get_value()); } else if (value.get_type() == rttr::type::get()) { result = v3::utils::format("{:.2f}", value.get_value()); } else if (value.get_type() == rttr::type::get()) { result = v3::utils::format("{:.2f}", value.get_value()); } else if (value.get_type() == rttr::type::get()) { result = std::to_string(value.get_value()); } else if (value.get_type() == rttr::type::get()) { if (value.get_value()) result = "true"; else result = "false"; } else if (value.get_type() == rttr::type::get()) { result = value.get_value(); } else if (value.get_type() == rttr::type::get()) { auto t = value.get_value(); result = t.to_string(); } else if (value.get_type() == rttr::type::get>()) { auto values = value.get_value>(); for (int i = 0; i < values.size(); ++i) { if (i + 1 == values.size()) { result += std::to_string(values[i]); } else { result += std::to_string(values[i]) + ","; } } } else if (value.get_type() == rttr::type::get>()) { auto values = value.get_value>(); for (int i = 0; i < values.size(); ++i) { if (i + 1 == values.size()) { result += std::to_string(values[i]); } else { result += std::to_string(values[i]) + ","; } } } else if (value.get_type() == rttr::type::get>()) { auto values = value.get_value>(); for (int i = 0; i < values.size(); ++i) { if (i + 1 == values.size()) { result += v3::utils::format("{:.2f}", values[i]); } else { result += v3::utils::format("{:.2f}", values[i]) + ","; } } } else if (value.get_type() == rttr::type::get>()) { auto values = value.get_value>(); for (int i = 0; i < values.size(); ++i) { if (i + 1 == values.size()) { result += v3::utils::format("{:.2f}", values[i]); } else { result += v3::utils::format("{:.2f}", values[i]) + ","; } } } else if (value.get_type() == rttr::type::get>()) { auto values = value.get_value>(); for (int i = 0; i < values.size(); ++i) { if (i + 1 == values.size()) { result += std::to_string(values[i]); } else { result += std::to_string(values[i]) + ","; } } } else if (value.get_type() == rttr::type::get>()) { auto values = value.get_value>(); for (int i = 0; i < values.size(); ++i) { if (i + 1 == values.size()) { result += values[i] ? "true" : "false"; } else { result += values[i] ? "true," : "false,"; } } } else if (value.get_type() == rttr::type::get>()) { auto values = value.get_value>(); for (int i = 0; i < values.size(); ++i) { if (i + 1 == values.size()) { result += values[i]; } else { result += values[i] + ","; } } } else if (value.get_type() == rttr::type::get>()) { auto values = value.get_value>(); for (int i = 0; i < values.size(); ++i) { if (i + 1 == values.size()) { result += values[i].to_string(); } else { result += values[i].to_string() + ","; } } } return result; } template static std::shared_ptr serialize(std::vector> const& values) { std::vector<_Type> valueTmp; for (auto it : values) { valueTmp.push_back(*it); } return serialize(valueTmp); } template static std::shared_ptr serialize(std::vector<_Type*> values) { std::vector<_Type> valueTmp; for (auto it : values) { valueTmp.push_back(*it); } return serialize(valueTmp); } template static std::shared_ptr serialize(std::vector<_Type> values) { std::shared_ptr result(new class_value); rttr::type type = rttr::type::get<_Type>(); std::string className(type.get_name()); result->class_name = className; result->is_list = true; for (auto it : values) { std::map elem; web_reflect::get_object_value(it, elem); result->class_value.push_back(elem); } return result; } template static std::shared_ptr serialize(std::shared_ptr<_Type> value) { return serialize(*value); } template static std::shared_ptr serialize(_Type* value) { return serialize(*value); } template static std::shared_ptr serialize(_Type value) { std::shared_ptr result(new class_value); rttr::type type = rttr::type::get<_Type>(); std::string className(type.get_name()); result->class_name = className; result->is_list = false; std::map elem = web_reflect::get_object_value(value); result->class_value.push_back(elem); return result; } }; struct pseudo_extract_label_value { /* * 伪代码结构 * 可嵌套 避开关键字 [' }] * ${model name='test_info.userName' value='['${model name='test_info.id' value='[#value]'/}':'启用','false':'禁用'/} * * switch case 中不能存在 #value 属性值 default可以有默认值 可以有固定值 * ${switch name='test_info.id'}{case value='1'}

值1

{/case}...{default}

#value

{/default}{/switch} * * if 运算符 == != >= <= * ${if name='test_info.id' value=='100'}

判断正确

{else}

判断错误

{/if} * * for for-index 从1开始 * ${for item='item' list='test_info'} {item name='item.for-index' value='[#value]'/}{item name='item.enable' value='['true':'启用','default':'#value']'/}{/for} * * file path 可以固定 可以嵌套 model设置html路径 * ${file path='C:/Users/86158/Desktop/404.html'/} */ static std::vector> analysis(std::string const& html) { std::vector> result; std::pair type = { "${mask","{/mask}" }; for (size_t i = 0; i < html.size(); ++i) { if (html.size() > i + type.first.size() && html[i] == type.first[0] && html.substr(i, type.first.size()) == type.first) { std::string retHtml = nounanalysis(html.substr(i, html.size() - i), result); result[result.size() - 1]->html_code; i += retHtml.size(); } } for (auto it : result) { it->analysis_baseinfo(); } return result; } private: static std::string nounanalysis(std::string const& html, std::vector>& childs, pseudo_type type = pseudo_type::MASK) { static std::map> g_pseudoLabel = { { pseudo_type::MASK, {"${mask","{/mask}"} }, { pseudo_type::MODEL, {"${model","/}"} }, { pseudo_type::SWITCH, {"${switch","{/switch}"} }, { pseudo_type::IF, {"${if","{/if}"} }, { pseudo_type::FOR, {"${for","{/for}"} }, { pseudo_type::FILE, {"${file","/}"} } }; if (!(html.size() > g_pseudoLabel[type].first.size() && html.substr(0, g_pseudoLabel[type].first.size()) == g_pseudoLabel[type].first)) { return ""; } if (type == pseudo_type::MASK) childs.push_back(std::make_shared(*g_pseudoLabel.find(type))); else if (type == pseudo_type::MODEL) childs.push_back(std::make_shared(*g_pseudoLabel.find(type))); else if (type == pseudo_type::FOR) childs.push_back(std::make_shared(*g_pseudoLabel.find(type))); else if (type == pseudo_type::IF) childs.push_back(std::make_shared(*g_pseudoLabel.find(type))); else if (type == pseudo_type::SWITCH) childs.push_back(std::make_shared(*g_pseudoLabel.find(type))); else if (type == pseudo_type::FILE) childs.push_back(std::make_shared(*g_pseudoLabel.find(type))); for (std::string::size_type i = 0; i < html.size(); ++i) { for (auto pseudo : g_pseudoLabel) { if (html.size() > i + pseudo.second.first.size() && html[i] == pseudo.second.first[0] && html.substr(i, pseudo.second.first.size()) == pseudo.second.first) { if (i == 0) { break; } else { std::string retHtml = nounanalysis(html.substr(i, html.size() - i), childs[childs.size() - 1]->childs, pseudo.first); childs[childs.size() - 1]->html_code += retHtml; i += retHtml.size(); } } } if (type != pseudo_type::NON) { childs[childs.size() - 1]->html_code += html[i]; } if (childs[childs.size() - 1]->html_code.size() > g_pseudoLabel[type].second.size() && childs[childs.size() - 1]->html_code.substr(childs[childs.size() - 1]->html_code.size() - g_pseudoLabel[type].second.size(), g_pseudoLabel[type].second.size()) == g_pseudoLabel[type].second) { return childs[childs.size() - 1]->html_code; } } return ""; } }; enum class from_storage_type { /** * @brief body */ BODY = 0, /** * @brief url */ QUERY, /** * @brief header */ HEADER, /** * @brief url */ URL }; enum class argument_type { Json = 0, Multipart, Int, Int64, Float, Double, Long, Bool_, String, DateTime, Binary, VecJson, VecInt, VecInt64, VecFloat, VecDouble, VecLong, VecBool, VecString, VecDateTime }; enum class http_method { /** * @brief get */ GET = 0, /** * @brief post */ POST = 1 }; struct multipart { std::string content_disposition; std::string name; std::vector data; std::string value; std::string content_type; std::string filename; std::string content_transfer_encoding; }; struct multipart_value { std::vector data; template _Type value() { _Type result; std::map elem; for (auto value : data) { elem[value.name] = value.value; } web_reflect::set_object_value(result, elem); return result; } }; struct action_argument { void parameter(http::web_request& request, const rttr::parameter_info& type) { parameter_value_.clear(); switch (from_storage_type_) { case from_storage_type::HEADER: analysis_header(request, type); return; case from_storage_type::QUERY: analysis_query(request, type); return; case from_storage_type::BODY: analysis_body(request, type); return; case from_storage_type::URL: analysis_url(request, type); return; } throw std::runtime_error("FormStorageType:错误!"); } /** * @brief 参数名称 */ std::string parameter_name_; /** * @brief 参数 */ rttr::variant parameter_value_; /** * @brief 数据类型 */ argument_type argument_type_ = argument_type::Binary; /** * @brief 表单存储类型 */ from_storage_type from_storage_type_ = from_storage_type::QUERY; private: /** * @brief 解析协议头 * @param request * @param type */ void analysis_header(http::web_request& request, const rttr::parameter_info& type) { std::map headers; for (auto& it : request) { std::string name(it.name_string().begin(), it.name_string().end()); std::string value(it.value().begin(), it.value().end()); headers[name] = value; } if (headers.find(parameter_name_) == headers.end()) throw std::runtime_error("解析协议头参数错误!"); convert(headers[parameter_name_], type); } /** * @brief 解析url参数 * @param request * @param type */ void analysis_query(http::web_request& request, const rttr::parameter_info& type) { std::string query(request.query().begin(), request.query().end()); std::map querys = v3::utils::split_http_form(query); if (querys.find(parameter_name_) == querys.end()) throw std::runtime_error("解析Query参数错误!"); convert(querys[parameter_name_], type); } /** * @brief 解析BODY * @param request * @param type */ void analysis_body(http::web_request& request, const rttr::parameter_info& type) { if (type.get_type() == rttr::type::get()) { multipart_value parameter; auto multipartValue = request.get_multipart(); for (auto it = multipartValue.begin(); it != multipartValue.end(); ++it) { multipart item; item.content_disposition = it->content_disposition(); item.name = it->name(); auto valueTmp = it->value(); item.data.insert(item.data.end(), valueTmp.begin(), valueTmp.end()); if (it->filename().empty()) { try { item.value = it->value(); } catch (...) {} } item.content_type = it->content_type(); item.filename = it->filename(); item.content_transfer_encoding = it->content_transfer_encoding(); parameter.data.push_back(item); } parameter_value_ = parameter; } else if (parameter_name_.empty()) { convert(std::string(request.body().begin(), request.body().end()), type); } else { std::string body(request.body().begin(), request.body().end()); std::map bodys = v3::utils::split_http_form(body); if (bodys.find(parameter_name_) == bodys.end()) throw std::runtime_error("解析Body参数错误!"); convert(bodys[parameter_name_], type); } } /** * @brief url * @param request * @param type */ void analysis_url(http::web_request& request, const rttr::parameter_info& type) { std::string query(request.find("http_route_parameter")->value()); std::map querys = v3::utils::split_http_form(query); if (querys.find(parameter_name_) == querys.end()) throw std::runtime_error("解析Query参数错误!"); convert(querys[parameter_name_], type); } /** * @brief 数据转换 * @param data * @param type */ void convert(std::string_view data, const rttr::parameter_info& type) { try { switch (argument_type_) { case argument_type::Json: { parameter_value_ = type.get_type().create(); v3::json_convert::deserialize(std::string(data.begin(), data.end()), parameter_value_); return; } case argument_type::Multipart: { return; } case argument_type::Int: { parameter_value_ = std::atoi(std::string(data.begin(), data.end()).c_str()); return; } case argument_type::Int64: { parameter_value_ = std::atoll(std::string(data.begin(), data.end()).c_str()); return; } case argument_type::Float: { parameter_value_ = std::atof(std::string(data.begin(), data.end()).c_str()); return; } case argument_type::Double: { parameter_value_ = std::atof(std::string(data.begin(), data.end()).c_str()); return; } case argument_type::Long: { parameter_value_ = std::atol(std::string(data.begin(), data.end()).c_str()); return; } case argument_type::Bool_: { std::string tmpValue(data.begin(), data.end()); parameter_value_ = tmpValue == "1" || tmpValue == "true" || tmpValue == "TRUE" || tmpValue == "True"; return; } case argument_type::String: { parameter_value_ = std::string(data.begin(), data.end()); return; } case argument_type::DateTime: { parameter_value_ = datetime(std::string(data.begin(), data.end())); return; } case argument_type::Binary: { parameter_value_ = std::vector(data.begin(), data.end()); return; } case argument_type::VecInt: { std::vector vec = v3::utils::split(std::string(data.begin(), data.end()), ","); std::vector vecValue; for (auto vecIt : vec) { vecValue.push_back(std::atoi(vecIt.c_str())); } parameter_value_ = vecValue; return; } case argument_type::VecInt64: { std::vector vec = v3::utils::split(std::string(data.begin(), data.end()), ","); std::vector vecValue; for (auto vecIt : vec) { vecValue.push_back(std::atoll(vecIt.c_str())); } parameter_value_ = vecValue; return; } case argument_type::VecFloat: { std::vector vec = v3::utils::split(std::string(data.begin(), data.end()), ","); std::vector vecValue; for (auto vecIt : vec) { vecValue.push_back(std::atof(vecIt.c_str())); } parameter_value_ = vecValue; return; } case argument_type::VecDouble: { std::vector vec = v3::utils::split(std::string(data.begin(), data.end()), ","); std::vector vecValue; for (auto vecIt : vec) { vecValue.push_back(std::atof(vecIt.c_str())); } parameter_value_ = vecValue; return; } case argument_type::VecLong: { std::vector vec = v3::utils::split(std::string(data.begin(), data.end()), ","); std::vector vecValue; for (auto vecIt : vec) { vecValue.push_back(std::atol(vecIt.c_str())); } parameter_value_ = vecValue; return; } case argument_type::VecBool: { std::vector vec = v3::utils::split(std::string(data.begin(), data.end()), ","); std::vector vecValue; for (auto vecIt : vec) { vecValue.push_back(vecIt == "1" || vecIt == "true" || vecIt == "TRUE" || vecIt == "True"); } parameter_value_ = vecValue; return; } case argument_type::VecString: { std::vector vecValue = v3::utils::split(std::string(data.begin(), data.end()), ","); parameter_value_ = vecValue; return; } case argument_type::VecDateTime: { std::vector vec = v3::utils::split(std::string(data.begin(), data.end()), ","); std::vector vecValue; for (auto vecIt : vec) { vecValue.push_back(datetime(vecIt)); } parameter_value_ = vecValue; return; } } } catch (...) { throw std::runtime_error("解析参数错误"); } } }; struct controller; struct controller_action { /** * @brief 执行动作 * @param request */ rttr::variant invoke(http::web_request& request, rttr::variant& variant, rttr::type const& controller_type) { auto http_current_ptr = http_context::current(); //判断contentType std::string content_type = "application/x-www-form-urlencoded"; if (request.find(http::field::content_type) != request.end()) { content_type = std::string(request.find(http::field::content_type)->value().begin(), request.find(http::field::content_type)->value().end()); } if (content_type.find(content_type) == std::string::npos) { throw std::runtime_error("Content-Type:错误!"); } //判断method if (!((method == http_method::GET && request.method() == http::verb::get) || (method == http_method::POST && request.method() == http::verb::post))) { throw std::runtime_error("Method:错误!"); } std::vector find_method; for (auto& it : controller_type.get_methods()) { if (it.get_name() == action_name && it.get_parameter_infos().size() == arguments.size()) { find_method.push_back(it); break; } } if (find_method.empty()) throw std::runtime_error("Action:错误!"); std::vector parameters; std::vector variantParameters; { std::lock_guard locker(mux); for (auto& it : find_method[0].get_parameter_infos()) { arguments[it.get_index()]->parameter(request, it); } for (auto& it : arguments) { variantParameters.push_back(it->parameter_value_); } } for (auto& it : variantParameters) { parameters.push_back(it); } return controller_type.invoke(action_name, variant, parameters); } /** * @brief 身份验证 */ bool authen = true; /** * @brief 未登录跳转 */ std::string authen_jump; /** * @brief 方法名称 */ std::string action_name; /** * @brief 方式 */ http_method method = http_method::GET; /** * @brief 数据类型 */ std::string content_type = "application/x-www-form-urlencoded"; /** * @brief 动作类型 */ std::string action_type; /** * @brief 文件映射 */ std::string file_mapping; /** * @brief 参数 */ std::vector> arguments; /** * @brief 控制器 */ std::shared_ptr parent; private: /** * @brief 锁 */ std::mutex mux; }; struct controller { rttr::variant invoke(http::web_request& request) { std::string key(request.find("http_route_key")->value()); if (!actions.contains(key)) throw std::runtime_error("Action:不存在!"); rttr::type type = rttr::type::get_by_name(controller_name); if (!variant.is_valid()) variant = type.create(); return actions[key]->invoke(request, variant, type); } /** * @brief 控制器名称 */ std::string controller_name; /** * @brief 动作列表 路由 动作 */ std::map> actions; private: /** * @brief 对象 */ rttr::variant variant; }; struct mvc_route { bool contains(http::web_request& req) { std::string key; std::string route(req.find("http_route")->value()); if (contains(route, key)) { std::size_t index = key.find('<'); std::vector params; if (std::string::npos != index) { auto keys = v3::utils::split(key.substr(index), "/"); auto values = v3::utils::split(route.substr(index), "/"); for (int i = 0; i < keys.size() && i < values.size(); ++i) { params.push_back(keys[i].substr(1, keys[i].size() - 2) + "=" + values[i]); } } req.insert("http_route_key",key); req.insert("http_route_parameter", v3::utils::join(params, "&")); return true; } return false; } bool contains(std::string const&route) { std::string key; return contains(route, key); } void add(std::string const& route, std::shared_ptr value) { routes_[route] = value; } std::shared_ptr& operator[](std::string const& route) { std::string key; if (contains(route, key)) { return routes_[key]; } throw std::runtime_error("key does not exist"); } std::shared_ptr& operator[](http::web_request& req) { return this->operator[](std::string(req.find("http_route_key")->value())); } private: bool contains(std::string const& route, std::string& result) { if (routes_.contains(route)) { result = route; return true; } else { int slash_count = std::count(route.begin(), route.end(), '/'); for (auto& [first, second] : routes_) { std::size_t index = first.find('<'); if (std::count(first.begin(), first.end(), '/') == slash_count && index != std::string::npos) { if (first.substr(0, index) == route.substr(0, index)) { result = first; return true; } } } return false; } } private: std::map> routes_; }; struct ico_controller { /** * @brief 注册 */ static void attach(std::shared_ptr controller) { for (auto& [first, second] : controller->actions) { controllers_.add(first, controller); } } /** * @brief * @param url * @return */ static std::shared_ptr resolve(http::web_request& req) { if (controllers_.contains(req)) { return controllers_[req]; } return nullptr; } private: /** * @brief */ static mvc_route controllers_; }; mvc_route ico_controller::controllers_; struct web_base_response { void set_header(std::string_view name, std::string_view value) { auto& response = http_context::current()->response; response.set(name, value); } void set_header(http::field field, std::string_view value) { auto& response = http_context::current()->response; response.set(field, value); } void set_value(std::string_view value) { auto& response = http_context::current()->response; response.body().file().close(); response.body().text() = std::move(value); auto const size = response.body().size(); response.content_length(size); } void set_value(std::vector const& value) { auto& response = http_context::current()->response; response.body().file().close(); response.body().text() = std::string((char*)&value[0], value.size()); auto const size = response.body().size(); response.content_length(size); } void set_status(http::status status = http::status::ok) { auto& response = http_context::current()->response; response.result(status); } std::filesystem::path get_root_directory() { auto& response = http_context::current()->response; return response.get_root_directory(); } http::status get_status() { auto& response = http_context::current()->response; return response.result(); } std::string get_value() { auto& response = http_context::current()->response; return response.body().text(); } void prepare_payload() { auto& response = http_context::current()->response; response.prepare_payload(); } void chunked(bool value) { auto& response = http_context::current()->response; response.chunked(value); } void set_server(std::string_view value = BEAST_VERSION_STRING) { auto& response = http_context::current()->response; response.set(http::field::server, value); } void set_version(int ver = 11) { auto& response = http_context::current()->response; response.version(ver); } void fill_file(std::filesystem::path const& path, http::status status = http::status::ok) { auto& response = http_context::current()->response; if (path.c_str()[0] == '/' || path.c_str()[0] == '\\') { response.fill_file(path, status); } else { response.fill_file("/" + path.string(), status); } } void fill_html(std::string const& html, http::status status = http::status::ok) { auto& response = http_context::current()->response; response.fill_html(html, status); } void fill_json(std::string const& json, http::status status = http::status::ok) { auto& response = http_context::current()->response; response.fill_json(json, status); } void fill_page(std::string const& value, http::status status = http::status::ok) { auto& response = http_context::current()->response; response.fill_page(status, value); } void fill_text(std::string const& text, http::status status = http::status::ok) { auto& response = http_context::current()->response; response.fill_text(text, status); } void fill_jump(std::string const& url) { set_header(http::field::content_type, http::extension_to_mimetype("html")); set_header(http::field::location, url); set_status(http::status::found); } void fill_stream(std::vector const& data, std::string const& extension, http::status status = http::status::ok) { set_header("Content-Type", http::extension_to_mimetype(extension)); set_value(data); set_status(status); } void fill_download(std::filesystem::path const& path, http::status status = http::status::ok) { set_header(http::field::content_type, "application/octet-stream"); set_header(http::field::content_disposition, v3::utils::format("attachment; filename={}", path.filename().string())); } void fill_download(std::filesystem::path const& path, std::string const& filename, http::status status = http::status::ok) { set_header(http::field::content_type, "application/octet-stream"); set_header(http::field::content_disposition, v3::utils::format("attachment; filename={}", filename)); } void fill_view(http::status status = http::status::ok) { fill_file(get_filename(), status); } std::filesystem::path get_filename() { return mvc::http_context::current()->get_controller_action()->file_mapping; } }; struct web_result_response : public web_base_response {}; struct unknown_response : public web_base_response { unknown_response(std::string const& text) { set_value(text); set_status(http::status::unknown); } virtual ~unknown_response() { } operator web_result_response() { return {}; } }; struct jump_response : public web_base_response { jump_response(std::string const& url) { if (!url.empty()) { if (url[0] != '/') fill_jump("/" + url); } else { fill_jump(url); } } virtual ~jump_response() { } operator web_result_response() { return {}; } }; struct text_response : public web_base_response { text_response(std::string const& text, http::status status = http::status::ok) { fill_text(text, status); } virtual ~text_response() { } operator web_result_response() { return {}; } }; struct html_response : public web_base_response { html_response(std::string const& html, http::status status = http::status::ok) { fill_html(html, status); } virtual ~html_response() { } operator web_result_response() { return {}; } }; struct file_response : public web_base_response { file_response() { fill_file(get_filename()); } file_response(http::status status = http::status::ok) { fill_file(get_filename(), status); } file_response(std::filesystem::path const& path, http::status status = http::status::ok) { fill_file(path, status); } virtual ~file_response() { } operator web_result_response() { return {}; } }; struct json_response : public web_base_response { json_response(std::string const& json) { fill_json(json); } json_response(char* json) : json_response(std::string(json)) { } json_response(const char* json) : json_response(std::string(json)) { } template json_response(_Type const& value) { fill_json(v3::json_convert::serialize(value)); } virtual~json_response() { } operator web_result_response() { return {}; } }; struct stream_response : public web_base_response { stream_response(std::vector const& data, std::string const& extension, http::status status = http::status::ok) { fill_stream(data, extension, status); } virtual~stream_response() { } operator web_result_response() { return {}; } }; struct download_response : public web_base_response { download_response(std::filesystem::path const& path, http::status status = http::status::ok) { fill_download(path, status); } download_response(std::filesystem::path const& path, std::string const& filename, http::status status = http::status::ok) { fill_download(path, filename, status); } virtual~download_response() { } operator web_result_response() { return {}; } }; struct view : public web_base_response { virtual ~view() { } view() : web_base_response() { fill_view(); } view(std::string const& path) : web_base_response() { fill_file(path); } template view(_Args&& ...args) { std::filesystem::path path = get_filename(); std::filesystem::path filepath; filepath = http_context::current()->response.get_root_directory(); filepath.make_preferred(); filepath /= path.make_preferred().relative_path(); std::vector value; std::ifstream file(filepath, std::ios::in | std::ios::binary); if (file.is_open()) { file.seekg(0, std::ios::end); int file_size = file.tellg(); file.seekg(0, std::ios::beg); value.resize(file_size); file.read((char*)&value[0], file_size); file.close(); } std::string html(value.begin(), value.end()); auto label_value_list = pseudo_extract_label_value::analysis(html); if (!label_value_list.empty()) { auto classValueList = pseudo_extract_class_value::get_class_value(std::forward<_Args>(args)...); label_value_list[0]->set_class_value(classValueList); html = label_value_list[0]->analysis(); fill_html(html); } } operator web_result_response() { return {}; } }; template struct captcha_response : public v3::mvc::web_base_response { captcha_response(std::string const& session_key = "code") : session_key_(session_key) { } virtual ~captcha_response() { } operator v3::mvc::web_result_response() { //验证码 std::string captcha_code = generate_captcha_digits(); http_context::current()->add_session(session_key_, captcha_code); std::vector img_vec = generate_captcha_image(captcha_code); return v3::mvc::stream_response(img_vec, "bmp"); } private: class CImage : public cimg_library::CImg { public: CImage(const cimg_library::CImg& img) : cimg_library::CImg(img) {} cimg_library::CImg& save_memory_bmp(std::vector& result) { cimg_library::CImg header(54, 1, 1, 1, 0); unsigned char align_buf[4] = {}; const unsigned int align = (4 - (3 * _width) % 4) % 4, buf_size = (3 * _width + align) * height(), file_size = 54 + buf_size; header[0] = 'B'; header[1] = 'M'; header[0x02] = file_size & 0xFF; header[0x03] = (file_size >> 8) & 0xFF; header[0x04] = (file_size >> 16) & 0xFF; header[0x05] = (file_size >> 24) & 0xFF; header[0x0A] = 0x36; header[0x0E] = 0x28; header[0x12] = _width & 0xFF; header[0x13] = (_width >> 8) & 0xFF; header[0x14] = (_width >> 16) & 0xFF; header[0x15] = (_width >> 24) & 0xFF; header[0x16] = _height & 0xFF; header[0x17] = (_height >> 8) & 0xFF; header[0x18] = (_height >> 16) & 0xFF; header[0x19] = (_height >> 24) & 0xFF; header[0x1A] = 1; header[0x1B] = 0; header[0x1C] = 24; header[0x1D] = 0; header[0x22] = buf_size & 0xFF; header[0x23] = (buf_size >> 8) & 0xFF; header[0x24] = (buf_size >> 16) & 0xFF; header[0x25] = (buf_size >> 24) & 0xFF; header[0x27] = 0x1; header[0x2B] = 0x1; result.insert(result.end(), reinterpret_cast(header._data), reinterpret_cast(header._data) + 64); const unsigned char * ptr_r = data(0, _height - 1, 0, 0), * ptr_g = (_spectrum >= 2) ? data(0, _height - 1, 0, 1) : 0, * ptr_b = (_spectrum >= 3) ? data(0, _height - 1, 0, 2) : 0; switch (_spectrum) { case 1: { cimg_forY(*this, y) { cimg_forX(*this, x) { const unsigned char val = (unsigned char)*(ptr_r++); result.push_back(val); result.push_back(val); result.push_back(val); } result.insert(result.end(), reinterpret_cast(align_buf), reinterpret_cast(align_buf) + align); ptr_r -= 2 * _width; } } break; case 2: { cimg_forY(*this, y) { cimg_forX(*this, x) { result.push_back(0); result.push_back((unsigned char)(*(ptr_g++))); result.push_back((unsigned char)(*(ptr_r++))); } result.insert(result.end(), reinterpret_cast(align_buf), reinterpret_cast(align_buf) + align); ptr_r -= 2 * _width; ptr_g -= 2 * _width; } } break; default: { cimg_forY(*this, y) { cimg_forX(*this, x) { result.push_back((unsigned char)(*(ptr_b++))); result.push_back((unsigned char)(*(ptr_g++))); result.push_back((unsigned char)(*(ptr_r++))); } result.insert(result.end(), reinterpret_cast(align_buf), reinterpret_cast(align_buf) + align); ptr_r -= 2 * _width; ptr_g -= 2 * _width; ptr_b -= 2 * _width; } } } return *this; } }; private: //获取验证码 std::string generate_captcha_digits() { static const char data_character[] = "23456789abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ\0"; int min = 0; int max = std::strlen(data_character) - 1; std::string result; for (int i = 0; i < _Length; ++i) { result += data_character[v3::utils::random(min, max)]; } return result; } //生成随机颜色 unsigned char* generate_random_color() { static unsigned char colors[][3] = { {47,79,79}, {100,149,237}, {0,0,205}, {0,191,255}, {30,144,255}, {70,130,180}, {0,206,209}, {0,255,255}, {0,100,0}, {85,107,47}, {46,139,87}, {107,142,35}, {184,134,11}, {218,165,32}, {139,69,19}, {165,42,42}, {255,127,80}, {255,69,0}, {176,48,96}, {160,32,240}, {139,137,137}, {58,95,205}, {16,78,139}, {0,154,205}, {159,182,205}, {0,197,205}, {121,205,205}, {69,139,116}, {67,205,128}, {0,205,102}, {205,155,29}, {205,85,85}, {205,133,63}, {205,38,38}, {205,133,0}, {205,91,69}, {205,79,57}, {205,55,0}, {205,145,158}, {139,28,98}, {180,82,205}, {125,38,205}, {139,123,139}, {0,0,139}, {0,139,139}, {139,0,139}, {139,0,0} }; return colors[v3::utils::random(0, sizeof(colors) / sizeof(colors[0]) - 1)]; } //生成字体大小,位置 std::vector> generate_font_size() { std::vector> result; int s_width = _Width / _Length; int s_height = _Height; for (int i = 0; i < _Length; ++i) { int x = 0, y = 0, s = 0; double font_centre_pos = 0.0; if (s_width > s_height) { s = v3::utils::random(double(s_height) - double(s_height) * 0.2, double(s_height) * 1.2); } else { s = v3::utils::random(double(s_width) - double(s_width) * 0.2, double(s_width) * 1.2); } font_centre_pos = double(s) * 0.8; x = v3::utils::random(0, double(s_width) - font_centre_pos); y = v3::utils::random(0, double(s_height) - font_centre_pos); result.push_back(std::make_tuple(s, x, y)); } return result; } //验证码图片 std::vector generate_captcha_image(std::string const& code) { std::vector result; cimg_library::CImg image(_Width, _Height, 1, 3, 255); // 创建一个白色背景的图像 int s_width = _Width / _Length; int s_height = _Height; std::vector> font_info = generate_font_size(); char c[2] = { 0 }; //绘制验证码 for (int i = 0; i < _Length; ++i) { c[0] = code[i]; image.draw_text(i * s_width + std::get<1>(font_info[i]), std::get<2>(font_info[i]), c, generate_random_color(), 0, 1, std::get<0>(font_info[i])).width(); } //绘制干扰线 for (int i = 0; i < _Line; ++i) { int x1 = std::rand() % _Width; int y1 = std::rand() % _Height; int x2 = std::rand() % _Width; int y2 = std::rand() % _Height; // 生成随机颜色 image.draw_line(x1, y1, x2, y2, generate_random_color()); } //绘制干扰点 for (int i = 0; i < _Spot; ++i) { int x = std::rand() % _Width; int y = std::rand() % _Height; // 生成随机颜色 image.draw_point(x, y, generate_random_color()); } //扭曲图像 cimg_library::CImg distorted_image(_Width, _Height, 1, 3, 255); cimg_forXY(image, xx, yy) { int new_xx = xx + static_cast(1.32 * sin(2 * 3.14 * yy * 0.045)); int new_yy = yy + static_cast(1.42 * sin(2 * 3.14 * xx * 0.062)); if (new_xx >= 0 && new_xx < _Width && new_yy >= 0 && new_yy < _Height) { distorted_image(xx, yy, 0, 0) = image(new_xx, new_yy, 0, 0); distorted_image(xx, yy, 0, 1) = image(new_xx, new_yy, 0, 1); distorted_image(xx, yy, 0, 2) = image(new_xx, new_yy, 0, 2); } } CImage(distorted_image).save_memory_bmp(result); return result; } private: std::string session_key_; }; class http_server_t { public: http_server_t() { server_.bind(std::string("/*"), &http_server_t::on_base_request, this); } bool start(std::string const& ip, int port) { return server_.start(ip, port); } void stop() { server_.stop(); } void set_root_directory(std::filesystem::path const& path) { server_.set_root_directory(path); } protected: virtual void on_request(http::web_request& req, http::web_response& rep) = 0; private: void on_base_request(std::shared_ptr& session_ptr, http::web_request& req, http::web_response& rep) { on_request(req, rep); } private: asio2::http_server server_; }; class https_server_t { public: https_server_t() { server_.bind("/*", &https_server_t::on_base_request, this); } bool start(std::string const& ip, int port) { return server_.start(ip, port); } void stop() { server_.stop(); } void set_root_directory(std::filesystem::path const& path) { server_.set_root_directory(path); } void set_cert_file(std::string const& ca_cert_file, std::string const& private_cert_file, std::string const& private_key_file, std::string const& private_password) { server_.set_cert_file(ca_cert_file, private_cert_file, private_key_file, private_password); } protected: virtual void on_request(http::web_request& req, http::web_response& rep) = 0; private: void on_base_request(std::shared_ptr& session_ptr, http::web_request& req, http::web_response& rep) { on_request(req, rep); } private: asio2::https_server server_; }; template class http_server_base : public _TypeBase { public: http_server_base() : _TypeBase() { } private: virtual void on_request(http::web_request& req, http::web_response& rep)override { http_context_gc http_context(req, rep); try { req.insert("http_route",std::string(req.path()).substr(1)); std::shared_ptr controller = v3::mvc::ico_controller::resolve(req); std::string key(req.find("http_route_key")->value()); http_context::current()->set_controller_action((bool)controller ? controller->actions[key] : nullptr); if (before_.before((bool)controller ? controller->actions[key] : nullptr, req, rep)) { rttr::variant variant = controller->invoke(req); if (variant.is_valid()) { auto response = variant.get_value(); response.set_version(); response.set_server(); response.chunked(true); response.prepare_payload(); } } //文件 else { rep.fill_file(req.path()); rep.set(http::field::cache_control, "max-age=31536000"); rep.chunked(true); } } catch (unknown_response const&) { } catch (jump_response const&) { } catch (text_response const&) { } catch (html_response const&) { } catch (file_response const&) { } catch (json_response const&) { } catch (std::exception const& ec) { err_.error(ec.what(), req, rep); } catch (...) { err_.error("未知异常", req, rep); } after_.after(req, rep); } private: _Before before_; _Err err_; _After after_; }; template using http_server = http_server_base<_Before, _Err, _After, http_server_t>; template using https_server = http_server_base<_Before, _Err, _After, https_server_t>; } } }