| --- |
| title: APISIX Lua 编码风格指南 |
| --- |
| |
| <!-- |
| # |
| # Licensed to the Apache Software Foundation (ASF) under one or more |
| # contributor license agreements. See the NOTICE file distributed with |
| # this work for additional information regarding copyright ownership. |
| # The ASF licenses this file to You under the Apache License, Version 2.0 |
| # (the "License"); you may not use this file except in compliance with |
| # the License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| # |
| --> |
| |
| ## 缩进 |
| |
| 使用 4 个空格作为缩进的标记: |
| |
| ```lua |
| --No |
| if a then |
| ngx.say("hello") |
| end |
| ``` |
| |
| ```lua |
| --Yes |
| if a then |
| ngx.say("hello") |
| end |
| ``` |
| |
| 你可以在使用的编辑器中把 tab 改为 4 个空格来简化操作。 |
| |
| ## 空格 |
| |
| 在操作符的两边,都需要用一个空格来做分隔: |
| |
| ```lua |
| --No |
| local i=1 |
| local s = "apisix" |
| ``` |
| |
| ```lua |
| --Yes |
| local i = 1 |
| local s = "apisix" |
| ``` |
| |
| ## 空行 |
| |
| 不少开发者会在行尾增加一个分号: |
| |
| ```lua |
| --No |
| if a then |
| ngx.say("hello"); |
| end; |
| ``` |
| |
| 增加分号会让 Lua 代码显得非常丑陋,也是没有必要的。 |
| |
| 另外,不要为了显得“简洁”节省代码行数,而把多行代码变为一行。这样会在定位错误的时候不知道到底哪一段代码出了问题: |
| |
| ```lua |
| --No |
| if a then ngx.say("hello") end |
| ``` |
| |
| ```lua |
| --Yes |
| if a then |
| ngx.say("hello") |
| end |
| ``` |
| |
| 函数之间需要用两个空行来做分隔: |
| |
| ```lua |
| --No |
| local function foo() |
| end |
| local function bar() |
| end |
| ``` |
| |
| ```lua |
| --Yes |
| local function foo() |
| end |
| |
| |
| local function bar() |
| end |
| ``` |
| |
| 如果有多个 if elseif 的分支,它们之间需要一个空行来做分隔: |
| |
| ```lua |
| --No |
| if a == 1 then |
| foo() |
| elseif a== 2 then |
| bar() |
| elseif a == 3 then |
| run() |
| else |
| error() |
| end |
| ``` |
| |
| ```lua |
| --Yes |
| if a == 1 then |
| foo() |
| |
| elseif a== 2 then |
| bar() |
| |
| elseif a == 3 then |
| run() |
| |
| else |
| error() |
| end |
| ``` |
| |
| ## 每行最大长度 |
| |
| 每行不能超过 80 个字符,超过的话,需要换行并对齐: |
| |
| ```lua |
| --No |
| return limit_conn_new("plugin-limit-conn", conf.conn, conf.burst, conf.default_conn_delay) |
| ``` |
| |
| ```lua |
| --Yes |
| return limit_conn_new("plugin-limit-conn", conf.conn, conf.burst, |
| conf.default_conn_delay) |
| ``` |
| |
| 在换行对齐的时候,要体现出上下两行的对应关系。 |
| |
| 就上面示例而言,第二行函数的参数,要在第一行左括号的右边。 |
| |
| 如果是字符串拼接的对齐,需要把 `..` 放到下一行中: |
| |
| ```lua |
| --No |
| return limit_conn_new("plugin-limit-conn" .. "plugin-limit-conn" .. |
| "plugin-limit-conn") |
| ``` |
| |
| ```lua |
| --Yes |
| return limit_conn_new("plugin-limit-conn" .. "plugin-limit-conn" |
| .. "plugin-limit-conn") |
| ``` |
| |
| ```lua |
| --Yes |
| return "param1", "plugin-limit-conn" |
| .. "plugin-limit-conn") |
| ``` |
| |
| ## 变量 |
| |
| 应该永远使用局部变量,不要使用全局变量: |
| |
| ```lua |
| --No |
| i = 1 |
| s = "apisix" |
| ``` |
| |
| ```lua |
| --Yes |
| local i = 1 |
| local s = "apisix" |
| ``` |
| |
| 变量命名使用 `snake_case`(蛇形命名法)风格: |
| |
| ```lua |
| --No |
| local IndexArr = 1 |
| local str_Name = "apisix" |
| ``` |
| |
| ```lua |
| --Yes |
| local index_arr = 1 |
| local str_name = "apisix" |
| ``` |
| |
| 对于常量要使用全部大写: |
| |
| ```lua |
| --No |
| local max_int = 65535 |
| local server_name = "apisix" |
| ``` |
| |
| ```lua |
| --Yes |
| local MAX_INT = 65535 |
| local SERVER_NAME = "apisix" |
| ``` |
| |
| ## 表格/数组 |
| |
| 使用 `table.new` 来预先分配数组: |
| |
| ```lua |
| --No |
| local t = {} |
| for i = 1, 100 do |
| t[i] = i |
| end |
| ``` |
| |
| ```lua |
| --Yes |
| local new_tab = require "table.new" |
| local t = new_tab(100, 0) |
| for i = 1, 100 do |
| t[i] = i |
| end |
| ``` |
| |
| 不要在数组中使用 `nil`: |
| |
| ```lua |
| --No |
| local t = {1, 2, nil, 3} |
| ``` |
| |
| 如果一定要使用空值,请用 `ngx.null` 来表示: |
| |
| ```lua |
| --Yes |
| local t = {1, 2, ngx.null, 3} |
| ``` |
| |
| ## 字符串 |
| |
| 不要在热代码路径上拼接字符串: |
| |
| ```lua |
| --No |
| local s = "" |
| for i = 1, 100000 do |
| s = s .. "a" |
| end |
| ``` |
| |
| ```lua |
| --Yes |
| local new_tab = require "table.new" |
| local t = new_tab(100000, 0) |
| for i = 1, 100000 do |
| t[i] = "a" |
| end |
| local s = table.concat(t, "") |
| ``` |
| |
| ## 函数 |
| |
| 函数的命名也同样遵循 `snake_case`(蛇形命名法): |
| |
| ```lua |
| --No |
| local function testNginx() |
| end |
| ``` |
| |
| ```lua |
| --Yes |
| local function test_nginx() |
| end |
| ``` |
| |
| 函数应该尽可能早的返回: |
| |
| ```lua |
| --No |
| local function check(age, name) |
| local ret = true |
| if age < 20 then |
| ret = false |
| end |
| |
| if name == "a" then |
| ret = false |
| end |
| -- do something else |
| return ret |
| end |
| ``` |
| |
| ```lua |
| --Yes |
| local function check(age, name) |
| if age < 20 then |
| return false |
| end |
| |
| if name == "a" then |
| return false |
| end |
| -- do something else |
| return true |
| end |
| ``` |
| |
| ## 模块 |
| |
| 所有 `require` 的库都要 `local` 化: |
| |
| ```lua |
| --No |
| local function foo() |
| local ok, err = ngx.timer.at(delay, handler) |
| end |
| ``` |
| |
| ```lua |
| --Yes |
| local timer_at = ngx.timer.at |
| |
| local function foo() |
| local ok, err = timer_at(delay, handler) |
| end |
| ``` |
| |
| 为了风格的统一,`require` 和 `ngx` 也需要 `local` 化: |
| |
| ```lua |
| --No |
| local core = require("apisix.core") |
| local timer_at = ngx.timer.at |
| |
| local function foo() |
| local ok, err = timer_at(delay, handler) |
| end |
| ``` |
| |
| ```lua |
| --Yes |
| local ngx = ngx |
| local require = require |
| local core = require("apisix.core") |
| local timer_at = ngx.timer.at |
| |
| local function foo() |
| local ok, err = timer_at(delay, handler) |
| end |
| ``` |
| |
| ## 错误处理 |
| |
| 对于有错误信息返回的函数,必须对错误信息进行判断和处理: |
| |
| ```lua |
| --No |
| local sock = ngx.socket.tcp() |
| local ok = sock:connect("www.google.com", 80) |
| ngx.say("successfully connected to google!") |
| ``` |
| |
| ```lua |
| --Yes |
| local sock = ngx.socket.tcp() |
| local ok, err = sock:connect("www.google.com", 80) |
| if not ok then |
| ngx.say("failed to connect to google: ", err) |
| return |
| end |
| ngx.say("successfully connected to google!") |
| ``` |
| |
| 自己编写的函数,错误信息要作为第二个参数,用字符串的格式返回: |
| |
| ```lua |
| --No |
| local function foo() |
| local ok, err = func() |
| if not ok then |
| return false |
| end |
| return true |
| end |
| ``` |
| |
| ```lua |
| --No |
| local function foo() |
| local ok, err = func() |
| if not ok then |
| return false, {msg = err} |
| end |
| return true |
| end |
| ``` |
| |
| ```lua |
| --Yes |
| local function foo() |
| local ok, err = func() |
| if not ok then |
| return false, "failed to call func(): " .. err |
| end |
| return true |
| end |
| ``` |