API网关Kong实践笔记

Kong[nginx]-19 玩转ngx.redirect

2019-08-25  本文已影响7人  国服最坑开发

KONG专题目录


NR;TL

今天我们要实现的需求如下:

App组的小伙伴们会不定期发布一个新产品, 包含iOSAndroid两个平台.
他们希望后端提供一个统一的下载链接, 可以根据手机的不同, 自动返回相应的实现下载地址.

本文会以Kong插件的形式提供一个解决方案.

0x01 以终为始,我们的效果是什么?

我们会提供一个这样的统一入口URL:

http://aaa.com/app/url/{appName}

当我们的插件监听到这个路由的请求时,
通过分析Header里的User-Agent来区分手机设备类型.
然后跳转到与appName相应平台的下载结果.
这里会用到一个方法ngx.redirect

ngx.redirect
syntax: ngx.redirect(uri, status?)

context: rewrite_by_lua, access_by_lua, content_by_lua*

Issue an HTTP 301 or 302 redirection to uri.

Redirecting arbitrary external URLs is also supported, for >example:

return ngx.redirect("http://www.google.com")

说一下重点:

0x02 代码实现

为了方便理解代码实现, 我们先看一下使用效果图
插件配置时, 我们需要配置一个参数项: appSettings
它可以接收多个值.
每一个值的字符格式为 : appName`android下载地址`iOS下载地址`
注意的是, 我使用了反引号作为字符串分隔符

插件参数配置

然后需要说明的地方就是这个配置参数加载的时候
我做了两步:

这一部分的代码实现受限于目前对lua的熟悉程序, 请小伙伴们自行优化完善.


local BasePlugin = require "kong.plugins.base_plugin"

local Handler    = BasePlugin:extend()

Handler.VERSION  = "1.0.0"
Handler.PRIORITY = 10

local iOSDevices = {
    'iPhone',
    'iPad',
    'iPod',
    'iOS',
}

local function splitStr (inputStr, sep)
    if sep == nil then
        sep = "%s"
    end
    local t={}
    for str in string.gmatch(inputStr, "([^"..sep.."]+)") do
        table.insert(t, str)
    end
    return t
end

local function not_found()
    return kong.response.exit(404, "404 Not Found", {
        ["Content-Type"]     = "text/plain",
    })
end

function Handler:access(config)
    Handler.super.access(self)

    -- 查找{appName}参数
    local pathVars = splitStr(kong.request.get_path(), '/')
    if #pathVars < 3 then
        not_found()
    end

    local appName = pathVars[3]

    -- 判断客户端设备类型
    local userAgent = kong.request.get_header("User-Agent") or 'android'
    local isAndroid = true
    for _, v in ipairs(iOSDevices) do
        if string.find(userAgent, v) then
            isAndroid = false
        end
    end

    kong.log(isAndroid)

    -- 加载插件配置
    local appUrlTable = {}
    for _, v in ipairs(config.appSettings) do
        local _app = splitStr(v, '`')
        appUrlTable[_app[1]] = _app
    end

    -- 查找appName
    local appNameUrls = appUrlTable[appName]
    if appNameUrls == nil then
        not_found()
    end

    -- 根据类型返回相应实际下载URL
    if isAndroid then
        ngx.redirect(appNameUrls[2])
    else
        ngx.redirect(appNameUrls[3])
    end

end

return Handler
local typedefs     = require "kong.db.schema.typedefs"

local string_array = {
    type     = "array",
    default  = {},
    elements = { type = "string" },
}

return {
    name   = "app-url",
    fields = {
        { protocols = typedefs.protocols_http },
        { config = {
            type   = "record",
            fields = {
                { appSettings = string_array },
            },
        },
        },
    },
}

0x03 插件使用

插件安装部分, 这里略过, 前文已经多次提及, 这里真的不想写了 - -!

输入内容为 jianshu`http://www.android.com`http://www.apple.com

配置
0x04 验证

成功时的请求: http://aaa.com/app/url/jianshu

成功时效果

android.com 网站打不开, 看不到效果, 哇哈哈哈, 不想改地址重测了.

0x05 小结

是的, 我们做到了, 技术难度不大, 但真的在API网关层面做完了..
不需要后端服务开发, 而且还带UI配置界面, 使用体验极好 :)

PS: 昨天中午午觉后,突发耳鸣,唉, 我真的想静静了


KONG专题目录


上一篇下一篇

猜你喜欢

热点阅读