NGINX从proxy_pass响应读取正文

时间:2019-12-16 10:39:05

标签: nginx lua proxypass

我有两台服务器:

  1. NGINX(它将文件ID交换到文件路径)
  2. Golang(接受文件ID并返回其路径)

示例:当浏览器客户端向https://example.com/file?id=123发出请求时,NGINX应将此请求代理到Golang服务器https://go.example.com/getpath?file_id=123,它将把响应返回给NGINX:

{
  data: {
    filePath: "/static/..."
  },
  status: "ok"
}

然后NGINX应该从filePath获取值并从该位置返回文件。

所以问题是如何在NGINX中读取响应(获取filePath)?

2 个答案:

答案 0 :(得分:5)

我假设您是软件开发人员,并且对您的应用程序拥有完全控制权,因此这里无需在圆孔中强加方钉。

不同种类的反向代理支持 ESI(Edge Side Includes)技术,该技术使开发人员可以用静态文件的内容或上游服务器的响应主体替换响应主体的不同部分。

Nginx也具有这种技术。它称为 SSI(服务器端包含)

location /file {
    ssi on;
    proxy_pass http://go.example.com;
}

您的上游服务器可以生成内容为<!--# include file="/path-to-static-files/some-static-file.ext" -->的主体,而 nginx将用文件内容替换此主体指令

但是您提到了流式传输...

这意味着文件将具有任意大小,并且使用 SSI的构建响应肯定会消耗宝贵的RAM 资源,因此我们需要计划#B

有一种“足够好”的方法可以将大文件提供给客户端,而无需向客户端显示文件的静态位置。 您可以使用nginx的错误处理程序根据上游服务器提供的信息来处理静态文件。 例如,上游服务器可以发送带有位置标头字段的重定向302,该标头字段包含文件的真实文件路径。 此响应未到达客户端,并被送入错误处理程序。

以下是配置示例:

location /file {
    error_page 302 = @service_static_file;
    proxy_intercept_errors on;
    proxy_set_header Host            $host;
    proxy_pass http://go.example.com;
}

location @service_static_file {
    root /hidden-files;
    try_files $upstream_http_location 404.html;
}

通过这种方法,您将能够在不使系统过载的情况下提供文件,同时可以控制向谁提供文件。

要使其正常工作,上游服务器应使用状态302和典型的“位置:”字段进行响应,并且nginx将使用位置内容在静态文件的“新”根目录中查找文件。

此方法之所以具有“足够好”类型(而不是完美)的原因,是因为它不支持部分请求(即范围:字节...)

答案 1 :(得分:1)

看起来您想对数据进行api调用,以运行决策和逻辑。那不是代理的全部内容。

nginx的核心代理功能并非针对您的工作而设计。

可能的解决方法:扩展nginx ...


Nginx + PHP

您的php代码将完成此工作。
作为客户端连接到Golang服务器,并将其他逻辑应用于响应。

<?php
    $response = file_get_contents('https://go.example.com/getpath?file_id='.$_GET["id"]);
    preg_match_all("/filePath: \"(.*?)\"/", $response, $filePath);
    readfile($filePath[1][0]);
?>
    location /getpath {
        try_files /getpath.php;
    }

这只是使它滚动的伪代码示例。

一些其他观察/评论:

  • Golang响应看起来不是有效的json,如果是,则将preg_match_all替换为json_decode。
  • readfile效率不是很高。考虑使用302响应来发挥创意。

Nginx + Lua

已启用网站:

lua_package_path "/etc/nginx/conf.d/lib/?.lua;;";

server {
    listen 80 default_server;
    listen [::]:80 default_server;

    location /getfile {
        root /var/www/html;
        resolver 8.8.8.8;
        set $filepath "/index.html";
        access_by_lua_file /etc/nginx/conf.d/getfile.lua;
        try_files $filepath =404;
    }
}

测试lua的行为是否符合预期:

getfile.lua(v1)

  ngx.var.filepath = "/static/...";

简化Golang响应主体,使其仅返回平淡的路径,然后使用它来设置文件路径:

getfile.lua(v2)

local http = require "resty.http"
local httpc = http.new()
local query_string = ngx.req.get_uri_args()
local res, err = httpc:request_uri('https://go.example.com/getpath?file_id=' .. query_string["id"], {
    method = "GET",
    keepalive_timeout = 60,
    keepalive_pool = 10
})

if res and res.status == ngx.HTTP_OK then
    body = string.gsub(res.body, '[\r\n%z]', '')
    ngx.var.filepath = body;
    ngx.log(ngx.ERR, "[" .. body .. "]");
else
    ngx.log(ngx.ERR, "missing response");
    ngx.exit(504);
end

resty.http

mkdir -p /etc/nginx/conf.d/lib/resty
wget "https://raw.githubusercontent.com/ledgetech/lua-resty-http/master/lib/resty/http_headers.lua" -P /etc/nginx/conf.d/lib/resty
wget "https://raw.githubusercontent.com/ledgetech/lua-resty-http/master/lib/resty/http.lua" -P /etc/nginx/conf.d/lib/resty