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应用安装在一起,打包成单个服务
- 无作用koa应用
const Koa = require('koa');
const app = new Koa();
app.listen(9800);
- app.listen()是语法糖,最后执行的还是下面这个
const http = require('http');
const Koa = require('koa');
const app = new Koa();
http.createServer(app.callback()).listen(9800);
- 一个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);
- 多个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
,setter
和Object.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的一些属性:
- res.statusCode
- res.writeHead()
- res.write()
- 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个等级的日志级别:
- ALL:输出所有的日志
- TRACE
- DEBUG
- INFO
- WARN
- ERROR
- FATAL
- MARK
- 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('日期分割');
网友评论