获取请求 body

在 Nginx 的典型应用场景中,几乎都是只读取 HTTP 头即可,例如负载均衡、正反向代理等场景。但是对于 API Server 或者 Web Application ,对 body 可以说就比较敏感了。由于 OpenResty 基于 Nginx ,所以天然的对请求 body 的读取细节与其他成熟 Web 框架有些不同。

最简单的 “Hello ****”

我们先来构造最简单的一个请求,POST 一个名字给服务端,服务端应答一个 “Hello ****”。

  1. http {
  2. server {
  3. listen 80;
  4. location /test {
  5. content_by_lua_block {
  6. local data = ngx.req.get_body_data()
  7. ngx.say("hello ", data)
  8. }
  9. }
  10. }
  11. }

测试结果:

  1. ~ curl 127.0.0.1/test -d jack
  2. hello nil

大家可以看到 data 部分获取为空,如果你熟悉其他 Web 开发框架,估计立刻就觉得 OpenResty 弱爆了。查阅一下官方 wiki 我们很快知道,原来我们还需要添加指令 lua_need_request_body

究其原因,在于 Nginx 诞生之初主要是为了解决负载均衡问题,而这种情况,是不需要读取 body 就可以决定负载策略的,所以这个点对于 API Server 和 Web Application 开发的同学来说有点怪。

参看下面例子:

  1. http {
  2. server {
  3. listen 80;
  4. # 默认读取 body,全局行为,所有 location 均受影响
  5. lua_need_request_body on;
  6. location /test {
  7. content_by_lua_block {
  8. local data = ngx.req.get_body_data()
  9. ngx.say("hello ", data)
  10. }
  11. }
  12. }
  13. }

再次测试,符合我们预期:

  1. ~ curl 127.0.0.1/test -d jack
  2. hello jack

如果你只是某个接口需要读取 body(并非全局行为),那么这时候也可以显式调用 ngx.req.read_body() 接口,参看下面示例:

  1. http {
  2. server {
  3. listen 80;
  4. location /test {
  5. content_by_lua_block {
  6. ngx.req.read_body() -- 局部行为,仅在本接口内读取 body
  7. local data = ngx.req.get_body_data()
  8. ngx.say("hello ", data)
  9. }
  10. }
  11. }
  12. }

body 偶尔读取不到?

ngx.req.get_body_data() 读请求体,会偶尔出现读取不到直接返回 nil 的情况。

参考下面代码:

  1. http {
  2. server {
  3. listen 80;
  4. # 强制请求 body 到临时文件中(仅仅为了演示)
  5. client_body_in_file_only on;
  6. location /test {
  7. content_by_lua_block {
  8. function getFile(file_name)
  9. local f = assert(io.open(file_name, 'r'))
  10. local string = f:read("*all")
  11. f:close()
  12. return string
  13. end
  14. ngx.req.read_body()
  15. local data = ngx.req.get_body_data()
  16. if nil == data then
  17. local file_name = ngx.req.get_body_file()
  18. ngx.say(">> temp file: ", file_name)
  19. if file_name then
  20. data = getFile(file_name)
  21. end
  22. end
  23. ngx.say("hello ", data)
  24. }
  25. }
  26. }
  27. }

测试结果:

  1. ~ curl 127.0.0.1/test -d jack
  2. >> temp file: /Users/rain/Downloads/nginx/client_body_temp/0000000018
  3. hello jack

由于 Nginx 是为了应对负载均衡场景诞生的,所以它默认不读取 body 的行为,会对 API Server 和 Web Application 场景造成一些影响。 根据需要 正确读取、丢弃 body 对 OpenResty 开发是 至关重要 的。