ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

TMS XData 让 Delphi 12.3 快速暴露实体为 REST API

TMS XData 让 Delphi 12.3 快速暴露实体为 REST API 简介面向Delphi 12.3开发者的TMS XData v5.21.0.0控件包是一套基于REST架构的数据访问框架用于快速构建可提供增删改查接口的服务端应用解决网络服务开发中架构选型与组件集成的常见痛点。压缩包为7z格式体积仅3.2MB共582个文件包含249个pas源代码、33个dpr工程文件和29个dpk包文件用于编译安装76个dfm窗体布局文件37个html说明页面及chm编译帮助另有css/js等前端资源与少量pdf/txt说明。整体并非仅仅是库文件而是完整的组件工程与文档集合。目前已有67人学习下载对于正在选型或准备上手TMS XData的Delphi开发者具有直接参考价值。通过249个pas源码可深入理解组件内部实现借助dpr/dpk示例工程和chm帮助手册能快速建立RESTful服务支持JSON/XML数据交换包内html/css/js等Web资源可用于前端联调配合dfm窗体布局文件可降低界面与接口的对接成本。无论初学者还是资深Delphi程序员都能减少底层网络通信的重复劳动更专注于业务逻辑显著缩短数据服务开发周期。1. TMS XData v5.21.0.0在 Delphi 12.3 里让业务模型直接长出 REST API 的控件做 Delphi 后端的人应该都碰过这种局面Aurelius 实体已经写好了数据库表也定完了但前端要调一个GET /customers还得单独搭一套 HTTP 服务写路由、写 JSON 序列化、写鉴权折腾半天。TMS XData v5.21.0.0 就是专门解决这个重复劳动的控件集它基于 Delphi 12.3 编译链直接把 Aurelius ORM 实体暴露成标准 REST/JSON 资源自动生成 CRUD 端点顺带把 JWT 鉴权、CORS、日志这些服务端基础设施也放进组件里。适合已经用 Delphi 做业务系统、需要把数据开放给 Web/H5/移动端复用的团队也适合想在不换技术栈的前提下做轻量中台的场景。2. 环境与安装先让 Delphi 12.3 认识 TMS XData v5.21.0.02.1 版本匹配为什么是 v5.21.0.0 而不是手写 HTTP 服务先说结论TMS XData v5.21.0.0 这份资源是按 Delphi 12.3 Athens 编译的项目的源码路径、BPL 包名、设计期注册逻辑都对齐了这一代 IDE。如果你还在用 Delphi 10.3 或 11直接装大概率会报DCU版本不匹配或者找不到System.Net的某个类型这不是资源有问题而是编译工具链不匹配后面避坑部分会详细说。那为什么不手写一个 WebBroker 服务我早年也这么干过在TWebModule里自己解析Request.PathInfo、手写Response.Content再处理 URL 编码和日期格式。短期内接口少还能扛一旦实体超过十个每个实体都要写 CRUD 路由、参数校验、分页、排序代码量翻着倍走而且所有接口的 JSON 格式靠人肉保证前端稍微挑点格式问题就得来回返工。XData 在这个场景下更像一个资源映射器实体类就是 API 资源属性就是字段路由、序列化、HTTP 方法映射全部自动完成。2.2 安装步骤解压、加库路径、编译安装拿到的是 .7z 压缩包不是 installer所以安装要做几步手工操作但每一步都可控。先把压缩包释放到一个不带空格的目录我的习惯是放D:\Libs\tms\xdata_521不建议丢到系统盘或者带中文名的路径Delphi 的库路径对中文路径支持一直不太稳定。接下来打开 Delphi 12.3做三件事步骤操作说明1Tools Options Environment Variables里新增变量变量名TMS_XDATA指向解压根目录后续库路径可以直接引用2Tools Options Delphi Options Library Library Path追加源码路径把$(TMS_XDATA)\source或对应版本目录加进去让 IDE 能索引到单元文件3打开包工程组并顺序编译在packages目录下找到 Delphi 12 对应的.groupproj先编译运行时包.dpk再编译设计期包并右键Install注意第三步的顺序很关键XData 的运行时包依赖 Aurelius 的运行时包如果直接把设计期包拿去 Install会因为找不到底层 DCU 报错。正确顺序是从下往上编译先Aurelius再XData核心最后才是设计期注册包。看到Design package installed的提示并且 Tool Palette 里出现TMS XData分类就说明装上了。2.3 验证安装用自带 Demo 建立第一个服务器XData 安装包通常会带示例工程路径一般在demos或samples目录下。优先找名字带WebModule或者Server的示例打开后编译运行浏览器访问http://localhost:8080/tms/xdata/info如果返回一段 JSON列出 XData 版本号和服务状态就说明整条链路是通的。这里有个关键的验证点/tms/xdata是 XData 的默认根路径后面跟的info是框架自带的服务信息接口。如果浏览器访问能看到类似product : TMS XData的字段说明不仅是组件装好了连接池和请求分发也正常。如果这一步就 404优先查端口是不是被别的服务占了再查 WebModule 有没有被正确加载。3. 模型先行用 Aurelius 实体把数据表变成 REST 资源3.1 数据模型即 APIXData 如何做映射XData 的核心设计理念是模型即接口它不做独立的接口编排层而是直接读取 Aurelius 实体的元数据。实体类上的[Entity]、属性上的[Column]这些注解在运行期被 XData 翻译成资源的名称、字段的 JSON 键名和类型。也就是说你写实体类的时候接口文档其实已经同步确定了。这意味着两件事第一字段名、类型、是否可空都会直接影响 API 表现设计实体类时要站在前端视角想这个字段能不能为 null、应该返回字符串还是整数第二XData 端点的资源名默认是实体类名的复数形式比如TCustomer对应的资源路径就是/customers如果你在代码里写的是TUserInfo那路径就是/userInfo大小写和复数化规则要心里有数否则前端按文档调不到。3.2 写一个 TCustomer 实体注解、主键与默认命名下面是最小可运行的实体定义以客户表为例unit uCustomer; interface uses Aurelius.Mapping.Attributes; type [Entity] [Table(Customers)] TCustomer class private [Column(Id, [TColumnProp.Identity])] FId: Integer; [Column(Name, [])] FName: string; [Column(Email, [])] FEmail: string; public property Id: Integer read FId write FId; property Name: string read FName write FName; property Email: string read FEmail write FEmail; end; implementation end.这段代码里有两个容易忽略的参数TColumnProp.Identity表示主键自增XData 在生成POST /customers接口时会认为不需要客户端传入 Id而PUT /customers(1)则会要求 URL 里的(1)与实体主键匹配。如果主键属性没标Identity创建资源的接口就要求你在 JSON Body 里显式传主键行为完全不同。3.3 多表和关联关系Master-Detail 资源怎么暴露实际业务很少只有单表XData 对关联关系的处理是实体属性直接引用另一个实体类JSON 序列化时会输出嵌套对象。举个例子订单和客户的关系type [Entity] [Table(Orders)] TOrder class private [Column(Id, [TColumnProp.Identity])] FId: Integer; [Column(CustomerId, [])] FCustomerId: Integer; [Column(TotalAmount, [])] FTotalAmount: Double; FCustomer: TCustomer; public property Id: Integer read FId write FId; property CustomerId: Integer read FCustomerId write FCustomerId; property TotalAmount: Double read FTotalAmount write FTotalAmount; [Association([TAssociationProp.LazyLoading])] property Customer: TCustomer read FCustomer write FCustomer; end;注意Association注解上的LazyLoading参数。我一般会默认打开懒加载这样查询订单列表时不会把每个订单的客户对象都带出来JSON 体积会小很多。前端需要客户详情时再单独调/customers(2)取数据这也是 REST 接口更合理的使用方式。如果不加懒加载订单量大时响应会明显变慢这是新手最容易踩的坑。4. 把 CRUD 暴露成 APIXData 端点、筛选参数和自定义业务方法4.1 自动 CRUD 端点GET/POST/PUT/DELETE 映射规则实体类编译进去之后XData 会自动注册一套标准端点映射关系非常直观HTTP 方法URL 示例行为GET/customers返回客户列表默认分页 100 条GET/customers(15)返回单条记录主键为 15POST/customers创建记录Body 放 JSON 对象PUT/customers(15)整体更新记录 15DELETE/customers(15)删除记录 15这套映射不需要写一行路由代码。需要留意的坑是 URL 里主键的括号写法是 OData 风格不是常见的/customers/15前端如果按路径参数的习惯拼接就会 404。我一般会在接口文档里显式告诉前端这个规则顺手在 WebModule 的响应头里加上X-XData-EntitySet: customers之类的自定义头方便排查问题。4.2 分页、筛选与排序$top/$skip/$filter 的实际用法XData 对列表接口默认支持一组查询前缀参数这是它区别于一个实体一个固定接口的另一个优点。看一个实际请求GET /tms/xdata/customers?$top20$skip40$filterTotalAmount gt 1000$orderbyName desc参数含义拆开说$top20只取前 20 条等价于 SQL 里的LIMIT 20。$skip40跳过前 40 条和$top配合做分页。前端常见的第 3 页每页 20 条就是$skip40$top20。$filterTotalAmount gt 1000是服务端过滤gt是大于的意思XData 会把它翻译成 SQL 的 WHERE 条件不会出现查出全量再在内存里过滤这种灾难。$orderbyName desc指定排序字段和方向多个字段用逗号分隔。这套参数是可以叠加的但要注意$filter的字段名必须和 JSON 序列化后的字段名一致。如果你在实体属性上用了[Column(full_name)]但属性名是FName那 URL 里应该写full_name还是Name以 JSON 键名为准这个细节在联调前先确认一次能省半天排查时间。4.3 自定义业务方法在服务类上开新的路由自动 CRUD 覆盖的是标准增删改查但业务里总有查询某用户最近十笔订单这种聚合逻辑。XData 允许在服务器端注册服务类把方法暴露成额外端点。我常用的做法是在独立的单元里定义服务接口不写进实体类type TCustomerService class public function GetRecentOrders(CustomerId: Integer; Count: Integer 10): TListTOrder; end;服务器启动时把它注册进 XData 的服务集合里前端就可以直接请求GET /tms/xdata/Service/GetRecentOrders?CustomerId5Count10这个路径的规则是Service固定后面跟着类名和方法名参数走查询字符串。好处是业务逻辑留在 Delphi 端可以继续用 Aurelius 的查询语法、事务和连接池不用为了一个聚合查询去写裸 SQL。我建议自定义服务方法统一走这种模式而不是另开一个 HTTP 端口。4.4 客户端调用用原生 RESTClient 做最小联调虽然 TMS 提供了官方的 XData Client 组件但我联调时更习惯先用 Delphi 自带的TNetHTTPClient把接口调通少一层依赖出了问题也好判断是服务端还是客户端的事uses System.Net.HttpClient, System.JSON; var HttpClient: TNetHTTPClient; Response: IHTTPResponse; JSONValue: TJSONValue; begin HttpClient : TNetHTTPClient.Create(nil); try Response : HttpClient.Get(http://localhost:8080/tms/xdata/customers?$top10); if Response.StatusCode 200 then begin JSONValue : TJSONObject.ParseJSONValue(Response.ContentAsString(TEncoding.UTF8)); try // JSONValue 里就是客户列表数组 WriteLn(JSONValue.ToString); finally JSONValue.Free; end; end; finally HttpClient.Free; end; end;TNetHTTPClient.Get返回的IHTTPResponse里包含状态码、响应头和 Body状态码 200 是正常401 是鉴权失败404 多半是资源名和实体名没对上。这里用TJSONObject.ParseJSONValue解析响应不引入额外的 JSON 库联调阶段够用了。5. 避坑与排查XData 部署中常见的七个翻车现场5.1 安装后 IDE 里找不到 XData 组件面板现象按步骤编译了设计期包Install 也提示成功但 Tool Palette 里搜不到TMS XData。原因有两个一是库路径里源码目录顺序不对IDE 索引到了旧版本的 DCU二是安装了很多第三方组件组件包分类被折叠了。解决在 Tool Palette 右上角搜索框里直接输入TXDataServer如果搜不到回到Components Install Packages里确认设计期包名字前打了勾再把库路径里旧版路径删掉重启 IDE。5.2 GET 请求返回 500服务端日志却一片空白现象浏览器访问/customers返回 500 Internal Server ErrorWebModule 的 OnLog 事件什么都没打。原因通常是实体类的属性映射问题比如数据库表里没有Name列但实体里写了Name这个错误在 XData 生成查询语句时才抛出而异常被框架吞进了默认日志通道。解决先在浏览器访问/tms/xdata/info确认服务正常再直接调用/customers(1)缩小范围最后检查实体类映射和数据库字段是否一一对应。5.3 JSON 日期时间格式和前端预期不一致现象前端拿到的是2024-05-12T08:30:00.000Z但页面显示时多了 8 小时。原因Delphi 的TDateTime本身没有时区概念XData 序列化时按服务器本地时间输出而前端 JavaScript 的new Date()默认按浏览器时区渲染。解决在服务器端统一定义时间基准我一般会在部署文档里约定所有接口返回 UTC 时间前端拿到后再转当地时间。如果是内部系统不想改逻辑就在前端解析时忽略时区字段按纯字符串处理。5.4 SQLite 并发请求时频繁报database is locked现象请求量稍微上来SQLite 为后端的服务就开始间歇性 500。原因SQLite 默认单连接串行写XData 的连接池在并发写场景下会同时打开多个连接其中一个拿到锁其他就排队失败。解决SQLite 适合接口量小、并发低的内部工具如果要做多用户在线服务把数据库切到 MySQL/SQL Server连接字符串改一下应用代码不用动。这个项目里如果已经踩了 SQLite 的坑别犹豫趁早换库。5.5 CORS 配置不生效前端跨域被浏览器拦截现象前端跑在localhost:4200后端跑在8080浏览器控制台报跨域错误但代码里明明设置了Access-Control-Allow-Origin。原因CORS 的响应头必须要在预检请求阶段返回也就是OPTIONS请求也要有对应处理很多人只处理了 GET/POST 而漏掉了 OPTIONS。解决在 WebModule 的 BeforeDispatch 事件里统一加三个响应头Response.SetCustomHeader(Access-Control-Allow-Origin, *); Response.SetCustomHeader(Access-Control-Allow-Methods, GET, POST, PUT, DELETE, OPTIONS); Response.SetCustomHeader(Access-Control-Allow-Headers, Authorization, Content-Type, X-XData-*);Access-Control-Allow-Headers里的Authorization是给 JWT 用的Content-Type是给 JSON 请求体用的。注意*在需要携带 Cookie 的场景下不适用那时要写具体域名。5.6 URL 资源名大小写导致 404现象实体类叫TUserInfo前端访问/userinfo是通的但访问/userInfo就 404。原因XData 的资源名映射保留了实体类名的原始大小写而且默认复数化时不改大小写。解决约定所有资源名统一小写访问或者在实体类上用[Entity(userinfo)]显式指定资源名不要让前端去猜大小写规则。5.7 部署到 Windows 服务后模块不加载现象开发环境一切正常放到服务器上注册成 Windows 服务一启动就报模块不存在或找不到指定的模块。原因跑 WebBroker 应用需要依赖Delphi 12.3的运行时 DLL如rtl290.bpl等服务器上没装对应版本运行时库。解决发布时选用Release Static编译模式并勾选Link with runtime packages取消勾选让 BPL 依赖打进 EXE。如果你是做绿色发布记得把XData和Aurelius注册运行库包也做好静默安装脚本不然换台机器就翻车。6. 收尾技巧给 XData 服务加日志、鉴权和联调脚本6.1 用拦截器记录每次请求耗时生产环境里最怕的就是接口偶尔慢一下但说不出具体是哪个请求慢。XData 服务端的WebModule里有两个事件可以利用BeforeDispatch和AfterDispatch。我在BeforeDispatch里记录开始时间在AfterDispatch里计算耗时再拼上请求路径、查询参数、状态码一行结构化日志就出来了procedure TXDataWebModule.WebModuleBeforeDispatch(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); begin FStartTick : TStopwatch.GetTimestamp; end; procedure TXDataWebModule.WebModuleAfterDispatch(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); var ElapsedMs: Int64; begin ElapsedMs : (TStopwatch.GetTimestamp - FStartTick) * TStopwatch.TicksPerMillisecond; LogRequest(Request.PathInfo, Request.Query, Response.StatusCode, ElapsedMs); end;这一段代码简单直接只要在LogRequest里把信息写进日志文件或数据库就足够定位百分之八十的性能问题。慢查询的特征很统一$filter条件没走索引、$skip页数太大、关联对象懒加载被逐条触发日志里一看耗时就能确定方向。6.2 配合 FireMonkey 客户端快速联调XData 做服务端Delphi 12.3 的 FireMonkey 做客户端是同一个技术栈下很舒服的组合。我一般会在客户端工程里放一个调试窗体放一个TEdit填 URL一个TMemo看返回结果再放几个按钮对应 GET、POST、PUT、DELETE。联调时不需要断点直接在这个调试面板里发请求返回的 JSON 格式化打印出来前端同学要什么字段一眼就能对照。这个方法每次调试都帮我筛掉一类问题到底是接口数据不对还是客户端解析逻辑不对。接口返回的 JSON 在调试面板里先看一遍再让前端去对接沟通成本低很多。从那以后我每次接手 XData 项目都会强制自己先把/tms/xdata/info、实体映射、调试面板这三件事走一遍确认环境是健康的再去碰业务逻辑。希望这篇笔记能帮你少踩几个坑把 XData 真正用起来。本文还有配套的精品资源点击获取
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表