新建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-routerconst 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 = 22.是从上下文的请求对象中获取 请求对象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中,这种方式等到用的时候再深入研究。
需要用到的中间件rediskoa-rediskoa-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封装./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/ # 前端源代码目录