美文网首页
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