安装koa2
# 初始化package.json
npm init
# 安装koa2
npm install koa
新建index.js
const Koa = require('koa')
const app = new Koa()
app.use( async ( ctx ) => {
ctx.body = 'hello koa2'
})
app.listen(3000)
console.log('[demo] start-quick is starting at port 3000')
运行node index.js,然后在浏览器中访问http://localhost:3000/
路由:koa-router基本用法
npm install --save koa-router
const Koa = require('koa')
const fs = require('fs')
const app = new Koa()
const Router = require('koa-router')
let home = new Router()
// 子路由1
home.get('/', async ( ctx )=>{
let html = `
<ul>
<li><a href="/page/helloworld">/page/helloworld</a></li>
<li><a href="/page/404">/page/404</a></li>
</ul>
`
ctx.body = html
})
// 子路由2
let page = new Router()
page.get('/404', async ( ctx )=>{
ctx.body = '404 page!'
}).get('/helloworld', async ( ctx )=>{
ctx.body = 'helloworld page!'
})
// 装载所有子路由
let router = new Router()
router.use('/', home.routes(), home.allowedMethods())
router.use('/page', page.routes(), page.allowedMethods())
// 加载路由中间件
app.use(router.routes()).use(router.allowedMethods())
app.listen(3000, () => {
console.log('[demo] route-use-middleware is starting at port 3000')
})
获取请求数据:
获取get请求的数据有两种方法:
- 1.是从局部中直接获取
- 请求对象ctx.query,返回如{a:1,b:2}
- 请求字符串ctx.querystring,返回如a = 1&b = 2
- 2.是从上下文的请求对象中获取
- 请求对象ctx.request.query,返回如{a:1,b:2}
- 请求字符串ctx.request.querystring,返回如a = 1&b = 2
const Koa = require('koa')
const app = new Koa()
app.use( async ( ctx ) => {
let url = ctx.url
// 从上下文的request对象中获取
let request = ctx.request
let req_query = request.query
let req_querystring = request.querystring
// 从上下文中直接获取
let ctx_query = ctx.query
let ctx_querystring = ctx.querystring
ctx.body = {
url,
req_query,
req_querystring,
ctx_query,
ctx_querystring
}
})
app.listen(3000, () => {
console.log('[demo] request get is starting at port 3000')
})
在浏览器里输入:http://localhost:3000/page/user?a=1&b=2
获取post请求数据的方法:
koa2中并没有封装获取post请求数据的方法,所以需要手写这一获取过程
// 解析上下文里node原生请求的POST参数
function parsePostData( ctx ) {
return new Promise((resolve, reject) => {
try {
let postdata = "";
ctx.req.addListener('data', (data) => {
postdata += data
})
ctx.req.addListener("end",function(){
let parseData = parseQueryStr( postdata )
resolve( parseData )
})
} catch ( err ) {
reject(err)
}
})
}
// 将POST请求参数字符串解析成JSON
function parseQueryStr( queryStr ) {
let queryData = {}
let queryStrList = queryStr.split('&')
console.log( queryStrList )
for ( let [ index, queryStr ] of queryStrList.entries() ) {
let itemList = queryStr.split('=')
queryData[ itemList[0] ] = decodeURIComponent(itemList[1])
}
return queryData
}
还有另一种获取post数据的方法,使用koa-bodyparser中间件
npm install --save koa-bodyparser
const Koa = require('koa')
const app = new Koa()
const bodyParser = require('koa-bodyparser')
// 使用ctx.body解析中间件
app.use(bodyParser())
app.use( async ( ctx ) => {
if ( ctx.url === '/' && ctx.method === 'GET' ) {
// 当GET请求时候返回表单页面
let html = `
<h1>koa2 request post demo</h1>
<form method="POST" action="/">
<p>userName</p>
<input name="userName" /><br/>
<p>nickName</p>
<input name="nickName" /><br/>
<p>email</p>
<input name="email" /><br/>
<button type="submit">submit</button>
</form>
`
ctx.body = html
} else if ( ctx.url === '/' && ctx.method === 'POST' ) {
// 当POST请求的时候,中间件koa-bodyparser解析POST表单里的数据,并显示出来
let postData = ctx.request.body
ctx.body = postData
} else {
// 其他请求显示404
ctx.body = '<h1>404!!! o(╯□╰)o</h1>'
}
})
app.listen(3000, () => {
console.log('[demo] request post is starting at port 3000')
})
静态资源加载:
在koa中正常是访问不到项目中的静态资源的,所以可以通过使用中间件来达到这一目标。
npm install koa-static --save
const Koa = require('koa')
const path = require('path')
const static = require('koa-static')
const app = new Koa()
// 静态资源目录对于相对入口文件index.js的路径
const staticPath = './static'
app.use(static(
path.join( __dirname, staticPath)
))
app.use( async ( ctx ) => {
ctx.body = 'hello world'
})
app.listen(3000, () => {
console.log('[demo] static-use-middleware is starting at port 3000')
})
设置和读取cookie
koa提供了从本地直接读取,编写cookie的方法
- ctx.cookies.get(名称,[选项])
- ctx.cookies.set(名称,值,[选项])
const Koa = require('koa')
const app = new Koa()
app.use( async ( ctx ) => {
if ( ctx.url === '/index' ) {
ctx.cookies.set(
'cid',
'hello world',
{
domain: 'localhost', // 写cookie所在的域名
path: '/index', // 写cookie所在的路径
maxAge: 10 * 60 * 1000, // cookie有效时长
expires: new Date('2017-02-15'), // cookie失效时间
httpOnly: false, // 是否只用于http请求中获取
overwrite: false // 是否允许重写
}
)
ctx.body = 'cookie is ok'
} else {
ctx.body = 'hello world'
}
})
app.listen(3000, () => {
console.log('[demo] cookie is starting at port 3000')
})
session的使用
session也被称为“会话控制”,顾名思义,它用于控制网络会话,如用户登录信息、购物车中的商品。
session中的数据是保存在服务器端的,在服务器端有很多种存储方式,既可以直接保存在内存中,也可以保存在Redis、MongoDB、Mysql等数据库中。但是session中的数据一般都是在短时间内高频访问的,需要保证性能(在内存中缓存,读写快),同时这样容易丢失数据,如遇到服务器重启的情况。因此比较好的方式是内存缓存配合外部数据库对session做一个持久化,方便丢失数据后的数据找回。
将session存放到数据库中:
需要用到中间件:koa-session-minimal
适用于koa2 的session中间件,提供存储介质的读写接口 。koa-mysql-session
为koa-session-minimal
中间件提供Mysql数据库的session数据读写操作。
const Koa = require('koa')
const session = require('koa-session-minimal')
const MysqlSession = require('koa-mysql-session')
const app = new Koa()
// 配置存储session信息的mysql
let store = new MysqlSession({
user: 'root',
password: 'abc123',
database: 'koa_demo',
host: '127.0.0.1',
})
// 存放sessionId的cookie配置
let cookie = {
maxAge: '', // cookie有效时长
expires: '', // cookie失效时间
path: '', // 写cookie所在的路径
domain: '', // 写cookie所在的域名
httpOnly: '', // 是否只用于http请求中获取
overwrite: '', // 是否允许重写
secure: '',
sameSite: '',
signed: '',
}
// 使用session中间件
// koa-session-minimal需要一个options对象参数:
/***
* key:会话cookie名称和商店密钥前缀
store:会话外部存储
cookie:cookie选项,可以是对象(静态cookie选项)或返回对象的函数(动态cookie选项)。
只有maxAge,path,domain,secure,httpOnly是支持的。
*/
app.use(session({
key: 'SESSION_ID',
store: store,
cookie: cookie
}))
//下面就可以在自己的路由中间件中来定义session信息了
//每次请求路由接口,都会对session信息进行更新,请自行验证
app.use( async ( ctx ) => {
// 设置session
if ( ctx.url === '/set' ) {
ctx.session = {
user_id: Math.random().toString(36).substr(2),
count: 0
}
ctx.body = ctx.session
} else if ( ctx.url === '/' ) {
// 读取session信息
ctx.session.count = ctx.session.count + 1
ctx.body = ctx.session
}
})
app.listen(3000)
console.log('[demo] session is starting at port 3000')
查看数据库,多了一个_mysql_session_store
表
表中的data字段数据,就是存储的session
还有一种方式是将session存放在redis中,这种方式等到用的时候再深入研究。
需要用到的中间件redis
koa-redis
koa-session-minimal
模板引擎:
在使用了vue.js或者react.js的时候,为什么还会用到模板引擎?看了一些项目的示例后发现,有这么两种情况:一种是服务端渲染页面,另一种是仅通过模板引擎加载vue或者react项目压缩后的首页文件内容。(细节见项目)
# 安装koa模板使用中间件
npm install --save koa-views
# 安装ejs模板引擎
npm install --save ejs
文件目录
├── package.json
├── index.js
└── view
└── index.ejs
./view/index.ejs模板
<!DOCTYPE html>
<html>
<head>
<title><%= title %></title>
</head>
<body>
<h1><%= title %></h1>
<p>EJS Welcome to <%= title %></p>
</body>
</html>
连接mysql
npm install --save mysql
创建数据库会话
const mysql = require('mysql')
const connection = mysql.createConnection({
host : '127.0.0.1', // 数据库地址
user : 'root', // 数据库用户
password : '123456' // 数据库密码
database : 'my_database' // 选中数据库
})
// 执行sql脚本对数据库进行读写
connection.query('SELECT * FROM my_table', (error, results, fields) => {
if (error) throw error
// connected!
// 结束会话
connection.release()
});
注意:一个事件就是一个从开始到结束的过程,数据库会话操作执行完后,就需要关闭掉,以免占用连接资源。
创建数据连接池
一般情况下下操作数据库是很复杂的简化程序,不只是一个会话,如果直接用会话操作,就需要进行会话进行配置连接参数。因此这时候就需要连接池管理会话了。
const mysql = require('mysql')
// 创建数据池
const pool = mysql.createPool({
host : '127.0.0.1', // 数据库地址
user : 'root', // 数据库用户
password : '123456' // 数据库密码
database : 'my_database' // 选中数据库
})
// 在数据池中进行会话操作
pool.getConnection(function(err, connection) {
connection.query('SELECT * FROM my_table', (error, results, fields) => {
// 结束会话
connection.release();
// 如果有错误就抛出
if (error) throw error;
})
})
Promise封装mysql模块
Promise封装./async-db
const mysql = require('mysql')
const pool = mysql.createPool({
host : '127.0.0.1',
user : 'root',
password : '123456',
database : 'my_database'
})
let query = function( sql, values ) {
return new Promise(( resolve, reject ) => {
pool.getConnection(function(err, connection) {
if (err) {
reject( err )
} else {
connection.query(sql, values, ( err, rows) => {
if ( err ) {
reject( err )
} else {
resolve( rows )
}
connection.release()
})
}
})
})
}
module.exports = { query }
异步/等待使用
const { query } = require('./async-db')
async function selectAllData( ) {
let sql = 'SELECT * FROM my_table'
let dataList = await query( sql )
return dataList
}
async function getData() {
let dataList = await selectAllData()
console.log( dataList )
}
getData()
基本项目结构:
├── init # 数据库初始化目录
│ ├── index.js # 初始化入口文件
│ ├── sql/ # sql脚本文件目录
│ └── util/ # 工具操作目录
├── package.json
├── config.js # 配置文件
├── server # 后端代码目录
│ ├── app.js # 后端服务入口文件
│ ├── codes/ # 提示语代码目录
│ ├── controllers/ # 操作层目录
│ ├── models/ # 数据模型model层目录
│ ├── routers/ # 路由目录
│ ├── services/ # 业务层目录
│ ├── utils/ # 工具类目录
│ └── views/ # 模板目录
└── static # 前端静态代码目录
├── build/ # webpack编译配置目录
├── output/ # 编译后前端代码目录&静态资源前端访问目录
└── src/ # 前端源代码目录