koa的基本使用
目录结构
使用koa
创建app应用
- 创建app.js文件,导入需要的第三方插件。
var Koa = require('koa');
var views = require('koa-views');
var bodyParser = require('koa-bodyparser');
const serve = require('koa-static');
const render = require('koa-art-template');
const path = require('path');
var {router} = require('./router');
- 创建app, 并监听端口
var app = new Koa()
app.listen(3001)
使用路由
- 配置路由,在router.js中配置路由
var Router = require('koa-router')
var router = new Router()
// 配置路由
router.get('/', async (ctx) => {
// 返回数据,相当于:原生里面的res.writeHead() res.end()
ctx.body = '首页'
})
// 配置动态路由
router.get('/newsdetail/:id', async (ctx) => {
console.log(ctx.params);
})
- app.js中启动路由
// router.allowedMethods() 在所有路由中间件之后调用,此时根据 ctx.status 设置 response 响应头
app
.use(router.routes())
.use(router.allowedMethods())
获取 get 请求参数
GET: 接收方式有两种: query、querystring
- query: 返回的是格式化好的参数对象
- querystring: 返回的是请求字符串
router.get('/newsContent', async (ctx) => {
// ctx 获取get传值
console.log(ctx.query);
console.log(ctx.querystring);
// ctx中的request中获取
console.log(ctx.request);
console.log(ctx.params);
})
接收post请求参数
- 引入中间件
// ! post 参数解析中间件
app.use(bodyParser());
- 获取参数
router.post('/doAdd', async(ctx) => {
// 获取表单提交的数据
console.log(ctx.request.body);
})
中间件的使用
- 应用级中间件 在app.js中定义的中间件,在匹配路由前执行。
app.use(async(ctx, next) => {
// 在中间件中配置 ctx.state.userInfo 在任何页面中都可以使用 userInfo
ctx.state.userInfo = 'name'
console.log('第一个中间件 next 之前');
// 继续向下匹配
await next()
console.log('第一个中间件 next 之后');
})
- 错误中间件 发生错误时,返回对应的页面。
app.use(async(ctx, next) => {
console.log('第二中间件 next 之前');
await next()
console.log('第二中间件 next 之后');
// 当所有的路由匹配之后执行,返回404页面
if (ctx.status == 404) {
ctx.status = 404
ctx.body = '这是一个404页面'
}
})
- 路由级中间件
router.get('/news', async(ctx, next) => {
console.log('这是一个新闻页面');
await next()
})
router.get('/news', async(ctx) => {
ctx.body = '这是一个新闻页面'
})
中间件的执行流程
- 从上往下执行中间件
- 遇到 next 执行下一个中间件
- 中间件执行结束之后,匹配路由
- 路由匹配结束后,执行中间件 next 后的代码,从下往上执行 输出结果
- 输入 '/news' 路由,匹配路由成功 输出:
- 第一个中间件 next 之前
- 第二中间件 next 之前
- 这是一个新闻页面
- 第二中间件 next 之后
- 第一个中间件 next 之后
- 输入 '/news1' 路由,匹配路由失败 输出:
- 第一个中间件 next 之前
- 第二中间件 next 之前
- 第二中间件 next 之后
- 第一个中间件 next 之后
开放静态资源
配置静态资源中间件,开放 static 文件夹下的文件。
app.use(serve('static'))
模板引擎
ejs模板引擎
- 使用ejs模板引擎
// 模板为 ejs 文件
// app.use(views('views', { extension: 'ejs' }))
// 模板为 html 文件
app.use(views('views', { map: { html: 'ejs' }}))
2 模板
<!-- header.html -->
<h1>这是一个头部模块</h1>
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="/css/base.css">
</head>
<body>
<p>include()导入html文件, '-' 并解析</p>
<%- include('public/header.html') %>
<h1>模板引擎 --- html </h1>
<h2><%=title%></h2>
<ul>
<%for(var i = 0; i < list.length; i++) {%>
<li><%=list[i]%></li>
<%}%>
</ul>
<p>公共数据 在中间件中配置 ctx.state.userInfo --- <%= userInfo %></p>
<img src="/images/timg.jpg" alt="">
</body>
</html>
- 使用模板引擎
router.get('/', async (ctx) => {
let title = '你好,ejs'
let arr = [111, 222, 333]
await ctx.render('index', {
title: title,
list: arr
})
})
art-template模板引擎
- 引入模板引擎
render(app, {
root: path.join(__dirname, 'views'), // 视图位置
extname: '.html', // 后缀名
debug: process.env.NODE_ENV !== 'production' // 开启调试模式
});
- 模板
<!-- header.html -->
<h1>这是一个头部模块</h1>
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="/css/base.css">
</head>
<body>
<h1>模板引擎 --- koa-art-template </h1>
<hr>
<h3>子模版</h3>
{{include './public/header.html'}}
<h2>{{title}}</h2>
<hr>
<h3>循环</h3>
<ul>
{{each arr}}
<li>{{$value}}</li>
{{/each}}
</ul>
<hr>
<h3>条件</h3>
{{if num > 20}}
<strong>大于20</strong>
{{else}}
<strong>小于20</strong>
{{/if}}
<hr>
<img src="/images/timg.jpg" alt="">
</body>
</html>
- 使用模板引擎
router.get('/', async (ctx) => {
let title = '你好,art-template'
let arr = [111, 222, 333]
let num = 10
await ctx.render('index', { title, arr, num })
})
cookie 和 session 的使用
cookie 的使用
- 服务器设置cookie
router.get('/', async (ctx) => {
// koa中不支持直接设置中文的 cookie
var userInfo = new Buffer('张三').toString('base64')
ctx.cookies.set('userInfo', userInfo, {
maxAge: 1000 * 60 * 60,
httpOnly: true, // true 表示这个 cookie 只有服务器可以访问, false 表示客户端、服务器端都可以访问
})
await ctx.render('artIndex')
})
- 在服务器使用cookie
router.get('/news', async (ctx) => {
var cookieData = ctx.cookies.get('userInfo')
var transformCookie = new Buffer(cookieData, 'base64').toString()
console.log('获取cookie信息', transformCookie);
await ctx.render('news')
})
- 客户端获取cookie
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h2>新闻页面</h2>
<script>
console.log(document.cookie)
</script>
</body>
</html>
httpOnly: true
时,在 news.html 页面不能通过document.cookie
获取到 cookie。
httpOnly: false
时,在 news.html 页面可以通过document.cookie
获取到 cookie。
session 的使用
- 导入文件
const session = require('koa-session')
- 配置 session 中间件
app.keys = ['some secret hurr']; // cookie 的签名
const CONFIG = {
key: 'koa.sess', /** 默认*/
maxAge: 86400000, // cookie的过期时间
autoCommit: true, /** */
overwrite: true, /** */
httpOnly: true, /** true 只有服务器端可以获取cookie */
signed: true, /** 默认签名 */
rolling: false, /** 在每次请求的时候强行设置 cookie ,这将重置 cookie 过期时间 */
renew: true, /** (boolean) renew session when session is nearly expired, so we can always keep user logged in. (default is false)*/
secure: false, /** (boolean) secure cookie*/
sameSite: null, /** (string) session cookie sameSite options (default null, don't set it) */
};
app.use(session(CONFIG, app));
- 设置 session
router.get('/login', async (ctx) => {
// 设置 session
ctx.session.userInfo = 'session信息'
ctx.body = '登录页面'
})
- 获取 session
router.get('/', async (ctx) => {
// 获取session
console.log('获取session信息', ctx.session.userInfo);
await ctx.render('artIndex')
})
使用 mongodb 数据库
目录结构
连接 mongodb 数据库,完成增删改查功能,使用第三方插件 "mongodb": "^3.6.3"
- views 页面文件
- 首页 index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>模板引擎 --- koa-art-template </h1>
<hr>
<a href="/user/add">增加</a>
<table>
<tr>
<th>姓名</th>
<th>年龄</th>
<th>性别</th>
<th>状态</th>
<th>操作</th>
</tr>
{{each list}}
<tr>
<td>{{$value.username}}</td>
<td>{{$value.age}}</td>
<td>{{$value.sex}}</td>
<td>{{$value.status}}</td>
<td><a href="/user/edit?id={{@$value._id}}">修改</a><a href="/user/delete?id={{@$value._id}}">删除</a></td>
</tr>
{{/each}}
</table>
</body>
</html>
- 新增页面 add.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h2>新增数据页面</h2>
<form action="/user/doAdd" method="POST">
用户名: <input type="text" name="username">
年龄: <input type="text" name="age">
性别: <input type="text" name="sex">
状态: <input type="text" name="status">
<input type="submit" value="提交">
</form>
</body>
</html>
- 修改页面 edit.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h2>编辑数据页面</h2>
<form action="/user/doEdit" method="POST">
<input type="hidden" name="id" value="{{@list._id}}">
用户名: <input type="text" name="username" value="{{list.username}}">
年龄: <input type="text" name="age" value="{{list.age}}">
性别: <input type="text" name="sex" value="{{list.sex}}">
状态: <input type="text" name="status" value="{{list.status}}">
<input type="submit" value="提交">
</form>
</body>
</html>
- module 文件夹 用于连接数据库,封装并导出增删改查的方法
- 导出数据库的配置信息 - config.js
var app = {
dbUrl: 'mongodb://localhost:27017',
dbName: 'koa'
}
module.exports = app
- 封装操作数据库的方法 - db.js
const MongoClient = require('mongodb').MongoClient;
const ObjectID = require('mongodb').ObjectID
const Config = require('./config')
class Db {
// 使用单例 多次实例不共享的问题
static getInstance() {
if (!Db.instance) {
Db.instance = new Db()
}
return Db.instance
}
constructor() {
this.dbClient = null
}
connect() {
return new Promise((resolve, reject) => {
if (!this.dbClient) {
MongoClient.connect(Config.dbUrl, (err, client) => {
if(err) {
reject(err)
}else {
var db = client.db(Config.dbName)
this.dbClient = db
resolve(this.dbClient)
}
})
}
else resolve(this.dbClient)
})
}
// 查找方法 查找集合名字 查找规则
find(collectionName, json) {
return new Promise((resolve, reject) => {
this.connect().then((db) => {
var result = db.collection(collectionName).find(json)
result.toArray((err, docs) => {
if (err) reject(err)
resolve(docs)
})
})
})
}
// 更新方法 更新集合名字 需要更新的字段 更新后的数据
update(collectionName, json1, json2) {
return new Promise((resolve, reject) => {
this.connect().then(db => {
db.collection(collectionName).updateOne(json1, {
$set: json2
}, (err, result) => {
if(err) reject(err)
resolve(result)
})
})
})
}
// 插入方法 插入集合名字 插入的数据
insert(collectionName, json) {
return new Promise((resolve, reject) => {
this.connect().then((db) => {
db.collection(collectionName).insertOne(json, (err, result) => {
if(err) reject(err)
resolve(result)
})
})
})
}
// 删除方法 删除的集合名称 删除的字段
remove(collectionName, json) {
return new Promise((resolve, reject) => {
this.connect().then((db) => {
db.collection(collectionName).removeOne(json, (err, result) => {
if(err) reject(err)
resolve(result)
})
})
})
}
// 根据返回的id,生成mongodb需要的样式的id
getObjectId(id) {
return new ObjectID(id)
}
}
module.exports = Db.getInstance()
- router 文件
- user路由配置 user.js
var Router = require('koa-router')
var DB = require('../module/db')
var userRouter = new Router()
// 配置路由
userRouter.get('/', async (ctx) => {
var result = await DB.find('user', {})
await ctx.render('artIndex', {list: result})
})
userRouter.get('/add', async (ctx) => {
await ctx.render('add')
})
userRouter.post('/doAdd', async (ctx) => {
let addData = ctx.request.body
const result = await DB.insert('user', addData)
try {
if (result.result.ok) {
ctx.redirect('/user')
}
} catch (error) {
ctx.redirect('/user/add')
}
})
userRouter.get('/edit', async (ctx) => {
// 根据 id 获取用户信息
let userId = ctx.query.id
const result = await DB.find('user', { "_id": DB.getObjectId(userId) })
await ctx.render('edit', {list: result[0]})
})
userRouter.post('/doEdit', async (ctx) => {
let userData = ctx.request.body
var id = userData.id
var username = userData.username
var age = userData.age
var sex = userData.sex
var status = userData.status
const result = await DB.update('user', {"_id": DB.getObjectId(id)}, { username, age, sex, status })
try {
if (result.result.ok) {
ctx.redirect('/user')
}
} catch (error) {
ctx.redirect('/user/add')
}
})
userRouter.get('/delete', async (ctx) => {
let id = ctx.query.id
const result = await DB.remove('user', { "_id": DB.getObjectId(id) })
try {
if (result.result.ok) {
ctx.redirect('/user')
}
} catch (error) {
}
})
module.exports = userRouter
- 主路由下配置子路由 router.js
var Router = require('koa-router')
var router = new Router()
var userRouter = require('./user')
// 配置子路由, 并启动路由
router.use('/user', userRouter.routes())
module.exports = router
- 入口文件 app.js
var Koa = require('koa');
var bodyParser = require('koa-bodyparser');
const render = require('koa-art-template');
const path = require('path');
var router = require('./router/router');
var app = new Koa()
// 解析 post 请求数据
app.use(bodyParser())
// ! art-template
render(app, {
root: path.join(__dirname, 'views'), // 视图位置
extname: '.html', // 后缀名
debug: process.env.NODE_ENV !== 'production' // 开启调试模式
});
// todo 启动路由
app
.use(router.routes())
.use(router.allowedMethods())
app.listen(3001)