用 Prolog 提供 HTTP 服务
序言
书接上回,在上一篇文章中,我们通过尝试用 Prolog 编写 Hello World 程序,了解了 Prolog 程序的基本结构和运行方式。本篇将会在hello_world.pl
的基础上,实现一个简单的 HTTP 服务器。
启动 HTTP 服务器
利用 SWI-Prolog 内置的库,很容易就可以启动一个 HTTP 服务器。只需要调用函数http_server
即可
:- initialization(main, main).
:- use_module(library(http/http_server), [http_server/1]).
main(_) :-
http_server([port(8082)]),
sleep(100000000).
将上述代码保存到文件http_server.pl
中并运行便可以启动 HTTP 服务器
由于在源文件中没有定义任何接口,因此任何请求都只会返回 404 的响应
使用模块
在上文中出现的use_module
也是一种命令(directive
),它用于从模块中导入函数。例如,:- use_module(library(http/http_server), [http_server/1]).
的意思,就是从模块http/http_server
中导入一个参数个数为 1 的函数http_server
到当前模块中,如此一来,就可以直接调用它。
提供接口
正如前文所述,由于当前应用没有注册任何的路由规则,因此无论如何请求都只会返回 404 的响应。接下来我将给这个 HTTP 服务器添加一个非常简单的接口,来固定返回文本Hello, world!
。要注册一个接口,需要用到来自于模块http/http_dispatch
的函数http_handler
,从它的文档可以看到,它需要三个参数:
- 接口路径
Path
,如用一个原子来表示的路径/api/shorten
; - 负责实现接口业务逻辑的函数对象
Closure
; - 描述接口属性的列表
Options
,如描述该接口的请求方法。
这样一个固定返回 Hello World 字符串的接口可能是下面这样子的
:- initialization(main, main).
:- use_module(library(http/http_dispatch), [http_handler/3]).
:- use_module(library(http/http_server), [http_server/1]).
hello(_Request) :-
format('Content-Type: text/html~n~n'),
format('Hello, world!').
main(_) :-
http_handler('/api/shorten', hello, [methods([get])]),
http_server([port(8082)]),
sleep(100000000).
用老办法启动服务后的测试结果如下
接口路径
作为http_handler
的第一个参数,'/api/shorten'
看起来很像是其它主流语言中为人熟知的字符串,然而在 Prolog 中,它是一个原子(atom
)。尽管引号内的内容看起来一样,但原子与字符串并不相等,如下图所示
接口输出
从 SWI-Prolog 的文档可以知道,担任http_handler
第二个参数的Closure
必须负责输出至少Content-Type
和数据体部分。此外,尽管hello
的写法看起来是将响应的内容输出到了标准输出(颇有上古时期的 CGI 脚本的风格),但其实format
输出的目的地已经被框架所替换,可以换成下面的代码来更好地感受这一点
:- initialization(main, main).
:- use_module(library(http/http_dispatch), [http_handler/3]).
:- use_module(library(http/http_server), [http_server/1]).
hello(_Request) :-
current_output(Stream),
format(Stream, 'Content-Type: text/html~n~n', []),
format(Stream, 'Hello, world!', []).
main(_) :-
http_handler('/api/shorten', hello, [methods([get])]),
http_server([port(8082)]),
sleep(100000000).
而请求结果则是相同的。