OpenResty 獲取請(qǐng)求 body

2021-08-12 17:31 更新

在 Nginx 的典型應(yīng)用場(chǎng)景中,幾乎都是只讀取 HTTP 頭即可,例如負(fù)載均衡、正反向代理等場(chǎng)景。但是對(duì)于 API Server 或者 Web Application ,對(duì) body 可以說(shuō)就比較敏感了。由于 OpenResty 基于 Nginx ,所以天然的對(duì)請(qǐng)求 body 的讀取細(xì)節(jié)與其他成熟 Web 框架有些不同。

最簡(jiǎn)單的 “Hello ****”

我們先來(lái)構(gòu)造最簡(jiǎn)單的一個(gè)請(qǐng)求,POST 一個(gè)名字給服務(wù)端,服務(wù)端應(yīng)答一個(gè) “Hello ****”。

http {
    server {
        listen    80;

        location /test {
            content_by_lua_block {
                local data = ngx.req.get_body_data()
                ngx.say("hello ", data)
            }
        }
    }
}

測(cè)試結(jié)果:

?  ~  curl 127.0.0.1/test -d jack
hello nil

大家可以看到 data 部分獲取為空,如果你熟悉其他 web 開(kāi)發(fā)框架,估計(jì)立刻就覺(jué)得 OpenResty 弱爆了。查閱一下官方 wiki 我們很快知道,原來(lái)我們還需要添加指令 lua_need_request_body 。究其原因,主要是 Nginx 誕生之初主要是為了解決負(fù)載均衡情況,而這種情況,是不需要讀取 body 就可以決定負(fù)載策略的,所以這個(gè)點(diǎn)對(duì)于 API Server 和 Web Application 開(kāi)發(fā)的同學(xué)有點(diǎn)怪。

參看下面例子:

http {
    server {
        listen    80;

        # 默認(rèn)讀取 body
        lua_need_request_body on;

        location /test {
            content_by_lua_block {
                local data = ngx.req.get_body_data()
                ngx.say("hello ", data)
            }
        }
    }
}

再次測(cè)試,符合我們預(yù)期:

?  ~  curl 127.0.0.1/test -d jack
hello jack

如果你只是某個(gè)接口需要讀取 body(并非全局行為),那么這時(shí)候也可以顯示調(diào)用 ngx.req.read_body() 接口,參看下面示例:

http {
    server {
        listen    80;

        location /test {
            content_by_lua_block {
                ngx.req.read_body()
                local data = ngx.req.get_body_data()
                ngx.say("hello ", data)
            }
        }
    }
}

body 偶爾讀取不到?

ngx.req.get_body_data() 讀請(qǐng)求體,會(huì)偶爾出現(xiàn)讀取不到直接返回 nil 的情況。

如果請(qǐng)求體尚未被讀取,請(qǐng)先調(diào)用 ngx.req.read_body (或打開(kāi) lua_need_request_body 選項(xiàng)強(qiáng)制本模塊讀取請(qǐng)求體,此方法不推薦)。

如果請(qǐng)求體已經(jīng)被存入臨時(shí)文件,請(qǐng)使用 ngx.req.get_body_file 函數(shù)代替。

如需要強(qiáng)制在內(nèi)存中保存請(qǐng)求體,請(qǐng)?jiān)O(shè)置 client_body_buffer_size 和 client_max_body_size 為同樣大小。

參考下面代碼:

http {
    server {
        listen    80;

        # 強(qiáng)制請(qǐng)求 body 到臨時(shí)文件中(僅僅為了演示)
        client_body_in_file_only on;

        location /test {
            content_by_lua_block {
                function getFile(file_name)
                    local f = assert(io.open(file_name, 'r'))
                    local string = f:read("*all")
                    f:close()
                    return string
                end

                ngx.req.read_body()
                local data = ngx.req.get_body_data()
                if nil == data then
                    local file_name = ngx.req.get_body_file()
                    ngx.say(">> temp file: ", file_name)
                    if file_name then
                        data = getFile(file_name)
                    end
                end

                ngx.say("hello ", data)
            }
        }
    }
}

測(cè)試結(jié)果:

?  ~  curl 127.0.0.1/test -d jack
>> temp file: /Users/rain/Downloads/nginx/client_body_temp/0000000018
hello jack

由于 Nginx 是為了解決負(fù)載均衡場(chǎng)景誕生的,所以它默認(rèn)是不讀取 body 的行為,會(huì)對(duì) API Server 和 Web Application 場(chǎng)景造成一些影響。根據(jù)需要正確讀取、丟棄 body 對(duì) OpenResty 開(kāi)發(fā)是至關(guān)重要的。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)