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 8601RawByteString序列化为JSON的null或Base64编码的JSON字符串RawJSON存储为未序列化的原始JSON内容(例如任何值、对象或数组)TGUIDGUID序列化为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.pasmormot.pasmormot.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\mOrmothttps://pc/...数据(Data)JSONJSONJSONJSON服务(Service)是(Yes)否(No)是(Yes)是(Yes)客户端(Client)TSQLRestClientDBTSQL服务器(Server)TSQLRestServerDBTSQLRestServerDBTSQLRestServerDBTSQLRestServerDBExportServerExportServerMessageExportServer NamedPipeTSQLHttpWindows消息版本通常不太实用,因为包罗服务器历程在内的所有应用步伐必须处于图形用户界面(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日
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |