koa的基本使用

291 阅读4分钟

gitee项目地址

koa的基本使用

目录结构

使用koa

创建app应用

  1. 创建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');
  1. 创建app, 并监听端口
var app = new Koa()
app.listen(3001)

使用路由

  1. 配置路由,在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);
})
  1. 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请求参数

  1. 引入中间件
// ! post 参数解析中间件
app.use(bodyParser());
  1. 获取参数
router.post('/doAdd', async(ctx) => {
    // 获取表单提交的数据
    console.log(ctx.request.body);
})

中间件的使用

  1. 应用级中间件 在app.js中定义的中间件,在匹配路由前执行。
app.use(async(ctx, next) => {
	// 在中间件中配置 ctx.state.userInfo 在任何页面中都可以使用 userInfo
    ctx.state.userInfo = 'name'
    console.log('第一个中间件 next 之前');
    // 继续向下匹配
    await next()
    console.log('第一个中间件 next 之后');
})
  1. 错误中间件 发生错误时,返回对应的页面。
app.use(async(ctx, next) => {
   console.log('第二中间件 next 之前');
    await next()
    console.log('第二中间件 next 之后');
    // 当所有的路由匹配之后执行,返回404页面
    if (ctx.status == 404) {
        ctx.status = 404
        ctx.body = '这是一个404页面'
    }
})
  1. 路由级中间件
router.get('/news', async(ctx, next) => {
    console.log('这是一个新闻页面'); 
    await next()
})
router.get('/news', async(ctx) => {
    ctx.body = '这是一个新闻页面'
})

中间件的执行流程

  • 从上往下执行中间件
  • 遇到 next 执行下一个中间件
  • 中间件执行结束之后,匹配路由
  • 路由匹配结束后,执行中间件 next 后的代码,从下往上执行 输出结果
  1. 输入 '/news' 路由,匹配路由成功 输出:
  • 第一个中间件 next 之前
  • 第二中间件 next 之前
  • 这是一个新闻页面
  • 第二中间件 next 之后
  • 第一个中间件 next 之后
  1. 输入 '/news1' 路由,匹配路由失败 输出:
  • 第一个中间件 next 之前
  • 第二中间件 next 之前
  • 第二中间件 next 之后
  • 第一个中间件 next 之后

开放静态资源

配置静态资源中间件,开放 static 文件夹下的文件。

app.use(serve('static'))

模板引擎

ejs模板引擎

  1. 使用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>
  1. 使用模板引擎
router.get('/', async (ctx) => {
    let title = '你好,ejs'
    let arr = [111, 222, 333]
    await ctx.render('index', {
        title: title,
        list: arr
    })
})

art-template模板引擎

  1. 引入模板引擎
render(app, {
    root: path.join(__dirname, 'views'),  // 视图位置
    extname: '.html',  // 后缀名
    debug: process.env.NODE_ENV !== 'production'   // 开启调试模式
});
  1. 模板
<!-- 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>
  1. 使用模板引擎
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 的使用

  1. 服务器设置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')
})
  1. 在服务器使用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')
})
  1. 客户端获取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 的使用

  1. 导入文件
const session = require('koa-session')
  1. 配置 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));
  1. 设置 session
router.get('/login', async (ctx) => {
    // 设置 session
    ctx.session.userInfo = 'session信息'
    ctx.body = '登录页面'
})
  1. 获取 session
router.get('/', async (ctx) => {
    // 获取session
    console.log('获取session信息', ctx.session.userInfo);
    await ctx.render('artIndex')
})

使用 mongodb 数据库

目录结构

连接 mongodb 数据库,完成增删改查功能,使用第三方插件 "mongodb": "^3.6.3"

  1. 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>
  1. 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()

  1. 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
  1. 入口文件 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)