用 WebBroker 制作一个类似 WordPress 的简单的 CMS

时间:2022-09-19 15:38:42

代码初步完成。过段时间代码整理完毕,我会上传到 csdn 里面。


Delphi 已经支持 Linux Server。用 Delphi 来开发一个类似 WordPress 的 CMS 可以用在 Windows Server 上面,也可以跑在 Linux Server 上面。


Delphi 支持的 WEB 框架有三种:
1. WebBroker。这个是 Delphi 最早实现的 Web 框架;
2. WebSnap。提供了一些控件,实现了在设计期做一些网页界面自动创建的功能。
3. IntraWeb。提供了类似 Delphi VCL 的设计期拖拉控件设计界面的功能,直接将设计期界面输出为网页界面。


IntraWeb 非常好用,对熟悉 Delphi 拖拉控件来实现界面的程序员来说上手简单。也无需懂得 JavaScript 和 CSS 就能实现复杂功能的网页。但其为了掩盖网页代码的复杂性,做了很多封装,且因为无法看到其源代码,资料文档少,导致如果要做一些网页格式是它本身没有提供的,实现起来就比较麻烦了。
使用 IntraWeb 的另外一个问题是,普通的 Delphi 程序员如果一上来就用 IntraWeb 去开发一个网站,东西是做出来了,但因其封装得太好,对于一个网站的工作原理(WEB的原理,http 请求,等等)没法通过开发一个网站来了解和学习。


而使用 WebBroker 来开发网站,很容易就明白了一个 WEB 页面是如何与后台服务器通讯的。什么是 get 什么是 post。请求(Request)是什么,响应(Response)是什么。在 WebBroker 里面你可以任意更改响应的内容,格式等等,通过实验可以学到 http 的很多规范实际看起来是什么样子。


使用 WebBroker 还可以任意使用 JavaScript 和 CSS 因此网页的界面格式你可以随意更改,也可以用业界最新的开源的 JavaScript 和 CSS 来实现漂亮的页面,而无需去修改后台服务器端代码。


从构思如何实现一个 CMS 开始,我熟悉 Delphi 但并不熟悉网页前端开发。参考了一些别的网站,我为这个 CMS 制订了一个目标:
1. 前端页面应该把置顶的内容,最多4个,作为一个横排4个大框带图片的标题来呈现;
2. 其它文章的标题,用从上到下的列表方式程序。但标题需带上大图片作为题头图。
3. 标题列表支持翻页。
4. 文章的输入界面,要支持富文本输入,图文混排。
5. 文章输入界面要允许上传图片,图文混排。
6. 文章要允许添加上传附件,用于给文章读者下载附件。
7. 前端界面要同时支持手机浏览和电脑大屏幕。
8. 技术上,要将后端代码和前端代码分开,后端代码只实现业务逻辑,修改前端代码(页面显示风格)不用修改和重新编译后端的 WebBroker 的程序。这样才能让前端工程师在无需修改后端代码,无需阅读后端代码的情况下,直接修改前端代码来修改页面显示风格。(PHP 实现的程序,很多时候把前端代码和 PHP 程序混合写,导致修改前端代码需要去读 PHP 代码)。


---------------------------
实现方式和面临的技术问题:
1. WebBroker 入门:用 WebBroker 写一个 Web 服务器端程序,其原理是在 WebBroker 程序的代码框架里面的 WebModule 里面,增加一些 Actions,给每个 Action 的 path 属性指定一个字符串,比如 abc。那么,当浏览器访问到这个程序的时候,URL 里面的路径是 /myWebBroker.exe/abc 则 WebBroker 会触发 path 为 abc 的这个 Action。在这个 Action 里面,我们可以读到来自浏览器的请求参数 Request ,我们可以用 Response 将后端生成的页面发送回浏览器。
1.1. WebBroker 的 Action 其本质就是前端访问后端的 URL 里面,增加一个 path 来让后端可以知道前端究竟想访问什么内容。如果该 path 在后端 WebBroker 的代码里面不存在,则会触发 WebBroker 的默认 Action,我们可以在这个默认的 Action 里面,从 Request 读取来自浏览器的 URL 的参数,根据不同的参数,决定我们要向浏览器返回什么内容。
1.2. 基于上述原理,来自浏览器的访问,就算它访问的是一个静态文件比如 abc.html 只要这个访问被 WEB Server(IIS 或 APACHE 等)送到了我们的 WebBroker 程序,我们也可以找到这个 abc.html 并输出返回给浏览器,在浏览器看来,就是打开了一个 abc.html 文件。在我们的 WebBroker 代码找到 abc.html 并将它输出给浏览器之前,我们可以对 abc.html 的内容做一些改动(最简单的就是对里面的一些事先写入的 tag 字符串做改动),达到让页面动态变化的目的。
1.3. 基于上述原理,我们可以用 WebBroker 框架,写一个类似 php.exe 的网页 script 解释器。


2. 后端的程序需要在前端显示一些动态的内容:WebBroker 提供了一个 PageProducer 控件,可以把要返回给前端的页面代码(HTML代码)放到 PageProducer 里面。当这个 PageProducer 被请求内容时,会触发它的 OnHTMLTag 事件。我们可以在这个事件中,将标记字符串用后端 delphi 代码替换掉。这样就实现了动态内容。问题是,这种事件方法,使得代码支离破碎,没法顺序往下读。其实原理就是替换标记字符串,所以我们可以自己去顺序替换,用 Delphi 的 ReplaceText 函数。


