 自定HTTP Server服务器
自定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内容,点击右侧的发送按钮,下方日志处会接收到查询的结果集。服务端的数据表格也会显示查询的结果。

