自定HTTP Server服务器
# Smart之自定HTTP Server服务器
- 文档号:S-EQ-DEM-2029
# 1. 说明
采用HTTP协议,建立数据服务器,通过访问对应的API进行数据操作。该示例需与Smart之HTTP Client客户端配合使用。
范例使用的是类rest风格的请求方式,rest api 是前后端分离最佳实践,是开发的一套标准或者说是一套规范。其优点如下:
- 轻量,直接通过http,不需要额外的协议,通常有
post/get/put/delete
操作。 - 面向资源,一目了然,具有自解释性。
- 数据描述简单,一般通过json或者xml做数据通讯。
在使用该范例之前,请先在数据库Smart中找到一个Table_Test
表,创建语句的示例如下:
--创建数据表 Table_Test
Create Table Table_Test(
FCode nvarchar(50) not null,
FName nvarchar(100)
)
GO
--创建表主索引 PK_Table_Test
CREATE UNIQUE NONCLUSTERED INDEX [PK_Table_Test] ON [dbo].[Table_Test]
([FCode] ASC)
WITH (PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF,
SORT_IN_TEMPDB = OFF,
IGNORE_DUP_KEY = OFF,
DROP_EXISTING = OFF,
ONLINE = OFF,
ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
--插入测试用的数据
INSERT INTO Table_Test(FCode,FName)
VALUES('0101','小明')
INSERT INTO Table_Test(FCode,FName)
VALUES('0102','小王')
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
在TARS中建立该数据库的连接设置,使Smart能够连接到该数据库。TARS帐套配置请参阅TARS使用手册。
通过Smart智慧控制平台菜单[工具]
->[帐套设置]
,设置好要连接的TARS中间件之数据库,在使用过程中须保证TARS处于运行状态并开放服务。
通过本范例学习,您可以掌握Smart智慧控制平台作为HTTP服务器的功能。
# 2. 设计明细
开启Smart智慧控制平台,分别加入下插图之控件。或者通过点击菜单栏[文件]-[打开项目]
选择范例项目文件来打开该范例。

①:TImage组件,控件名称为Image1
。
②:TIdHTTPServer组件,控件名称为IdHTTPServer1
。
③:TLabel组件,控件名称为Label2
。
④:TLabel组件,控件名称为Label1
。
⑤:TEdit组件,控件名称为EditPort
。
⑥:TImage组件,控件名称为ImageStart
。
⑦:TImage组件,控件名称为ImageStop
。
⑧:TDBGrid组件,控件名称为DBGrid1
。
⑨:TLabel组件,控件名称为Label3
。
⑩:TSwitchButton组件,控件名称为SwitchButton1
。
(11):TRFDataSet组件,控件名称为RFDataSet1
。
(12):TDataSource组件,控件名称为DataSource1
。
(13):TMemo组件,控件名称为Memo1
。
Main窗体属性设置
BorderStyle
:设置边界样式=bsDialog
。Caption
:设置窗体显示的标题=自定HTTP Server服务器
。ClientHeight
:设置客户区窗体的高度=438
。ClientWidth
:设置窗体客户区的宽度=465
。
①Image1属性设置
Align
:设置控件对齐方式=alClient
。Stretch
:设置图片拉伸适应于控件。Picture
:设置图片。点击Picture
属性右侧的[...]
按钮,打开文件上传界面,点击[Load...]
从文件浏览器中选择对应的图片文件上传,返回该界面下,待显示出图片后点击[OK]
加载图片。
③Label2属性设置
Caption
:设置标签内容=端口:
。Font
:设置字体,双击该属性或者点击属性右侧的[...]
以打开字体设置界面。
④Label1属性设置
Caption
:设置标签内容=自定HTTP Server服务器
。Font
:设置字体,双击该属性或者点击属性右侧的[...]
以打开字体设置界面。
⑤EditPort属性设置
Name
:设置控件名称=EditPort
。NumbersOnly
:只输允许输入数字,设置为True
。Text
:设置文本内容=8806
。Font
:设置字体,双击该属性或者点击属性右侧的[...]
以打开字体设置界面。
⑥ImageStart属性设置
Height
:设置控件高度=32
。Width
:设置控件宽度=32
。Stretch
:设置图片拉伸=True
。Name
:设置控件名称为ImageStart
。Picture
:设置图片。点击Picture
属性右侧的[...]
按钮,打开文件上传界面,点击[Load...]
从文件浏览器中选择对应的图片文件上传,返回该界面下,待显示出图片后点击[OK]
加载图片。
⑦ImageStop属性设置
Height
:设置控件高度=32
。Width
:设置控件宽度=32
。Stretch
:设置图片拉伸=True
。Name
:设置控件名称为ImageStop
。Picture
:设置图片。点击Picture
属性右侧的[...]
按钮,打开文件上传界面,点击[Load...]
从文件浏览器中选择对应的图片文件上传,返回该界面下,待显示出图片后点击[OK]
加载图片。
⑧DBGrid1属性设置
DataSource
:设置数据源=DataSource1
。
⑨Label3属性设置
Caption
:设置标签内容=日志
。Font
:设置字体,双击该属性或者点击属性右侧的[...]
以打开字体设置界面。
⑩SwitchButton1属性设置
Height
:设置控件高度=56
。Width
:设置控件宽度=56
。Stretch
:设置图片拉伸=True
。SwitchOff
:设置处于关闭状态时显示的图片。点击属性右侧的[...]
按钮,打开文件上传界面,点击[Load...]
从文件浏览器中选择对应的图片文件上传,返回该界面下,待显示出图片后点击[OK]
加载图片。
SwitchOn
:设置处于开启状态时显示的图片。点击属性右侧的[...]
按钮,打开文件上传界面,点击[Load...]
从文件浏览器中选择对应的图片文件上传,返回该界面下,待显示出图片后点击[OK]
加载图片。
(12)DataSource1属性设置
DataSet
:设置绑定的数据集=RFDataSet1
。
# 3. 程序设置
# 3.1. 程序初始化设置
程序启动时,数据集控件修改为帐套设置。
constructor TMyHandler.Create(AOwner: TComponent);
begin
FThis :=TBaseForm(AOwner);
FThis.RFDataSet1.Connection := DM.DBConnection;
end;
2
3
4
5
# 3.2. 事件设置
- ⑩SwitchButton1-OnSwitch事件
点击切换信息栏位的显示与关闭。
procedure TMyHandler.SwitchButton1Switch;
//显示日志
begin
FThis.Memo1.Visible := FThis.SwitchButton1.IsChecked;
end;
2
3
4
5
- ②IdHTTPServer-OnCommandGet事件
当服务器接收到请求时,根据发送的命令进行相应的结果输出。
procedure TMyHandler.IdHTTPServer1CommandGet;
//当服务器接收到请求命令时触发,根据发送的命令请求进行相应的结果输出
var
ReportType:String;
APathInfo,SQL:string;
str:String;
export_path,filename:string;
Stream: TStringStream;
begin
Stream := TStringStream.Create;
export_path:='Samples';
if ARequestInfo.Command.ToLower = ('Get').ToLower then
begin
APathInfo := ARequestInfo.Document;
if APathInfo = '' then
Exit;
//Request.Host
// get地址
// http://localhost:12345/counter.jsp?name=zhangsan=123
//
// ARequestInfo.Document 是
// /counter.jsp
filename := ARequestInfo.Document;
FThis.Memo1.Lines.Insert(0, FormatDateTime('HH:NN:SS.ZZZ ', now) + filename);
// if log_path[length(log_path)]<>'\' then log_path := log_path+'\';
// if report_path[length(report_path)]<>'\' then report_path := report_path+'\';
// if export_path[length(export_path)]<>'\' then export_path := export_path+'\';
//获取生成的pdf文件
//export_path+filename;
//设定输出文件的编码类型,避免乱码
if FileExists(export_path + filename) then
begin
//AResponseInfo.ContentType := 'text/html;charset=GB2312'; //解决 Response 返回中文乱码问题
//form1.Memo1.Lines.Add(ExtractFileExt(filename));
//form1.Memo1.Lines.Add(Copy(ExtractFileExt(filename),2,ExtractFileExt(filename).Length));
if ExtractFileExt(filename).ToLower = '.pdf' then
AResponseInfo.ContentType := 'application/pdf;charset=GB2312' //解决 Response 返回中文乱码问题
else if ExtractFileExt(filename).ToLower = '.jpg' then
AResponseInfo.ContentType := 'application/x-jpg;charset=GB2312' //解决 Response 返回中文乱码问题
else if ExtractFileExt(filename).ToLower = '.xls' then
AResponseInfo.ContentType := 'application/x-xls;charset=GB2312' //解决 Response 返回中文乱码问题
else if ExtractFileExt(filename).ToLower = '.txt' then
AResponseInfo.ContentType := 'text/plain;charset=GB2312' //解决 Response 返回中文乱码问题
else if ExtractFileExt(filename).ToLower = '.html' then
AResponseInfo.ContentType := 'text/html;charset=GB2312' //解决 Response 返回中文乱码问题
else if ExtractFileExt(filename).ToLower = '.rtf' then
AResponseInfo.ContentType := 'application/x-rtf;charset=GB2312' //解决 Response 返回中文乱码问题
else if ExtractFileExt(filename).ToLower = '.csv' then
AResponseInfo.ContentType := 'text/csv;charset=GB2312' //解决 Response 返回中文乱码问题
else
AResponseInfo.ContentType := 'application/octet-stream;charset=GB2312'; //解决 Response 返回中文乱码问题
// //AResponseInfo.ContentType := 'application/pdf;charset=GB2312'; //解决 Response 返回中文乱码问题
// //AResponseInfo.ContentType := 'application/'+Copy(ExtractFileExt(filename),2,ExtractFileExt(filename).Length)+';charset=GB2312'; //解决 Response 返回中文乱码问题
// //AResponseInfo.SetCustomHeader('Access-Control-Allow-Origin', '*'); // 允许跨域访问
// //AResponseInfo.CustomHeaders.AddValue('Access-Control-Allow-Origin', '*'); // 允许跨域访问
// //AResponseInfo.CustomHeaders.Add('Access-Control-Allow-Origin:*'); // 允许跨域访问
// //Response.ContentType := 'text/html';
AResponseInfo.CustomHeaders.Add('Access-Control-Allow-Origin:*');
AResponseInfo.CustomHeaders.Add('Access-Control-Allow-Headers:*');
AResponseInfo.CustomHeaders.Add('Access-Control-Allow-Method:*');
AResponseInfo.ContentStream := TFileStream.Create(export_path + filename, $0000 + $0020);
AResponseInfo.WriteContent;
//AResponseInfo.Server:='IIS/6.0';
//AResponseInfo..CacheControl:='no-cache';
//AResponseInfo.Pragma:='no-cache';
//AResponseInfo.Date:=Now;
end
else
begin
AResponseInfo.ContentType := 'application/octet-stream;charset=GB2312'; //解决 AResponseInfo 返回中文乱码问题
AResponseInfo.CustomHeaders.Add('Access-Control-Allow-Origin:*'); // 允许跨域访问
AResponseInfo.ContentText := '404';
AResponseInfo.WriteContent;
end;
end;
if ARequestInfo.Command.ToLower = ('POST').ToLower then
begin
AResponseInfo.CustomHeaders.Add('Access-Control-Allow-Origin:*');
AResponseInfo.CustomHeaders.Add('Access-Control-Allow-Headers:*');
AResponseInfo.CustomHeaders.Add('Access-Control-Allow-Method:*');
APathInfo := ARequestInfo.Document;
//请求路径中包含/opensql则将执行SQL查询功能
if APathInfo = '/opensql' then
begin
Stream.LoadFromStream(ARequestInfo.PostStream);
SQL := Stream.DataString;
if SQL='' then
begin
str := ('{"status":"error","data":' + FThis.siLang1.GetTextW('"SQL内容不能为空!"') + '}');
FThis.Memo1.Lines.Insert(0, FormatDateTime('HH:NN:SS.ZZZ ', now) + FThis.siLang1.GetTextW('SQL内容不能为空!'));
AResponseInfo.ContentText := paxfunc.URLEncode(str);
AResponseInfo.WriteContent;
Exit;
end;
FThis.RFDataSet1.Open(SQL);
FThis.Memo1.Lines.Insert(0, FormatDateTime('HH:NN:SS.ZZZ ', now) + FThis.siLang1.GetTextW('接收到的SQL内容:') +SQL);
//返回查询的数据集结果
str := ('{"status":"ok", ' + dm.DataSetToJSON(FThis.RFDataSet1) + '}');
AResponseInfo.ContentText := paxfunc.URLEncode(str);
AResponseInfo.WriteContent;
end;
end;
Stream.Free;
end;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
- ⑥ImageStart-OnClick事件
点击以开启服务。
procedure TMyHandler.ImageStartClick;
//开启服务
begin
if not FThis.IdHTTPServer1.Active then
begin
try
FThis.IdHTTPServer1.Bindings.Clear;
FThis.IdHTTPServer1.DefaultPort := StrToInt(FThis.EditPort.Text);
FThis.IdHTTPServer1.Active := True;
FThis.Memo1.Lines.Insert(0, FormatDateTime('HH:NN:SS.ZZZ ', now) + 'HTTP服务 已启动。')
except
FThis.Memo1.Lines.Insert(0, FormatDateTime('HH:NN:SS.ZZZ ', now) + 'HTTP服务 启动失败。')
end;
end;
end;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- ⑦ImageStop-OnClick事件
点击以关闭服务
procedure TMyHandler.ImageStopClick;
//停止服务
begin
FThis.IdHTTPServer1.Active := False;
FThis.IdHTTPServer1.Bindings.Clear;
FThis.Memo1.Lines.Insert(0, FormatDateTime('HH:NN:SS.ZZZ ', now) + 'HTTP服务 已停止。');
end;
2
3
4
5
6
7
# 4. 运行结果
通过工具栏保存,将程序保存为 sdb 项目文件。
使用鼠标点击工具栏运行(Run),测试运行结果。在服务器端设置端口号,点击启动按钮,下方显示HTTP服务 已启动
。在客户端处填写服务器的地址端口号信息,填写查询的SQL内容,点击右侧的发送按钮,下方日志处会接收到查询的结果集。服务端的数据表格也会显示查询的结果。
