koa2框架使用心得

一、express和koa2的區別

  • express中間件是異步回調,koa2原生支持async/await
  • 新開發框架和系統,都開始基於koa2,例如egg.js
  • express雖然未過時,但是koa2肯定是未來趨勢

二、安裝koa2

1、使用腳手架

npm i -g koa-generator
Koa2 koa2-test # 初始化項目

2、路由

const router = require('koa-router')()
router.prefix('/api')
router.get('/', async (ctx, next) => {
  await ctx.render('index', {
    title: 'Hello Koa 2!'
  })
})

router.get('/string', async (ctx, next) => {
  ctx.body = 'koa2 string'
})

router.get('/json', async (ctx, next) => {
  ctx.body = {
    title: 'koa2 json'
  }
})

module.exports = router
app.use(index.routes(), index.allowedMethods());

3、ctx

ctx 可以看做req、和res的集合體

router.get('/bar', function (ctx, next) {
  	ctx.request.body;//請求參數體
    ctx.query;//請求參數
    ctx.session;//獲取session
    ctx.cookie;
    ctx.cookies.get(name, [options]);
    ctx.cookies.set(name, value, [options]);
    ctx.body={};//返回參數體
})

三、中間件

// 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`)
})

// routes
app.use(index.routes(), index.allowedMethods());
app.use(users.routes(), users.allowedMethods());
app.use(user.routes(), user.allowedMethods());
app.use(blog.routes(), blog.allowedMethods());

四、session

1、使用 koa-generic-session 和 koa-redis

yarn add  koa-generic-session  koa-redis redis
app.js

//session
const session = require('koa-generic-session');
const redisStore = require('koa-redis');
const {REDIS_CONF} = require('./conf/db');

//session配置 在註冊路由之前寫
app.keys = ['DSDSDsds890809'];
app.use(session({
    //配置cookie
    cookie: {
        path: '/',
        httpOnly: true,
        maxAge: 24 * 60 * 60 * 1000
    },
    //配置redis
    store: redisStore({
        all: `${REDIS_CONF.host}:${REDIS_CONF.port}`
    })
}));
測試:
router.get('/session-test', async function (ctx, next) {
    let session = ctx.session;
    if (session.vievCount == null) {
        session.vievCount = 0;
    }
    session.vievCount++;
    ctx.body = session.vievCount
});

2、req.session 保存登錄信息到redis

redis.js

const redis = require('redis')
const {REDIS_CONF} = require('../conf/db')

//創建客戶端
let redisClient = redis.createClient(REDIS_CONF.port, REDIS_CONF.host);
redisClient.on('error', err => {
    console.log(err)
});
module.exports = redisClient
app.js

let RedisStore = require('connect-redis')(session);

let redisClient = require('./db/redis');
let sessionStore = new RedisStore({
    client: redisClient
});

app.use(session({
    secret: 'asdXXnkj_asdnsk_90789_sad',
    cookie: {
        path: '/',//默認
        httpPnly: true,//默認
        maxAge: 24 * 60 * 60 * 1000
    },
    store: sessionStore
})); // 處理session

3、登錄校驗做成express中間件

loginCheck.js

const {SuccessModel, ErrorModel} = require('../model/resModel');

module.exports = (re, res, next) => {
    if (require.session.username) {
        next()
    } else {
        res.json(new ErrorModel("未登錄"))
    }
}

路由裏面:

blog.js

const loginCheck = require('../middleware/loginCheck')

router.post('/new', loginCheck, (req, res, next) => {
    req.body.author = req.session.username
    const result = newBlog(req.body)
    return result.then(data => {
        res.json(
            new SuccessModel(data)
        )
    })
})

五、日誌

1、 access日誌使用,使用morgan

yarn add koa-morgan
app.js


//日誌
const path = require('path');
const fs = require('fs');
const morgan = require('koa-morgan');

