This commit is contained in:
xx
2025-02-20 00:46:53 +08:00
parent 71328824f5
commit 287243d2ba
10 changed files with 208 additions and 379 deletions

View File

@@ -15,14 +15,6 @@ namespace ylib
int64 start = -1;
int64 count = -1;
};
enum sort {
ASC,
DESC
};
struct order_by {
std::string field;
ylib::sort sort;
};
struct keyvalue {
std::string name;
std::any value;
@@ -71,7 +63,7 @@ namespace ylib
/// <summary>
/// 排序
/// </summary>
select& orderby(const std::string& field, sort sort = DESC);
select& orderby(const std::string& expression);
/// <summary>
/// 查询数量
/// </summary>
@@ -106,7 +98,7 @@ namespace ylib
std::string m_table_name;
std::vector<std::string> m_fields;
ylib::limit m_limit;
order_by m_orderby;
std::string m_orderby;
};
/// <summary>
@@ -157,7 +149,7 @@ namespace ylib
/// <summary>
/// 排序
/// </summary>
update& orderby(const std::string& field, sort sort = DESC);
update& orderby(const std::string& expression);
/// <summary>
/// 查询
/// </summary>
@@ -181,7 +173,7 @@ namespace ylib
std::string m_table_name;
std::vector<struct ylib::update::set> m_sets;
ylib::limit m_limit;
order_by m_orderby;
std::string m_orderby;
};
/// <summary>
@@ -250,7 +242,7 @@ namespace ylib
/// <summary>
/// 排序
/// </summary>
delete_& orderby(const std::string& field, sort sort = DESC);
delete_& orderby(const std::string& expression);
/// <summary>
/// 查询
/// </summary>
@@ -271,7 +263,7 @@ namespace ylib
std::vector<ylib::where> m_wheres;
std::string m_table_name;
ylib::limit m_limit;
order_by m_orderby;
std::string m_orderby;
};
}

View File

