在前面的文章中,我们讨论REST相关的概念,以及REST在OpenResty中的应用。接下来,我们尝试使用OpenResty中搭建一个简单的API Server。
一、需求
实现一个能计算加减乘除的数学计算器
二、实现
我们先来看看一个最简陋的实现版本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| worker_processes 1; error_log logs/error.log; events { worker_connections 1024; } http { server { listen 80;
location /addition { content_by_lua_block { local args = ngx.req.get_uri_args() ngx.say(args.x + args.y) } }
location /subtraction { content_by_lua_block { local args = ngx.req.get_uri_args() ngx.say(args.x - args.y) } }
location /multiplication { content_by_lua_block { local args = ngx.req.get_uri_args() ngx.say(args.x * args.y) } }
location /division { content_by_lua_block { local args = ngx.req.get_uri_args() ngx.say(args.x / args.y) } } } }
|
可以看到,整个配置文件看起来非常复杂,下面我们来优化一下
三、优化
首先,入口要统一,接口的实现要独立,保持简洁:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| worker_processes 1; error_log logs/error.log; events { worker_connections 1024; }
http { lua_package_path '$prefix/lua/?.lua;/blah/?.lua;;';
lua_code_cache off;
server { listen 80;
location ~ ^/api/([-_a-zA-Z0-9/]+) { content_by_lua_file lua/$1.lua; } } }
|
${prefix}/lua目录下的文件分别如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| local args = ngx.req.get_uri_args() ngx.say(args.x + args.y)
local args = ngx.req.get_uri_args() ngx.say(args.x - args.y)
local args = ngx.req.get_uri_args() ngx.say(args.x * args.y)
local args = ngx.req.get_uri_args() ngx.say(args.x / args.y)
|
四、迭代
对于一个后端程序来说,怎么可以容忍输入参数不检查呢?万一客户端传入的不是数字或者为空怎么办?所以这些都要过滤掉。我们会发现,这些接口的参数检查过滤方法应该是统一的,那么如何在这几个 API 中如何共享这个方法呢?这时候就需要 Lua 模块来完成了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| worker_processes 1; error_log logs/error.log; events { worker_connections 1024; } http { server { listen 80;
location ~ ^/api/([-_a-zA-Z0-9/]+) { access_by_lua_file lua/access_check.lua;
content_by_lua_file lua/$1.lua; } } }
|
此处,我们将验证逻辑的执行时机放到了准入阶段,${prefix}/lua/access_check.lua的内容如下:
1 2 3 4 5 6 7
| local param= require("comm.param") local args = ngx.req.get_uri_args()
if not args.x or not args.y or not param.is_number(args.x, args.y) then ngx.exit(ngx.HTTP_BAD_REQUEST) return end
|
模块comm.param的路径为{$prefix}/lua/comm/param.lua,其内如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| local _M = {}
function _M.is_number(...) local arg = {...}
local num for _,v in ipairs(arg) do num = tonumber(v) if nil == num then return false end end
return true end
return _M
|
五、小结
通过如上几个步骤,一个简单的API Server就搭建完成了,最终整体的目录结构如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| . ├── conf │ ├── nginx.conf ├── logs │ ├── error.log │ └── nginx.pid ├── lua │ ├── access_check.lua │ ├── addition.lua │ ├── subtraction.lua │ ├── multiplication.lua │ ├── division.lua │ └── comm │ └── param.lua └── sbin └── nginx
|
六、参考
简单API Server框架