用Delphi构建Web数据库服务系统:架构、数据库访问与安全实践

发布时间:2026/10/30 12:59:59
用Delphi构建Web数据库服务系统:架构、数据库访问与安全实践 简介这份文档资料面向希望用Delphi构建Web数据库服务系统的开发者尤其适合具备一定Delphi基础、想从桌面应用转向Web端数据库开发的中级程序员。内容围绕Web浏览器、Web应用服务器、数据库服务器与DBMS的协作架构展开讲解如何借助ISAPI框架、TWebModule组件以及TPageProducer、TQueryTableProducer等数据库组件把HTTP请求转化为SQL操作并将结果以HTML返回浏览器同时涉及TSession会话管理与通讯录发布实例。资源包共1个doc文件约32KB属于纯文档型资料便于快速通读与查阅。目前已有93人学习适合作为理解Delphi Web数据库开发流程的入门参考帮助读者掌握组件化开发思路、请求响应处理机制与多用户会话管理要点为搭建定制化Web数据库方案提供可借鉴的实践框架。1. 用 DELPHI 制作 WEB 数据库服务系统老工具在新需求里的真实位置手里有一套 Delphi 写的桌面端业务系统客户突然要求「能在浏览器里查数据、改数据」预算又不允许推倒重来——这是我最近几年被问得最多的一类需求。标题里的「用 DELPHI 制作 WEB 数据库服务系统」说的就是这件事不换语言、不换团队用 Delphi 把已有的数据库访问逻辑包成 HTTP 服务前端用普通网页去调。它解决的不是「做一个互联网级高并发平台」而是让存量 Delphi 资产快速获得 Web 访问能力适合中小企业内部系统、设备管理后台、数据录入与查询这类场景。热搜里反复出现的「数据库增删改查」「web 服务器安全」「企业级 web 开发」落到这里就是三件具体的事接口怎么设计、连接怎么管、权限怎么卡。下面按我实际做过的路径拆开讲。2. 先定架构Delphi 做 Web 数据库服务系统的三种常见形态2.1 三种形态的取舍CGI/ISAPI、独立 HTTP 服务、REST 中间层在动手写第一行代码前必须先选形态因为它决定了后面所有代码的组织方式。Delphi 做 Web 数据库服务业内常见做法基本落在三条路上。第一条是 CGI/ISAPI 路线。用 Delphi 编译出 dll 或 exe挂到 IIS 或 Apache 下由 Web 服务器负责 HTTP 解析Delphi 只处理业务。优点是部署简单、和传统 Web 服务器安全体系天然兼容缺点是 ISAPI 的 dll 一旦崩溃会拖垮整个 Web 服务器进程调试也麻烦现在新项目我基本不推荐。第二条是独立 HTTP 服务。用 Delphi 自带的IdHTTPServerIndy 组件或TIdHTTPWebBrokerBridge起一个独立进程自己监听端口、自己解析请求。这是目前最主流的做法进程隔离好、调试方便、可以打包成 Windows 服务开机自启。热搜里的「web 服务器安全」在这个形态下要自己多操心一层因为没有了 IIS 的请求过滤。第三条是 REST 中间层。Delphi 只做纯 JSON 接口前端用 Vue/React 或纯 HTMLfetch 调用前后端彻底分离。这是我最推荐的形态因为接口一旦稳定前端换不换技术栈都无所谓也方便以后把某个接口单独拆出去。选型上给一个直接的判断标准如果只是给内部几个人用、数据量小、要求一周上线选第二条如果前端团队独立、接口要被多个端复用选第三条如果公司已经有 IIS 运维体系且不允许开新端口才考虑第一条。2.2 最小可运行骨架用 IdHTTPServer 起一个能返回 JSON 的服务下面这段是我常用的最小骨架能直接跑起来返回一段 JSON验证环境是否通。用 Delphi 10.4 及以上版本Indy 组件随 IDE 自带不需要额外装包。unit uMain; interface uses Winapi.Windows, System.SysUtils, System.Classes, System.JSON, IdContext, IdCustomHTTPServer, IdHTTPServer, IdSocketHandle; type TMainForm class(TForm) procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); private FServer: TIdHTTPServer; procedure OnCommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo); end; implementation procedure TMainForm.FormCreate(Sender: TObject); begin FServer : TIdHTTPServer.Create(Self); FServer.DefaultPort : 8080; // 监听端口按需改 FServer.OnCommandGet : OnCommandGet; FServer.Active : True; // 启动服务 end; procedure TMainForm.OnCommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo); var LJson: TJSONObject; begin // 只处理 /api/ping其余返回 404 if ARequestInfo.Document /api/ping then begin LJson : TJSONObject.Create; try LJson.AddPair(code, TJSONNumber.Create(0)); LJson.AddPair(msg, pong); AResponseInfo.ContentType : application/json; charsetutf-8; AResponseInfo.ContentText : LJson.ToJSON; finally LJson.Free; end; end else AResponseInfo.ResponseNo : 404; end; procedure TMainForm.FormDestroy(Sender: TObject); begin FServer.Active : False; end;逻辑说明OnCommandGet是 Indy 处理 GET 请求的回调所有路由判断都写在这里。ARequestInfo.Document是请求路径AResponseInfo.ContentText是返回体。参数上DefaultPort建议避开 80 和 443用 8080 或 8090避免和已有服务冲突ContentType一定要带charsetutf-8否则中文会乱码这是新手最常见的翻车点。2.3 路由与请求分发别把业务逻辑全塞进 OnCommandGet上面那段骨架只适合验证真实项目里如果把所有接口都堆在OnCommandGet里几百行之后就没法维护了。我一般会做一个极简的路由表用TDictionarystring, TProc...把路径映射到处理函数。type TApiHandler reference to procedure(ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo); // 注册路由 FRoutes : TDictionarystring, TApiHandler.Create; FRoutes.Add(/api/user/list, HandleUserList); FRoutes.Add(/api/user/add, HandleUserAdd); // 分发 if FRoutes.TryGetValue(ARequestInfo.Document, LHandler) then LHandler(ARequestInfo, AResponseInfo) else AResponseInfo.ResponseNo : 404;这样每个接口一个独立方法数据库操作、参数校验、返回组装都在各自方法里改一个接口不会影响其他接口。参数说明TApiHandler用匿名方法引用能直接捕获外部变量但要注意别在闭包里持有数据库连接对象否则连接释放时机不可控。3. 数据库访问层连接、参数化查询与增删改查落地3.1 连接方式选型ADO、FireDAC 与连接池Delphi 访问数据库常见的是 ADOTADOConnection和 FireDACTFDConnection。ADO 依赖系统 OLE DB 驱动部署省事但性能和跨平台性一般FireDAC 是 Embarcadero 官方主推支持 MySQL、SQL Server、SQLite、PostgreSQL 等参数化查询和连接池都更完善。新项目我基本都用 FireDAC。热搜里的「mysql 的数据库连接池」「托管数据库服务」在这里对应一个关键决策Web 服务是多线程的每个请求都可能并发访问数据库如果每个请求都新建连接几百并发就会把数据库打满。正确做法是用 FireDAC 的TFDManager配连接池或者自己维护一个连接对象池。// FireDAC 连接池配置放在程序启动时 FDManager.Close; FDManager.AddConnectionDef(Pool_Main, MySQL, DriverIDMySQL;Server127.0.0.1;Port3306; Databaseerp;User_Nameapp;Password***; PooledTrue;POOL_MaximumItems20;POOL_ExpireTimeout600);参数说明PooledTrue开启池化POOL_MaximumItems是池内最大连接数一般按「数据库最大连接数 ÷ 应用实例数」来估20 到 50 之间比较稳POOL_ExpireTimeout是空闲连接回收秒数设太短会频繁重建连接设太长会占着数据库连接不放。3.2 参数化查询把 SQL 注入挡在门外「web 服务器安全」这个词落到数据库层最实在的一条就是参数化查询。拼接 SQL 字符串是血泪教训的重灾区一个 OR 11就能把登录绕过。FireDAC 用TFDQuery的Params或Macros都能做参数化。procedure HandleUserAdd(ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo); var LQuery: TFDQuery; LBody: TJSONObject; begin LBody : TJSONObject.ParseJSONValue(ARequestInfo.Content) as TJSONObject; LQuery : TFDQuery.Create(nil); try LQuery.ConnectionDefName : Pool_Main; LQuery.SQL.Text : INSERT INTO t_user (user_name, mobile, create_time) VALUES (:name, :mobile, NOW()); LQuery.ParamByName(name).AsString : LBody.GetValue(name).Value; LQuery.ParamByName(mobile).AsString : LBody.GetValue(mobile).Value; LQuery.ExecSQL; // 执行增删改 AResponseInfo.ContentText : {code:0}; finally LQuery.Free; LBody.Free; end; end;逻辑说明:name和:mobile是占位符FireDAC 会把值单独传给数据库驱动不参与 SQL 文本拼接从根本上杜绝注入。参数说明AsString会做类型转换如果字段是整型要用AsInteger日期用AsDateTime类型不匹配会抛异常。查询用Open增删改用ExecSQL这两个方法别用混用错会报「数据集未打开」或「无法执行」。3.3 增删改查的接口设计统一返回结构接口返回结构不统一前端就要为每个接口写不同的解析逻辑这是后期维护成本的大头。我一般定一个固定结构code表示业务状态msg表示提示信息data放实际数据。查询接口返回数组增删改返回受影响行数。function BuildResp(ACode: Integer; const AMsg: string; AData: TJSONValue): string; var LRoot: TJSONObject; begin LRoot : TJSONObject.Create; LRoot.AddPair(code, TJSONNumber.Create(ACode)); LRoot.AddPair(msg, AMsg); if AData nil then LRoot.AddPair(data, AData); Result : LRoot.ToJSON; LRoot.Free; end;参数说明AData允许为 nil这样增删改接口不用硬塞一个空对象。code建议 0 表示成功非 0 表示各类错误具体错误码在文档里列清楚前端按码处理不要靠解析msg文本判断。4. 避坑与排查Delphi Web 数据库服务最容易翻车的五件事4.1 中文乱码请求和响应两头都要管现象前端提交的中文存进数据库变成问号或者查询返回的中文在浏览器里显示乱码。原因HTTP 请求体默认按 ISO-8859-1 解析响应头没声明 UTF-8。解决请求侧用ARequestInfo.PostStream读原始字节再按 UTF-8 解码响应侧固定设置ContentType : application/json; charsetutf-8数据库连接串里也要加CharacterSetutf8mb4。三处缺一处都会乱。4.2 多线程访问数据库崩溃现象单机测试正常一上并发就报「连接已关闭」或直接进程崩溃。原因Indy 的OnCommandGet是在工作线程里执行的多个线程共用一个TFDQuery或TFDConnection实例会冲突。解决每个请求内部自己创建TFDQuery连接用连接池按名获取绝不把连接对象放到全局变量里跨线程共享。这是最隐蔽也最致命的一条。4.3 端口被占用或防火墙拦截现象本机http://127.0.0.1:8080能访问局域网其他机器访问不了。原因Windows 防火墙默认拦截入站连接或者端口已被其他进程占用。解决用netstat -ano | findstr 8080查占用在防火墙入站规则里放行对应端口。部署到服务器时这一步不做前端永远连不上很多人会误以为是代码问题。4.4 大结果集把内存吃满现象查询一张几十万行的表服务内存飙升甚至 OOM。原因一次性把全部结果读进TJSONArray再序列化。解决接口强制分页page和pageSize作为必传参数pageSize上限卡在 200 到 500 之间SQL 用LIMIT加OFFSET同时返回总数供前端分页控件使用。别指望前端自己截断数据已经传过去了。4.5 服务异常退出没有日志现象服务跑几天突然没了重启后也不知道发生了什么。原因没有全局异常捕获未处理异常直接终止进程。解决在OnCommandGet外层包try...except把异常堆栈写进日志文件同时用Application.OnException兜底。日志按天切分保留最近 30 天。没有日志的线上服务排查全靠猜这是我最不愿意看到的局面。5. 进阶技巧把接口文档、鉴权和部署一次做对5.1 用接口签名做轻量鉴权内部系统不一定要上 OAuth但裸奔的接口绝对不行。我常用的做法是登录接口返回一个 token后续请求在 Header 里带X-Token服务端校验 token 有效性并绑定用户身份。token 用 GUID 加时间戳生成存数据库或内存缓存设过期时间。校验逻辑放在路由分发之前未通过直接返回 401业务代码里就不用每个接口都写一遍。function CheckToken(ARequestInfo: TIdHTTPRequestInfo): Boolean; var LToken: string; begin LToken : ARequestInfo.HeaderValues[X-Token]; Result : (LToken ) and TokenStore.Exists(LToken); end;参数说明HeaderValues大小写不敏感前端传x-token也能取到。TokenStore可以用TDictionary加定时清理量大了再换 Redis。5.2 打包成 Windows 服务并配置自启独立 exe 双击运行一关窗口服务就停了这不是生产做法。用TService把服务逻辑包起来或者用sc create把 exe 注册成系统服务。注册命令sc create DelphiWebDB binPath D:\app\WebDBService.exe start auto sc description DelphiWebDB Delphi Web 数据库服务 sc start DelphiWebDB参数说明binPath后面等号前要有空格这是sc命令的坑少空格会报参数错误。start auto表示开机自启。注册后可以在「服务」管理器里看到日志建议写到固定目录方便运维查看。5.3 上线前必做的三项验证第一项用curl或 Postman 把所有接口跑一遍确认正常返回和异常返回都符合预期特别是参数缺失、类型错误、越权访问这三种情况。第二项用ab或wrk做一轮并发压测观察连接池是否被打满、响应时间是否稳定。第三项检查日志目录、数据库备份、端口放行三件事是否都到位。这三项做完一个 Delphi Web 数据库服务系统才算真正能交付。我自己踩过最深的一次坑是早期图省事把数据库连接放在全局变量里单机测了一周都没事上线第二天并发一上来整个服务直接崩查了一整晚才定位到线程冲突。从那以后凡是 Web 服务连接一律走池、对象一律请求内创建这个习惯帮我省了无数次后悔药。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询