@@ -1,104 +1,91 @@
//#pragma once
//#include "http_define.h"
//#if USE_NET_HTTP_WEBSITE
//#include "http_request.h"
//#include "http_response.h"
//#include "http_reqpack.h"
//#include "http_interface.h"
//#define _DBL_MIN 2.2250738585072014e-308 // min positive value
//#define _DBL_MAX 1.7976931348623158e+308 // max value
//#define _INT_MIN (-2147483647 - 1)
//#define _INT_MAX 2147483647
//#define _UINT_MAX 0xffffffff
//#define _LONG_MIN (-2147483647L - 1)
//#define _LONG_MAX 2147483647L
//#define _ULONG_MAX 0xffffffff
//#ifndef _LLONG_MAX
//#define _LLONG_MAX 9223372036854775807
//#endif
//#define _LLONG_MIN (-9223372036854775807 - 1)
//#define _ULLONG_MAX 0xffffffffffffffff
//#define _INT8_MIN (-127 - 1)
//#define _INT16_MIN (-32767 - 1)
//#define _INT32_MIN (-2147483647 - 1)
//#define _INT64_MIN (-9223372036854775807 - 1)
//#define _INT8_MAX 127
//#define _INT16_MAX 32767
//#define _INT32_MAX 2147483647
//#define _INT64_MAX 9223372036854775807
//#define _UINT8_MAX 0xff
//#define _UINT16_MAX 0xffff
//#define _UINT32_MAX 0xffffffff
//#define _UINT64_MAX 0xffffffffffffffff
//#define _FLT_MIN 1.175494351e-38F // min normalized positive value
//#define _FLT_MAX 3.402823466e+38F // max value
//using namespace ylib::network::http;
//
//// 成功回复
//#define REPLY_SUCC rpjson()["code"] = 200;return RT_OK
//// 自定义回复
//#define REPLY(CODE,MSG) rpjson()["code"] = CODE;rpjson()["msg"]=MSG;return RT_OK
//// 失败回复
//#define REPLY_ERROR(MSG) rpjson()["code"] = -1;rpjson()["msg"]=MSG;return RT_OK
///**********************************************************
// * ClassHttp控制器接口
// *********************************************************/
////#define check_qry_json(NAME) request()->parser()->json()[NAME].is_empty()
//#define qry_json_string(NAME) request()->parser()->json()[NAME].to<std::string>(true)
//#define qry_json_uint32(NAME) request()->parser()->json()[NAME].to<uint32>(true)
//#define qry_json_uint64(NAME) request()->parser()->json()[NAME].to<uint64>(true)
//#define qry_json_int32(NAME) request()->parser()->json()[NAME].to<int32>(true)
//#define qry_json_double(NAME) request()->parser()->json()[NAME].to<double>(true)
//#define qry_json_short(NAME) request()->parser()->json()[NAME].to<short>(true)
//#define qry_json_bool(NAME) request()->parser()->json()[NAME].to<bool>(true)
//namespace ylib
//{
// namespace network
// {
// namespace http
// {
// class router;
// class controller :public network::http::interface_
// {
// public:
// controller();
// ~controller();
// network::http::request* request();
// network::http::response* response();
//
// //min和max为<=或>= ,若填-1则不使用该条件
// std::string qry_string(const std::string& name,bool exception = true);
// int32 qry_int32(const std::string& name, bool exception = true);
// uint32 qry_uint32(const std::string& name, bool exception = true);
// int64 qry_int64(const std::string& name, bool exception = true);
// uint64 qry_uint64(const std::string& name, bool exception = true);
// double qry_double(const std::string& name, bool exception = true);
// float qry_float(const std::string& name, bool exception = true);
// bool qry_empty(const std::string& name, bool exception = true);
// bool qry_bool(const std::string& name, bool exception = true);
// ylib::buffer qry_buffer(const std::string& name, bool exception);
//
// // 请求参数
// bool request_param(const std::string& name, std::string& value);
//
// // 获取回复JSON
// inline ylib::json& rpjson() { return response()->sjson["data"]; }
// inline ylib::json& rpcode() { return response()->sjson["code"]; }
// //inline ylib::json& rp() { return response()->sjson["code"]; }
// inline void rp(int code, const std::string& msg = "", const ylib::json& data = ylib::json())
// {
// response()->sjson["code"] = code;
// response()->sjson["msg"] = msg;
// response()->sjson["data"] = data;
// };
//
// void send(const ylib::json& data);
//
// friend class router;
// private:
// network::http::reqpack* m_reqpack = nullptr;
// };
// }
// }
//}
//#endif
#pragma once
#include "http_define.h"
#if USE_NET_HTTP_WEBSITE
#include "http_request.h"
#include "http_response.h"
#include "http_reqpack.h"
#include "http_interface.h"
#define _DBL_MIN 2.2250738585072014e-308 // min positive value
#define _DBL_MAX 1.7976931348623158e+308 // max value
#define _INT_MIN (-2147483647 - 1)
#define _INT_MAX 2147483647
#define _UINT_MAX 0xffffffff
#define _LONG_MIN (-2147483647L - 1)
#define _LONG_MAX 2147483647L
#define _ULONG_MAX 0xffffffff
#ifndef _LLONG_MAX
#define _LLONG_MAX 9223372036854775807
#endif
#define _LLONG_MIN (-9223372036854775807 - 1)
#define _ULLONG_MAX 0xffffffffffffffff
#define _INT8_MIN (-127 - 1)
#define _INT16_MIN (-32767 - 1)
#define _INT32_MIN (-2147483647 - 1)
#define _INT64_MIN (-9223372036854775807 - 1)
#define _INT8_MAX 127
#define _INT16_MAX 32767
#define _INT32_MAX 2147483647
#define _INT64_MAX 9223372036854775807
#define _UINT8_MAX 0xff
#define _UINT16_MAX 0xffff
#define _UINT32_MAX 0xffffffff
#define _UINT64_MAX 0xffffffffffffffff
#define _FLT_MIN 1.175494351e-38F // min normalized positive value
#define _FLT_MAX 3.402823466e+38F // max value
using namespace ylib::network::http;
// 成功回复
#define REPLY_SUCC rpjson()["code"] = 200;return RT_OK
// 自定义回复
#define REPLY(CODE,MSG) rpjson()["code"] = CODE;rpjson()["msg"]=MSG;return RT_OK
// 失败回复
#define REPLY_ERROR(MSG) rpjson()["code"] = -1;rpjson()["msg"]=MSG;return RT_OK
/**********************************************************
* ClassHttp控制器接口
*********************************************************/
//#define check_qry_json(NAME) request()->parser()->json()[NAME].is_empty()
#define qry_json_string(NAME) request()->parser()->json()[NAME].to<std::string>(true)
#define qry_json_uint32(NAME) request()->parser()->json()[NAME].to<uint32>(true)
#define qry_json_uint64(NAME) request()->parser()->json()[NAME].to<uint64>(true)
#define qry_json_int32(NAME) request()->parser()->json()[NAME].to<int32>(true)
#define qry_json_double(NAME) request()->parser()->json()[NAME].to<double>(true)
#define qry_json_short(NAME) request()->parser()->json()[NAME].to<short>(true)
#define qry_json_bool(NAME) request()->parser()->json()[NAME].to<bool>(true)
namespace ylib
{
namespace network
{
namespace http
{
class router;
class controller :public network::http::interface_
{
public:
controller();
~controller();
const std::shared_ptr<network::http::request>& request();
const std::shared_ptr<network::http::response>& response();
// 获取回复JSON
inline ylib::json& rpjson() { return response()->sjson["data"]; }
inline ylib::json& rpcode() { return response()->sjson["code"]; }
//inline ylib::json& rp() { return response()->sjson["code"]; }
inline void rp(int code, const std::string& msg = "", const ylib::json& data = ylib::json())
{
response()->sjson["code"] = code;
response()->sjson["msg"] = msg;
response()->sjson["data"] = data;
};
inline ylib::json rqjson() { return ylib::json::from(request()->body().to_string()); }
void send(const ylib::json& data);
friend class router;
private:
network::http::reqpack* m_reqpack = nullptr;
};
}
}
}
#endif

