无节操非程序猿

莫催稿,催稿也不交

CodeTyphon 服务器端应用开发

CodeTyphon 提供了强大的 fpweb 框架,可以方便的实现 Web 模块,从而提供了支持服务器端开发的能力。该框架允许开发者轻松的构建出 CGI,FastCGI,Apache Module,独立 HTTP 服务器,爬虫,甚至是嵌入式服务器等。生成的程序可以独立运行,也可被 Apache、Nginx 等进行配置管理。

由于 CodeTyphon 在跨平台方面显得过于强大,大部分开发者并不重视 CodeTyphon 在服务器端的开发能力,导致了服务器端相关的内容无人研究、资料缺失,编写本章时亦无任何资料可供参考。不过技术本身是要靠钻研的,查看框架源码已可以帮助开发者获得大量信息,接下去我们就一起研究一下,CodeTyphon 如何实现一个服务器端的应用,以及如何将应用部署到 Apache 和 Nginx。


『新建服务器程序』

首先来实现一个独立HTTP服务器,熟悉一下其工作模式,CodeTyphon 的向导已可以帮我们完成服务器的基本设置。先选取菜单内 Project | New Project,在弹出的新建项目框内,选择 HTTP Server Application。


   (图1 新建HTTP Server Application)

新建时,系统会再次弹出对话框,对服务器作一些基本的设置,如文件路径,端口号,线程支持等,在此要勾选 Use threads to serve requests in,表示以多线程方式处理请求,如不勾选,则请求会进入队列。同时,为了避免与其他服务器软件冲突,占用的端口号尽量改大一些,如常用的 8000、8080 等则尽量不要使用,在正式部署时,可以再次修改端口号以满足实际部署的需要。此处我们使用 12300 作为端口号。


   (图2 设置多线程与端口)

此时系统会生成一个 FPWebModule 的空窗口,这个窗口上空无一物,但是它已经是一个可以正常运行的服务器了,只是目前还无法处理用户的请求。为了直观起见,先将 FPWebModule1 这个难看的类名修改一下,改为 WMHello,WM 即取 WebModule 之简写。保存项目,将 lpr 文件命名为 helloserver,将 pas 文件命名为 untwm_hello。

然后在属性区域内勾选 CreateSession,表示使用 Session 来维持用户状态。


   (图3 勾选CreateSession)

完成后,需要对 WebModule 进行引用的补足,从菜单中选取 Project | View Project Source,可以查阅项目文件,即 hello_server.lpr 的代码,在 uses 的最前端增加 cthreads 的引用,完成后如图4所示:


   (图4 引用cthreads)

随后再打开 untwmhello.pas 的代码,在 uses 后加入 iniwebsession 的引用,系统会自动寻找 session 管理的类,如果不引用 iniwebsession,则在运行时会报没有 session 管理器的异常。完成后如图5所示:


   (图5 引用iniwebsession)

此刻已完成了服务器的基本设置,下面就需要编写请求的接收和处理了,这部分相对之前的开发,可能会略显麻烦,此处无法利用所见即所得的开发了,只能拖放非可视组件,同时视图部分的开发需要使用 HTML。


『接收并处理请求』

对于 WebModule 来说,有两种处理请求的方式,一种是 WebModule 的 OnRequest 事件,它会接收所有发送到该 WebModule 的请求,另一种是 WebModule 的 Actions 属性,该属性内可以设置多个 Action,对应不同的请求形式,同时每一个 Action 拥有自己的 OnRequest 事件。一个标准的 OnRequest 事件形式如下:


   (图6 OnRequest请求)

ARequest 参数用于获取请求的信息,AResponse 用于输出响应,Handled 表示该请求是否要传递。将 Handled 设为 True,即表示请求不再传递,否则请求会传递至 Actions。对于 Actions 内的 OnRequest,Handled 设为 False 会将请求传递给下一个 Action,直到 Handled 被设为 True。需要注意的是,如果整个 WebModule 内,没有任何一个 OnRequest 的 Handled 被设为 True,则服务器在接收到请求时会报错。

下面来写一个最简单的请求响应,只使用 WebModule 的 OnRequest,接收到响应时,返回一个 HTML 页面,显示 Hello CodeTyphon 字样。 我们先把页面完成,HTML 代码如下:


   (图7 HTML代码)

将 HTML 保存至项目目录下。然后写一段响应请求的代码,如下所示:


   (图8 响应请求并返回HTML页面)

还需要再做最重要的一步,注册 HTTP 模块,只有注册模块后,请求才有可能被转发到该模块。针对单一 WebModule 的情况下,注册的 HTTP 模块即是默认模块,所有的请求均会被发到此模块。


   (图9 注册HTTP模块)

此处需注意,在新建项目时,注册 HTTP 模块的代码即会自动生成,但是当我们修改了项目后,生成的内容已不再符合需要,必须将其修改为符合的内容。另外 HTTP 模块在处理分隔符方面比较不智能,是否带有斜线会被认为是不同的请求,因此均需要予以注册。

现在直接按 F9 运行程序,服务器即成功运行了,如果提示有异常,可以选择 Ignore 并继续运行。在服务器运行后,可以打开浏览器访问以下网址:

http://localhost:12300 或 http://localhost:12300/hello

即可看到如图10所示效果:


   (图10 服务器运行效果)


『实现多个Action』

