Luci采用的是MVC的Web框架,即Model、View、Controller。
/usr/lib/lua/luci/controller/ --控制层
/usr/lib/lua/luci/view/ --视图层
/usr/lib/lua/luci/model/cbi/ --模型层
一、控制层Controller
控制层中的文件语法精简结构如下,主要为两个部分:模块声明和路由定义。
module("luci.controller.admin.network", package.seeall)
function index()
--定义方式1
entry({"admin", "network"}, firstchild(), _("Network") , 1)
--定义方式2
local page;
page = node("admin", "network", "vlan")
page.target = cbi("admin_network/vlan")
page.title = _("Switch")
page.order = 20
entry({"admin",'network',"tabs"},firstchild(),"Tabs",30)
--添加三级菜单
entry({"admin","network","tabs","tab1"},call("fun"),'Tab1',1)
entry({"admin","network","tabs","tab4"},template("tab4_tpl"),"Tab4",4)
end
local function fun ()
--执行fun方法
end
1、模块声明:
文件定义时需要声明其模块名称,按照其路径定义即可,比如该文件位于 /luci/controller/admin/network.lua
module(“luci.controller.admin.network”, package.seeall)
2、路由定义:
上面代码中我们可以看到使用了两种定义路由的方式,一种是直接使用entry方法来定义一个路由,并传入以下4个参数。第二种方式是使用node方法直接定义路由的路径,然后分开指定其他target,title等属性。entry方法内部也是使用node方法,所以两种方式等效。
entry(path, target, title=nil, order=nil)
-
path : 规定了此路由的访问路径,参数是一个字符串数组,比如 {“admin”, “test”, “test_view”} 这样的参数,在页面访问的时候,路径为 /admin/test/test_view ,"test" 是一级菜单。"test_view" 为二级菜单。如果使用node方法直接创建,则需要散列传参,不需要传入table。
-
target :规定访问此路径时,Luci框架执行的调用目标,根据调用目标的不同,LuCI
也有不同的调度行为。所有的target类型,在源码文件 dispatcher.lua中可以找到。target主要使用的有alias、firstchild、call、template、cbi、form、arcombine。
-
title:定义该节点在菜单上显示的名称,非必填项,如果使用了国际化配置,可以使用_(“Router”)这样的表示方式,在中文环境下显示路由,在英文环境下显示Router。如果不填title,则不会在菜单栏上面显示。但是可以通过url进行访问。
-
order:该节点在同级节点下的显示顺序。非必填项,从上至下,从左至右数值越小显示越靠前。 属性
-
target主要有如下种类:
-
firstchild() :自动指向该节点的第一个子节点(order最小的那个节点)
-
call(func_name):调用该文件下的指定函数,比如 call(“test_function”),则会在访问当前路径时,调用该文件下的test_function函数。在这些函数中,我们也可以处理一些数据。
-
template(tpl_name):根据给定的HTM模板名称访问指定的HTML页面,路径对应luci/view下面的文件。比如template(“admin_system/hello”),对应luci/view/admin_system/hello.htm 文件。
-
cbi(model,config):根据给定的model文件名,渲染一个CBI模型。路径对应luci/model/cbi下面的文件。同时可以传入一些config信息,比如 {nofooter:不显示cbi_footer,noheader:不显示cbi_header} 等等。其他用法可以在源码 dispatcher.lua 的 _cbi方法中进行查看。
-
form(model) : 与cbi()类似,不过渲染的是一个表单CBI模型。
-
arcombine(trg1,trg2):当需要访问同一个路由,而这个路由在有参和无参时的调用目标需要不一致的时候,可以使用arcombine把两个target进行组合起来(比如某个列表和其详情页分开时)。当有参数时调用trg2,无参时调用trg1.
-
alias(Path):重定向到另外一个节点上。
-- 访问/admin/test 时, 重定向到 /admin/test/test_view
entry({"admin", "test"}, alias("admin", "test", "test_view"), "重定向", 1)
节点属性:使用变量接收node方法或者entry方法返回的节点后,可以选择添加的属性。
-
i18n :定义了在请求页面时应自动加载哪个翻译文件
-
dependent :保护插件在父节点丢失时从上下文中调用
-
leaf :停止在此节点处解析请求,并且在调度树中不再进一步
-
sysauth :要求用户使用指定的帐户进行身份验证
-
sysauth_authenticator :验证时手动指定验证方法
-
setgroup :设置当前进程的组ID
-
setuser:设置当前进程的用户ID
上面的属性的使用,在dispatcher.lua文件中的dispatch方法中有体现。
二、视图层 View
在controller中提到,target 可以使用 template 可以指定对应的htm文件作为界面显示。同样,在使用cbi,form等方法指定target时,最后也会根据htm文件来渲染界面。具体流程在后续解释。这个htm文件所属范畴就是View中,浏览器界面主要由两种途径产生,使用CBI模型进行生成,或者是自定义htm显示。CBI模型是使用Luci预定义的一些CBI组件来生成常用的表单。如果需要制作一些CBI文件满足不了的页面,可以自己定义htm文件来满足需求。
htm文件中通常由三大部分构成:Lua代码、HTML代码、JavaScript代码。
--lua代码
<%
local sys = require "luci.sys"
-%>
--JavaScript代码
<script>
function(){
console.log('This is JavaScript Code')
}
</script>
--HTML代码
<%+header%>
<h1><%: HelloWorld %></h1>
<h1><%= sys.hostname() %></h1>
<%+footer%>
语法解释
在htm文件中,可以在HTML代码和JavaScript代码中插入lua代码,使用下面的语法格式标志代码为Lua代码。可以嵌套进行使用。具体实例在后续CBI模型渲染过程的解析中。
1、 包含Lua代码块:
<% code %>
<%- code -%>
2、 输出变量和函数值:
<% write(value) %>
<%=value%>
3、 引用另一个模板:
<% include(templatesName) %>
<%+ templatesName %>
4、 翻译转换:
<%= translate(“Text to translate”) %>
<%:Text to translate%>
5、 注释:
<%# comment %>
三、模型层 Model
在controller中提到。target 使用 cbi可以根据给出的文件创建一个CBI模型进行界面渲染。官方的说,CBI模型是Lua文件描述UCI配置文件的结构和由此参生的HTML表单来评估CBI解析器。通俗的说就是CBI模型通常由LuCI预定义的一些CBI控件组成,不同的CBI控件根据其对应的模板生成页面上可见的表单元素。这些控件可以执行大部分的UCI配置的获取或者设置的处理工作。我们也可以覆盖其本来的行为来达到我们满足项目需求的目的。
在我们定义的CBI文件中,与UCI的配置息息相关,UCI配置暂时可以理解为系统的各个应用程序的配置的统一接口。后面再详细介绍UCI。CBI模型文件定义在 /model/cbi/ 目录下
简单介绍一下CBI模型中的语法结构。后面对CBI控件进行详细介绍。以下面的代码为例。
m = Map("sn_file", translate("产品序列号"))
d = m:section(TypedSection, "gateway_sn",nil,nil)
a = d:option(Value, "sn", translate("序列号"));
return m
-------------------------------
1、表示生成一个map对象,对/etc/config/sn_file 文件进行映射
2、添加TypedSection 对type为"gateway_sn"的配置进行映射
3、添加一个Value输入框,对"gateway_sn"下的选项"sn"进行映射
对应的 /etc/config/sn_file 文件的UCI配置为
config gateway_sn
option sn "sn"
所有CBI luci.cbi.Map类型的模型文件必须返回一个map对象。
m = Map(config, title,description)
-- 第一个参数,代表 cbi 基于某个配置文件 config 建立Map,
-- 比如'system'对应配置文件 /etc/config/system
-- 第二个参数, 映射页面的标题,第三个参数, 描述信息
d = m:section(sectionClass,...)
--section是Map实例上的一个方法,作用是
--为map对象添加一个sectionClass类。
--section方法接收的参数只会使用到第一个,剩余的参数
--全部传递给sectionClass类使用。
--常用的sectionClass类有TypedSection NameSection两种
--比如 m:section(TypedSection, type, title, description)
--表示为Map对象增添一个TypedSection类,
-- type, title, description 参数会传递给 TypedSection
-- type 表示对 /etc/config/system 下类型为 type 的配置进行映射。
a = d:option(optionClass,...)
-- option是TypedSection实例的一个方法,作用是
-- 添加一个选项,对TypedSection所定义的某个type下的option进行映射。
-- 第一个参数为optionClass,比如Value、ListValue等等,
-- 同样,传递给option方法的参数都会传递到optionClass中