View File

@@ -17,6 +17,7 @@ namespace ylib
void merge(const std::string& ck);
std::string to_string();
void clear();
std::map<std::string, std::string>& toSTL();
private:
std::map<std::string, std::string> m_param;
};

View File

@@ -19,7 +19,8 @@ namespace ylib
std::regex regex;
std::string pattern;
std::string extra;
std::function<void(network::http::request* request,network::http::response* response,const std::string& pattern,const std::string& extra)> callback;
std::function<void(network::http::request* request, network::http::response* response, const std::string& pattern, const std::string& extra)> callback;
std::function<void(network::http::request* request, network::http::response* response)> callback2;
};
/******************************************************
* class订阅器

View File

@@ -23,7 +23,7 @@ namespace ylib
ylib::buffer de(const std::string& data);
}
namespace url {
std::string en(const ylib::buffer& data);
std::string en(const std::string& data);
ylib::buffer de(const std::string& data);
}
#if 1

View File

@@ -183,10 +183,9 @@ ylib::select& ylib::select::limit(uint32 start, uint32 count)
return *this;
}
ylib::select& ylib::select::orderby(const std::string& field, sort sort)
ylib::select& ylib::select::orderby(const std::string& expression)
{
m_orderby.field = field;
m_orderby.sort = sort;
m_orderby = expression;
return *this;
}
uint64 ylib::select::count()
@@ -238,7 +237,7 @@ void ylib::select::clear()
m_fields.clear();
m_limit.count = -1;
m_limit.start = -1;
m_orderby.field = "";
m_orderby = "";
}
void ylib::select::make_sql(std::string& field_name, std::string& where, std::string& orderby, std::string& limit, std::vector<std::any>& insert_values)
{
@@ -278,9 +277,9 @@ void ylib::select::make_sql(std::string& field_name, std::string& where, std::st
insert_values.push_back(m_limit.start);
insert_values.push_back(m_limit.count);
}
if (m_orderby.field.empty() == false)
if (m_orderby.empty() == false)
{
orderby = " ORDER BY " + m_orderby.field + " " + std::string(m_orderby.sort == DESC ? "DESC" : "ASC");
orderby = " ORDER BY " + m_orderby;
}
}
@@ -373,10 +372,9 @@ ylib::update& ylib::update::limit(uint32 start, uint32 count)
return *this;
}
ylib::update& ylib::update::orderby(const std::string& field, sort sort)
ylib::update& ylib::update::orderby(const std::string& expression)
{
m_orderby.field = field;
m_orderby.sort = sort;
m_orderby = expression;
return *this;
}
@@ -406,7 +404,7 @@ void ylib::update::clear()
m_table_name = "";
m_limit.count = -1;
m_limit.start = -1;
m_orderby.field = "";
m_orderby = "";
m_sets.clear();
}
@@ -463,9 +461,9 @@ void ylib::update::make_sql(std::string& set, std::string& where, std::string& o
insert_values.push_back(m_limit.start);
insert_values.push_back(m_limit.count);
}
if (m_orderby.field.empty() == false)
if (m_orderby.empty() == false)
{
orderby = " ORDER BY " + m_orderby.field + " " + std::string(m_orderby.sort == DESC ? "DESC" : "ASC");
orderby = " ORDER BY " + m_orderby;
}
}
@@ -618,10 +616,9 @@ ylib::delete_& ylib::delete_::limit(uint32 start, uint32 count)
return *this;
}
ylib::delete_& ylib::delete_::orderby(const std::string& field, sort sort)
ylib::delete_& ylib::delete_::orderby(const std::string& exp)
{
m_orderby.field = field;
m_orderby.sort = sort;
m_orderby = exp;
return *this;
}
@@ -650,7 +647,7 @@ void ylib::delete_::clear()
m_table_name = "";
m_limit.count = -1;
m_limit.start = -1;
m_orderby.field = "";
m_orderby = "";
}
void ylib::delete_::make_sql(std::string& where, std::string& orderby, std::string& limit, std::vector<std::any>& insert_values)
@@ -685,8 +682,8 @@ void ylib::delete_::make_sql(std::string& where, std::string& orderby, std::stri
insert_values.push_back(m_limit.start);
insert_values.push_back(m_limit.count);
}
if (m_orderby.field.empty() == false)
if (m_orderby.empty() == false)
{
orderby = " ORDER BY " + m_orderby.field + " " + std::string(m_orderby.sort == DESC ? "DESC" : "ASC");
orderby = " ORDER BY " + m_orderby;
}
}

View File

@@ -583,7 +583,10 @@ bool ylib::network::http::client_plus::request()
m_headers_request.set("Connection", "close");
}
if (m_headers_request.exist("Host") == false)
m_headers_request.set("Host", m_ipaddress);
{
m_headers_request.set("Host", m_ipaddress+(m_port==80?"":(":"+std::to_string(m_port))));
}
}
THeader* pHeader = nullptr;

View File

@@ -1,220 +1,43 @@
///*Software License
//
//Copyright(C) 2024[liuyingjie]
//License Terms
//Usage Rights
//
//Any individual or entity is free to use, copy, and distribute the binary form of this software without modification to the source code, without the need to disclose the source code.
//If the source code is modified, the modifications must be open - sourced under the same license.This means that the modifications must be disclosed and accompanied by a copy of this license.
//Future Versions Updates
//From this version onwards, all future releases will be governed by the terms of the latest version of the license.This license will automatically be nullified and replaced by the new version.
//Users must comply with the terms of the new license issued in future releases.
//Liability and Disclaimer
//This software is provided “as is”, without any express or implied warranties, including but not limited to the warranties of merchantability, fitness for a particular purpose, and non - infringement.In no event shall the author or copyright holder be liable for any claims, damages, or other liabilities, whether in an action of contract, tort, or otherwise, arising from, out of, or in connection with the software or the use or other dealings in the software.
//Contact Information
//If you have any questions, please contact us: 1585346868@qq.com Or visit our website fwlua.com.
//*/
//
//#include "net/http_controller.h"
//#if USE_NET_HTTP_WEBSITE
//
//#include "base/exception.h"
//ylib::network::http::controller::controller()
//{
// m_reqpack = nullptr;
//}
//ylib::network::http::controller::~controller()
//{
//
//}
//
//std::string ylib::network::http::controller::qry_string(const std::string& name, bool exception)
//{
//
// std::string value;
// bool result = request_param(name, value);
//
// if (!result)
// {
// if (exception == true)
// throw ylib::exception("The request parameter '" + name + "' was not found");
// else
// return "";
// }
// return value;
//}
//int32 ylib::network::http::controller::qry_int32(const std::string& name, bool exception)
//{
// std::string _param_value_;
// bool result = request_param(name, _param_value_);
// int32 value = ylib::stoi(_param_value_);
// if (!result)
// {
// if (exception == true)
// throw ylib::exception("The request parameter '" + name + "' was not found");
// else
// return 0;
// }
// if (!strutils::is_num(_param_value_))
// {
// if (exception == true)
// throw ylib::exception("The request parameter '" + name + "' not int32 type");
// else
// return 0;
//
// }
// return value;
//}
//uint32 ylib::network::http::controller::qry_uint32(const std::string& name, bool exception)
//{
// std::string _param_value_;
// bool result = request_param(name, _param_value_);
// uint32 value =ylib::stoi(_param_value_);
// if (!result)
// {
// if (exception == true)
// throw ylib::exception("The request parameter '" + name + "' was not found");
// else
// return 0;
// }
// if (!strutils::is_num(_param_value_))
// {
// if (exception == true)
// throw ylib::exception("The request parameter '" + name + "' not uint32 type");
// else
// return 0;
// }
// return value;
//}
//int64 ylib::network::http::controller::qry_int64(const std::string& name, bool exception)
//{
// std::string _param_value_;
// bool result = request_param(name, _param_value_);
// int64 value = ylib::stoll(_param_value_);
// if (!result)
// {
// if (exception == true)
// throw ylib::exception("The request parameter '" + name + "' was not found");
// else
// return 0;
// }
// if (!strutils::is_num(_param_value_))
// {
// if (exception == true)
// throw ylib::exception("The request parameter '" + name + "' not int64 type");
// else
// return 0;
//
// }
// return value;
//}
//uint64 ylib::network::http::controller::qry_uint64(const std::string& name, bool exception)
//{
// std::string _param_value_;
// bool result = request_param(name, _param_value_);
// uint64 value = ylib::stoull(_param_value_);
// if (!result)
// {
// if (exception == true)
// throw ylib::exception("The request parameter '" + name + "' was not found");
// else
// return 0;
// }
// if (!strutils::is_num(_param_value_))
// {
// if (exception == true)
// throw ylib::exception("The request parameter '" + name + "' not uint64 type");
// else
// return 0;
// }
// return value;
//}
//double ylib::network::http::controller::qry_double(const std::string& name, bool exception)
//{
// std::string _param_value_;
// bool result = request_param(name, _param_value_);
// double value = ylib::stod(_param_value_);
// if (!result)
// {
// if (exception == true)
// throw ylib::exception("The request parameter '" + name + "' was not found");
// else
// return 0.0f;
// }
// return value;
//}
//float ylib::network::http::controller::qry_float(const std::string& name, bool exception)
//{
// std::string _param_value_;
// bool result = request_param(name, _param_value_);
// float value = ylib::stof(_param_value_);
// if (!result)
// {
// if (exception == true)
// throw ylib::exception("The request parameter '" + name + "' was not found");
// else
// return 0.0f;
// }
// return value;
//}
//bool ylib::network::http::controller::qry_empty(const std::string& name, bool exception)
//{
// if (request()->parser()->url_param_exist(name) == false && request()->parser()->body_param_exist(name) == false)
// return false;
// return true;
//}
//bool ylib::network::http::controller::qry_bool(const std::string& name, bool exception)
//{
// std::string _param_value_;
// bool result = request_param(name, _param_value_);
// if (!result)
// {
// if (exception == true)
// throw ylib::exception("The request parameter '" + name + "' was not found");
// else
// return false;
// }
// return _param_value_ == "true";
//}
//ylib::buffer ylib::network::http::controller::qry_buffer(const std::string& name, bool exception)
//{
// network::http::form_info form_info;
// if (request()->parser()->form()->get(name, form_info) == false)
// {
// if (exception == true)
// throw ylib::exception("The request parameter '" + name + " was not found");
// else
// return ylib::buffer();
// }
//
// if (form_info.start != -1 && form_info.length != -1 && form_info.length != 0)
// {
// return m_reqpack->data().sub(form_info.start,form_info.length);
// }
// else
// {
// if (exception == true)
// throw ylib::exception("failed to parse buffer, start=" + std::to_string(form_info.start) + ", length=" + std::to_string(form_info.length));
// else
// return ylib::buffer();
// }
//}
//bool ylib::network::http::controller::request_param(const std::string& name, std::string& value)
//{
// if (request()->parser()->url_param(name, value) == false)
// return request()->parser()->body_param(name, value);
// return true;
//}
//void ylib::network::http::controller::send(const ylib::json& data)
//{
// response()->send(data);
//}
//network::http::request* ylib::network::http::controller::request()
//{
// return m_reqpack->request();
//}
//network::http::response* ylib::network::http::controller::response()
//{
// return m_reqpack->response();
//}
//#endif
/*Software License
Copyright(C) 2024[liuyingjie]
License Terms
Usage Rights
Any individual or entity is free to use, copy, and distribute the binary form of this software without modification to the source code, without the need to disclose the source code.
If the source code is modified, the modifications must be open - sourced under the same license.This means that the modifications must be disclosed and accompanied by a copy of this license.
Future Versions Updates
From this version onwards, all future releases will be governed by the terms of the latest version of the license.This license will automatically be nullified and replaced by the new version.
Users must comply with the terms of the new license issued in future releases.
Liability and Disclaimer
This software is provided “as is”, without any express or implied warranties, including but not limited to the warranties of merchantability, fitness for a particular purpose, and non - infringement.In no event shall the author or copyright holder be liable for any claims, damages, or other liabilities, whether in an action of contract, tort, or otherwise, arising from, out of, or in connection with the software or the use or other dealings in the software.
Contact Information
If you have any questions, please contact us: 1585346868@qq.com Or visit our website fwlua.com.
*/
#include "net/http_controller.h"
#if USE_NET_HTTP_WEBSITE
#include "base/exception.h"
ylib::network::http::controller::controller()
{
m_reqpack = nullptr;
}
ylib::network::http::controller::~controller()
{
}
void ylib::network::http::controller::send(const ylib::json& data)
{
response()->send(data);
}
const std::shared_ptr<network::http::request>& ylib::network::http::controller::request()
{
return m_reqpack->request();
}
const std::shared_ptr<network::http::response>& ylib::network::http::controller::response()
{
return m_reqpack->response();
}
#endif

