Nginx配合Lua实现根据body参数进行路由


文章目录

  • 前言
  • 一、前置条件
  • 二、配置步骤
  • 1.docker-compose配置文件
  • 2.nginx.conf文件
  • 3.lua脚本
  • 3.启动测试
  • 3.1 执行docker-compose up -d启动nginx
  • 3.2 使用postman调用http://172.39.10.3:8080/data/handle;根据入参查看目的服务的日志,有调用日志即成功转发



前言

由于公司需求,需要根据post请求中某个字段的值进行判断调用哪个第三方接口,接口路径都是相同的,入参也相同;且属于特殊需求,在不改动代码情况下,使用nginx+lua脚本形式实现此需求


提示:以下是本篇文章正文内容,下面案例可供参考

一、前置条件

  • docker-compose
  • nginx镜像:fabiocicerchia/nginx-lua:latest(需使用包含lua模块的镜像)

二、配置步骤

1.docker-compose配置文件

version: '3'

services:
  nginx:
    image: fabiocicerchia/nginx-lua:latest
    container_name: nginx
    ports:
      - "8080:80"  # 将容器的 80 端口映射到主机的 8080 端口
    volumes:
      - ./conf/nginx.conf:/etc/nginx/nginx.conf  # 挂载本地的 nginx.conf 到容器内部
      - ./html:/usr/share/nginx/html  # 挂载本地的 HTML 文件到 Nginx 默认的站点目录
      - ./script:/home

2.nginx.conf文件

worker_processes 1;
error_log /var/log/nginx/error.log info;
events {
    worker_connections 1024;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    lua_package_path "/home/?.lua;;";
    log_format main '$remote_addr - $remote_user [$time_local] '
                    '"$request" $status $body_bytes_sent '
                    '"$http_referer" "$http_user_agent"';

    access_log /var/log/nginx/access.log main;  # 使用 main 格式记录访问日志
    sendfile on;
    
    server {
        listen 80;
        server_name localhost;

        location /data {
            # 使用lua脚本
           content_by_lua_file /home/route.lua;
        }

        location /api/route1 {
            # 配置代理到另一台服务器的接口地址
            proxy_pass http://172.39.10.22:8788/data/handle;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
        
        location /api/route2 {
            # 配置代理到另一台服务器的接口地址
            proxy_pass http://172.39.10.22:8789/data/handle;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }
}

3.lua脚本

-- 获取请求体数据
local function get_request_body()
    ngx.req.read_body()
    local body_data = ngx.req.get_body_data()
    return body_data
end

-- 解析请求体数据,提取参数
local function parse_request_body(body_data)
    ngx.log(ngx.INFO, "Request Body Data: ", body_data)
    if not body_data then
        return {}
    end

    local args = {}
   
    -- 去掉请求体数据中的空白字符
    body_data = body_data:gsub("%s+", "")

    -- 解析 JSON 对象
    if body_data:sub(1, 1) == "{" and body_data:sub(-1) == "}" then
        -- 去掉开头的 "{" 和结尾的 "}"
        body_data = body_data:sub(2, -2)

        -- 分割键值对
        for pair in body_data:gmatch('("[^"]-":%s*"[^"]-"),?') do
            local key, value = pair:match('("[^"]-"):%s*"([^"]-)"')
            if key and value then
                -- 去掉键和值的引号
                key = key:gsub('"', '')
                value = value:gsub('"', '')

                -- 处理嵌套的对象或数组
                if value:sub(1, 1) == "{" then
                    value = parse_request_body("{" .. value .. "}")
                elseif value:sub(1, 1) == "[" then
                    -- 此处处理数组,根据需要解析数组内部的元素
                    -- 这里简化为直接存储数组字符串
                end

                args[key] = value
            end
        end
    end
    return args
end

-- 根据参数值进行路由
local function route_request(args)
    -- ngx.log(ngx.INFO, args)
    local action = args.HospitalCode
    ngx.log(ngx.INFO, action)
    if action == "0001" then
        ngx.exec("/api/route1")  -- 内部重定向到 /api/route1
    elseif action == "0002" then
        ngx.exec("/api/route2")  -- 内部重定向到 /api/route2
    else
        ngx.exit(400)  -- 返回 400 错误
    end
end

-- 主处理逻辑
local function main()
    local body_data = get_request_body()
    local args = parse_request_body(body_data)
    route_request(args)
end

-- 执行主处理逻辑
main()

脚本通过手动处理 JSON 字符串以解析请求体数据,如需要API解析可以导入 cjson 模块,用于处理 JSON 数据;因fabiocicerchia/nginx-lua没有这个模块,故未使用,有需要的可以研究一下。

3.启动测试

3.1 执行docker-compose up -d启动nginx

3.2 使用postman调用http://172.39.10.3:8080/data/handle;根据入参查看目的服务的日志,有调用日志即成功转发