前言

  前面一有写到一篇Node.js+Express构建网站简单示例:http://www.cnblogs.com/zhongweiv/p/nodejs_express_webapp.html

  这篇还是用以前的例子, 用Node.js+Koa2构建

  Koa:   https://github.com/koajs/koa

      http://koa.bootcss.com  (中文)

  Koa就不多介绍了,前面也写过Express,同一个团队打造,前面也过express文章,对比着看,自然可以看出些优点!

搭建项目及其它准备工作

创建数据库

CREATE DATABASE IF NOT EXISTS nodesample CHARACTER SET UTF8;

USE nodesample;

SET FOREIGN_KEY_CHECKS=0;

DROP TABLE IF EXISTS `userinfo`;
CREATE TABLE `userinfo` (
`Id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
`UserName` varchar(64) NOT NULL COMMENT '用户名',
`UserPass` varchar(64) NOT NULL COMMENT '用户密码',
PRIMARY KEY (`Id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户信息表';

创建Koa2项目

安装koa-generator:  https://github.com/17koa/koa-generator

npm install -g koa-generator

安装成功后下图(版本:1.1.16)

然后创建Koa2项目,安装相关依赖项

cd 工作目录
koa2 项目名
cd 项目目录 && npm install

安装项目其它需要包

1.安装使用MySQL需要的包

npm install --save mysql

没有使用过的可以看我以前写的相关操作文章:http://www.cnblogs.com/zhongweiv/p/nodejs_mysql.html

2.安装ejs(koa2默认为jade,我习惯使用ejs)

npm install --save ejs

没有使用过的可以看我以前写的相关操作文章:http://www.cnblogs.com/zhongweiv/p/nodejs_express.html

3.安装Session存储相关包(存储到redis)

npm install koa-session  https://github.com/koajs/session

npm install --save koa-session

koa-session-redis https://github.com/Chilledheart/koa-session-redis

npm install --save koa-session-redis

清除冗余文件并重新规划项目目录

1.删除掉创建项目后自带的views和routes下的文件

2.重新规划项目目录,规划后如下

目录规则解释:

1.新增pub目录:主要为了统一存放"数据访问"、"业务逻辑"、"公共方法文件"、"数据库帮助文件"、"配置文件"等

2.新增pub目录下utils目录:主要为了统一存放类似"公共函数文件"、"返回值文件"、"枚举文件"等公共文件

3.新增pub目录下config目录:主要为了统一存放各种类型的配置文件

4.新增pub目录下db目录:主要为了统一存放各种数据库帮助类,比如:"mysql-helper.js"、"mongo-helper.js"等等

5.新增pub目录下model目录:主要为了统一存放各种数据库各表CURD操作

6.新增pub目录下bll目录:主要为了统一存放各种业务逻辑的具体实现

配置文件

从上面的图可以看出,我在pub下新建的config目录下新建了一个config.js

这个config.js中将编写“开发环境”和“发布环境”中所需的配置,代码如下

/**
* 配置文件
*/
//发布配置
const production = { //服务器端口
SERVER_PORT : 3000, //REDIS配置
REDIS: {
host: 'localhost',
port: 6379,
password: "abcd",
maxAge: 3600000
}, //MYSQL数据库配置
MYSQL: {
host: "localhost",
user: "root",
password: "abcd",
port: "3306",
database: "nodesample",
supportBigNumbers: true,
multipleStatements: true,
timezone: 'utc'
} } //开发配置
const development = { //服务器端口
SERVER_PORT : 3000, //REDIS配置
REDIS: {
host: 'localhost',
port: 6379,
password: "abcd",
maxAge: 3600000
}, //MYSQL数据库配置
MYSQL: {
host: "localhost",
user: "root",
password: "abcd",
port: "3306",
database: "nodesample",
supportBigNumbers: true,
multipleStatements: true,
timezone: 'utc'
} } const config = development module.exports = config

规划示例路由,并新建相关文件

示例中将有注册、登录功能,先规划好路由,新建routes、views下的相关需要的文件(如项目目录图中文件),并修改app.js文件

const Koa = require('koa')
const app = new Koa()
const views = require('koa-views')
const json = require('koa-json')
const onerror = require('koa-onerror')
const bodyparser = require('koa-bodyparser')
const logger = require('koa-logger') const config = require('./pub/config/config.js');
const session = require('koa-session');
const RedisStore = require('koa2-session-redis'); const index = require('./routes/index')
const reg = require('./routes/reg')
const login = require('./routes/login')
const logout = require('./routes/logout') // error handler
onerror(app) // middlewares
app.use(bodyparser({
enableTypes:['json', 'form', 'text']
}))
app.use(json())
app.use(logger())
app.use(require('koa-static')(__dirname + '/public')) app.use(views(__dirname + '/views', {
extension: 'ejs'
})) // logger
app.use(async (ctx, next) => {
const start = new Date()
await next()
const ms = new Date() - start
console.log(`${ctx.method} ${ctx.url} - ${ms}ms`)
}) app.keys = ['Porschev'];
const redis_conf = {
key: 'Porschev',
maxAge: config.REDIS.maxAge,
overwrite: true,
httpOnly: true,
rolling: false,
sign: true,
store: new RedisStore({
host: config.REDIS.host,
port: config.REDIS.port,
password: config.REDIS.password
})
}; app.use(session(redis_conf, app)); // routes
app.use(index.routes(), index.allowedMethods())
app.use(reg.routes(), reg.allowedMethods())
app.use(login.routes(), login.allowedMethods())
app.use(logout.routes(), logout.allowedMethods()) // error-handling
app.on('error', (err, ctx) => {
console.error('server error', err, ctx)
}); app.listen(config.SERVER_PORT, () => {
console.log(`Starting at port ${config.SERVER_PORT}!`)
}); module.exports = app

  注意看红色标记修改或增加的部分

实现数据访问和业务逻辑相关方法

1.首先编写一个mysql-helper.js方便以连接池的方式进行操作

const config = require('./../config/config.js')
const mysql = require("mysql") const pool = mysql.createPool(config.MYSQL) let query = function(sql, args) { return new Promise((resolve, reject) => {
pool.getConnection(function(err, connection) {
if (err) {
resolve(err)
} else {
connection.query(sql, args, (err, result) => { if (err) {
reject(err)
} else {
resolve(result)
}
connection.release() })
}
})
}) } module.exports = {
query
}

2.编写数据访问相关方法(model目录下的userinfo.js),如下

const mysqlHelper = require('./../db/mysql-helper.js')

const userinfo = {

  /**
* 增加一条数据
* @param {object} args 参数
* @return {object} 结果
*/
async add ( args ) {
let sql = 'INSERT INTO userinfo(UserName, UserPass) VALUES(?, ?)'
let params = [args.username, args.userpass]
let result = await mysqlHelper.query(sql, params)
return result
}, /**
* 根据UserName得到一条数据
* @param {object} args 参数
* @return {object} 结果
*/
async getByUserName( args ){
let sql = 'SELECT Id, UserName, UserPass FROM userinfo WHERE UserName = ?'
let params = [args.username]
let result = await mysqlHelper.query(sql, params)
return result
}, /**
* 根据UserName得到数量
* @param {object} args 参数
* @return {object} 结果
*/
async getCountByUserName( args ){
let sql = 'SELECT COUNT(1) AS UserNum FROM userinfo WHERE UserName = ?'
let params = [args.username]
let result = await mysqlHelper.query(sql, params)
return result
}, } module.exports = userinfo

3.在写业务逻辑之前先规划好返回值(utils目录下retcode.js)

retcode.js

/*
* 返回码
*/
const RetCode = {
SessionExpired: -1, //session过期
Fail: 0, //失败
Success: 1, //成功
ArgsError: 2, //参数错误
UserExisted: 10, //用户已经存在
UsernameOrPasswordError: 11, //用户名或者密码错误
UserNotExist: 12, //用户不存在
}; module.exports = RetCode

4.编写“登录”、“注册”等业务逻辑(bll下userinfo.js)

const usermodel = require('./../model/userinfo.js')
const retCode = require('./../utils/retcode.js') const userinfo = { /**
* 注册
* @param {object} ctx 上下文
* @return {object} 结果
*/
async register ( ctx ) {
let form = ctx.request.body const args = {
username: form.username,
userpass: form.userpass
} let result = {
code: retCode.Success,
data: null
} //验证非空
if(!args.username || !args.userpass){
result.code = retCode.ArgsError
return result
} //根据用户名得到用户数量
let userNumResult = await usermodel.getCountByUserName(args) //用户名已被注册
if(userNumResult[0].UserNum > 0){
result.code = retCode.UserExisted
return result
} //插入注册数据
let userResult = await usermodel.add(args) if(userResult.insertId <= 0){
result.code = retCode.Fail
return result
} return result
}, /**
* 登录
* @param {object} ctx 上下文
* @return {object} 结果
*/
async login ( ctx ) {
let form = ctx.request.body const args = {
username: form.username,
userpass: form.userpass
} let result = {
code: retCode.Success,
data: null
} //验证非空
if(!args.username || !args.userpass){
result.code = retCode.ArgsError
return result
} //根据用户名得到用户信息
let userResult = await usermodel.getByUserName(args) //用户不存在
if(userResult.length == 0){
result.code = retCode.UserNotExist
return result
} //用户名或密码错误
if(userResult[0].UserName != args.username || userResult[0].UserPass != args.userpass){
result.code = retCode.UsernameOrPasswordError
return result
} //将用户ID存入Session中
ctx.session = {id: userResult[0].Id} return result
}, } module.exports = userinfo

注册

1.views目录下reg.ejs

<html>
<head>
<title>Nodejs学习笔记(十五)--- Node.js + Koa2 构建网站简单示例</title>
</head>
<body>
<h1><%= title %></h1>
登录名:<input type="text" id="txtUserName" maxlength="20" />
<br/>
<br/>
密码:<input type="password" id="txtUserPwd" maxlength="12" />
<br/>
<br/>
密码:<input type="password" id="txtUserRePwd" maxlength="12" />
<br/>
<br/>
<input type="button" id="btnSub" value="注册" />
</body>
</html> <script src="/javascripts/jquery-1.11.2.min.js" type="text/javascript"></script>
<script src="/javascripts/md5.js" type="text/javascript"></script> <script type="text/javascript">
$(function(){
$('#btnSub').on('click', function(){
var $txtUserName = $('#txtUserName'),
txtUserNameVal = $.trim($txtUserName.val()),
$txtUserPwd = $('#txtUserPwd'),
txtUserPwdVal = $.trim($txtUserPwd.val()),
$txtUserRePwd = $('#txtUserRePwd'),
txtUserRePwdVal = $.trim($txtUserRePwd.val()); if(txtUserNameVal.length == 0){
alert('用户名不能为空');
return false;
} if(txtUserPwdVal.length == 0){
alert('密码不能为空');
return false;
} if(txtUserRePwdVal.length == 0){
alert('重复密码不能为空');
return false;
} if(txtUserPwdVal != txtUserRePwdVal){
alert('两次密码不一致');
return false;
} $.ajax({
url: '/reg',
type: 'POST',
dataType: 'json',
data: {
username: txtUserNameVal,
userpass: hex_md5(txtUserPwdVal)
},
beforeSend: function (xhr) {},
success: function (res) {
if (res != null && res.code) { var retVal = parseInt(res.code); switch (retVal) {
case 2:
alert('输入有误');
break;
case 0:
alert('注册失败');
break;
case 1:
alert('注册成功!');
location.href = '/login'
break;
case 10:
alert('用户已注册');
break;
}
}
else {
alert('操作失败');
} },
complete: function (XMLHttpRequest, textStatus) {},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert('操作失败');
}
});
})
}); </script>

2.routes目录下reg.js

const router = require('koa-router')()
const userBll = require('./../pub/bll/userinfo.js')
const title = '注册' router.prefix('/reg') router.get('/', async (ctx, next) => {
await ctx.render('reg', { title })
}) router.post('/', async (ctx, next) => { let result = await userBll.register(ctx) ctx.body = result; }) module.exports = router

登录

1.views目录下login.ejs

<html>
<head>
<title>Nodejs学习笔记(十五)--- Node.js + Koa2 构建网站简单示例</title>
</head>
<body>
<h1><%= title %></h1>
登录名:<input type="text" id="txtUserName" maxlength="20" />
<br/>
<br/>
密码:<input type="password" id="txtUserPwd" maxlength="12" />
<br/>
<br/>
<input type="button" id="btnSub" value="登录" />
</body>
</html> <script src="/javascripts/jquery-1.11.2.min.js" type="text/javascript"></script>
<script src="/javascripts/md5.js" type="text/javascript"></script> <script type="text/javascript">
$(function(){
$('#btnSub').on('click', function(){
var $txtUserName = $('#txtUserName'),
txtUserNameVal = $.trim($txtUserName.val()),
$txtUserPwd = $('#txtUserPwd'),
txtUserPwdVal = $.trim($txtUserPwd.val()); if(txtUserNameVal.length == 0){
alert('用户名不能为空');
return false;
} if(txtUserPwdVal.length == 0){
alert('密码不能为空');
return false;
} $.ajax({
url: '/login',
type: 'POST',
dataType: 'json',
data: {
username: txtUserNameVal,
userpass: hex_md5(txtUserPwdVal)
},
beforeSend: function (xhr) {},
success: function (res) {
if (res != null && res.code) { var retVal = parseInt(res.code); switch (retVal) {
case 2:
alert('输入有误');
break;
case 0:
alert('登录失败');
break;
case 1:
alert('登录成功!');
location.href = '/'
break;
case 11:
alert('用户名或者密码错误');
break;
case 12:
alert('用户不存在');
break;
}
}
else {
alert('操作失败');
} },
complete: function (XMLHttpRequest, textStatus) {},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert('操作失败');
}
});
})
}); </script>