3. 后端程序需要让前端执行一段动态的 JavaScript 代码。比如后端要前端弹窗提示“登录成功” 或 “登录失败”,是两个不同的 JavaScript 代码,对后端来说,是两个不同的 JavaScript 字符串。后端可以在向前端返回的页面的末尾,加上 JavaScript 字符串,前端就会在加载完网页后自动执行这段 JavaScript。因此在 WebBroker 中后端向前端发送 JavaScript 并让前端自动执行,非常简单方便。


4. 页面里面的部分元素,可以在后端组装完成后统一发送网页代码给前端,页可以让前端加载完页面后,再通过 ajax 的方式来向后端请求。这两种方式,各有优缺点,根据需要来选择使用。ajax 的实现,在前端采用 jquery 框架,在页面里写一些 jquery 的代码来实现向后端请求部分数据然后更新前端页面里面的某个区域。前端 ajax 请求后端 WebBroker 也同样是在请求的 URL 里面给定一个 path 让 WebBroker 可以根据 path 去调用特定的 Action 里面的代码来生成前端所需要的页面内容。


5. 页面的风格和布局:我在这里采用了目前比较流行的 BootStrap 这个 CSS 框架。其实有些布局可以自己用 CSS 来写,并不复杂。之所以用现成框架,一方面是可以节省自己的开发时间,一方面是现成框架多半把浏览器兼容性做得比较好了。而调整 CSS 代码的浏览器兼容性是非常消耗时间的事。


6. 富文本输入框:这个采用 CKEditor 这个现成的 html 富文本输入框。它背后的技术也是 JavaScript 和 CSS。要把它嵌入到 WebBroker 的程序里面,其实非常简单:
6.1. 在页面里面实现它:读它的文档,搜搜资料,发现在页面 HTML 代码里面,很容易嵌入一个 CKEditor 进去;
6.2. 如何把它的内容提交给 WebBroker 程序:前端的这个 CKEditor 在一个 form 里面,form 的 action 是提交到服务器的路径,这个路径指向 WebBroker 程序接受前端输入的路径。这里是纯页面代码,和后端代码无关。
6.3. 后端代码,WebBroker 里面对应的 Action 里面的代码,读取 Request.ContentFields.Values['CKEditorName'] 就可以读到用户在前端浏览器里面的 CKEditor 里面输入的内容。非常简单。
6.4. 前端 Post 很多内容到后端,在 WebBroker 里面需要注意的一个问题:Delphi 帮我们生成 WebBroker 代码框架时,没有自动 uses  WEB.ReqMulti 这个单元。这里必须手动加上。否则会出现两种奇怪的错误和异常:
6.4.1. 无法根据前端 form 里面的 input 元素的 name 读到前端 form 里面的 input 里面用户输入的内容;
6.4.2. 如果用户输入的文字包括中文,读内容时会出现多字节编码不支持的异常提示。


7. 文件上传:原理同样是前端有一个 form ,其 method 必须是 post,然后其 path 指向后端 WebBroker 的某个 Action 的 path。这样用户选择文件并点提交,浏览器就会将文件 post 到服务器。WebBroker 的代码接收到来自浏览器的请求,从 Request 里面读取文件名,将文件内容(这里是一个 TStream)保存下来(写入磁盘文件,还是写入数据库的 Blob 字段,根据实际需要)。


8. CKEditor 里面直接上传文件和上传图片:这个需要对 CKEditor 进行一些设置,然后在 WebBroker 的后端代码里面,将接收到的上传文件保存好,最后要返回给 CKEditor 一段 JavaScript 代码让 CKEditor 可以在上传完成后自动将图片在服务器端的地址贴到 CKEditor 的内容里面,让图片直接就自动显示出来,无需用户去做贴图操作。我写有一篇文章介绍如何实现这个功能的详细细节。


9. 为了将业务逻辑代码(包括数据代码)和页面模板分开,所有页面模板都以外部文件的方式保存。后端程序加载外部文件,然后替换掉一些根据程序需要动态变化的内容后输出给浏览器。因此,前端工程师或美工可以任意修改模板页面。JavaScript 和 CSS 文件页同样处理。
9.1. 加载模板页面文件,然后根据需要对模板的内容进行处理的代码,放在一个单独的类里面,这个类实现一个接口。WebModule 里面的代码需要向浏览器返回页面 HTML 代码时,调用接口函数。这里采用接口,使得将来如果要大规模修改更换页面内容创建方式的后端代码,只要重新实现一个类,采用相同接口,则 WebModule1 里面的代码无需做任何改动。


10. 一些动态配置,这里没采用 ini。直接用一个文本文件,里面是 Name=Value 的格式,在 Delphi 里面用 TStringList 来处理,比 ini 更方便。然后用一个类来集中管理这些配置。这个类直接从 TStringList 继承下来。


在开发过程中,为了实现前端的一些效果,学习了一下 jquery 和 CSS,学习了一下 BootStrap。

碰到问题的时候,采用 Chrome 浏览器和 FireFox 浏览器的调试功能,可以看到后端发送给前端的内容是否正确,JavaScript 执行得是否正确,等等,比较容易找到问题所在。


后端的 WebBroker 程序,采用 StandAlone 模式,可以在 Delphi IDE 里面运行和单步跟踪。当前端访问到来,后端在 Delphi IDE 里面设置断点,单步跟踪,也容易看到问题在哪里。因此开发起来也非常的容易 debug 发现问题。