下面我们来尝试完成拥有多个 Action 的服务器,再次重申一下,Actions 是 WebModule 下 OnRequest 的后续,如果该 Handled 被设为 True,则请求不会走到下属的 Actions。当然也可以取消 WebModule 的 OnRequest,仅使用 Actions 进行处理。

此处我们修改一下 HTML 模板代码,以区分请求的 Action,如下:


   (图11 修改HTML模板)

然后修改 WebModule 的属性,双击 Actions 属性,在弹出的窗口中添加三个 Action,并分别命名为 act1、act2、act3,将 act1 设为 Default,以防止 Action 不命中引起的异常,完成后再将它们的 OnRequest 事件指向一处。


   (图12 设置Actions属性)

然后即可着手编写这三个 Action 的 OnRequest 代码,由于已将 OnRequest 指向一处,因此就需要判断 Sender,以确定请求来源。


   (图13 编写Action的请求)

最后还需要修改一下 WebModule 的 OnRequest,并在此判断是否进入 Actions 处理。


   (图14 修改WebModule的OnRequest)

完成后,就可以通过URL来访问已经写好的服务器了,试试输入以下 URL,就会看到不同的结果:

http://localhost:12300/hello
http://localhost:12300/hello/act1

不带Action的情况,直接返回Hello CodeTyphon,而带有Action的情况,则返回Action来源。


   (图15 返回Action来源)

目前我们已经完成了多个Action的分支请求响应,然而对于服务器来说,只能返回页面是完全不够的,还需要对请求方式,以及参数进行处理。


『接受并处理请求参数』

在 WebModule 内获取参数是非常容易的,还是按上述示例,我们进一步强化功能,接受一些参数,同时展现在返回的页面上,再次改一下 HTML 页面模板:


   (图16 再次修改HTML模板)

此处我们增加了一个 Hello 用户的显示,即传入用户名称作为参数,而页面将该名称予以显示。接下去修改 WebModule 的 OnRequest 代码:


   (图17 修改WebModule的OnRequest代码)

修改后重新按 F9 运行服务器,然后在浏览器内输入以下 URL 查看执行效果:

http://localhost:12300/hello?name=user
http://localhost:12300/hello/act1?name=user


   (图18 参数传递)


   (图19 参数传递)

可以看到,参数已被成功解析。上面的请求是 GET 请求,在常规的 Web 开发中,也会使用 POST 请求,WebModule 同样可以轻松的接到 POST 请求的参数,如下代码所示:

AName := ARequest.ContentFields.Values[‘name’];

需要注意的是,ContentFields 仅可以接受标准的 HTML FORM 请求方式,即 mimetype 必须为 application/x-www-form-urlencoded 或 multipart/form-data,如果是自定义的 mimetype,则请求的内容会被放到 ARequest.Content,以字符串形式传递到服务器端。

对于使用 POST 上传文件的情况,可以使用以下方法来获取文件:

AFileName := ARequest.Files[i].LocalFileName;

对于响应时需要发送二进制数据给客户端的情况,可以参考以下方法,并注意 SendContent 后尽可能的不再做其他操作:

AResponse.ContentSream := TMemoryStream.Create;
AResponse.ContentStream.LoadFromFile(‘/tmp/sample.png’);
AResponse.ContentType := ‘image/png’;
AResponse.ContentLength := AResponse.ContentStream.Size;
AResponse.SendContent;
AResponse.ContentStream.Free;

到现在为止,也许开发一个微型的服务器已经不在话下了,但是对于较大的服务器应用,显然不可能只有一个 WebModule,这样会大幅度降低性能,同时也使代码维护变得非常不便。解决方案就是使用多个 WebModule,让它们按照功能点的不同,各司其职,下面就讲述如何使用多个 WebModule。


『实现多个WebModule』

还是在上述示例代码的基础上进行修改,选取菜单中 File | New,在弹出的窗口中选择 WebModule:


   (图20 新建WebModule)

得到的 WebModule 与之前的并无差异,我们将其名称改为 WMWorld,并且保存为 untwmworld.pas。然后修改注册 HTTP 模块的部分,在这里需要注意的是,HTTP 模块的名称不能相同,之前的模块名称为 hello,则不再允许有第二个 hello 出现,否则 HTTP 服务器将无法启动。此处我们将 HTTP 模块注册为 world,如图21所示:


   (图21 注册新的HTTP模块)

此时编译程序后,会发现一个问题,之前我们可以通过访问 http://localhost:12300/ 直接访问到 hello 模块,而此时居然不能了,会提示出错。原因就是加了一个模块,服务器不知道哪一个才是默认的,按之前的设置显然不合理,于是就抛出了这样的错误。

要解决这个问题也不难,手动把 WMHello 设置为默认模块即可,代码如下:


   (图22 把WMHello设为默认)

此时再访问 http://localhost:12300/ 即可以正确的跳转至 WMHello。

若是在 HTTP 服务器内需要访问数据库,则可以按桌面应用的解决方案,拖放数据库相关组件至 WebModule,直接以组件操作数据即可。


   (图23 放置数据库组件)

独立的 HTTP 服务器用来做技术预演或是测试已经够用,但是要正式发布,供企业级应用,是远远不够的,还需要容器的支持,后面将讲述如何配置和发布服务器程序。


发表评论:

Powered By Z-BlogPHP 1.5.1 Zero

Copyright Rarnu 2017. All Rights Reserved.