172人参与 • 2024-05-12 • Delphi
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自定义变体类型 |
rest(表征状态转移)是一种从客户端向服务器请求/传输信息,并从服务器返回信息到客户端的策略。
使用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。
无状态意味着每个请求都是独立的。这意味着服务器甚至可以在请求之间重新启动,或者负载均衡器可以将请求转发到不同的实际主机。服务器不维护任何形式的状态表,如会话变量等。
结果是更精简、更高效、更可扩展的服务器或集群。
当一个系统实现了非常接近纯粹rest的东西时,该系统被称为restful系统。
mormot可以使用http/https,但它也适用于使用windows安全模型的命名管道。
它还可以使用另一种不需要使用昂贵的证书及其缓慢握手的加密形式——这是谷歌也曾描述过的一个话题。这对于封闭的mormot客户端(多平台)和服务器系统非常有用。但你仍然可以访问基于标准的更慢、更常见的版本。
mormot允许以下非标准功能来加速对记录的访问。
它们是可选的。
blobs通过单独的事务处理,使用的uri类似于modelroot/tablename/tableid/blobfieldname。
这样做的优点是能够使用高效的二进制传输,而将blobs存储在json中大约需要两倍的空间/数据/时间。
传输方式是指客户端和服务器之间通信使用的方法。目前,mormot支持四种传输方式:
比较这些方法:
进程内(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版本的服务。
以下是如何使用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 可以简单地从最快的选项故障转移到最兼容的选项,只要是被允许的:
要启用 https,您必须在 tsqlhttpserver.create() 构造函数的 ahttpserversecurity 参数中设置 secssl。
您还需要证书。您可以使用本地证书颁发机构或商业机构之一。这些按照 windows iis 的通常方式安装。
mormot 网站包含当前所有操作系统的该过程的最新说明。
aes是一种加密选项,不需要分发ssl的pki对。
首先,积极的一面是:
不利的一面是:
如果你能忍受这些限制,aes是一个很好的选择。
在服务器上,可以通过secsynshaaes启用它。
compressshaaessetkey('gudmw324dajklaf(*\&' ); myserver := tsqlhttpserver.create( '888',[database],'+',usehttpapi,32,secsynshaaes);
在客户端上,你只需将compression属性设置为hcsynshaaes。
compressshaaessetkey('gudmw324dajklaf') myclient.compression := [hcsynshaaes];
你还应该考虑压缩,因为它可以通过减少可用于收集信息的信息内容(例如数据包大小)来提高安全性。
压缩有几个目的:
你可以单独启用一种压缩或另一种压缩。我建议同时启用两者,这样当synlz可用时,mormot将使用synlz进行压缩,否则默认使用deflate,并完全基于标准。
myclient.compression := [hcsynlz,hcdeflate];
以下是一个在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日
您想发表意见!!点此发布评论
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
发表评论