• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

转载和积累系列-luci框架-LUA的一个web框架使用

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

http://blog.csdn.net/initphp/article/details/17527639

 

LUCI 这个在百度上搜索除了一篇我的百度文库 luci 的介绍文章之外,前三页都是些不知所云的名词(足见百度在专业领域的搜索之烂),我却在大学毕业的大半年的大部分时间里与它纠结,由于开始的发懵到后来逐渐感觉到这家伙还很好玩的,现在就把我对 luci 的浅显认识介绍给大家。

官网: http://luci.subsignal.org/

  有关luci 的各个方面,你几乎都可以从这里获得,当然,只是浅显的获得, luci 的文档写的还算比较全,但是写的稍显简略,开始看的时候会有一点不知所措。

UCI  熟悉 openwrt 的人都会有所了解,就是 Uni fi ed Con fi guration Interface 的简称,而 luci 这个 openwrt上的默认 web 系统,是一个独立的由严谨的德国人开发的 web 框架,是 Lua  Con fi guration Interface 的简称,如果在您的应用里, luci 是对 openwrt 的服务,我们就有必要做一下 uci 的简介,我这里就不说了,见链接:

http://www.google.com.hk/url?sa=t&source=web&cd=5&ved=0CEMQFjAE&url=http://nbd.name/openwrt-fosdem-09.pdf&ei=h52iTcXvOcrMcJ-xxOwD&usg=AFQjCNGFhumCIgS5tK_mDJ2dDFU4qsskfQ

 

有的时候,我们开发的luci 是在自己的 Linux PC 上开发,在普通的 linux 上,一般是没有 uci 命令的,为了开发方便,可以手动编译一下,方法见链接:

https://forum.openwrt.org/viewtopic.php?id=15243

OK ,之前罗里罗嗦的说了很多,现在就进入正题,进入正题的前提是你已经 make install 正确的安装了 lua  ,luci ,以及编译好链接了相关的 so (如果你需要,比如 uci.so nixio.so ),以及 make install 正确 web server,(我用的 web server 是 thttpd ,也编译过 mongoose , lighttpd ,在这三个之中, lighttpd 是功能最完善的, mongoose 是最小巧的)。

进入正题:

一:luci 的启动

  在web server 中的 cgi-bin 目录下,运行 luci 文件(权限一般是 755 ), luci 的代码如下:

 

  1. #!/usr/bin/lua      --cgi的执行命令的路径  
  2. require"luci.cacheloader"    --导入cacheloader包  
  3. require"luci.sgi.cgi"         --导入sgi.cgi包   
  4. luci.dispatcher.indexcache = "/tmp/luci-indexcache"   --cache缓存路径地址  
  5. luci.sgi.cgi.run()  --执行run方法,此方法位于*/luci/sgi/cgi.lua中  

 

 run方法的主要任务就是在安全的环境中打开开始页面(登录页面),在 run 中,最主要的功能还是在dispatch.lua 中完成。

 运行luci 之后,就会出现登录界面:

 

  1.  -bash-4.0# pwd    
  2. /var/www/cgi-bin    
  3. -bash-4.0# ./luci    
  4.   Status: 200 OK        
  5.   Content-Type: text/html;     
  6.   charset=utf-8         
  7.   Cache-Control: no-cache       
  8.   Expires: 0    
  9. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"     
  10. "http://www.w3.org/TR/html4/strict.dtd">        
  11.  <html class=" ext-strict">    
  12.          
  13.  </html>    

 

二:LUCI 的 MVC

1:用户管理:

      在luci 的官方网站说明了 luci 是一个 MVC 架构的框架,这个 MVC 做的可扩展性很好,可以完全的统一的写自己的 html 网页,而且他对 shell 的支持相当的到位,(因为 luci 是 lua 写的, lua 是 C 的儿子嘛,       与 shell 是兄弟)。在登录界面用户名的选择很重要,      luci 是一个单用户框架,公用的模块放置在 */luci/controller/ 下面,各个用户的模块放置在 */luci/controller/ 下面对应的文件夹里面,比如              admin 登录,最终的页面只显示 /luci/controller/admin 下面的菜单。这样既有效的管理了不同管理员的权限。