2.routes目录下login.js

const router = require('koa-router')()
const userBll = require('./../pub/bll/userinfo.js')
const title = '登录' router.prefix('/login') router.get('/', async (ctx, next) => {
await ctx.render('login', { title })
}) router.post('/', async (ctx, next) => { let result = await userBll.login(ctx); ctx.body = result; }) module.exports = router

首页

1.views目录下index.ejs

<html>
<head>
<title>Nodejs学习笔记(十五)--- Node.js + Koa2 构建网站简单示例</title>
</head>
<body>
<h1><%= title %></h1> <% if(id != null) {%>
<h3>登录用户ID:<%= id %> <a id="btnLogOut" href="javascript:void(0);">安全退出</a></h3>
<% } %>
</body>
</html> <script src="/javascripts/jquery-1.11.2.min.js" type="text/javascript"></script> <script type="text/javascript">
$(function(){
$('#btnLogOut').on('click', function(){ if(!confirm('确认要退出吗?')){
return;
} $.ajax({
url: '/logout',
type: 'POST',
dataType: 'json',
data: {},
beforeSend: function (xhr) {},
success: function (res) {
if (res != null && res.code) { var retVal = parseInt(res.code); switch (retVal) {
case 0:
alert('失败');
break;
case 1:
alert('成功!');
location.href = '/login'
break;
}
}
else {
alert('操作失败');
} },
complete: function (XMLHttpRequest, textStatus) {},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert('操作失败');
}
});
})
}); </script>

