全部產品
Search
文件中心

API Gateway:使用Lua外掛程式

更新時間:Jan 08, 2025

Lua是一種輕量級、高效的指令碼語言,在雲原生API Gateway開發中,Lua可以用於編寫和執行各種網關程式,例如API Gateway、訊息網關、反向 Proxy等。通過Lua指令碼,開發人員可以實現請求的路由、過濾、鑒權等功能,並進行定製化的處理。在一些代理中,比如Nginx和Envoy,Lua可以被嵌入用於處理請求和響應,並進行日誌輸出和其他定製化操作。本文中的Lua指令碼用於在Envoy代理中處理請求和響應,並將請求和響應的頭部和本文資訊以日誌的形式輸出。

使用限制

雲原生API Gateway版本為2.0.0以上。

注意事項

出於安全考慮,雲原生API Gateway預設禁用以下Lua庫和函數。

  • debug.debug

  • debug.getfenv

  • debug.getregistry

  • dofile

  • io

  • loadfile

  • os.execute

  • os.getenv

  • os.remove

  • os.rename

  • os.tmpname

操作步驟

  1. 登入雲原生API Gateway控制台

  2. 在左側導覽列,選擇執行個體,並在頂部功能表列選擇地區。

  3. 執行個體頁面,單擊目標網關執行個體名稱。

  4. 在左側導覽列,選擇外掛程式,單擊安裝外掛程式

  5. 安裝外掛程式頁面的搜尋方塊中搜尋lua,找到lua資源卡片並單擊安裝並配置

  6. 規則配置頁面,配置如下參數。

    • 生效範圍處,選擇Lua外掛程式的應用範圍。

    • 選擇介面/路由級外掛程式規則網域名稱級外掛程式規則時單擊添加規則,在添加規則彈框中配置相關參數,然後在外掛程式規則框中填寫Lua代碼,最後單擊確定

    • 選擇執行個體級外掛程式規則時,在外掛程式規則框中填寫Lua代碼,最後單擊儲存

API參考

關於網關提供的Lua API詳細資料,請參見Lua

如果您有使用JSON的訴求,可通過如下方式。

  • 引入JSON包:local json = require "json"

  • 序列化JSON:local json_str = json.encode(obj)

  • 還原序列化JSON:local obj = json.decode(json_str)

如果在序列化或還原序列化時出現錯誤,Lua將調用error函數拋出錯誤,並終止當前處理。

常見用例