2: controller 文件夹下的 lua 文件说明:(以 mini 用户为例)

     在mini 目录下面,会有一个 index.lua 文件,简略的代码如下:

 

  1.  module("luci.controller.mini.index", package.seeall)    
  2. 17      
  3. 18  function index()    
  4. 19      luci.i18n.loadc("admin-core")    
  5. 20      local i18n = luci.i18n.translate    
  6. 21      
  7. 22      local root = node()    
  8. 23      if not root.lock then    
  9. 24          root.target = alias("mini")    
  10. 25          root.index = true    
  11. 26      end    
  12. 27         
  13. 28      entry({"about"}, template("about")).i18n = "admin-core"    
  14. 29         
  15. 30      local page   = entry({"mini"}, alias("mini", "index"), i18n("essentials", "Essentials"), 10)    
  16. 31      page.i18n    = "admin-core"    
  17. 32      page.sysauth = "root"    
  18. 33      page.sysauth_authenticator = "htmlauth"    
  19. 34      page.index = true    
  20. 35         
  21. 36      entry({"mini", "index"}, alias("mini", "index", "index"), i18n("overview"), 10).index = true    
  22. 37      entry({"mini", "index", "index"}, form("mini/index"), i18n("general"), 1).ignoreindex = true    
  23. 38      entry({"mini", "index", "luci"}, cbi("mini/luci", {autoapply=true}), i18n("settings"), 10)    
  24. 39      entry({"mini", "index", "logout"}, call("action_logout"), i18n("logout"))    
  25. 40  end    
  26. 41      
  27. 42  function action_logout()    
  28. 43      luci.http.header("Set-Cookie", "sysauth=; path=/")    
  29. 44      luci.http.redirect(luci.dispatcher.build_url())    
  30. 45  end    

 

这个文件定义了node ,最外面的节点,最上层菜单的显示等等。在其他的 lua 文件里,定义了其他菜单的显示和html 以及业务处理路径。每个文件对应一个菜单相。

例如 system.lua 文件

  1.  function index()    
  2. 19      luci.i18n.loadc("admin-core")    
  3. 20      local i18n = luci.i18n.translate    
  4. 21      
  5. 22      entry({"mini", "system"}, alias("mini", "system", "index"), i18n("system"), 40).index = true    
  6. 23      entry({"mini", "system", "index"}, cbi("mini/system", {autoapply=true}), i18n("general"), 1)    
  7. 24      entry({"mini", "system", "passwd"}, form("mini/passwd"), i18n("a_s_changepw"), 10)    
  8. 25      entry({"mini", "system", "backup"}, call("action_backup"), i18n("a_s_backup"), 80)    
  9. 26      entry({"mini", "system", "upgrade"}, call("action_upgrade"), i18n("a_s_flash"), 90)    
  10. 27      entry({"mini", "system", "reboot"}, call("action_reboot"), i18n("reboot"), 100)    
  11. 28  end    

 

mudel是对应文件的, function index 定义了菜单,比如这一句entry({"mini", "system", "reboot"}, call("action_reboot"), i18n("reboot"), 100)

1 项为菜单入口:

{"mini", "system", "reboot"}, mini 是最上层的菜单,即为用户项, system 为一个具体的菜单, reboot 为这个菜单的子菜单,如果 reboot 还需要加上子菜单的话,可以这样写:

entry({"mini", "system", "reboot", "chreboot"}, call("action_chreboot"), i18n("chreboot"), 1), 这样就会在reboot 上产生一个新的子菜单,以此类推,可以产生 N 层菜单。

第二项为菜单对应的页面,可以是lua 的源代码文件,也可以是 html 页面。

alias cgi form call 等定义了此菜单相应的处理方式, form 和 cgi 对应到 model/cbi 相应的目录下面,那里面是对应的定制好的 html 和 lua 业务处理。

alias是等同于别的链接, call 调用了相应的 action_function 。还有一种调用,是 template ,是直接链接到view 相应目录下面的 htm 页面。(说明: luci 框架下面的 htm 都是可以嵌入 lua 语句的,做业务处理,相当于 jsp 页面的内部的 Java 语句)。

问价查找对应简介:

entry({"mini", "system", "reboot"}, call("action_reboot"), i18n("reboot"), 100)  :对应此文件的action_reboot function

entry({"mini", "system", "index"}, cbi("mini/system", {autoapply=true}), i18n("general"), 1):对应*/model/cbi/mini/system.lua  {autoapply=true}   这个失传的参数。

。。。。。

第三项为i18n 显示,比如entry({"mini", "system", "reboot"}, call("action_reboot"), i18n("reboot"), 100),菜单的名字为admin-core 文件内的对应显示。此处也可以这样写,  i18n("reboot"," 重启 ") ,即直接做了国际化。菜单上显示的就是“重启”。