//日誌記錄
const ENV = process.env.NODE_ENV
if (ENV !== 'production') {
    // 開發環境 / 測試環境
    app.use(morgan('dev'));
} else {
    // 線上環境
    const logFileName = path.join(__dirname, 'logs', 'access.log')
    const writeStream = fs.createWriteStream(logFileName, {
        flags: 'a'
    })
    app.use(morgan('combined', {
        stream: writeStream
    }));
}

六、完整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');

//session
const session = require('koa-generic-session');
const redisStore = require('koa-redis');
const {REDIS_CONF} = require('./conf/db');

//路由
const index = require('./routes/index')
const users = require('./routes/users')
const user = require('./routes/my/user')
const blog = require('./routes/my/blog')

//日誌
const path = require('path');
const fs = require('fs');
const morgan = require('koa-morgan');

// 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: 'pug'
}))

// logger
app.use(async (ctx, next) => {
    await next();
    let rt = ctx.response.get('X-Response-Time');
    console.log(`${ctx.method} ${ctx.url} - ${rt}`)
})

app.use(async (ctx, next) => {
    const start = new Date();
    await next();
    const ms = new Date() - start;
    ctx.set('X-Response-Time', `${ms}ms`)
})

//日誌記錄
const ENV = process.env.NODE_ENV
if (ENV !== 'production') {
    // 開發環境 / 測試環境
    app.use(morgan('dev'));
} else {
    // 線上環境
    const logFileName = path.join(__dirname, 'logs', 'access.log')
    const writeStream = fs.createWriteStream(logFileName, {
        flags: 'a'
    })
    app.use(morgan('combined', {
        stream: writeStream
    }));
}

//session配置
app.keys = ['DSDSDsds890809'];
app.use(session({
    //配置cookie
    cookie: {
        path: '/',
        httpOnly: true,
        maxAge: 24 * 60 * 60 * 1000
    },
    //配置redis
    store: redisStore({
        all: `${REDIS_CONF.host}:${REDIS_CONF.port}`
    })
}));

// routes
app.use(index.routes(), index.allowedMethods());
app.use(users.routes(), users.allowedMethods());
app.use(user.routes(), user.allowedMethods());
app.use(blog.routes(), blog.allowedMethods());

// error-handling
app.on('error', (err, ctx) => {
    console.error('server error', err, ctx)
});

module.exports = app

七、模擬中間件

const http = require('http');

//組合中間件
function compose(middlewareList) {
    return function (ctx) {
        //中間件調用
        function dispatch(i) {
            let fn = middlewareList[i]
            try {
                return Promise.resolve(fn(ctx, dispatch.bind(null, i + 1)))
            } catch (e) {
                return Promise.reject(e)
            }
        }

        return dispatch(0);
    }
}

class LikeKoa2 {
    constructor() {
        //中間件
        this.middlewareList = []
    }

    use(fn) {
        this.middlewareList.push(fn)
        return this
    }


    listen(...args) {
        let server = http.createServer(this.callback());
        server.listen(...args)
    }

    createCtx(req, res) {
        let ctx = {req, res};
        ctx.query = req.query;
        return ctx
    }

    handleRequest(ctx, fn) {
        return fn(ctx);
    }

    callback() {
        let fn = compose(this.middlewareList);
        return (req, res) => {
            let ctx = this.createCtx(req, res);
            return this.handleRequest(ctx, fn)
        };
    }
}

module.exports = () => {
    return new LikeKoa2();
}

測試:

const Koa = require('./likeKoa2');
const app = Koa();

// logger
app.use(async (ctx, next) => {
    await next();
    const rt = ctx['X-Response-Time'];
    console.log(`${ctx.req.method} ${ctx.req.url} - ${rt}`);
});

// x-response-time
app.use(async (ctx, next) => {
    const start = Date.now();
    await next();
    const ms = Date.now() - start;
    ctx['X-Response-Time'] = `${ms}ms`;
});

// response
app.use(async ctx => {
    ctx.res.end('This is like koa2');
});

app.listen(8000);

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章