1、BOM對象 BOM:Broswer object model,即瀏覽器提供我們開發者在javascript用於操作瀏覽器的對象。 1.1、window對象 視窗方法 // BOM Browser object model 瀏覽器對象模型 // js中最大的一個對象.整個瀏覽器視窗出現的所有東西都 ...
mORMot 1.18 第十八章 使用REST/JSON的客戶端/伺服器
JSON是一種被多種語言和眾多領先公司接受的標準。正如我們在JSON章節中所解釋的,它是標準化的,緊湊且解析速度快,同時當加入非關鍵性空格時,也易於人類閱讀。這些事實使其成為數據交換最受歡迎的格式之一。
JSON支持六種數據類型:
JSON類型 | 描述 |
---|---|
數字 | JavaScript中的雙精度浮點數格式,通常取決於具體實現。沒有特定的整數類型 |
字元串 | 雙引號括起來的Unicode,帶有反斜杠轉義 |
布爾值 | true 或 false |
數組 | 一個有序的值序列,以逗號分隔並括在方括弧中;這些值不需要是同一類型 |
對象 | 一個無序的"鍵值對"集合,使用':'字元分隔鍵(Key)和值(Value),這些鍵值對被逗號分隔並包含在大括弧中;其中的鍵必須是字元串且應該各不相同。 |
null | 空值/未定義的值 |
結構性字元包括大括弧{}、中括弧[]、冒號:和逗號,。當你查看示例時,你會發現像電話號碼這樣的複雜格式可以簡單地視為字元串處理。
比如:
{
"firstName": "John",
"lastName": "Smith",
"age": 25,
"address": {
"streetAddress": "21 2nd Street",
"city": "New York",
"state": "NY",
"postalCode": 10021
},
"phoneNumbers": [{
"type": "home",
"number": "212 555-1234"
},
{
"type": "fax",
"number": "646 555-4567"
}
]
}
JSON的預設編碼是UTF8,與SQLite3和EWB相同。這允許存儲和傳輸完整的Unicode字元集在客戶端和伺服器之間。
當我們需要存儲或傳輸二進位BLOBs時,使用Base64編碼。
下表描述了Pascal變數是如何轉換的:
Pascal類型 | 備註 |
---|---|
Boolean | 序列化為JSON布爾值 |
byte, word, integer, cardinal, Int64, single, double, currency | 序列化為JSON數字 |
string, RawUTF8, SynUnicode, WideString | 序列化為JSON字元串 |
DateTime, TTimeLog | 序列化為JSON文本,編碼為ISO 8601 |
RawByteString | 序列化為JSON的null或Base64編碼的JSON字元串 |
RawJSON | 存儲為未序列化的原始JSON內容(例如任何值、對象或數組) |
TGUID | GUID序列化為JSON文本 |
嵌套記錄 | 序列化為JSON對象,標識為record ... end; 或 { ... },包含其嵌套定義 |
嵌套註冊記錄 | 序列化為與定義的回調相對應的JSON |
記錄的動態數組 | 序列化為JSON數組,標識為array of ... 或 [ ... ] |
簡單類型的動態數組 | 序列化為JSON數組,例如標識為array of integer |
靜態數組 | 序列化為JSON數組,通過增強的RTTI處理,尚未通過文本定義處理 |
Variant | 序列化為JSON,完全支持TDocVariant自定義變體類型 |
18.1 REST
REST(表徵狀態轉移)是一種從客戶端向伺服器請求/傳輸信息,並從伺服器返回信息到客戶端的策略。
- 一切都是資源
- 每個資源都通過一個唯一標識符(URI中的I)來標識
- 使用簡單且統一的介面
- 通過表徵進行通信
- 每個請求都是無狀態的
使用REST時,你會用到URI(唯一資源標識符)來標識資源。
例如:
客戶 | 數據URI |
---|---|
獲取名為“dupont”的客戶的詳細信息 | http://www.mysite.com/Customer/dupont |
獲取名為“smith”的客戶的詳細信息 | http://www.mysite.com/Customer/smith |
獲取名為“dupont”的客戶所下的訂單 | http://www.mysite.com/Customer/dupont/Orders |
獲取名為“smith”的客戶所下的訂單 | http://www.mysite.com/Customer/smith/Orders |
實際上,叫“Dupont”和“Smith”的人很多,所以通常人們會使用像客戶編號、十六進位值或GUID這樣的唯一ID。
CRUD操作的介面包括POST、GET、PUT和DELETE。
HTTP方法 | 操作 |
---|---|
GET | 列出集合中的一個或多個成員 |
PUT | 更新集合中的一個成員 |
POST | 在集合中創建一個新條目 |
DELETE | 刪除集合中的一個成員 |
表徵是我們描述對象的方式,通常使用JSON或XML來完成。
XML表徵:
<Customer>
<ID>1234</ID>
<Name>Dupond</Name>
<Address>Tree street</Address>
</Customer>
相同的JSON表徵:
{"Customer": {"Name":"Dupond", "Address":"Tree street"}}
使用XML或JSON通過POST添加記錄將返回剛剛創建的ID。
無狀態意味著每個請求都是獨立的。這意味著伺服器甚至可以在請求之間重新啟動,或者負載均衡器可以將請求轉發到不同的實際主機。伺服器不維護任何形式的狀態表,如會話變數等。
結果是更精簡、更高效、更可擴展的伺服器或集群。
18.2 RESTful mORMot
當一個系統實現了非常接近純粹REST的東西時,該系統被稱為RESTful系統。
mORMot可以使用HTTP/HTTPS,但它也適用於使用Windows安全模型的命名管道。
它還可以使用另一種不需要使用昂貴的證書及其緩慢握手的加密形式——這是谷歌也曾描述過的一個話題。這對於封閉的mORMot客戶端(多平臺)和伺服器系統非常有用。但你仍然可以訪問基於標準的更慢、更常見的版本。
mORMot允許以下非標準功能來加速對記錄的訪問。
它們是可選的。
- LOCK,用於鎖定集合中的一個成員;
- UNLOCK,用於解鎖集合中的一個成員;
- BEGIN,用於啟動事務;
- END,用於提交事務;
- ABORT,用於回滾事務。
BLOBs通過單獨的事務處理,使用的URI類似於ModelRoot/TableName/TableID/BlobFieldName。
這樣做的優點是能夠使用高效的二進位傳輸,而將BLOBs存儲在JSON中大約需要兩倍的空間/數據/時間。
18.3 傳輸方式的選擇
傳輸方式是指客戶端和伺服器之間通信使用的方法。目前,mORMot支持四種傳輸方式:
- 進程內通信——在同一進程內部進行最高速度的訪問。
- Windows消息——在同一臺機器上的進程之間進行非常快速的通信。與進程內通信相比,這種方式略有開銷。非常適合少數客戶端,但不具備擴展性。
- 命名管道——在同一臺或不同的Windows機器上的兩個進程之間進行快速通信。適合工作組使用,但擴展性不好。
- HTTP/HTTPS——在Internet或私有內網上的任意兩台電腦之間進行相當快速的通信。使用標準技術。在win32中可以擴展到50,000多個,在64位模型中可以擴展到更多。
比較這些方法:
進程內(In-process) | Windows消息(Messages) | 命名管道(Named Pipe) | HTTP/HTTPS | |
---|---|---|---|---|
實現單元(Unit) | mormot.pas | mormot.pas | mormot.pas | |
速度(Speed) | 最快(fastest) | 極快(extremely fast) | 很快(very fast) | 快(fast) |
擴展性(Scaling) | 最佳(受限於RAM)(Best limited by RAM) | 較差(例如10)(poor eg. 10) | 較差(poor) | 非常好(Very good) |
托管(Hosting) | 單一進程(one process) | 單一電腦(one computer) | 區域網/互聯網(LAN/Internet) | 內網(intranet) |
協議(Protocol) | 方法調用(method call) | WM_COPYDATA | \\pc\mOrmot | https://pc/... |
數據(Data) | JSON | JSON | JSON | JSON |
服務(Service) | 是(Yes) | 否(No) | 是(Yes) | 是(Yes) |
客戶端(Client) | TSQLRestClientDB | TSQL | ||
伺服器(Server) | TSQLRestServerDB | TSQLRestServerDB | TSQLRestServerDB | TSQLRestServerDB |
ExportServer | ExportServerMessage | ExportServer NamedPipe | TSQLHttp |
Windows消息版本通常不太實用,因為包括伺服器進程在內的所有應用程式必須處於圖形用戶界面(GUI)模式,並且都在同一臺機器上運行。
命名管道在通信中曾很受歡迎,但從Windows Vista開始,對命名管道的區域網訪問預設是關閉的,因此您必須手動啟用它。
HTTP/HTTPS是一個很好的通用解決方案。它被普遍接受,具有良好的擴展性,並且已經過每台伺服器超過50,000個連接的測試。通信速度仍然很快。
請註意,甚至可以同時使用多種協議。例如,使用進程內通信來完成某些任務,同時也提供區域網或HTTP版本的服務。
18.4 HTTP/HTTPS 傳輸
以下是如何使用HTTP初始化資料庫的方法。
Model := CreateSampleModel;
DBServer := TSQLRestServerDB.Create( Model,ChangeFileExt( paramstr(0), '.db3'),true);
DBServer.CreateMissingTables;
HttpServer := TSQLHttpServer.Create('8080', [DBServer],'+', HTTP_DEFAULT_MODE);
通常,您還可以使用以下行允許跨站點的AJAX查詢:
HttpServer.AccessControlAllowOrigin := '*'; // 允許跨站點AJAX查詢
您通常會使用以下設置進行功能變數名稱重定向:
HttpServer.DomainHostRedirect('project.com','root'); // 'root' 是當前的 Model.Root
HttpServer.DomainHostRedirect('blog.project.com','root/blog'); // MVC 應用程式
包含三種類型的伺服器,但幸運的是,mORMot 可以簡單地從最快的選項故障轉移到最相容的選項,只要是被允許的:
- THttpApiServer - 使用 Windows 的高速內部內核模式 http.sys 驅動程式,並且如果您選擇,它還可以實現用於 TLS/SSL 安全通信的 HTTPS。這應該是您的首選。
- THttpServer 是基於套接字或 WebSockets 的實現。
18.5 HTTPS
要啟用 HTTPS,您必須在 TSQLHttpServer.Create() 構造函數的 aHttpServerSecurity 參數中設置 secSSL。
您還需要證書。您可以使用本地證書頒發機構或商業機構之一。這些按照 Windows IIS 的通常方式安裝。
mORMot 網站包含當前所有操作系統的該過程的最新說明。
18.6 AES加密 - HTTPS的替代方案
AES是一種加密選項,不需要分發SSL的PKI對。
首先,積極的一面是:
- 它使用了標準的SHA-256和AES-256/CTR演算法,這些演算法都很好。這意味著沒有共用密碼的人閱讀數據流將無法攔截查詢或結果,也無法製造虛假的查詢或結果。
- 它的速度也比HTTPS快。
不利的一面是:
- AES在客戶端和伺服器之間使用共用密鑰,如果密鑰被泄露,所有數據都可以通過適當的嗅探技術被讀取。
- PKI的作用不僅僅是防止中間人查看數據,它還保證你正在連接的是你請求的伺服器。如果有人知道這個秘密,他可能會製造一些極有可能會成功的攻擊。
- 雖然該演算法是標準的,但它的應用卻不是。非mORMot編譯的客戶端無法使用此技術。
如果你能忍受這些限制,AES是一個很好的選擇。
在伺服器上,可以通過secSynShaAes啟用它。
CompressShaAesSetKey('Gudmw324daJKLAF(*\&' );
MyServer := TSQLHttpServer.Create(
'888',[DataBase],'+',useHttpApi,32,secSynShaAes);
在客戶端上,你只需將Compression屬性設置為hcSynShaAes。
CompressShaAesSetKey('Gudmw324daJKLAF')
MyClient.Compression := [hcSynShaAes];
你還應該考慮壓縮,因為它可以通過減少可用於收集信息的信息內容(例如數據包大小)來提高安全性。
18.7 壓縮
壓縮有幾個目的:
- 減少慢速鏈接上的數據傳輸時間;
- 降低昂貴鏈接(企業上行鏈路、手機數據費用)上的數據成本;
- 混淆數據...尤其是與上述加密結合使用時。mORMot支持兩種加密方法。
- Deflate - 一種基於ZIP的標準壓縮技術。它的缺點是它有點CPU密集型,但也能達到最好的效果;
- SynLZ - 一種專有的方法,對伺服器CPU負載更輕。它只適用於mORMot編碼的客戶端,不適用於EWB或其他人的客戶端。
你可以單獨啟用一種壓縮或另一種壓縮。我建議同時啟用兩者,這樣當SynLZ可用時,mORMot將使用SynLZ進行壓縮,否則預設使用Deflate,並完全基於標準。
MyClient.Compression := [hcSynLZ,hcDeflate];
18.8 示例
以下是一個在8080埠上實現HTTP協議的極簡資料庫伺服器。
program cssimpleserver;
{$APPTYPE CONSOLE}
uses
SysUtils,
SynCommons, mORMot, mORMotSQLite3, SynSQLite3Static, // 引入必要的單元
mORMotHttpServer,
csclass in 'csclass.pas';
var
Model: TSQLModel; // SQL模型變數
DB: TSQLRestServerDB; // 資料庫變數
Server: TSQLHttpServer; // HTTP伺服器變數
s: string; // 字元串變數
procedure Start;
begin
Model := CreateSampleModel; // 創建樣本模型
DB := TSQLRestServerDB.Create(Model, 'd:\cstest.db3', true); // 創建資料庫連接
DB.CreateMissingTables; // 創建缺失的表
Server := TSQLHttpServer.Create('8080', [DB], '+', HTTP_DEFAULT_MODE); // 在8080埠上創建HTTP伺服器
Server.AccessControlAllowOrigin := '*'; // 設置跨域資源共用策略,允許任何來源的請求
end;
procedure Stop;
begin
Server.Free; // 釋放HTTP伺服器資源
DB.Free; // 釋放資料庫資源
Model.Free; // 釋放模型資源
end;
begin
try
Start; // 啟動伺服器
writeln('按"Enter回車"鍵退出'); // 提示用戶按回車鍵退出
readln(s); // 讀取用戶輸入,等待用戶按下回車鍵
except
on E: Exception do // 異常處理
writeln(E.ClassName, ': ', E.Message); // 輸出異常類型和異常信息
end;
Stop; // 停止伺服器並釋放資源
end.
註釋說明:
TSQLModel
:表示資料庫模型的類,定義了資料庫的結構和關係。TSQLRestServerDB
:表示基於REST的資料庫伺服器類,用於處理資料庫的CRUD操作。TSQLHttpServer
:表示HTTP伺服器類,用於監聽HTTP請求並返迴響應。CreateSampleModel
:是一個自定義函數,用於創建一個示例的資料庫模型。DB.CreateMissingTables
:調用此方法會創建在資料庫中缺失的表,這些表是基於Model
定義的。Server.AccessControlAllowOrigin
:設置HTTP響應頭Access-Control-Allow-Origin
,用於控制哪些源可以訪問該資源,'*'
表示允許任何源訪問。readln(s)
:用於等待用戶輸入,直到用戶按下回車鍵,程式才會繼續執行。這裡主要是為了讓程式持續運行,直到用戶主動停止。Stop
過程中釋放資源的順序很重要,通常先釋放依賴其他資源的對象(如Server
),再釋放被依賴的資源(如DB
和Model
)。
它使用了一個小的TSQLRecord派生類:
program cssimpleserver;
{$APPTYPE CONSOLE}
uses
System.SysUtils,
SynCommons, mORMot, mORMotSQLite3, SynSQLite3Static, // 引入必要的單元
mORMotHttpServer,
csclass in 'csclass.pas';
var
Model: TSQLModel; // SQL模型變數
DB: TSQLRestServerDB; // 資料庫變數
Server: TSQLHttpServer; // HTTP伺服器變數
s: string; // 字元串變數
procedure Start;
begin
Model := CreateSampleModel; // 創建樣本模型
DB := TSQLRestServerDB.Create(Model, 'd:\cstest.db3', true); // 創建資料庫連接
DB.CreateMissingTables; // 創建缺失的表
Server := TSQLHttpServer.Create('8080', [DB], '+', HTTP_DEFAULT_MODE); // 在8080埠上創建HTTP伺服器
Server.AccessControlAllowOrigin := '*'; // 設置跨域資源共用策略,允許任何來源的請求
end;
procedure Stop;
begin
Server.Free; // 釋放HTTP伺服器資源
DB.Free; // 釋放資料庫資源
Model.Free; // 釋放模型資源
end;
begin
try
Start; // 啟動伺服器
writeln('press return to exit'); // 提示用戶按回車鍵退出
readln(s); // 讀取用戶輸入,等待用戶按下回車鍵
except
on E: Exception do // 異常處理
writeln(E.ClassName, ': ', E.Message); // 輸出異常類型和異常信息
end;
Stop; // 停止伺服器並釋放資源
end.
註釋說明:
TSQLModel
:表示資料庫模型的類,定義了資料庫的結構和關係。TSQLRestServerDB
:表示基於REST的資料庫伺服器類,用於處理資料庫的CRUD操作。TSQLHttpServer
:表示HTTP伺服器類,用於監聽HTTP請求並返迴響應。CreateSampleModel
:是一個自定義函數,用於創建一個示例的資料庫模型。DB.CreateMissingTables
:調用此方法會創建在資料庫中缺失的表,這些表是基於Model
定義的。Server.AccessControlAllowOrigin
:設置HTTP響應頭Access-Control-Allow-Origin
,用於控制哪些源可以訪問該資源,'*'
表示允許任何源訪問。readln(s)
:用於等待用戶輸入,直到用戶按下回車鍵,程式才會繼續執行。這裡主要是為了讓程式持續運行,直到用戶主動停止。Stop
過程中釋放資源的順序很重要,通常先釋放依賴其他資源的對象(如Server
),再釋放被依賴的資源(如DB
和Model
)。
這個客戶端也很簡單。
{$APPTYPE CONSOLE}
uses
System.SysUtils,
SynCommons, mORMot, mORMothttpclient, // 引入必要的單元
csclass in 'csclass.pas';
var
Model: TSQLModel; // 定義SQL模型變數
DB: TSQLHttpClient; // 定義HTTP客戶端資料庫連接變數
s: string; // 定義字元串變數
procedure Start;
var
Server: AnsiString; // 定義伺服器地址變數
begin
if ParamCount = 0 then // 如果沒有傳入命令行參數
Server := 'localhost' // 則預設伺服器為本地
else
Server := AnsiString(Paramstr(1)); // 否則取第一個命令行參數為伺服器地址
Model := CreateSampleModel; // 創建樣本模型
DB := TSQLHttpClient.Create(Server, '8080', Model); // 創建HTTP客戶端資料庫連接
DB.SetUser('User', 'synopse'); // 設置資料庫用戶
end;
procedure Stop;
begin
DB.Free; // 釋放資料庫連接
Model.Free; // 釋放模型
end;
// 讀取指定用戶的記錄
procedure readone(user: string);
var
rec: TSQLSampleRecord;
res: string;
begin
try
rec := TSQLSampleRecord.Create(DB, 'Name = ?', [StringToUTF8(user)]);
if rec.ID = 0 then
res := 'Not found'
else
res := UTF8ToString(rec.Question);
writeln('Question for ', user, ' is ', res);
finally
rec.Free;
end;
end;
// 寫入指定用戶的記錄
procedure writeone(user, Question: string);
var
rec: TSQLSampleRecord;
begin
try
rec := TSQLSampleRecord.Create;
rec.Name := StringToUTF8(user);
rec.Question := StringToUTF8(Question);
if DB.Add(rec, True) = 0 then
writeln('ERROR: adding message to db!')
else
writeln('message added.')
finally
rec.Free;
end;
end;
var
user: string; // 定義用戶名字元串變數
begin
try
Start; // 啟動程式
user := 'erick'; // 設置用戶名為erick
readone(user); // 讀取用戶記錄
writeone(user, 'Happy Day'); // 寫入用戶記錄
readone(user); // 再次讀取用戶記錄
except
on E: Exception do // 異常處理
writeln(E.ClassName, ': ', E.Message); // 輸出異常類名和異常信息
end;
Stop; // 停止程式
end.
註釋說明:
TSQLModel
是一個代表資料庫模型的類,用於定義資料庫的結構。TSQLHttpClient
是一個用於與HTTP伺服器通信的客戶端類,它實現了通過HTTP協議與伺服器進行數據交換的功能。CreateSampleModel
是一個創建樣本模型的函數,它返回一個TSQLModel
實例,該實例包含了資料庫中所有表的結構信息。readone
函數用於從資料庫中讀取指定用戶的記錄,並輸出相關信息。writeone
函數用於向資料庫中寫入指定用戶的記錄,並輸出操作結果。- 在
try...except
塊中調用Start
、readone
、writeone
和Stop
等函數,以確保在程式運行過程中出現異常時能夠正常處理並釋放資源。同時,通過輸出異常信息來幫助定位問題所在。 - 程式首先通過
Start
函數初始化資料庫連接和模型,然後通過調用readone
和writeone
函數來讀取和寫入用戶記錄,最後在Stop
函數中釋放資源並結束程式運行。
註意:本文由hieroly翻譯於2024年04月26日