第四项为现实的顺序,这个数字越小,显示越靠前,靠上。


 

现在说一下这些文件的解析是怎么解析的呢?你当然是说dispatch.lua中,你说对了,但是真正解析成菜单的递归算法确实在header.htm中 位置:*/view/themes/openwrt/
代码如下:
  1.  <%    
  2. require("luci.sys")    
  3. local load1, load5, load15 = luci.sys.loadavg()    
  4. local request  = require("luci.dispatcher").context.path    
  5. local category = request[1]    
  6. local tree     = luci.dispatcher.node()    
  7. local cattree  = category and luci.dispatcher.node(category)    
  8. local node     = luci.dispatcher.context.dispatched    
  9. local hostname = luci.sys.hostname()    
  10. local c = tree    
  11. for i,r in ipairs(request) do    
  12.      if c.nodes and c.nodes[r] then    
  13.           c = c.nodes[r]    
  14.           c._menu_selected = true    
  15.      end    
  16. end    
  17. require("luci.i18n").loadc("default")    
  18. require("luci.http").prepare_content("application/xhtml+xml")    
  19. -%>    
  20. <?xml version="1.0" encoding="utf-8"?>    
  21. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">    
  22. <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<%=luci.i18n.context.lang%>" lang="<%=luci.i18n.context.lang%>">    
  23. <head>    
  24. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />    
  25. <meta http-equiv="Content-Script-Type" content="text/javascript" />    
  26. <link rel="stylesheet" type="text/css" media="screen" href="<%=media%>/cascade.css" />    
  27. <!--[if lt IE 7]><link rel="stylesheet" type="text/css" media="screen" href="<%=media%>/ie6.css" /><![endif]-->    
  28. <!--[if IE 7]><link rel="stylesheet" type="text/css" media="screen" href="<%=media%>/ie7.css" /><![endif]-->    
  29. <% if node and node.css then %><link rel="stylesheet" type="text/css" media="screen" href="<%=resource%>/<%=node.css%>" />    
  30. <% end -%>    
  31. <mce:script type="text/javascript" src="<%=resource%><!--    
  32. /VarType.js">    
  33. // --></mce:script>    
  34. <mce:script type="text/javascript" src="<%=resource%><!--    
  35. /XHTML1.js">    
  36. // --></mce:script>    
  37. <mce:script type="text/javascript" src="<%=resource%><!--    
  38. /Dropdowns.js">    
  39. // --></mce:script>    
  40. <title><%=striptags( hostname .. ( (node and node.title) and ' - ' .. node.title or '')) %> - LuCI</title>    
  41. </head>    
  42. <body class="lang_<%=luci.i18n.context.lang%>">    
  43. <class="skiplink">    
  44. <span id="skiplink1"><href="#navigation" mce_href="#navigation"><%:skiplink1 Skip to navigation%></a></span>    
  45. <span id="skiplink2"><href="#content" mce_href="#content"><%:skiplink2 Skip to content%></a></span>    
  46. </p>    
  47. <div id="header">    
  48. <h1><%=luci.version.distname%></h1>    
  49. <p>    
  50. <%=luci.version.distversion%><br />    
  51. <%:load%>: <%="%.2f" % load1%<%="%.2f" % load5%<%="%.2f" % load15%><br />    
  52. <%:hostname%>: <%=hostname%>    
  53. </p>    
  54. </div>    
  55. <div id="menubar">    
  56. <h2 class="navigation"><id="navigation" name="navigation"><%:navigation Navigation%></a></h2>    
  57. <ul id="mainmenu" class="dropdowns">    
  58. <%-    
  59. local function submenu(prefix, node)    
  60.      if not node.nodes or node.hidden then    
  61.           return false    
  62.      end    
  63.      local index = {}    
  64.      local count = 0    
  65.      for k, n in pairs(node.nodes) do    
  66.           if n.title and n.target then    
  67.                table.insert(index, {name=k, order=n.order or 100})    
  68.                count = count + 1    
  69.           end    
  70.      end    
  71.      table.sort(index, function(a, b) return a.order b.order end)    
  72.      if count > 0 then    
  73. %>    
  74. <ul id="submenu_<%=string.gsub(string.gsub(prefix, "/", "_"), "^_(.-)_$", "%1")%>">    
  75. <%-    
  76.           for j, v in pairs(index) do    
  77.                if #v.name > 0 then    
  78.                     local nnode = node.nodes[v.name]    
  79.                     local href = controller .. prefix .. v.name .. "/"    
  80.                     href = (nnode.query) and href .. luci.http.build_querystring(nnode.query) or href    
  81.                         
  82.                     if nnode.nodes then    
  83.                          for k1, n1 in pairs(nnode.nodes) do    
  84.                               href = "#"    
  85.                              
  86.                          end    
  87.                     end         
  88. %>    
  89. <li><a<% if nnode._menu_selected then %class="active"<%end%href="<%=luci.util.pcdata(href)%>"><%=nnode.title%></a><%-    
  90. submenu(prefix .. v.name .. "/", nnode)    
  91. %></li>    
  92. <%-    
  93.                end    
  94.           end    
  95. %>    
  96. </ul>    
  97. <%    
  98.      end    
  99. end    

 