View File

@@ -82,4 +82,8 @@ void ylib::network::http::cookie::clear()
{
m_param.clear();
}
std::map<std::string, std::string>& ylib::network::http::cookie::toSTL()
{
return m_param;
}
#endif

View File

@@ -160,30 +160,51 @@ ylib::buffer codec::base64::de(const std::string &data)
return result.left(resultLen);
}
std::string codec::url::en(const buffer& data)
std::string codec::url::en(const std::string& data)
{
DWORD resultLen = SYS_GuessUrlEncodeBound((const BYTE*)data.data(),(DWORD)data.length());
if (resultLen <= 0)
return ylib::buffer();
if (resultLen > 8589934592)
return ylib::buffer();
ylib::buffer result;
result.resize(resultLen);
auto rcode = SYS_UrlEncode((BYTE*)data.data(), (DWORD)data.length(), (BYTE*)result.data(), resultLen);
return result.left(resultLen);
std::ostringstream encoded;
for (char c : data) {
// 保留字母、数字和特定符号
if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
encoded << c;
}
else {
// 对其他字符进行编码为 %xx 格式
encoded << '%' << std::uppercase << std::hex << static_cast<int>(static_cast<unsigned char>(c));
}
}
return encoded.str();
}
ylib::buffer codec::url::de(const std::string& data)
ylib::buffer codec::url::de(const std::string& str)
{
DWORD resultLen = SYS_GuessUrlDecodeBound((const BYTE*)data.c_str(), (DWORD)data.length());
if (resultLen <= 0)
return ylib::buffer();
if (resultLen > 8589934592)
return ylib::buffer();
ylib::buffer result;
result.resize(resultLen);
auto rcode = SYS_UrlDecode((BYTE*)data.c_str(), (DWORD)data.length(), (BYTE*)result.data(), resultLen);
return result.left(resultLen);
std::ostringstream decoded;
for (size_t i = 0; i < str.length(); ++i) {
char c = str[i];
if (c == '%') {
// 确保后面至少有两个字符供解析
if (i + 2 >= str.length()) {
throw std::invalid_argument("Invalid URL encoding: incomplete percent-encoded sequence.");
}
// 获取两个十六进制字符
char hex[3] = { str[i + 1], str[i + 2], '\0' };
if (!isxdigit(hex[0]) || !isxdigit(hex[1])) {
throw std::invalid_argument("Invalid URL encoding: invalid hex digits.");
}
// 转换为实际字符
decoded << static_cast<char>(std::strtol(hex, nullptr, 16));
i += 2; // 跳过已处理的两个字符
}
else if (c == '+') {
// '+' 在 URL 编码中表示空格
decoded << ' ';
}
else {
// 保留普通字符
decoded << c;
}
}
return decoded.str();
}