一个基于OpenResty的仿Yii的web框架

网友投稿 1065 2022-10-24

一个基于OpenResty的仿Yii的web框架

一个基于OpenResty的仿Yii的web框架

Lua-resty-yii

A network framework based on OpenResty Imitation Yii, through this framework can greatly reduce the entry threshold of openresty development. 中文

#System Requirements

Install OpenResty 1.0+ https://openresty.org/en/download.html

Quick start

Linux OS: Modify the /usr/local/openresty/bin/openresty path in your runtime/start.sh to your openresty path

At the beginning, bash CD to the current directory, execute: runtime/start.sh Stop, celebrate the CD to the current directory, execute: runtime/stop.sh

Windows: Modify runtime/start.bat in d:\openresty-1.11.2.2--Win32\ path for your installed openresty path

Start, double-click: runtime/win-start.batStop, double-click: runtime/win-stop.bat

Development instructions

application structure

Directory and documents:| ____ index.lua # entry file| ____ lua-releng # Check code quality / whether global variables are used| ____ config # configuration file directory| | ____ db.lua # database configuration| | ____ lang.lua # language pack configuration| | ____ memcache.lua # memcache configuration| | ____ session.lua #session configuration| ____ web.lua # Website basic information configuration| ____ Controller #controller directory| | ____ site.lua # front page| ____ Model #models directory| | ____ LoginForm.lua # Login logical processing class| ____ Userinfo.lua # User Information Management Class| ____ runtime # Openresty run directory| | ____ client_body_temp #post Upload temporary save directory| ____ fastcgi_temp #fastcig temporary directory| ____ Logs # The site log directory| | | ____ access.log # Request Log| | | ____ error.log # error log| | | ____ nginx.pid #nginx Process File| | ____ nginx.conf # Site nginx configuration file| | ____ proxy_temp #proxy_temp| | ____ scgi_temp #scgi_temp| ____ start.sh # Site Launcher| | ____ stop.sh # Stop the program| | ____ uwsgi_temp #uwsgi_temp| ____ vendor # framework and third-party classes| ____ ActiveRecord.lua # database processing base class| ____ Application.lua # Request processing class| | ____ Controller.lua #Controller base class| ____ Files.lua # Upload file receiving class| | ____ Memcache.lua #Memcache operation class| ____ Model.lua #Model base class| | ____ Mysql.lua #Mysql operation class| ____ Pager.lua # Paging classes| | ____ Query.lua # Query builder| ____ Request.lua # request information tool class| ____ resty # third-party resty tools| | | ____ http.lua #http Request Tool| | | ____ template.lua #lua Template Tools| | ____ Session.lua #Session action class| | ____ User.lua # user information action class| ____ Util.lua # Basic Tools| ____ views # page template directory| ____ layout # page frame directory| | | ____ main.lua.html # basic site framework| ____ site # site page directory| | | ____ error.lua.html # error message page| | | ____ guide.lua.html # Development Notes page| | | ____ index.lua.html # Home| | | ____ login.lua.html # Login page| ____ web # Static Resource Directory| | ____ css # style| | | ____ bootstrap-theme.css #bootstrap| | | ____ bootstrap-theme.min.css #bootstrap| | | ____ bootstrap.css #bootstrap| | | ____ bootstrap.min.css #bootstrap| | | ____ site.css # site style| | ____ 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 # icon| | ____ robots.txt # robots.txt

Overview of the operating mechanism

Every time an application starts to process an HTTP request, it performs an approximate process.

--User submits request for entry script index.lua --The entry script creates an Application instance to handle the request and loads the configuration --The application parses the requested route through the request application component. --Application to create a Controller instance to handle the request. --Execute the before() method in the controller for request filtering. --Continue with action if before() returns true, otherwise terminate. --Action will load a data model, usually loaded from the database. --The action renders a View and provides it with the required data model. --Rendered results are returned to the response (response) application component. --The response component sends the result back to the user's browser.

Debug mode:

--Access / lua / index will use lua_code_cache off mode to access the site --Visit / lua / {filepath} Can debug the corresponding lua script

Application

Each time an HTTP request is received, the entry script index.lua creates an instance of the application for processing the request After the application instance is created, the global variable ngx.ctx is overwritten (for each request corresponding to the life cycle of the global variable)

ngx.ctx = require ("vendor.Application"): new()

When the application is created, it will load the basic configuration and basic tools of the website as attributes, such as:

