lovol v0.5.9
一、router介绍
这是一个简单的路由中间件,旨在帮助你快速搭建一个小型网站。将路由对象化Json的结构思维让你理解更加容易,支持常用的权限控制、模板、后端渲染、静态化等常用功能。 初期文档比较简陋,在后期会慢慢完善。
1.1开始
1.1.1引入并导出常用方法和变量
这些方法和变量都是常用的,可以先导出备用。
const lovol = require('lovol');
const {
API, AUTH, STATIC,//方法描述
GET, POST, PUT, DELETE,//请求方法
initRouter, clearTemplate, clearStatic, clearCache//常用方法
} = lovol.router;
1.1.2初始化
第一个参数是路由的对象,第二个是配置参数。
const router = initRouter({
[AUTH]() {//访问控制
return true;
},
about() {//domain/about
return {[STATIC]: true, qwe: 123}//可以静态化domain/about/1.html
},
admin: {
async [AUTH]({request, response, ...data}) {
// console.log(data);
// return data.redirect('/about');//重定向
return true;
},
[GET]({redirect, view}) {//默认的GET路由//domain/admin
// redirect('/about');
return view('/about');//使用别的页面
},
log: {
async [GET]() {//domain/admin/log
return {qwe: 123};
},
api: {
[API]: true,//定义为接口,没有页面
[GET]() {
},
[POST]() {
}
}
},
}
}
,
{
max: 1024 * 1024 * 16,//请求的最大尺寸
lang: 'zh-cn',//本地化
charset: 'utf-8',
defaultMethod: [POST],//表单请求会返回页面的方法
// cacheTimeout: 10,//内存缓存的时间
cacheTimeout: false,
serverError: false,//是否显示服务器端错误
bridge: {qwe: 123},//对象桥
mime: {//mime-type支持格式
json: 'application/json',
},
});
1.1.2使用
nodejs中有HTTP、HTTP/2、HTTPS,这里以HTTP为例。
http.createServer(async (request, response) => {
let result = await router(request, response);//调用方法传入request,response
// console.log(result);
response.writeHead(
result.head.statusCode,
result.head.statusMessage,
result.head.headers
);
if (typeof result.body.pipe === "function")
result.body.pipe(response);//路由结果为流
else
response.end(result.body);//路由结果为值
}).listen(80);
1.2目录结构
将会自动生成几个目录:shared(共享模板)、static(静态缓存)、views(页面模板)、wwwroot(资源目录)
┌ shared ─ admin ┬ head.htm //模板缓存文件
│ ├ head.html //模板文件
│ └ foot.html
├ static ─ about ┬ 1.html //静态缓存文件
│ └ data.json //静态缓存文件
├ views ┬ about.html
│ ├ admin.html
│ └ admin ─ log.html
└ wwwroot ┬ index.html
├ js
├ css
└ img
- 我不太会写文档这些,我就直接上例子吧。
1.3实例
1.3.1访问控制
[AUTH]() {
...
return false;//false:浏览器会收到一个403错误,true:路由会继续向下进行。
...
}
//我比较懒直接贴代码
async [auth]({pathArray, getCookie, redirect, request}) {
let cookie = getCookie();//校验部分cookie可以使用httponly来防止js直接篡改。
let auth = cookie._token && cookie._token.length === 32 &&
cookie._token === md5('my key 2019' + cookie._uid + cookie._menu);//验证cookie完整性
if (pathArray[0] === 'login') {
if (auth) {
return redirect('/admin');//可以直接重定向
}
} else if (pathArray[0] === 'logout') {
if (!auth) {
return redirect('/admin');//可以直接重定向
}
} else if (!auth) {
return redirect('/admin/login');
} else if (pathArray.length === 0)
return true;
else if (cookie.menu)
return cookie.menu.split(',').includes(pathArray[0]);//这是复杂一些的判断。
else
return false;//也可以返回403错误
return true;
},
//这里面你还能做ip限制、流量控制、防恶意访问等你能想到的操作。
1.3.2目录默认文件
只要有defaultMethod里面的方法都会生成一个默认的文件根目录就是index.html,其他就是 目录名.html。如果defaultMethod加入了POST。
initRouter({
[GET]() {}
about: null,
admin: {
[POST]() {},
log() {}
}
});
//生成如下文件
├ views ┬ index.html // [GET]() {}
├ about.html // about: null
├ admin.html // admin: {[POST]() {}}
└ admin ─ log.html // admin: {log() {}}
1.3.3动态转静态
return的对象里面加入STATIC: true值这样浏览器以访问静态文件的时候就会在static目录里面保存相应的静态文件,第二次访问同一个文件的时候后台就不会重新渲染,而是直接访问这个静态文件。 所有这个的使用要严格判断控制,不然会被而已生成静态文件消耗服务器资源,好处就是能自动缓存文件,比如产品页。
product({pathArray, view}) {//domain/about
let [key, id] = pathArray;
id = id.split('.')[0];
if (key === 'id' && isValid(id))
return {[STATIC]: true, qwe: 123}//可以静态化domain/about/1.html
else
return view('/404', {id});
},
//生成文件
├ static ─ product - id ┬ 1.html // domain/product/id/1.html
└ 9.html // domain/product/id/9.html
1.3.4后端渲染
后端渲染是将路由到的方法对应views下的html文件动态执行返回渲染后的html文件,一个url路由结果如下, 比如domain/notify/index.html(含html后缀),它路由的顺序如下,逐条去搜索,都没找到就返回404:
1、/wwwroot/notify/index.html
2、/static/notify/index.html
3、根据路由路劲去找相应的方法,如果方法属于API则直接返回返回值
4、如果方法不是API再找/views/notify/index.html,有则将返回值传入scope里面去渲染模板,没有则返回404。
再比如domain/notify/index(不含html后缀)就不再去/wwwroot和/static目录去匹配了。如果在notify下找到了index的默认方法(GET、POST)就去/views 目录下找相应的模板文件,将方法返回值传入scope里面去渲染,将结果返给浏览器。如果没有则使用notify的默认方法和对应模板文件去渲染将多余的路由值(index)传入scope(pathArray: 'index')中去渲染, 如果notify也没有就使用根路由的默认方法和模板文件去渲染,如果根目录也没有则返回404。
- 注意,因为这个特性所以在用路由带参数的时候(domain/notify/参数1/参数2;domain/notify/qwe/123),最好是notify就是节点的末端这样效率是最高的, 不然它会先遍历节点在子节点都没匹配的时候再选中notify方法来渲染。
1.4关键字
1.4.1关键字1:#{}
//代码
...
scope.test.key = 123;
...
<div>#{scope.test.key}</div>
...
//结果
<div>123</div>
//可以等价的看作
`<div>${scope.test.key}</div>`
//所以这样用也是一样的
<div>#{1+1}</div> //<div>${1+1}</div> //<div>2</div>
<div>#{parseInt(1.5)}</div> //<div>${parseInt(1.5)}</div> //<div>1</div>
//这个也能转义
<div>\#{1+1}</div> //<div>#{1+1}</div>
<div>\#{</div> //<div>#{</div>
1.4.2关键字2:{/}和{~}
//共享模板
/shared/comm/head.html
<h1>{scope.title}</h1>
//代码
...
{/comm/head,{title:'这是标题'}}//参数可缺省
<p>这是内容</p>
...
//结果
<h1>这是标题</h1>
<p>这是内容</p>
//以上面的代码我前提,则:
...
{~comm/head,{title:'标题标题标题'}}
<p>这是内容</p>
...
//结果
<h1>这是标题</h1>
<p>这是内容</p>
因为'{~}'会在.html生成.htm,下次使用则直接使用.htm文件,不在运算.html文件里的。
1.4.3关键字3: <script server></script>
//代码
<div>
<script server>
if (1) {
write(`<h1>${1+1}</h1>`);
} else {
write(`<h2>我不会被执行</h2>`);
}
</script>
</div>
//结果
<div>
<h1>2</h1>
</div>
1.4.4关键字混合使用
/shared/foot.html
<p>#{scope.world}</p>
//代码
<div>
<script server>
scope.share = 'foot';
scope.world = 'hello';
</script>
{/#{scope.share}}
</div>
//结果
<div>
<p>hello</p>
</div>
1.4.5错误提示
//代码
<div>
{/qwe}
<script server>
write(qwe)
</script>
</div>
//结果
<div>
<!-- 未加载源摸板 /shared/qwe.html -->
<!-- server script 代码有误:ReferenceError: qwe is not defined-->
</div>
1.5路由方法的参数
initRouter({
[GET](scope) {
console.log(Object.keys(scope));
//[ 'view',//使用其它模板,可使API返回页面:view('/more/help')
'query',//url参数:domain/test?qwe=123&asd=456 //query: {qwe: 123, asd: 456}
'bridge',//桥对象:bridge: {qwe: 123, asd: 456}//第二个参数中传入
'urlObj',//url解析后的对象:Url {port: '', href: '', query: {}, ...}
'nonView',//不返回页面,直接返回数据:nonView({qwe: 123})
'request',//原生对象request
'response',//原生对象response
'writeBody',//等价response.write();
'redirect',//重定向:redirect('http://domain2/')
'getCookie',//获取cookie对象
'setCookie',//设置cookie:setCookie([{key: '', value: '', path: '', domain: '', secure: '', httponly: '', expires: ''}]);
'delCookie',//删除cookie:delCookie([{key: '', path: ''}, {key: '', path: ''}]);
'data'//表单提交的数据:data: {qwe: 123, asd: 456},如果有文件则:data.file1.save('tmp', 'f1.jpg');则保存为:/wwwroot/tmp/f1.jpg
]
}
}, {bridge: {qwe: 123, asd: 456}});
4 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago