一个传统的网站往往会有许多路径(/
,/login
,/user
),不同的路径对应不同的页面,有不同的处理逻辑,涉及表单的页面往往还会同时存在get
和post
两种形式的请求,其往往也承担了核心的业务。
先摘一段我工程的代码举个例子:
router.get('/', async function (ctx, next) {
await ctx.render('index', {
title: 'OA',
user: ctx.session.user
});
});
router.get('/reg', async function (ctx, next) {
await ctx.render('reg', {
title: 'OA-注册'
});
});
router.get('/login', async function (ctx, next) {
await ctx.render('login', {
title: 'OA-登录'
});
});
router.get('/logout', async function (ctx, next) {
ctx.session.user = null;
return ctx.redirect('/');
});
router.get('/user_app', async function (ctx, next) {
await ctx.render('user_app', {
title: 'OA-应用角色管理'
});
});
router.get('/user_db', async function (ctx, next) {
await ctx.render('user_db', {
title: 'OA-数据库角色管理'
});
});
router.get('/waf_log', async function (ctx, next) {
var wafLogs = await WafLogs.fetchAll();//从数据库中查询所有的日志信息
var logs = {};
for(var i = 0;i < wafLogs.length;i++){
logs[i] = wafLogs.models[i].attributes;
}
await ctx.render('waf_log', {
title: 'OA-waf日志',
logs: logs
});
});
这一部分基本都是简单的get页面的请求,koa使用ctx.render(模板名,{模板需要的变量})
做页面渲染,第一个参数指定要使用哪一个模板文件作为返回的页面,第二个参数用于传入变量给jade模板使用。
GET请求时,通常需要服务器返回一个静态页面,所以逻辑都还算比较简单,只需要指定好页面模板和相应的变量即可。
上述例子中,有一个GET /waf_log
的route比较特别,这一个get请求需要服务器先查询存放日志的数据库,将日志信息保存成json格式,再由render函数传给模板进行渲染,下面将这一部分前后端的关键代码放在一起对比的看一下。
//后端
router.get('/waf_log', async function (ctx, next) {
var wafLogs = await WafLogs.fetchAll();//从数据库中查询所有的日志信息
var logs = {};
for(var i = 0;i < wafLogs.length;i++){
logs[i] = wafLogs.models[i].attributes;
}
await ctx.render('waf_log', {
title: 'OA-waf日志',
logs: logs
});
});
//前端
doctype html
html
head
meta(charset='utf-8')
title= title
link(rel='stylesheet' href='http://cdn.static.runoob.com/libs/bootstrap/3.3.7/css/bootstrap.min.css')
link(rel='stylesheet' href='/stylesheets/mycss.css')
script(src='http://cdn.static.runoob.com/libs/jquery/2.1.1/jquery.min.js')
script(src='http://cdn.static.runoob.com/libs/bootstrap/3.3.7/js/bootstrap.min.js')
body
div.container
div.row
ul.list-inline
li.col-lg-3.nonpadding
p.content-title WAF日志
li.col-lg-6.nonpadding
li.float-right.col-lg-3.nonpadding
div.input-group
input#searchBox.form-control(type='text',placeholder='输入关键字,支持模糊查询',required)
span.input-group-btn
input.btn.btn-warning.btn-search(type='submit',value='搜索')
div#table.row
table.table
thead
tr
th 序号(id)
th 时间(time)
th 用户名(username)
th 访问的功能(function)
th 访问的URL(url)
th 参数(param)
th 操作结果(result)
tbody
each log in logs
tr
each val in log
td #{val}
可以看到后端传给模板两个参数{title: 'OA-waf日志',logs: logs}
,
模板中title= title
,说明前端页面中
标签内容是一个变量,值是传入的title参数的值,
模板中tbody
下是两个each循环,用于将传入的logs解析出来,动态的生成表格。
async
和await
是ES7标准中用于处理异步操作的关键字,async
声明的函数A中可以使用await
调用别的函数B,并等到函数B返回之后继续执行A剩余的部分。
ctx
是koa2中包装req
和res
的对象。
next
用于链式操作,指代当前函数的下一个函数,常常通过await next()
调用。
下面这一部分的routes相对复杂一些:
router.post('/reg', async function (ctx, next) {
if(ctx.request.body['username'].length > 25) {
//判断用户名是否过长,数据库设置username字段为varchar(25)
await ctx.render('reg', {
title: 'OA-注册',
error: '用户名不得超过25个字符'
});
} else if(ctx.request.body['password2'] !== ctx.request.body['password']) {
//判断两次密码是否一致
console.log('两次密码不一致');
await ctx.render('reg', {
title: 'OA-注册',
error: '两次密码不一致'
});
} else {
//判断用户名是否存在
var count = await Users.where('username', ctx.request.body['username']).count('username');
if(count != 0) {
console.log('用户名已存在!');
await ctx.render('reg', {
title: 'OA-注册',
error: '用户名已存在'
});
} else {
var hmac = crypto.createHmac('sha256', 'liuyueyi');
var password = hmac.update(ctx.request.body['password']).digest('hex');
console.log(password);
console.log(password.length);
var newUser = new Users({
username: ctx.request.body['username'],
password: password
});
await newUser.save();
console.log('注册成功,可以直接登录!');
ctx.session.user = newUser;
await ctx.render('reg', {
title: 'OA-注册',
success: '注册成功,可以直接登录'
});
return ctx.redirect('/login');
}
}
});
router.post('/login', async function (ctx, next) {
//需要判断的逻辑:用户名不存在或者密码错误
var count = await Users.where('username', ctx.request.body['username']).count('username');
if(count == 0) {
console.log('用户名不存在!');
await ctx.render('login', {
title: 'OA-登录',
error: '用户名不存在'
});
} else {
var hmac = crypto.createHmac('sha256', 'liuyueyi');
var password = hmac.update(ctx.request.body['password']).digest('hex');
var user = await Users.where('username', ctx.request.body['username']).fetch();
if (user.attributes.password == password) {
console.log('登陆成功!'+ user.attributes.username);
ctx.session.user = user.attributes.username;
return ctx.response.redirect('/');
} else {
console.log('密码错误!');
await ctx.render('login', {
title: 'OA-登录',
error: '密码错误'
});
}
}
});
这一部分是处理前端post回来的表单数据的逻辑代码。
上面的代码中,页面切换使用到了两种方法
二者是有区别的。
render用于页面渲染,是将指定的模板通过引擎转换成html之后直接画在当前页面之上,如果render指定的模板和当前页不同,地址栏上可以清晰的看见路径没有变化。
redirect则是用于页面跳转,服务器端的后台可以看到跳转的时候响应码是302,并且地址栏的路径也会变化。
这一部分我之后单独写。
routes代码可以直接写在app.js当中,但是当业务越来越复杂,页面越来越多的时候,app.js会变得很长,从而不易维护,所以正确的做法是将其移到routes路径下,做成一个模块,使用的时候导入到app.js中。