简介:这份文档资料面向具备一定Delphi基础的开发者,讲解如何用Delphi构建Web数据库服务系统,解决从桌面应用转向Web端数据交互的开发需求。内容围绕Web浏览器、Web应用服务器、数据库服务器与DBMS的协作架构展开,重点说明ISAPI、CGI、WinCGI三种服务程序类型的选型,以及TWebModule、Request与Response属性、Action事件处理机制的使用方法。资源包共1个doc文件,约32KB,属于轻量级技术文档,便于快速通读与查阅。文中结合通讯录管理实例,演示了TPageProducer、TQueryTableProducer、TDataSetPageProducer等组件如何将查询结果转换为HTML页面,并借助TSession管理多用户会话,覆盖数据浏览、搜索与录入等典型场景。目前已有93人学习,适合希望掌握Delphi Web开发框架、理解ODBC与BDE桥接思路、并需要可落地组件化方案的中级开发者参考。
1. 用 DELPHI 制作 WEB 数据库服务系统:老工具为什么还能打
接手过不少工厂内部管理系统,甲方预算有限、服务器只有一台 Windows Server、IT 管理员只会装 exe,这种场景下用 DELPHI 做 WEB 数据库服务系统反而是最稳的路子。标题里的三个词拆开看:DELPHI 是开发工具,WEB 是访问方式,数据库服务系统是最终交付形态。合起来就是——用 DELPHI 写一个能通过浏览器访问、背后连着数据库、能增删改查的业务系统。它解决的核心问题是:让不装客户端的用户也能用上 DELPHI 写出来的业务逻辑,同时保留 DELPHI 在数据库操作上的成熟生态。适合谁?适合手里有存量 DELPHI 代码、团队只有一两个熟手、又必须交付 B/S 架构的从业者。热搜里 DELPHI、WEB、数据库三个词反复出现,说明搜的人多半卡在“怎么把桌面端那套搬到浏览器”这一步。
2. 先想清楚架构:DELPHI 在 WEB 数据库服务系统里站哪个位置
2.1 三种主流架构的取舍
用 DELPHI 做 WEB 数据库服务系统,绕不开一个根本问题:DELPHI 代码跑在哪一端。常见做法有三种,选错了后面全是返工。
第一种是 CGI/ISAPI 模式。DELPHI 编译出 dll 或 exe,挂到 IIS 或 Apache 下,浏览器请求进来,DELPHI 代码处理完直接吐 HTML。优点是部署简单,一个 dll 丢进站点目录就能跑;缺点是每次请求都要走一遍进程/线程调度,并发一高就顶不住,而且页面逻辑和业务逻辑容易搅在一起。适合内部几十人用的系统。
第二种是中间层模式。DELPHI 写一个独立的服务端程序(常见用 Indy 或 DataSnap 做 HTTP 服务),浏览器通过 AJAX 调接口,DELPHI 负责查数据库、拼 JSON 返回。前端可以是纯 HTML+JS,也可以是别的框架。这种模式把 DELPHI 的强项(数据库访问、业务计算)和 WEB 的展示层分开了,维护起来清楚得多。我一般会推荐这种,尤其是系统还要活好几年的情况。
第三种是 WebBroker + 模板引擎。DELPHI 自带的 WebBroker 框架配合 PageProducer,能把 HTML 模板和动态数据拼起来。适合页面结构固定、不需要复杂前端交互的后台管理类系统。
选哪种,看三个指标:并发用户数、前端交互复杂度、团队里有没有人懂前端。三个都不高,第一种够用;有一个高,就往第二种靠。
2.2 数据库连接层的选型
DELPHI 连数据库的组件不少,选哪个直接影响后面写代码的手感。
| 组件方案 | 适用数据库 | 特点 | 注意点 |
|---|---|---|---|
| ADO | SQL Server / Access | Windows 原生,部署省事 | 依赖系统 OLE DB 驱动 |
| dbExpress | MySQL / Oracle / SQL Server | 跨平台,轻量 | 驱动要单独配,dbx 数据库工具可辅助调试 |
| FireDAC | 主流数据库全覆盖 | 功能最全,性能好 | 学习曲线略陡 |
| UniDAC | 主流数据库全覆盖 | 第三方,直连不依赖客户端 | 需要授权 |
热搜里出现 dbx 数据库工具,说明不少人在用 dbExpress 这条线。dbExpress 的好处是 Delphi 自带、不需要额外装数据库客户端库,配好驱动就能连。缺点是某些冷门数据库支持一般。如果目标库是 MySQL 或 SQL Server,dbExpress 够用;如果要连多种库或者对性能有要求,FireDAC 更稳。
连接字符串这块有个血泪经验:不要把密码硬编码在代码里。常见做法是放在配置文件或者环境变量里,代码里只读配置。后面换服务器、改密码的时候,不用重新编译。
2.3 最小可跑的服务端骨架
下面这段代码用 Indy 的 TIdHTTPServer 起一个最简 HTTP 服务,收到请求后查数据库返回 JSON。这是中间层模式的最小骨架,能跑通之后再往上加业务。
// 服务端最小骨架:Indy HTTP Server + FireDAC 查库返回 JSON procedure TMainForm.IdHTTPServerCommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo); var Conn: TFDConnection; Query: TFDQuery; JsonArr: TJSONArray; JsonObj: TJSONObject; begin // 1. 建立数据库连接(实际项目应复用连接池) Conn := TFDConnection.Create(nil); try Conn.DriverName := 'MySQL'; Conn.Params.Add('Server=127.0.0.1'); Conn.Params.Add('Database=testdb'); Conn.Params.Add('User_Name=appuser'); Conn.Params.Add('Password=******'); // 从配置文件读取,不要硬编码 Conn.Connected := True; // 2. 根据请求路径分发 Query := TFDQuery.Create(nil); try Query.Connection := Conn; if ARequestInfo.Path = '/api/users' then begin Query.SQL.Text := 'SELECT id, name, email FROM users LIMIT 100'; Query.Open; JsonArr := TJSONArray.Create; while not Query.Eof do begin JsonObj := TJSONObject.Create; JsonObj.AddPair('id', Query.FieldByName('id').AsInteger); JsonObj.AddPair('name', Query.FieldByName('name').AsString); JsonObj.AddPair('email', Query.FieldByName('email').AsString); JsonArr.AddElement(JsonObj); Query.Next; end; AResponseInfo.ContentType := 'application/json; charset=utf-8'; AResponseInfo.ContentText := JsonArr.ToString; JsonArr.Free; end else begin AResponseInfo.ResponseNo := 404; AResponseInfo.ContentText := '{"error":"not found"}'; end; finally Query.Free; end; finally Conn.Free; end; end;这段代码的逻辑说明:TIdHTTPServer 的 OnCommandGet 事件在每次 HTTP GET 请求时触发,ARequestInfo.Path 拿到请求路径,按路径分发到不同查询。查完结果逐行拼成 JSON 数组返回。参数说明:DriverName 按实际数据库改,MySQL 就写 MySQL,SQL Server 写 MSSQL;Params 里的 Server、Database、User_Name、Password 按实际环境填;LIMIT 100 是防止一次拉太多数据把内存撑爆,实际项目要加分页。
注意:上面每次请求都新建连接,生产环境必须改成连接池,否则并发一上来数据库连接数就爆了。FireDAC 可以用 TFDManager 配连接池,或者用中间件层统一管理。
3. 把数据库增删改查搬到浏览器:接口设计与前端对接
3.1 REST 风格的接口约定
中间层模式的核心是接口设计。我一般按 REST 风格来定,路径和 HTTP 方法对应操作:
- GET /api/users —— 查列表
- GET /api/users/1 —— 查单条
- POST /api/users —— 新增
- PUT /api/users/1 —— 修改
- DELETE /api/users/1 —— 删除
DELPHI 这边在 OnCommandGet 里处理 GET,在 OnCommandPost 里处理 POST,PUT 和 DELETE 需要看 Indy 版本是否支持,老版本可能要在 POST 里用 _method 参数模拟。这个约定定下来之后,前端不管用什么框架,对接逻辑都是一样的。
请求体和响应体统一用 JSON。响应格式固定成{"code":0,"data":...,"msg":""},前端只判断 code 是不是 0,不用去猜 HTTP 状态码。这个约定看起来简单,但能省掉大量前后端扯皮的时间。
3.2 参数化查询防注入
数据库增删改查里最容易翻车的就是 SQL 注入。DELPHI 里用参数化查询,不要用字符串拼接。
// 参数化查询示例:新增用户 procedure TUserService.AddUser(const AName, AEmail: string); var Query: TFDQuery; begin Query := TFDQuery.Create(nil); try Query.Connection := FConn; Query.SQL.Text := 'INSERT INTO users (name, email, created_at) VALUES (:name, :email, NOW())'; Query.ParamByName('name').AsString := AName; Query.ParamByName('email').AsString := AEmail; Query.ExecSQL; finally Query.Free; end; end;逻辑说明:SQL 语句里用:name、:email占位,实际值通过 ParamByName 传入。数据库驱动会自动处理转义,用户输入里带单引号也不会破坏 SQL 结构。参数说明:AsString 对应字符串类型,如果是整数用 AsInteger,日期用 AsDateTime。注意 NOW() 是 MySQL 的函数,SQL Server 要用 GETDATE()。
前端这边,用 fetch 调接口:
// 前端调用示例:新增用户 async function addUser(name, email) { const resp = await fetch('/api/users', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: name, email: email }) }); const result = await resp.json(); if (result.code !== 0) { alert('新增失败:' + result.msg); return false; } return true; }逻辑说明:fetch 发 POST 请求,body 是 JSON 字符串。服务端 DELPHI 在 OnCommandPost 里读 ARequestInfo.PostStream 拿到请求体,解析 JSON 后调 AddUser。参数说明:Content-Type 必须写 application/json,否则服务端可能按表单解析。返回值统一判断 code。
3.3 分页和排序的处理
列表查询一定要分页,不然数据一多浏览器直接卡死。DELPHI 这边接收 page 和 pageSize 两个参数,拼到 SQL 里。
// 分页查询 Query.SQL.Text := 'SELECT id, name, email FROM users ORDER BY id DESC LIMIT :offset, :limit'; Query.ParamByName('offset').AsInteger := (APage - 1) * APageSize; Query.ParamByName('limit').AsInteger := APageSize;参数说明:offset 是跳过的行数,limit 是每页条数。MySQL 用 LIMIT offset, limit 语法,SQL Server 要用 OFFSET FETCH,Oracle 要用 ROWNUM 嵌套。排序字段不要直接拼用户传的值,要在服务端做白名单映射,防止 ORDER BY 注入。
前端分页组件拿到总条数后算总页数,翻页时重新请求。总条数可以单独一个接口,也可以在列表接口里带回来,看数据量决定。
4. 避坑与排查:DELPHI WEB 数据库服务系统最常见的五个翻车点
4.1 中文乱码
现象:浏览器显示的中文全是问号或方块。原因:DELPHI 默认字符串编码和 HTTP 响应的 charset 不一致,或者数据库连接字符集没设对。解决:三处都要设——数据库连接参数加CharacterSet=utf8mb4,HTTP 响应头设ContentType := 'application/json; charset=utf-8',返回的字符串用 UTF8Encode 转一遍。三处缺一处都可能乱码。
4.2 并发一高就崩
现象:几个人同时用没事,十几个人同时点就报连接超时或程序无响应。原因:每次请求新建数据库连接,连接数超过数据库上限;或者 Indy 默认是单线程处理,请求排队。解决:数据库连接用连接池(FireDAC 的 TFDManager 或自己维护一个连接队列);Indy 的 TIdHTTPServer 设置 MaxConnections 并确认是线程池模式。如果还顶不住,考虑换 DataSnap 或上 ISAPI。
4.3 编译报内存错误
现象:DELPHI 编译时报内存错误,尤其是大项目。原因:常见的是第三方组件版本冲突、编译器缓存损坏、或者项目文件里引用了不存在的路径。解决:先清理项目目录下的 dcu 文件和 __history 目录,重新编译;还不行就检查组件包是否都正确安装,特别是 dbExpress 驱动和 Indy 版本是否匹配。热搜里 delphi 编译程序报内存错误这个词出现频率不低,多半是环境问题不是代码问题。
4.4 部署到服务器后连不上数据库
现象:开发机跑得好好的,部署到服务器就报数据库连接失败。原因:服务器上没装数据库客户端库,或者防火墙拦了数据库端口,或者连接字符串里的 Server 写的是 localhost 但数据库在另一台机器。解决:确认服务器能 ping 通数据库主机,telnet 数据库端口通不通;如果用 ADO 或 dbExpress,确认服务器上装了对应的驱动;连接字符串里的 Server 改成实际 IP。
4.5 前端跨域被拦
现象:前端页面和 DELPHI 服务不在同一个端口,浏览器报 CORS 错误。原因:浏览器同源策略,DELPHI 服务端没返回 CORS 头。解决:在 OnCommandGet 里给响应加Access-Control-Allow-Origin、Access-Control-Allow-Methods、Access-Control-Allow-Headers三个头。如果前端带 cookie,Allow-Origin 不能写*,要写具体域名。生产环境最好前后端同源部署,用反向代理把 /api 转发到 DELPHI 服务,省掉跨域问题。
5. 进阶技巧:让这套系统跑得更稳的几个习惯
5.1 日志要记全,但别记密码
DELPHI 服务端出问题的时候,没有日志基本等于黑匣子。我一般会在三个地方打日志:请求进来时记路径和参数、数据库操作前记 SQL 和参数、异常时记堆栈。用 TStringList 或者第三方日志库写到文件里,按天切分。注意:SQL 参数里如果有密码字段,记日志前要脱敏,不然日志文件泄露等于密码泄露。
5.2 接口版本化
系统上线后需求一定会变,接口不能随便改。常见做法是在路径里带版本号,比如/api/v1/users。新需求加/api/v2/users,老接口保留一段时间让前端迁移。DELPHI 这边在路径分发的地方按版本号走不同分支。这个习惯能省掉大量“改了接口前端全挂”的事故。
5.3 数据库结构变更要有后悔药
热搜里 mysql 数据库修改结构这个词说明很多人在这上面吃过亏。我的习惯是:任何表结构变更都写成 SQL 脚本文件,按日期编号存到版本控制里。改之前先备份,改完在测试库跑一遍确认没问题再上生产。DELPHI 这边如果用了 ORM 或者数据模块,字段变更后要同步更新 FieldDefs,不然运行时报字段找不到。
5.4 性能验证的简单方法
系统上线前,用简单的压测工具(比如 ab 或者自己写个循环请求的脚本)跑一下核心接口,看响应时间和错误率。DELPHI 服务端可以在代码里加计时,记录每个请求的处理耗时,超过阈值的记到慢查询日志里。数据库那边开慢查询日志,看哪些 SQL 拖了后腿。这两个日志对着看,基本能定位大部分性能问题。
5.5 一个具体技巧:用 WebBroker 做静态资源服务
如果前端页面不多,可以直接用 DELPHI 的 WebBroker 把静态文件也一起服务了,不用单独装 Nginx。在 OnCommandGet 里判断路径,如果是 /static/ 开头就去读磁盘文件返回,ContentType 按扩展名映射。这样部署的时候只有一个 exe,拷贝过去就能跑,对只有一台 Windows 服务器的场景特别友好。注意路径要做安全校验,防止../目录穿越。
这套方案我前后在几个工厂内部系统上用过,最久的跑了五年没大改。DELPHI 做 WEB 数据库服务系统不是最时髦的路子,但在特定约束下它是最省心的。关键是想清楚架构选型,把连接池、参数化查询、日志这三件事做扎实,剩下的就是业务代码的体力活。希望帮到你。
本文还有配套的精品资源,点击获取