自定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內容,點選右側的發送按鈕,下方日誌處會接收到查詢的結果集。服務端的數據表格也會顯示查詢的結果。