3: model 业务处理和页面生成简介

    我认为model 的业务处理和 html 生成,是 luci 框架的精华,但是想用好它,最终扩展定义自己的页面也是最难的,但是一旦定义好了,后面的工作就会轻松高效简介统一,不失为一种好的解决方案。但是它又有缺点,就是写页面虽然统一,但是不够灵活。

下面以 SimpleForm为例,讲解一下。

具体文件 */luci/model/cbi/passwd.lua

f = SimpleForm("password", translate("a_s_changepw"), translate("a_s_changepw1"))  --调用SimpleForm 页面  当然还是 I18N 从中捣乱,看上去没那么直观,不理他 
pw1=f:field(Value,"pw1",translate("password")) --  

SimpleForm 里面加一个 field   至于 SimpleForm  和 fiemd 是什么,一会去看 SimpleForm 页面去 
pw1.rmempty=false -- 把 SimpleForm的 rmempty 为不显示  后面就不做注释了 应该看得懂了 

 

  1. pw2 = f:field(Value, "pw2", translate("confirmation"))   
  2. pw2.rmempty = false   
  3. function pw2.validate(self, value, section)   
  4.      return pw1:formvalue(section) == value and value   
  5. end   
  6. function f.handle(self, state, data)   
  7.      if   state == FORM_VALID   then     --这个就是业务处理了  你懂得  呵呵   
  8.           local stat = luci.sys.user.setpasswd("admin", data.pw1) == 0  -- root --> admin       
  9.           if stat then   
  10.                f.message = translate("a_s_changepw_changed")   
  11.           else   
  12.                f.errmessage = translate("unknownerror")   
  13.           end   
  14.             
  15.           data.pw1 = nil   
  16.           data.pw2 = nil   
  17.      end   
  18.      return true   
  19. end   
  20. return f  


说明:( simpleForm  位于 view/cbi   下面,可以研究一下,各个元素是如何定义的 )

现在在给一个小例子:

.*/luci/model /cbi/admin_system/version_manage.lua 为例,介绍一下 luci 中 web 页面 lua 代码 

  1.   6 local h = loadfile("/usr/local/luci/help.lua")   
  2.   7 if h then   
  3.   8     h()   
  4.   9 end   
  5. 10 local help_txt = help_info and  help_info.version   


加载帮助帮助文件help.lua, 关于 loadfile() 的用法可以查看 lua 的手册 ( 我还没完全弄明白,先用了 
help_txt 是一个全局变量 

12 appadmin_path = "/usr/local/appadmin/bin/" 


定义一个全局变量,其实跟功能跟宏一样,定义appadmin 的绝对路径 

  1. 14 versionlist = {}   
  2. 15   
  3. 16 function getline (s)   
  4. .........   
  5. 32 end   
  6. 33   
  7. 34 function get_versionlist()   
  8. .........   
  9. 68 end   
  10. 69   
  11. 70 versionlist = get_versionlist()   


定义一个全局变量和两个函数,并初始化此变量 



接下来就是和最终展现的Web 页面直接相关的代码了,大部分都是对 luci 封装好的一些 html 控件(代码)的使用和扩展。 luci  封装好的 html 控件 

类可以在以下文件查看:./host/usr/lib/lua/luci/cbi.lua 

  1. 71 m = SimpleForm("version", translate("版本管理 "))   
  2. 72 m.submit = false   
  3. 73 m.reset = false   
  4. 74 m.help = help_txt and true or false   
  5. 75 m.helptxt = help_txt or ""   


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
LUA和C++绑定的一些天然的麻烦发布时间:2022-07-22
下一篇:
Lua + Redis 解决高并发发布时间:2022-07-22
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap