美文网首页
koa相关介绍和操作

koa相关介绍和操作

作者: 小小了墨 | 来源:发表于2019-08-16 10:27 被阅读0次

koa 概述

node的一个轻量级框架,内置类似connect用来处理中间件,但是并没有捆绑任何中间件,采用的是洋葱模型进行级联

koa 安装

依赖

koa依赖node v7.6.0 或者 ES2015(ES6)及更高版本和async方法支持

安装

$ node -v
# 大于等于 7.6.0
$ yarn add koa

要在 node < 7.6.0的版本中使用koa的async

require('babel-register');
// 应用的其他require 需要放在这个后面
// eg:
const app = require('./app');

要解析和编译 async 方法, 需要 transform-async-to-generator 插件

在.babelrc 中 增加

{
    "plugins": ["transform-async-to-generator"]
}

也可以用 env preset 的 target 参数 "node": "current" 替代.

使用

起一个基本服务

const Koa = require('koa');
const app = new Koa();

app.use(async ctx => {
  ctx.body = 'Hello World';
});

app.listen(9800);
console.log('server is started in :  \n http://127.0.0.1:9800');

级联模式(洋葱模型)

使用async,实现从哪里出发最后回到哪里去

当一个请求开始,先按顺序执行中间件,当一个中间件调用next()时,该中间件会暂停并将控制传递到下一个中间件。最后当没有中间件执行的时候,会从最后一个中间件的next()后开始执行,执行完后去找上一个中间件的next()执行,一直到第一个中间件

const Koa = require('koa');
const app = new Koa();

// logger

app.use(async (ctx, next) => {
    console.log(1);
    await next();
    console.log(4);
    // 这时候因为已经设置好了,所以可以获取到我们想要的
    const responseTime = ctx.response.get('X-Response-Time');
    console.log(`【${ctx.method}】 ${ctx.url} - ${responseTime}`);
});

// set x-response-time

app.use(async (ctx, next) => {
    console.log(2);
    // 请求到这的时间
    const start = Date.now();
    await next();
    console.log(3);
    // 处理完的时间
    // 处理的时间
    const ms = Date.now() - start;
    ctx.set('X-Response-Time', `${ms}ms`);
});

app.use(async ctx => {
    ctx.body = 'connect execution order';
})

const port = 9800;
app.listen(port);
console.log(`server is started in : \n http://127.0.0.1:${port}`);

koa-router

使用

require('koa-router')返回的是一个函数

const Koa = require('koa');
// 函数,别忘了
const router = require('koa-router')();
/**
或者
const KoaRouter = require('koa-router');
const router = new KoaRouter();
*/

const app = new Koa();

// 增加路由
router.get('/', async(ctx, next) => {
  ctx.body = '<h1>index page</h1>'
});

router.get('/home', async(ctx, next) => {
  ctx.body = '<h1>home page</h1>'
});

router.get('not-found', '/404', async(ctx, next) => {
  ctx.body = '<h1>Not Found page</h1>'
});

router.redirect('/*', 'not-found', 301);
/**
    等价于
router.all('/*', async(ctx, next) => {
    ctx.redirect('/404');
    ctx.status = 301;
})
*/

// 使用路由中间件
app.use(router.routes());

app.listen(9800, () => {
    console.log('server is running at http://127.0.0.1:9800');
})

命名路由

router.get('user', '/users/:id'. (ctx, next) => {
    
});
// 使用路由生成器
router.url('user', 3);
// 生成路由 '/users/3'
router.url('user', { id: 3 });
// 生成路由 '/users/3'
router.use((ctx, next) =>{
    ctx.redirect(ctx.router.url('user', 3));
})

router.url根据路由名称和可选的参数生成具体的URL,而不用采用字符串拼接的方式生成URL

单个路由多中间件

router.get('/users/:id', async(ctx, next) => {
    // 中间件
    const user = await User.findOne(ctx.params.id);
    ctx.user = user;
    next();
}, async(ctx) => {
    console.log(ctx.user);
})

URL参数

router.get('/:id/:title', async(ctx, next) => {
    console.log(ctx.params);
    const { id, title } = ctx.params;
});

路由前缀

为一组路由添加一个统一的前缀,是一个固定的字符串

const router = new KoaRouter({
    prefix: '/api'
});
router.get('/', ...) 
// /api
router.get('/:id', ...)
// /api/:id

方法和参数

app

  • app.env默认是NODE_ENV或者'development'

  • app.proxy 当真正的代理头字段将被信任时

  • app.subdomainOffset 对于要忽略的 .subdomains 偏移[2]

这句不太懂

app.listen(port)

koa可以将一个或者多个koa应用安装在一起,打包成单个服务

  1. 无作用koa应用
const Koa = require('koa');
const app = new Koa();
app.listen(9800);
  1. app.listen()是语法糖,最后执行的还是下面这个
const http = require('http');
const Koa = require('koa');
const app = new Koa();
http.createServer(app.callback()).listen(9800);
  1. 一个koa应用多个地址/服务
const http = require('http');
const https = require('https');
const Koa = require('koa');
const app = new Koa();
http.createServer(app.callback()).listen(9800);
https.createServer(app.callback()).listen(9801);
  1. 多个koa应用一个地址/服务

怎么将多个 Koa 应用程序安装在一起以形成具有单个HTTP服务器的更大应用程序?

app.callback()

这个回调函数返回值可以用http.createServer()方法处理请求,也可以使用这个回调函数将koa应用挂载到Connect/Express应用中

app.use(function)

将需要的中间件方法添加到应用里

这里还有很多需要学习,比如开发中间件

app.keys

设置Cookie密钥

// 使用koa的KeyGrip
app.keys = ['new secret', 'demo'];
// 使用别的
app.keys = new KeyGrip(['new secret', 'demo'], 'sha256');

这些签名可以倒换,并在使用{ signed: true }参数签名Cookie时使用

倒换????

ctx.cookies.set('name', 'company', { signed: true });

app.context

ctx 是 基于app.context原型创建的,可以编辑app.context为ctx添加其他属性

app.context.db = db();

app.use(async ctx => {
    console.log(ctx.db);
});

注意: ctx上属性都是使用getter,setterObject.defineProperty()定义的。只能通过在app.context上使用Object.defineProperty()来编辑这些属性,但是不推荐修改

错误处理

app.silent = true

错误日志不输出,这时候需要手动记录错误和抛出

app.on('error', (err, ctx) => {
    // 这里对错误做处理
    console.log('server error >>> ', err);
});

context

ctx.req ctx.request

  • ctx.req:Node的request对象
  • ctx.request:koa的request对象

ctx.res ctx.response

  • ctx.res: Node
  • ctx.response:koa
  • 绕过koa的response处理是不被支持的,不能使用node的一些属性:
    1. res.statusCode
    2. res.writeHead()
    3. res.write()
    4. res.end()

所以建议使用 ctx.response

ctx.state

命名空间,用于通过中间件传递信息,

ctx.state.user = await User.find(id);

ctx.app

应用程序的实例引用

ctx.cookies

cookie的相关操作,使用的是 cookies模块

  • ctx.cookies.get(name, [options])

    通过options获取 cookie name

signed所请求的cookie应该被签名

  • ctx.cookies.set(name, value, [options])

    通过options设置cookie name 的值:

    • maxAge 一个数字表示从Date.now()得到的毫秒数
    • signed cookie签名值
    • expires cookie过期的Date
    • path cookie路径,默认是'/'
    • domain cookie域名
    • secure 安全cookie
    • httpOnly 服务器可访问cookie, 默认是true
    • overwrite 布尔值, 表示是否覆盖以前设置的同名的cookie, 默认是false

cookie这方面还要再看下,好多不懂

ctx.throw([statusCode], [msg], [properties])

Helper方法抛出一个.status属性默认的 500 的错误,这允许koa做出适当响应

使用 http-errors 来创建的错误

ctx.throw(400);
ctx.throw(400, 'name is required');
ctx.throw(400, 'name is required', { user: user });

等效于:

const err = new Error('name is required');
err.status = 400;
err.expose = true;
throw err;

一般用于用户级错误,消息适用于客户端响应,这通常不是错误信息的内容,因为一些信息不能返回给客户端

err.expose 是什么

ctx.assert(value, [status], [msg], [properties])

断言,一般测试用的

判断value是否为真值,具体用法同node的assert()

ctx.respond

ctx.respond = false;

绕过koa对response的处理

koa 不支持使用此功能,主要是为了能在koa中使用传统道德fn(req, res)功能和中间件

中间件使用

中间件的使用顺序要注意,不同顺序产生的结果可能不同,洋葱模型引起的

koa-bodyparser

解析post/表单发送的信息

  • 使用
const Koa = require('koa');
const router = require('koa-router')();
const bodyParser = require('koa-bodyparser');

const app = new Koa();

app.use(bodyParser());

router.post('/user/login', async(ctx, next) => {
    const { name, password } = ctx.request.body;
    if (name === 'abc' && password === '123') {
        ctx.response.body = `Hello, ${name}`;
    } else {
        ctx.response.body = '账号信息错误'
    }
});

koa-static

处理静态文件

const path = require('path');
const koaStatic = require('koa-static');
app.use(koaStatic(path.resolve(__dirname, './static')));

log4js

日志记录模块

日志等级

有7个等级的日志级别:

  1. ALL:输出所有的日志
  2. TRACE
  3. DEBUG
  4. INFO
  5. WARN
  6. ERROR
  7. FATAL
  8. MARK
  9. OFF:所有的日志都不输出

基本使用

const log4js = require('log4js');
log4js.configure({
    /**
    * 指定要记录的日志分类 cheese
    * 展示方式为文件类型 file
    * 日志输出的文件名 cheese.log
    */
  appenders: { cheese: { type: 'file', filename: 'cheese.log' } },
  /**
    * 指定日志的默认配置项
    * 如果 log4js.getLogger 中没有指定,默认为 cheese 日志的配置项
    * 指定 cheese 日志的记录内容为 error和error以上级别的信息
    */
  categories: { default: { appenders: ['cheese'], level: 'error' } }
});

const logger = log4js.getLogger('cheese');
logger.trace('Entering cheese testing');
logger.debug('Got cheese.');
logger.info('Cheese is Gouda.');
logger.warn('Cheese is quite smelly.');
logger.error('Cheese is too ripe!');
logger.fatal('Cheese was breeding ground for listeria.');

按日期分割日志

const log4js = require('log4js');
log4js.configure({
    appenders: {
        cheese: {
        type: 'dateFile', // 日志类型 
        filename: `logs/task`,  // 输出的文件名
        pattern: '-yyyy-MM-dd.log',  // 文件名增加后缀
        alwaysIncludePattern: true   // 是否总是有后缀名
        layout: { // 设置输出格式, 如果默认输出日志格式不满意
            /**
            * 有好多模式,默认是 default
            * pattern 是自己写格式
            */
            type: 'pattern',
            /**
            * 下面这个是默认的,自己改动的不展示
            * 我不喜欢日期中的T, 所以把T手动去掉了
            */
            pattern: '[%d{yyyy-MM-ddThh:mm:ss}] [%p] %c %m%n'
          }
       }
    },
    categories: {
      default: {
        appenders: ['cheese'],
        level:'info'
      }
    }
});
const logger = log4js.getLogger('cheese');
logger.info('日期分割');

相关文章

网友评论

      本文标题:koa相关介绍和操作

      本文链接:https://www.haomeiwen.com/subject/vfcfsctx.html