系列入口:编程实战:自己编写HTTP服务器(系列1:概述和应答)-CSDN博客
本文介绍用户功能接口。
目录
一、概述
二、执行接口
三、接口设置
3.1 主要属性
3.2 CWebCommandParam
3.3 添加参数
3.4 生成form
作为通常的执行功能的接口,一般就是三个功能:初始化、执行、退出(清理)。
本web服务器呢,因为要考虑浏览器界面,让用户能从浏览器上直接调用功能,所以还包括了生成用户界面的部分。
整个接口的名称为CWebCommand,后面介绍的代码都是这个类的一部分。
执行接口很简单,只有三个函数:
初始化和卸载很简单。执行接口则有三个参数:请求、应答和socket。
请求和应答很容易理解,为什么要提供socket呢?因为如果页面比较大的话,可能需要提前输出内容,不让用户等得太着急。
如果提前返回一部分应答,那么就无法再添加内容长度头标了,因为投标部分已经发送,不可能再更改,应答对象对此做了检查。
如果用户功能很特殊,仅仅直接使用socket而不使用应答对象也是可以的(也就是说,完全可以当作一个普通socket连接来使用)。
一个示例:
class CWebCommand_SetDebug : public CWebCommand
{
public:
CWebCommand_SetDebug()
{
clear();
AddWebCommandParam("SetDebug", "SetDebug", "1:开启调试/0:关闭调试", "");
SetWebCommand("SetDebug", "设置调试开关", "开启G_IS_DEBUG,注意,开启后产生海量日志");
}
virtual bool doWebFunction(CHttpRequest const * pRequest, CSocket & s, CHttpRespond * pRespond)
{
string param = pRequest->GetParam("SetDebug");
if ("1" == param)
{
G_IS_DEBUG = true;
pRespond->AppendBody("打开调试成功");
}
else if ("0" == param)
{
G_IS_DEBUG = false;
pRespond->AppendBody("关闭调试成功");
}
else
{
pRespond->AppendBody("非法参数,超出限制");
}
return true;
}
};
这个示例很简单,根据参数设置调试开关,完全没有使用socket。构造函数设置了自身的参数,后面详细介绍。初始化和卸载没有使用。
bool isAdmin;//是否是内置页面
bool notonhomepage;//是否不出现在首页
bool hide;//是否隐藏,只能通过直接命令调用
bool AutoRefresh;//是否允许自动刷新
bool NotStdPage;//非标准页面,不生成标准数据,所有数据,包括应答头都由命令自己生成
string command_id;//命令ID,也作为页面名称,但不包括后缀名
string name;//显示名称
string note;
vector params;//命令参数
前面几个bool的含义是很明显的,由框架来控制各种行为。后面ID、名称和说明也很显然。最后的CWebCommandParam是重点,决定了需要用户输入几个参数,这个类会自动生成HTML的form代码。
这个类相当的繁琐,其实就是HTML的基本的form内容,供C++程序员快速生成form表单,如果想要奇幻的效果,可以请前端给做一个css。
//命令接口
class CWebCommandParam
{
public:
string id;//唯一标识
string name;//显示名称
string note;//说明
bool isCheckBox;//是否是选择框
bool isChecked;//是否选中
string defaultvalue;//默认值
bool isNotNull;//是否必填
bool isSelectOnly;//是否只能在选项表选择
bool isHide;//是否隐藏
bool isPassword;//是否是密码输入
bool isReadOnly;//是否只读
long size;//宽度
long rows;//行数,改值不为0则显示为textaera
vector > optionvalue;//选项集合
CWebCommandParam() { clear(); }
void clear()
{
isCheckBox = isChecked = false;
isNotNull = isSelectOnly = isHide = isPassword = isReadOnly = false;
id = name = note = defaultvalue = "";
size = rows = 0;
optionvalue.clear();
}
void SetFormatInput(char const * _id, char const * _name, long _size, long _rows = 0, char const * _default = "", char const * _note = "")
{
clear();
id = _id;
name = _name;
size = _size;
rows = _rows;
defaultvalue = _default;
note = _note;
}
void SetFormatPasswd(char const * _id, char const * _name, long _size, char const * _default = "", char const * _note = "")
{
clear();
isPassword = true;
id = _id;
name = _name;
size = _size;
defaultvalue = _default;
note = _note;
}
void ReadOnly() { isReadOnly = true; }
string toHtmlInputOnly(char const* value, char const* desc, bool canEdit)
{
CWebCommandParam tmp = *this;
tmp.isReadOnly = !canEdit;
return tmp._toHtmlInput(false, false, value, desc, false);
}
string toHtmlInput(bool smallinput = false, bool showParamId = false, string NewDefault = "")
{
return _toHtmlInput(smallinput, showParamId, NewDefault);
}
string _toHtmlInput(bool smallinput = false, bool showParamId = false, string NewDefault = "", char const* desc = NULL, bool showName = true)
{
char buf[10240];
string type;
string ret;
CHTMLEncode encode;
char namestr[256];
if (showName)sprintf(namestr, "%s %s%s", encode(name).c_str(), encode(note).c_str(), (isNotNull ? "*" : ""));
else strcpy(namestr, "");
//从请求更新参数的值
if (0 != NewDefault.size())
{
if (isCheckBox && "1" == NewDefault)isChecked = true;
else defaultvalue = NewDefault;
}
if (isHide)
{
sprintf(buf, ""
, encode(id).c_str(), encode(defaultvalue).c_str());
ret += buf;
return ret;
}
if (showParamId)
{
ret = id + " : ";
}
if (this->isCheckBox)
{
sprintf(buf, "%s
"
, (this->isChecked ? "checked" : ""), this->id.c_str(), this->name.c_str());
ret += buf;
}
else
{
if (isReadOnly && !isPassword)
{
string _desc;
if (NULL == desc || 0 == strlen(desc))_desc = defaultvalue;
else _desc = desc;
sprintf(buf, "%s
%s
"
, namestr, encode(id).c_str(), encode(defaultvalue).c_str(), encode(_desc).c_str());
ret += buf;
}
else
{
if (rows != 0)
{
sprintf(buf, ""
, namestr, (isReadOnly ? "READONLY" : ""), (smallinput ? 16 : size), (smallinput ? 1 : rows), encode(id).c_str(), encode(defaultvalue).c_str());
ret += buf;
}
else
{
if (optionvalue.size() != 0)
{
sprintf(buf, "");
ret += buf;
}
else
{
if (this->isPassword)type = "PASSWORD";
else type = "INPUT";
sprintf(buf, ""
, namestr, (isReadOnly ? "READONLY" : ""), type.c_str(), (smallinput ? 16 : size), encode(id).c_str(), encode(defaultvalue).c_str());
ret += buf;
}
}
}
}
return ret;
}
};
代码没什么奥秘,就是根据参数组合出HTML代码。
为了简化代码,提供了几个快速添加参数的函数:
void AddWebCommandParam(char const * _id, char const * _name, char const * _note, char const * _default, bool isPassword = false, bool isNotNull = false, long size = 16, long rows = 0, vector > * _optionvalue = NULL)
{
CWebCommandParam tmp;
tmp.id = _id;
tmp.name = _name;
tmp.note = _note;
tmp.defaultvalue = _default;
tmp.isPassword = isPassword;
tmp.isNotNull = isNotNull;
tmp.size = size;
tmp.rows = rows;
if (NULL != _optionvalue)tmp.optionvalue = *_optionvalue;
else tmp.optionvalue.clear();
this->params.push_back(tmp);
}
void AddWebCommandParamCheckBox(char const * _id, char const * _name, char const * _note, bool isChecked)
{
CWebCommandParam tmp;
tmp.id = _id;
tmp.name = _name;
tmp.note = _note;
tmp.isCheckBox = true;
tmp.isChecked = isChecked;
this->params.push_back(tmp);
}
由于某种原因生成form被分为两部分,一部分包含form开始和参数,另一部分包含提交和结束:
string toHtmlFormInput(bool smallinput, bool showParamId, CHttpRequest const * pRequest)
{
string str;
if (command_id.size() != 0)
{
str += "\r\n");
}
else
{
if (both)
{
sprintf(buf, ""
"\r\n\r\n"
, name.c_str(), GetFormName().c_str(), name.c_str(), GetFormName().c_str());
}
else
{
sprintf(buf, "
\r\n\r\n"
, name.c_str(), GetFormName().c_str());
}
}
return buf;
}
这两个函数连起来就是一个完整的form表单。
(这里是结束,但不是整个系列的结束)