在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
开源软件名称:lua-resty-yii开源软件地址:https://gitee.com/hylun/lua-resty-yii开源软件介绍:lua-resty-yii一个基于OpenResty的仿Yii的web框架,通过本框架能够极大降低openresty的开发入门门槛。 [English](README.md) 系统要求安装OpenResty 1.0以上版本 https://openresty.org/en/download.html 快速开始Linux:修改runtime/start.sh中/usr/local/openresty/bin/openresty路径为您所安装的openresty路径 开始,bash中cd到当前目录,执行:runtime/start.sh停止,bash中cd到当前目录,执行:runtime/stop.sh Windows:修改runtime/start.bat中D:\openresty-1.11.2.2-win32\路径为您所安装的openresty路径 开始,双击:runtime/win-start.bat停止,双击:runtime/win-stop.bat 开发说明应用结构目录及文件:|____index.lua #入口文件|____lua-releng #检查代码质量/是否使用了全局变量|____config #配置文件目录| |____db.lua #数据库配置| |____lang.lua #语言包配置| |____memcache.lua #memcache配置| |____session.lua #session配置| |____web.lua #网站基本信息配置|____controllers #controller目录| |____site.lua #前端页面|____models #models目录| |____LoginForm.lua #登录逻辑处理类| |____Userinfo.lua #用户信息管理类|____runtime #Openresty运行目录| |____client_body_temp #post上传的临时保存目录| |____fastcgi_temp #fastcig临时目录| |____logs #站点日志目录| | |____access.log #请求日志| | |____error.log #错误日志| | |____nginx.pid #nginx进程文件| |____nginx.conf #站点nginx配置文件| |____proxy_temp #proxy_temp| |____scgi_temp #scgi_temp | |____start.sh #站点启动程序| |____stop.sh #站点停止程序| |____uwsgi_temp #uwsgi_temp|____vendor #框架及第三方类| |____ActiveRecord.lua #数据库处理基类| |____Application.lua #请求处理类| |____Controller.lua #Controller基类| |____Files.lua #上传文件接收类| |____Memcache.lua #Memcache操作类| |____Model.lua #Model基类| |____Mysql.lua #Mysql操作类| |____Pager.lua #分页类| |____Query.lua #查询构建器| |____Request.lua #请求信息工具类| |____resty #第三方resty工具| | |____http.lua #http请求工具| | |____template.lua #lua模版工具类| |____Session.lua #Session操作类| |____User.lua #用户信息操作类| |____Util.lua #基本工具类|____views #页面模版目录| |____layout #页面框架目录| | |____main.lua.html #基本站点框架| |____site #站点页面目录| | |____error.lua.html #错误信息页| | |____guide.lua.html #开发说明页| | |____index.lua.html #首页| | |____login.lua.html #登录页|____web #静态资源目录| |____css #样式| | |____bootstrap-theme.css #bootstrap| | |____bootstrap-theme.min.css #bootstrap| | |____bootstrap.css #bootstrap| | |____bootstrap.min.css #bootstrap| | |____site.css #站点样式| |____js #javascript | | |____bootstrap.js #bootstrap| | |____bootstrap.min.js #bootstrap| | |____jquery.js #jquery| | |____jquery.min.js #jquery| | |____jquery.slim.js #jquery| | |____jquery.slim.min.js #jquery| |____favicon.ico #图标| |____robots.txt #robots.txt 运行机制概述每一次应用开始处理 HTTP 请求时,它都会进行一个近似的流程。
调试模式:
应用(Application)每接收到一个 HTTP 请求,入口脚本 index.lua 会创建一个 应用(Application) 实例用于处理该请求Application实例创建后,会保存覆盖全局变量ngx.ctx(此全局变量生命周期对应每个请求) ngx.ctx = require("vendor.Application"):new() Application在创建时,会加载网站的基本配置及基本工具作为属性,如:
在执行Application的new()时,会创建一个继承于Application的Controller实例,因此ngx.ctx即是一个Application也是一个Controller实例,并且controller中也可以通过self.web,self.lang,self.session.self.user.self.request直接引用以上配置及工具 注意:在使用时,尽量少使用全局变量ngx.ctx,而应该通过函数传值的方式进行传递,能获得更快的运行速度 控制器(Controllers):local _M = require("vendor.Controller"):new{_VERSION='0.01'} --生成Controller新实例function _M:indexAction() --动作方法名必须以name+Action组成 if not self.user.isLogin then return self:loginAction() end --使用用户信息类判断用户是否登录 local Product = require('models.Product') --使用Product数据表操作类 local query = Product.find().where{'in','product_id',self.user.resource} --fin()方法生成新的查询器 local page = Pager{ --使用分页类 pageSize = 8, --设置每页显示最多8条信息 totalCount = query.count(), --使用查询器查询符合条件的数据总条数 } local list = query.orderBy('create_time desc').page(page).all() --使用查询器查询分页数据集 return self:render('index',{ page = page, list = list, })endretrun _M 如果保存为controllers/filename.lua,则访问?act=filename.index时,会执行上面对应的indexAction()方法 ##模型(Models):模型是 MVC 模式中的一部分, 是代表业务数据、规则和逻辑的对象。可通过继承 "vendor.Model" 或它的子类定义模型类, 基类"vendor.Model"支持许多实用的特性,如: local _M = require("vendor.Model"):new{_version='0.0.1'} --生成Model新实例local Userinfo = require('models.Userinfo') --在Model里使用其他modelfunction _M.rules() --方法rules添加数据校验规则 return { {{'username','password','sacode'}, 'trim'}, --通过trim自动过滤输入 {'username', 'required',message='请填写登录账号'}, --通过required规则设置必须填写 --{'username', 'email'}, --如果需要用户名必须为email时设置 {'password', 'required',message='请填写登录密码'}, --使用自定义方法校验参数 {'password','checkPass'} --使用自定义checkPass方法进行校验 }endfunction _M:checkPass(key) if self:hasErrors() then return end local user = Userinfo.getUserByName(self.username) if not user then self:addError('username','账号不存在') elseif user.password ~= self.password then self:addError('password','密码错误') else self.userinfo = user endendfunction _M:login(user) if not self:validate() then return false end return user.login(self.userinfo, rememberMe and 3600*24*30 or 0)endreturn _M 在Controller里使用Model: --因为Model包含有数据,一定要调用new方法生成新实例,避免出现数据缓存问题local model = require('models.LoginForm'):new() --通过model的load()方法,可自动装载数据if model:load(self.request.post()) and model:login(self.user) then return self:goHome()end 视图(Views):视图基于lua-resty-template实现 在视图中必须使用以下标签:
与lua-resty-template的普通用法不同,所有视图文件,以lua文件的形式保存在views子目录下,文件名为*.lua.htmlcontroller中的render方法渲染试图时,会以require的形式去获取视图内容 views/layout 目录存放框架视图,渲染视图时默认使用views/layout/main.lua.html,controller中可通过layout属性设置使用不同的框架视图,如: local _M = require("vendor.Controller"):new{layout = 'manage'} views下的其他子目录分别为不同功能模块对应的内容视图,所有页头,页尾,菜单等内容应在框架视图中实现内容视图中不推荐使用{(template)}的形式来包含其他视图,因与默认渲染方式不同,会导致找不到文件等错误 controller在渲染视图时,会将所有application及controller的数据传递给视图,因此在视图中可以直接使用这些数据如:{{lang.siteName}}可输出语言包中配置的网站名称 视图中可以通过设置context属性的方式用于内容视图跟框架视图之间传值,如设置:{% context.title = '开发说明' %}则可以在框架视图中{{title}}输出显示 请求处理获取请求参数local request = require("vendor.Request")--在Controller方法里可以直接通过self.request调用local get = request.get()--等价于php: $get = $_GET;local id = request.get('id'); --等价于php: $id = isset($_GET['id']) ? $_GET['id'] : null;local id = request.get('id', 1) --等价于php: $id = isset($_GET['id']) ? $_GET['id'] : 1;local post = request.post()--等价于php: $post = $_POST;local name = request.post('name') --等价于php: $name = isset($_POST['name']) ? $_POST['name'] : null;local name = request.post('name', '') --等价于php: $name = isset($_POST['name']) ? $_POST['name'] : '';local cookie = request.cookie()--等价于php: $cookie = $_COOKIE;local sso = request.cookie('sso') --等价于php: $sso = isset($_COOKIE['sso']) ? $_COOKIE['sso'] : null;local sso = request.cookie('sso', '') --等价于php: $sso = isset($_COOKIE['sso']) ? $_COOKIE['sso'] : '';--判断是否有上传文件ngx.say(request.isPostFile())--注意跟php不同的地方,如果有上传文件时,普通参数是无法通过request.post()方法获取到的--建议上传文件时,普通参数通过GET传递--接收上传文件local file = require('vendor.Files')local savename = path..filenamelocal ok,err = file.receive('upfile',savename) --接收name为upfile的上传文件if not ok then return {retcode=1,retmsg='接收文件失败,请重试:'..err} end 设置cookie:local util = require "vendor.Util"util.set_cookie('abc','123')util.set_cookie('def','456',3600)util.set_cookie(name,value,expire,path,domain,secure,httponly) Session操作:ocal session = require "vendor.Session"--在Controller方法里可以直接通过self.session调用--session.start() --启用session,会去设置一个_sessionid的cookie,此操作可以省略--session.exptime = 1800 --修改session失效时间时,默认30分钟--session.cache = ngx_shared['session_cache'] --默认使用ngx的缓存,对应runtime/nginx.conf里的lua_shared_dict配置项--session.cache = require("vendor.Memcache"):new{host="127.0.0.1",port="11211"} --使用memcached缓存打开此配置local sessions = session.get()--等价于php: $sessions = $_SESSION;local abc = session.get('abc') --等价于:local abc = session.abc --等价于:local abc = session['abc']--等价于php: $abc = isset($_SESSION['abc']) ? $_SESSION['abc'] : null;local abc = session.get('abc', '') --等价于php: $abc = isset($_SESSION['abc']) ? $_SESSION['abc'] : '';--设置session值session.set('abc','abc-value')--等价于:session.abc = 'abc-value'--等价于:session['abc'] = 'abc-value' 数据库操作(Working with Databases:数据库访问 (DAO)local db = require('vendor.Mysql'):new{ host = "127.0.0.1", port = 3306, database = "mytest", user = "root", password = "", charset = "utf8", --建议配置Mysql的默认连接字符集为utf8,可去掉此配置项,此项配置会增加一次'SET NAMES utf8'的操作}--或者直接通过配置文件获得:local db = require('config.db')--执行sql:local rows = db:query('select * from mytable')ngx.say(#rows) 使用查询生成器 (Query Builder)vendor.Query 封装了 vendor.Mysql,并提供快捷构建 安全 查询语句的方法,例如: local db = require('config.db')local query = require('vendor.Query')()local rows = query.use(db).from('products').where({product_id=123}).all()--相当于执行local rows = db:query("select * from products where product_id='123'")ngx.say(query.get('sql')) --能获取到构造出的sql语句--query.use()用于指定链接的数据库query.use(require('config.db'))--query.select()用于指定要查询的字段,不指定默认为select *query.select({'id', 'email'})--等同于:query.select('id, email')--query.from()用于指定要查询的表格 SELECT * FROM `user`query.from('user') query.where()用于定义 SQL 语句当中的 WHERE 子句。 你可以使用如下三种格式来定义 WHERE 条件:
query.orderBy() 方法是用来指定 SQL 语句当中的 ORDER BY 子句的。例如,要实现 ... ORDER BY create_time desc 可以: query.orderBy('create_time desc')--等价于:query.orderBy{create_time=desc} query.groupBy() 方法是用来指定 SQL 语句当中的 GROUP BY 片断的。例如,要实现 ... GROUP BY query.groupBy{'id', 'status'} query.limit() 和 query.offset() 是用来指定 SQL 语句当中 的 LIMIT 和 OFFSET 子句的。例如,要实现 ... LIMIT 10 OFFSET 20 等价于mysql的limit 20,10 可以: query.limit(10).offset(20) 如果你指定了一个无效的 limit 或者 offset(例如,一个负数),那么它将会被忽略掉。 query.page() 能通过传递"vendor.Pager"对象,设置 LIMIT 和 OFFSET 达到分页查询的目的: local page = require("vendor.Pager"){ pageSize = 8, totalCount = 10,}query.page(page) --等价于:query.limit(page.limit).offset(page.offset) ####查询方法vendor.Query 提供了一整套的用于不同查询目的的方法。
例如: local db = require('config.db')local query = require('vendor.Query'){}.use(db).from('products').where{product_id=123}local rows = query.all()local row = query.one()local count = query.count() 其他方法:
例如: local db = require('config.db')local data = {product_id=123,product_name='name123'}local query = require('vendor.Query')(data).use(db).from('products')--插入:local row = query.insert()--要获取新插入的数据的自增长id,请使用:local id = row.get('insert_id') --等价于query.get('insert_id')--更新:row.product_name = 'name456'row.update() --或者query.update(row)--删除:row.delete() --或者query.update(row) ###使用活动记录 (Active Record)vendor.ActiveRecord 进一步封装了查询生成器vendor.Query同时vendor.ActiveRecord继承于vendor.Model,能够使用模型对象的load(),rules(),hasErrors(),validate()等方法 使用示例: local Product = require('vendor.ActiveRecord'){ --db = require('config.db'), --可选属性,指定使用的数据库连接,未设置时默认使用'config.db' tableName = function() --必须实现tableName方法,返回要操作的数据表名称 return 'products' end}--插入数据:local product = Product.new{ product_id = 123, product_name = 'name123',} --返回的对象支持使用vendor.Query方法product.save() --等价于product.insert()--快速查找一行local row = Product.findOne{product_id = 123} --返回的对象支持使用vendor.Query方法--快速查找多行local rows = Product.findAll{user='creater'} --返回的对象支持使用vendor.Query方法--复杂查询local query = Product.find().where{user='creater'} --返回的对象支持使用vendor.Query方法local page = Pager{ pageSize = 8, totalCount = query.count(),}local list = query.orderBy('create_time desc').page(page).all()--同样可以执行以下操作query.insert() --插入数据。query.update() --更新数据。query.delete() --删除数据。 建议每个数据表格在models目录下建立一个继承于'vendor.ActiveRecord'的操作类 ###数据库安全问题使用vendor.ActiveRecord 或 vendor.Query 自动生成查询语句在构造sql语句的过程中,如果传递的参数是表格,构造器会进行转义操作,防止sql注入 但如果传递的是字符串,则不会进行转义操作,建议尽量少用,并能确保sql安全可以通过 query.get('sql') 获取执行的sql语句 如果要进行转义,可使用util.mescape()方法 local util = require("vendor.Util")value = util.mescape(value) lua-resty-yii is available under the MIT license. See the LICENSE filefor more information. |
请发表评论