2.routes目录下index.js

const router = require('koa-router')()
const title = '首页' router.get('/', async (ctx, next) => {
//判断登录
if(!ctx.session || !ctx.session.id){
await ctx.redirect('/login')
}else{
const id = ctx.session.id;
await ctx.render('index', { title, id })
}
}) module.exports = router

  index.js文件中实现如果不存在session则跳回登录页

安全退出

1.routes目录下logout.js

const router = require('koa-router')()
const retCode = require('./../pub/utils/retcode.js') router.prefix('/logout') router.get('/', async (ctx, next) => {
await ctx.render('logout', {})
}) router.post('/', async (ctx, next) => { ctx.session = null; let result = {
code: retCode.Success,
data: null
} ctx.body = result; }) module.exports = router

写在之后

没有去说一些细节API,写这篇主要可以对比 Nodejs学习笔记(七)--- Node.js + Express 构建网站简单示例 来看,完全是一亲的示例,只是这次用的Koa2,方便大家看看Koa2和express写出来的不同

总的来说Koa2还是比较好上手,async、await这个对于有C#语言基础的来说也比较亲切,不用二次理解

可以对比一下express时的各种嵌套回调写法,Koa2写好更优雅、更易阅读

 示例有限,其它操作通过官网查找API或github找一些组件来动手试,比如最常用的一些功能:操作cookies、上传文件、session存储到其它介质等

 参考资料: https://koa.bootcss.com/

 源码详见http://bijian1013.iteye.com/blog/2425085,虽然是示例结构,但是尽量按照平常做项目的想法去实现的,有兴趣的动手去搭项目做才会理解一些思路,代码都放在文章中了。

 

