3

小小前言

前一章里我阐述了利用LUCI的自身机制实现表单的构建及配置的读取,现在我来说一下自行构建表单进行提交处理的方法。个人认为,掌握了这个,才不会被LUCI的架构所局限。刚接触那会,我是花了蛮长的时间才弄通了这一点,也因为在网上没找到什么资料,所以想把自己的经验写下来,也希望能帮到大家。

具体步骤如下:

  1. 确定配置文件
  2. 编写路由
  3. 建立表单
  4. 编写处理方法
1. 确定配置文件

这一个在前一章也有说过,大家可以先看看我的这一篇文章:
LUCI 使用其原有机制的建立新的页面

这里我们仍然沿用之前的network配置的interface那项配置就好:

config interface testnets
    option ifname 'testnet'
    option type 'test'
    option ipaddr '192.168.1.1'
    option net '192.168.251.1'
2. 编写路由

个人习惯,我喜欢先定好路由规则再往下写。大家若是不想后面二进宫,那么可以先进行第3步。
话不多话,接下来我们到luci/controller/admin/net.lua里编写路由:

module luci.controller.admin.net
function index()
    entry({"admin", "testnet", "form"}, template("test/form"), translate("Form"), 12)    -- 表单加载页面路由
   entry({"admin", "testnet", "control"}, call("form"), nil) 
end
3. 建立表单

好了,到了建立表单的时间。相信做过WEB开发的人都知道表单如何建立,其实就是一个HTML页面。但LUCI这里有所不同,用的是htm文件,但基本语法是一样的,所以不用担心。
好了,我们接下来简单写一下我们的表单页面,页面的文件路径是在luci/view/test/下的form.htm(因为之前路径定义了template("test/form)这一句):

<%+header%>
<%
    local cbi = require 'luci.model.uci'
    
    local row = cbi:get("network", "testnets")
%>
<form action="<%=url("admin", "testnet", "control")%>" method="post">
<div><label>Ifname: </label><span><input type="text" name="ifname" value="<%=row.ifname%>"/></span></div>
<div><label>Type: </label><span><input type="text" name="type" value="<%=row.type%>"/></span></div>
<div><label>ipaddr: </label><span><input type="text" name="ipaddr" value="<%=row.ipaddr%>"/></span></div>
<div><label>Net: </label><span><input type="text" name="net" value="<%=row.net%>"/></span></div>
<input type="submit" value="提交" />
</form>
<%+footer%>

在表单里有几点我想说一下:

  • 在LUCI里的视图层(也就是view层),是可以嵌套LUCI的代码的,跟PHP也能在HTML页面嵌套代码一样,LUCI使用<%%>这个符号作为LUCI代码在.htm嵌套代码的起始和结尾符;
  • <%+header%><%+footer%>这两个是加载头和尾文件的,相当于PHP的include方法。加载的文件是在视图文件夹之下,即luci/view/header.htmluci/view/footer.htm两个文件
  • 表单里的action使用的luci自带的路径生成函数url(),写法与控制器里的entry()函数的第一个参数写法一致;
  • 因为LUCI主要的功能是对配置文件进行存取,而取这一点的体现就在于view层的页面上,所以我们需要将其取出来就要使用LUCI的代码。无论存取,我们都需要用到LUCI的uci接口,也就是加载里的luci.model.uci
  • uci接口的具体内容在它的是官方接口里有的具体的说明,不过个人觉得它没有实例,看得会比较吃力,但若是理解了LUCI的机制并用过一两个接口之后就能比较好地掌握了;
  • 这里用到的是uci的get()接口,在官方的描述是这样的:
Uci:get (config, section, option)       
     Get a section type or an option
Parameters
    config: UCI config
    section: UCI section name
    option: UCI option (optional)
    
Return value:
    UCI value
uci:get(config, sectionType/sectionName[, optionType])接口获取一个section或option
section若是只有类型(如:config interface)第二个参数就是section的类型,如interface
section若是有name名(如:config interface 'testnets'),则第二个参数则写testnets,即section的name名。
若是多有个相同类型的section存在,获取其中的某个section需要获取到单个interface的ID,这个ID的获取若是用LUCI的传统方式则能够通过每一行的控件或是tr的标签id中获取。
多个同类型的section个人建议使用uci:foreach(config, sectionType, callbackFun)接口
Uci:foreach (config, type, callback)
    Call a function for every section of a certain type.
Parameters
    config: UCI config
    type: UCI section type
    callback: Function to be called
Return value:
    Boolean whether operation succeeded
3. 书写处理函数

刚接触LUCI,其实最令我感到难以下手的就是用法,本人C语言比较薄弱一些,LUCI的语法比较绕啥的,看得比较晕,研究了好些天,勉强看懂了它的基本语法,再加上LUCI官方接口文档,才算是找到了点感觉。
[尴尬地笑]不发牢骚了,接下来编写处理这个表单内容的代码:

function form()
    local cbi = require 'luci.model.uci'
    local h = require 'luci.http'
    local dsp = require 'luci.dispatcher'
    
    local ifname = h.formvalue("ifname")
    local in_type = h.formvalue("type")
    local in_ipaddr = h.formvalue("ipaddr")
    local in_net = h.formvalue("net")
    cbi:delete("network", "testnets")
    cbi:section("network", "interface", "testnets", {
        ifname = ifname,
        type = in_type,
        ipaddr = in_ipaddr,
        net = in_net
    })
    cbi:save("network")
    cbi:commit("network")
    h.redirect(dsp.bulid_url("admin", "testnet", "form"))
end
  • cbi:section(config, sectionType, sectionName/nil, {'...'})接口是建立新的section节点,该节点的参数有四个,第一个参数是config文件的文件名,第二个参数是section的type名,第三个参数是section的name名(若是没有,可为空),第四个参数是具体的配置项内容
  • 也许是我没找到替代方法的原因,我对于接口的修改都是先删掉再新建。因此,需要调用先cbi:delete(config, section[, option])接口。该接口与前面获取section的uci:get()接口的使用方法一致。
  • 在Linux使用过uci命令的童鞋应该会知道:section/option进行操作之后都需要commit才是完成了操作。而LUCI里也提供了uci:commit(config)接口,在LUCI里需要先保存再提交,即应该先调用uci:save(config)接口,再调用commit接口。
Uci:delete (config, section, option)
    Deletes a section or an option.
Parameters
    config: UCI config
    section: UCI section name
    option: UCI option (optional)
Return value:
    Boolean whether operation succeeded
Uci:section (config, type, name, values)
    Create a new section and initialize it with data.
Parameters
    config: UCI config
    type: UCI section type
    name: UCI section name (optional)
    values: Table of key - value pairs to initialize the section with
Return value:
    Name of created section
Cursor:save (config)
    Saves changes made to a config to make them committable.
Parameters
    config: UCI config
Return value:
    Boolean whether operation succeeded
Cursor:commit (config)
    Commit saved changes.
Parameters
    config: UCI config
Return value:
    Boolean whether operation succeeded

至此,我们自己进行section的存取操作就完成了。加上上一章,好像内容说得比较啰嗦,因为写技术文写得不多,还请见谅;同时也是因为我觉得这些经验都是挺重要的,所以写了下来。LUCI我接触的时间并不长,有说明不清或者错误地方,欢迎大家不吝指出~
希望本文能帮到大家,也希望能于大家多多交流!


沐酒
27 声望2 粉丝

擅长的是PHP,但也由于工作需要的原因,接触了一些其他的语言。


引用和评论

0 条评论