--ngx.ctx.web is equivalent to require ("config.web"), --Site Configuration Information Table --ngx.ctx.lang is equivalent to require ("config.lang"), --language package configuration information --ngx.ctx.session is equivalent to require ("config.session"), --site session class --ngx.ctx.user is equivalent to require ("vendor.User"), --Site User Tools --ngx.ctx.request is equivalent to require ("vendor.Request"), --Site Request Processing

When executing a new() application, a controller instance is inherited from the application, So ngx.ctx is an application that is also a controller instance and is also available in the controller self.web, self.lang, self.session.self.user.self.request Direct reference to the above configuration and tools

** Note: When using, minimize the use of the global variable ngx.ctx, but should pass the value of the function pass, get faster speed **

Controller (Controller):

local _M = require("vendor.Controller"):new{_VERSION='0.01'} --Generate a new Controller instancefunction _M:indexAction() --The action method name must be name + Action if not self.user.isLogin then return self:loginAction() end --use the user information to determine whether the user login local Product = require('models.Product') --Uses the Product Datasheet operation class local query = Product.find().where{'in','product_id',self.user.resource} --The fin() method generates a new querier local page = Pager{ pageSize = 8, totalCount = query.count(), } local list = query.orderBy('create_time desc').page(page).all() return self:render('index',{ page = page, list = list, })endretrun _M

** If saved as controller / filename.lua then access? ACT = filename.index, it will perform the above corresponding indexAction() method **

model (model):

The model is part of the MVC pattern and represents the business data, rules, and logic objects. The model class can be defined by inheriting "vendor.Model" or its subclasses. The base class "vendor.Model" supports many useful features such as:

local _M = require ("vendor.Model"): new {_version = '0.0.1'} --Generate a new instance of ModelLocal Userinfo = require ('models.Userinfo') --Use other models in the ModelFunction _M.rules() --Method rules Add data validation rulesReturn {{{'Username', 'password', 'sacode'}, 'trim'}, --Filter input automatically through trim{'Username', 'required', message = 'Please fill in the login account'}, --Required by the required rules set--{'username', 'email'}, --if required username must be set for email time{'Password', 'required', message = 'Please fill in the login password'},--Use custom methods to verify the parameters{'Password', 'checkPass'} --Validate using the custom checkPass method}EndFunction _M: checkPass (key)If self: hasErrors() then return to the endLocal user = Userinfo.getUserByName (self.username)If not user thenFrom: addError ('username', 'account does not exist')Elseif user.password ~ ​​= self.password ThenFrom: addError ('password', 'wrong password')OtherSelf.userinfo = userEndEndFunction _M: login (user)If not self: validate() then returns falseThe company is located in:Return user.login (self.userinfo, rememberMe and 3600 * 24 * 30 or 0)Endreturn _M

Use in controller Model:

--Because the model contains data, be sure to call the new method to generate a new instance, to avoid data caching problemslocal model = require ('models.LoginForm'): new() --Load data automatically through model load() methodIf model: load (self.request.post()) and model: login (self.user) thenBack to myself: goHome()End

view (view):