列印完整的請求應答資訊到外掛程式日誌

  1. 登入雲原生API Gateway控制台

  2. 在左側導覽列,選擇執行個體,並在頂部功能表列選擇地區。

  3. 執行個體頁面,單擊目標網關執行個體名稱。

  4. 在左側導覽列,選擇外掛程式,然後單擊目標外掛程式操作列下的規則配置

  5. 規則配置中配置以下Lua代碼。

    說明

    根據此代碼配置,只會列印以下content-type類型的請求Body和應答Body,且Body不能超過1024位元組(1 KB)。

    • application/x-www-form-urlencoded

    • application/json

    • text/plain

    local maxBodySize = 1024
    
    function check_content_readable(type)
      if type == nil then
        return false
      end
      if string.find(type, "application/x-www-form-urlencoded",1,true) or string.find(type, "application/json",1,true) or string.find(type, "text/plain",1,true) then
         return true
      end
      return false
    end
    
    function envoy_on_request(request_handle)
      local headers = request_handle:headers()
      local headersStr = ""
      local contentType
      for key, value in pairs(headers) do
        if key == "content-type" then
           contentType = value
        end
        headersStr = headersStr  .. key .. "=" .. value .. ", "
      end
      request_handle:streamInfo():dynamicMetadata():set("envoy.lua","request_headers",headersStr)
      local requestBody = ""
      if check_content_readable(contentType) then
        for chunk in request_handle:bodyChunks() do
          if (chunk:length() > 0) then
            requestBody = requestBody .. chunk:getBytes(0, chunk:length())
          end
          if (#requestBody > maxBodySize) then
             requestBody = requestBody .. "<truncated>"
             break
          end
        end
      end
      request_handle:streamInfo():dynamicMetadata():set("envoy.lua","request_body",string.gsub(requestBody,"\n","\\n"))
    end
    
    function envoy_on_response(response_handle)
      local headers = response_handle:headers()
      local headersStr = ""
      local contentType
      local contentEncoding = false
      for key, value in pairs(headers) do
        if key == "content-type" then
           contentType = value
        elseif key == "content-encoding" then
           contentEncoding = true
        end
        headersStr = headersStr .. key .. "=" .. value .. ", "
      end
      local responseBody = ""
      if check_content_readable(contentType) and not contentEncoding then
        for chunk in response_handle:bodyChunks() do
          if (chunk:length() > 0) then
            responseBody = responseBody .. chunk:getBytes(0, chunk:length())
          end
          if (#responseBody > maxBodySize) then
             responseBody = responseBody .. "<truncated>"
             break
          end
        end
      end
      local reqHeaders = ""
      local reqBody = ""
      local metadata = response_handle:streamInfo():dynamicMetadata():get("envoy.lua")
      if metadata ~= nil then
        local headers = response_handle:streamInfo():dynamicMetadata():get("envoy.lua")["request_headers"]
        if headers ~= nil then
          reqHeaders = headers
        end
        local body = response_handle:streamInfo():dynamicMetadata():get("envoy.lua")["request_body"]
        if body ~= nil then
          reqBody = body
        end
      end
      response_handle:logInfo("request Headers: [" .. reqHeaders .. "] request Body: [" .. string.gsub(reqBody,"\n","\\n") .. "] response Headers: [" .. headersStr .. "] response Body: [" .. string.gsub(responseBody,"\n","\\n") .. "]")
    end
  6. 單擊目標Lua外掛程式,選擇日誌頁簽,單擊立即開啟日誌投遞功能

  7. 日誌投遞開啟後,外掛程式日誌將投遞到SLS並可在頁面中查看。

    如下所示,可以使用訪問日誌中的request-id,在Lua外掛程式日誌中找到完整的請求和響應資訊。

    image

將完整的請求應答資訊添加到訪問日誌

  1. 登入雲原生API Gateway控制台

  2. 在左側導覽列,選擇執行個體,並在頂部功能表列選擇地區。

  3. 執行個體頁面,單擊目標網關執行個體名稱。

  4. 在左側導覽列,選擇外掛程式,然後單擊目標外掛程式操作列下的規則配置

  5. 規則配置中配置以下Lua代碼。

  6. 網關的訪問日誌參數支援配置自訂動態中繼資料,首先需要在外掛程式中定義中繼資料。以下代碼一共定義了四個中繼資料資訊,分別對應要求標頭、請求Body、回應標頭、響應Body:

    • envoy.lua:request_headers

    • envoy.lua:request_body

    • envoy.lua:response_headers

    • envoy.lua:response_body

    local maxBodySize = 1024
    
    function check_content_readable(type)
      if type == nil then
        return false
      end
      if string.find(type, "application/x-www-form-urlencoded",1,true) or string.find(type, "application/json",1,true) or string.find(type, "text/plain",1,true) then
         return true
      end
      return false
    end
    
    function envoy_on_request(request_handle)
      local headers = request_handle:headers()
      local headersStr = ""
      local contentType
      for key, value in pairs(headers) do
        if key == "content-type" then
           contentType = value
        end
        headersStr = headersStr  .. key .. "=" .. value .. ", "
      end
      request_handle:streamInfo():dynamicMetadata():set("envoy.lua","request_headers",headersStr)
      local requestBody = ""
      if check_content_readable(contentType) then
        for chunk in request_handle:bodyChunks() do
          if (chunk:length() > 0) then
            requestBody = requestBody .. chunk:getBytes(0, chunk:length())
          end
          if (#requestBody > maxBodySize) then
             requestBody = requestBody .. "<truncated>"
             break
          end
        end
      end
      request_handle:streamInfo():dynamicMetadata():set("envoy.lua","request_body",string.gsub(requestBody,"\n","\\n"))
    end
    
    function envoy_on_response(response_handle)
      local headers = response_handle:headers()
      local headersStr = ""
      local contentType
      local contentEncoding = false
      for key, value in pairs(headers) do
        if key == "content-type" then
           contentType = value
        elseif key == "content-encoding" then
           contentEncoding = true
        end
        headersStr = headersStr .. key .. "=" .. value .. ", "
      end
      response_handle:streamInfo():dynamicMetadata():set("envoy.lua","response_headers",headersStr)
      local responseBody = ""
      if check_content_readable(contentType) and not contentEncoding then
        for chunk in response_handle:bodyChunks() do
          if (chunk:length() > 0) then
            responseBody = responseBody .. chunk:getBytes(0, chunk:length())
          end
          if (#responseBody > maxBodySize) then
             responseBody = responseBody .. "<truncated>"
             break
          end
        end
      end
      response_handle:streamInfo():dynamicMetadata():set("envoy.lua","response_body",string.gsub(responseBody,"\n","\\n"))
    end
  7. 在左側導覽列,選擇參數配置。在可觀測性參數地區對日誌格式進行調整。在日誌格式調整彈框中添加對應的中繼資料資訊,就可以在訪問日誌中看到對應的資訊。

    image