此系列的源代码可到http://bijian1013.iteye.com/blog/2425085下载。 

文章来源:https://www.cnblogs.com/zhongweiv/p/nodejs_koa2_webapp.html

最新文章

  1. 运算符.png
  2. [EF2]Sneak Preview: Persistence Ignorance and POCO in Entity Framework 4.0
  3. 不定长链表队列C语言实现
  4. Php 笔记
  5. Keil代码中for循环延时问题
  6. 加载cocos studio场景
  7. linux删除某个php程序进程的组合命令
  8. Java for LeetCode 168 Excel Sheet Column Title
  9. 黄聪:VS2010每次F5启动都重新编译但是没办法进入断点
  10. Windows操作 - Photoshop为图片添加透明立体水印
  11. SDP协议分析
  12. Ⅵ.AngularJS的点点滴滴-- 指令
  13. 51单片机C语言学习笔记4:keil C51绝对地址访问
  14. Android数据存储
  15. 《Java技术》第二次作业--面向对象基础
  16. protobuf中的枚举缺省值应该为UNKNOWN
  17. Android的actionBar的菜单使用-android学习之旅(四十三)
  18. Django里URL配置中name参数的作用
  19. awk 中的难懂符号解释
  20. Saiku连接mysql数据库(二)

热门文章

  1. springMVC的两种下载方式
  2. Centos7下安装netstat
  3. delphi xe5 android 开发数据访问手机端(一)
  4. Advance Installer安装问题
  5. 导入礼包时,遇到file_get_contents读取的文本,去除空格、换行等的方法
  6. ElasticSearch 学习记录之ES查询添加排序字段和使用missing或existing字段查询
  7. c#简单操作MongoDB_2.4
  8. ASP.NET CORE中使用Cookie身份认证
  9. JavaScript基础知识从浅入深理解(一)
  10. maven 导包报错
  11. [Solution] 885. Spiral Matrix Ⅲ
  12. JAX-WS 使用maven创建
  13. Vue开源项目汇总(史上最全)(转)
  14. 【LeetCode】14. 最长公共前缀
  15. css样式重置样式
  16. IIS下实现帝国CMS搜索页伪静态
  17. http4e eclipse plugin 插件介绍
  18. python学习笔记——python JSON
  19. Luogu P1726 上白泽慧音
  20. 屌丝就爱尝鲜头——java8初体验