The view is based on the [LUA-resty template (https://github.com/bungle/lua-resty-template)

The following tags must be used in the view:  --{{expression}}, output the expression, and html formatted  --{* expression }, the result of the expression expression is output as it is  --{% lua code%}, execute Lua code*  --{# comments #} All content between {# and #} is considered commented (ie not output or executed)

Unlike the normal usage of the LUA-resty template, all view files are saved as LUA files in the sub-directory with the filename * .lua.html Rendering methods in your controller When you try to render, you get the content of the view as you need it

views / layout directory storage frame view, rendering view default views / layout / main.lua.html, Controller can be set by the layout of the property to use a different frame view, such as: lua Local_M = require ("vendor.Controller"): new {layout = 'manage'}

Under the views of the other sub-directories for different functional modules corresponding to the content view, all page headers, page footers, menus and other content should be implemented in the frame viewOther views are deprecated in the content view as {(templates)}, which can cause errors such as missing files due to different default renderingsWhen the controller renders the view, it passes all application and controller data to the view so that it can be used directly in the viewSuch as: {{lang.siteName}} output the name of the site configured in the language packIn the view, you can set a contextual property to pass values ​​between the content view and the frame view, such as setting:{% Context.title = 'Development Description'%}You can display the {{title}} output in the frame view## request processing### Get request parametersluaLocal request = require ("vendor.Request")--In the controller method can be obtained directly through self.request calllocal get = request.get()--equivalent to php: $get = $_GET;Local id = request.get ('id');--equivalent to php: $id = isset ($_ GET ['id'])? $_GET ['id']: null;Local id = request.get ('id', 1)--equivalent to php: $id = isset ($_ GET ['id'])? $_GET ['id']: 1;Local post = request.post()--equivalent to php: $post = $_POST;Local name = request.post ('name')--equivalent to php: $name = isset ($_ POST ['name'])? $_POST ['name']: null;Local name = request.post ('name', '')--equivalent to php: $name = isset ($_ POST ['name'])? $_POST ['name']: '';Local cookie = request.cookie()--equivalent to php: $cookie = $_COOKIE;Local sso = request.cookie ('sso')--equivalent to php: $sso = isset ($_ COOKIE ['sso'])? $_COOKIE ['sso']: null;Local sso = request.cookie ('sso', '')--equivalent to php: $sso = isset ($_ COOKIE ['sso'])? $_COOKIE ['sso']: ''; --Determine whether there is an upload filengx.say (request.isPostFile()) --Note different places with PHP, if there is an upload file, the normal parameters can not be obtained through the request.post() method --Recommended to upload files, common parameters passed by GET --Receive uploaded filesLocal file = required ('vendor.Files')Local savename = path .. filenameLocal ok, err = file.receive ('upfile', savename) --receive upload file named upfileIf notReturn {retcode = 1, retmsg = 'Receive file failed, please try again:' .. err}End

Set cookie:

Local util = needs "vendor.Util"util.set_cookie ('ABC', '123')util.set_cookie ('HD', '456', 3600)util.set_cookie (Name, Value, Expired, Path, Domain, Security, Http Only)

Session operation:

ocal session = require "vendor.Session"--In the Controller method can be called directly through self.session--session.start() --Enable session, will set a _sessionid cookie, this operation can be omitted--session.exptime = 1800 --The default session time is 30 minutes--session.cache = ngx_shared ['session_cache'] --By default, ngx's cache corresponds to the lua_shared_dict configuration in runtime / nginx.conf--session.cache = require ("vendor.Memcache"): new {host = "127.0.0.1", port = "11211"} --Open this configuration with memcached cachelocal sessions = session.get()--equivalent to php: $ sessions = $ _SESSION;local abc = session.get ('abc')--equivalent to: local abc = session.abc--equivalent to: local abc = session ['abc']--equivalent to php: $ abc = isset ($ _ SESSION ['abc'])? $ _SESSION ['abc']: null;local abc = session.get ('abc', '')--equivalent to php: $ abc = isset ($ _ SESSION ['abc'])? $ _SESSION ['abc']: '';--set the session valuesession.set ('abc', 'abc-value')--equivalent to: session.abc = 'abc-value'--equivalent to: session ['abc'] = 'abc-value'

database operation (Working with Databases:

Database Access (DAO)

local db = require ('vendor.Mysql'): new {    host = "127.0.0.1",    port = 3306,    database = "mytest",    user = "root",    password = "",    charset = "utf8", --It is recommended to configure Mysql default connection character set utf8, you can get rid of this configuration item, this configuration will add a 'SET NAMES utf8' operation}--Or get it directly from the configuration file:local db = require ('config.db')--Execute sql:local rows = db: query ('select * from mytable')ngx.say (#rows)

Using Query Builder (Query Builder)

vendor.Query encapsulates vendor.Mysql and provides a quick way to build a secure query, for example:

local db = require ('config.db')local query = require ('vendor.Query')()local rows = query.use (db) .from ('products'). where ({product_id = 123}). all()--equivalent to executing local rows = db: query ("select * from products where product_id = '123'")ngx.say (query.get ('sql')) --can get to construct the sql statement--query.use() Used to specify the linked databasequery.use (require ('config.db'))--query.select() is used to specify the field to query, do not specify the default is select *query.select ({'id', 'email'})--Equivalent to:query.select ('id, email')--query.from() is used to specify the table to be queried SELECT * FROM `user`query.from ('user')

query.where() is used to define the WHERE clause in the SQL statement. You can use the following three formats to define the WHERE condition: **

--** string format **, for example: 'status = 1', this method does not automatically add quotes or escapes.

--** hash format **, for example: {status = 1, type = 2} This method will correctly quote the field name and escape the range of values

--** operator format **, for example: {'in', {'2012', '2011'}}     The operator format allows you to specify any conditional statement of the class style, as follows:     {Operator, operand1, operand2, ...}     Each of these operands can be a string format, a hash format, or a nested operator format, and the operator can be one of the following:

Operator | usage     --------| --------------------------------------------     ** and ** | Operands are concatenated with the AND keyword. For example, {'and', 'id = 1', 'id = 2'} will generate id = 1 AND id = 2. If the operand is an array, it is also converted to a string as described above. For example, {'and', 'type = 1', {'or', 'id = 1', 'id = 2'}} will generate type = 1 AND (id = 1 OR id = 2). This method does not automatically add quotes or escapes.     ** or ** | Similar to the and operator.     ** between ** | The first operand is the name of the field, and the second and third represent the range of values ​​for this field. For example, {'between', 'id', 1, 10} will generate id BETWEEN 1 AND 10.     ** not between ** | usage is similar to between     ** in ** | The first operand should be the field name, and the second operator is both an array. For example, {'in', 'id', {1, 2, 3}} will generate id IN ('1', '2', '3'). This method will correctly quote the field name and escape the value range     ** not in ** | Usage is similar to in operator.

The query.orderBy() method is used to specify the ORDER BY clause in the SQL statement. ** For example, to achieve ... ORDER BY create_time desc can:

query.orderBy ('create_time desc')--equivalent to: query.orderBy {create_time = desc}

The query.groupBy() method is used to specify the GROUP BY fragment in the SQL statement. ** For example, to achieve ... GROUP BY id, status could:

query.groupBy {'id', 'status'}

The query.limit() and query.offset() are used to specify the LIMIT and OFFSET clauses in SQL statements. ** For example, to achieve ... LIMIT 10 OFFSET 20 Equivalent to mysql limit 20,10 Can:

query.limit (10) .offset (20)

If you specify an invalid limit or offset (for example, a negative number), it will be ignored.

** query.page() can set LIMIT and OFFSET for paging query by passing "vendor.Pager" object: **

local page = require ("vendor.Pager") {    pageSize = 8,    totalCount = 10,}query.page (page) --Equivalent to: query.limit (page.limit) .offset (page.offset)

Query method

vendor.Query provides a complete set of methods for different query purposes. query.all(): will return an array of rows. query.one(): returns the first row of the result set. query.count(): returns the result of a COUNT query.

E.g:

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()

Other methods: query.insert(): Insert data. query.update(): update the data. query.delete(): delete data. query.save(): The new data call is equivalent to query.insert() and the query result set call is equivalent to query.update().

E.g:

local db = require ('config.db')local data = {product_id = 123, product_name = 'name123'}local query = require ('vendor.Query') (data) .use (db) .from ('products')--insert:local row = query.insert()--To get the self-growth id of newly inserted data, use:local id = row.get ('insert_id') --equivalent to query.get ('insert_id')--Updated:row.product_name = 'name456'row.update() --or query.update (row)--delete:row.delete() --or query.update (row)

Active Record

vendor.ActiveRecord further encapsulates the query generator vendor.Query At the same time, vendor.ActiveRecord inherits from vendor.Model and can use the load(), rules(), hasErrors(), validate() methods of the model object

Example of use

local Product = require ('vendor.ActiveRecord') {    --db = require ('config.db'), --optional attribute, specify the database connection to use, default 'config.db'    tableName = function() --The tableName method must be implemented to return the name of the data table to manipulate        return 'products'    end}--insert data:local product = Product.new {    product_id = 123,    product_name = 'name123',} --The returned object supports using the vendor.Query methodproduct.save() --equivalent to product.insert()--Quickly find a linelocal row = Product.findOne {product_id = 123} --The returned object supports using the vendor.Query method--Quickly find multiple lineslocal rows = Product.findAll {user = 'creater'} --The returned object supports using the vendor.Query method--Complex querieslocal query = Product.find(). where {user = 'creater'} --The returned object supports using the vendor.Query methodlocal page = Pager {    pageSize = 8,    totalCount = query.count(),}local list = query.orderBy ('create_time desc'). page (page) .all()--You can also do the followingquery.insert() --insert data.query.update() --Update the data.query.delete() --delete the data.

It is recommended that each data table be created in the models directory as an operation class that inherits from 'vendor.ActiveRecord'

Database security issues

Use vendor.ActiveRecord or vendor.Query to automatically generate query statements Sql statement in the construction process, if the parameters passed is a form, the constructor will be escaped operation to prevent sql injection

But if the string is passed, it will not escape operation, it is recommended to minimize the use of, and to ensure that sql security Query.get ('sql') can be obtained by the implementation of the sql statement

If you want to escape, you can use the util.mescape() method

local util = require ("vendor.Util")value = util.mescape (value)

lua-resty-yii is available under the MIT license. See the LICENSE file for more information.

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:PSpider - Python3下极为简洁的爬虫框架
下一篇:resty的缓存技术设计及使用
相关文章

 发表评论

暂时没有评论,来抢沙发吧~