Koa 起步
const http = require('http');
const server = http.createServer((req, res) => {
res.writeHead(200);
res.end('hello world');
})
server.listen(3000, () => console.log('server start at 3000'));
相信上面的代码大家都是印象深刻 , 这是Node
原生创建一个http
服务器的示例 , 这里结合上面介绍, 我们大致猜想一下 koa
应该是什么样的 ?
- 她应该有
async/await
是异步操作; - 她应该有错误处理机制;
- 她应该有比
http
模块更优雅的方法;
那就看 koa
的第一个示例 ; 当然你要先下载她( 保证你的 node
在 7.6.0 以上, 以方便支持async/await
操作 )
npm install koa --save-dev
const Koa = require('koa');
const app = new Koa();
app.use(async ctx => {
ctx.body = 'hello world'
});
app.listen(3000, () => console.log('server start at 3000'));
上面的代码是 koa
的示例代码 ; 这段代码中可以看出
async
异步了 虽然没有出现await
req
res
变成了ctx
也就是context
上下文- 错误处理还没有看到, 这个后面说
总结: 底层仍然是http
通过各种封装用着更加顺手了, 当然啦这只是表象, 肯定还是要深入了解的 ;
那么再看 , app 既然是 Koa 的实例 , 那这个实例里面都有哪些内容呢 ? 这里呢实例的属性我就不说了简单看一下我认为比较重要的几个方法吧
-
app.use()
: 将给定的中间件方法添加到应用程序中, 并且这个方法会返回this
也就是说可以链式调用 -
app.callback()
: 返回适用于http.createServer()
方法的回调函数来处理请求, 可以说是另一种方法创建服务器吧, 为什么这么用不要管, 后面你肯定会用到, 比如创建多个服务器?, 暂时就记住吧 -
app.listen()
: Koa 应用程序不是 HTTP 服务器的1对1展现。 可以将一个或多个 Koa 应用程序安装在一起以形成具有单个HTTP服务器的更大应用程序。const Koa = require('koa'); const app = new Koa; app.use(async ctx => { ctx.body = { msg: 'hello koa' }; }); app.listen(3000) // 其实就是下面的 createServer 语法糖 // ------------------------------------------------------- const http = require('http'); const Koa = require('koa'); const app = new Koa() app.use(async ctx => { ctx.body = { msg: 'hello koa' }; }); http.createServer(app.callback()).listen(3000);
-
app.on('error', function)
: 通过监听 app 上的error
事件对这些错误做进一步的统一处理和集中管理。app.on('error', async (err, ctx) => { console.log('err', err) }) app.use(async ctx => { // 这时运行程序就会出现错误 err ReferenceError: tx is not defind ctx.body = { msg: tx }; });
中间件 Middleware
试想我们现在想实现一个登录的功能; 主业务肯定是用户名, 密码 然后对比数据库齐活 ;
// 伪代码
app.use(async ctx => {
const user = 'admin'
const pwd = '123456'
const { username, password } = ctx.query
if (user === username && pwd === password) {
ctx.body = { status: 200, msg: 'success' }
}
});
const Koa = require('koa');
const app = new Koa();
app.use(async (ctx, next) => {
ctx.state.user = 'admin'
ctx.state.pwd = '111'
console.log(`即将开始对比user: ${ctx.state.user} pwd: ${ctx.state.pwd}`)
await next()
console.log(`对比结束username: ${ctx.query.username}, password: ${ctx.query.password}`)
})
app.use(async ctx => {
const { username, password } = ctx.query
if (ctx.state.user === username && ctx.state.pwd === password) {
ctx.body = { status: 200, msg: 'success' }
}
});
app.listen(3000, () => console.log('server start at 3000'));
看控制台的结果在理解上面的那段就很好理解了; 至此终于可以将这个用到烂的图拿出来了?真的是都在用,太經典了!!!
我要是单纯的给你放个图就太对不起你们了, 万一你们看不懂我的赞就没有了?, so 我又专门给你们盗了一个更直白的
当一个中间件调用 next()
则该函数暂停并将控制传递给定义的下一个中间件。当在下游没有更多的中间件执行后,堆栈将展开并且每个中间件恢复执行其上游行为。
上下文 Context
app.use(async ctx => {
const username = ctx.query.name
ctx.body = { code: 200, msg: 'success' }
})
相信看到上面的 ctx
新中肯定也有一个疑问 , 为什么 ctx
既可以有操作 request
的能力也有操作 response
的能力 ; 这个肯定是故意为之的, 也是为了我们开发方便 , 比如你也可以这样
app.use(async ctx => {
const username = ctx.request.query.name
ctx.response.body = { code: 200, msg: 'success' }
})
koa 呢 帮我们把一些很常用的方法放到 ctx
上下文中, 具体有哪些呢 ? 还要去官网查看? 个人觉得太多且没有必要列出来, 大家可以去官网慢慢看, 但是除去 request
和 response
的当然还有ctx
自己的一些属性和方法 ; 那就列举几个个人认为比较重要的吧
-
ctx.request
: Koa 的request
对象 -
ctx.response
: Koa 的response
对象 -
ctx.state
: Koa推荐使用的命名空间, 主要用于中间件传递信息 -
ctx.throw
: Koa 手动抛出异常的方法, 也可以使用 http-errors 来创建错误ctx.throw(404, 'NotFound');
-
ctx.assert
: 当断言的值不存在或者出现错误的时候, 就会抛出异常// 如果 ctx.state.name 没有定义就会出现这个错误 ctx.assert(ctx.state.name, 401, 'user is not defined');
-
ctx.cookies.get
ctx.cookies.set
: cookie 的操作, 也可以根据官方的推荐使用 cookies 模块 ; 这里我简单示例一下吧, 要深入 cookie 的知识点还是有点多, 而且我又要百度查了?(我也不是很了解)ctx.cookies.get(name, [options]) ctx.cookies.set(name, value, [options])
app.use(async ctx => { ctx.cookies.set('login_cookie', '123456', { maxAge: 36000 // 这里注意表示从 Date.now() 得到的毫秒数 毫秒级时间戳 }) const loginCookie = ctx.cookies.get('login_cookie') console.log(loginCookie) ctx.response.body = { code: 200, msg: 'success' } })
Request Response
Koa-router(路由)
app.use(async ctx => {
if (ctx.url === '/') {
ctx.body = { msg: 'index page' };
} else if (ctx.url === '/detail') {
ctx.body = { msg: 'detail page' };
}
});
这...... 真的不是很优雅, 为此社区也为我们提供了一个比较优秀的中间件库 koa-router
npm install koa-router --save-dev
那我们先来个示例实现上面不优雅的代码
const Koa = require('koa');
const Router = require('koa-router');
const app = new Koa();
// 实例化路由
const router = new Router()
// 注册路由
router.get('/', async ctx => {
ctx.body = { msg: 'index page' };
});
router.get('/detail', async ctx => {
ctx.body = { msg: 'detail page' };
});
// 注册路由中间件
app.use(router.routes())
app.listen(3000);
通过以上代码的查看可读性明显提升不是一个档次, 下面加看一下她的基本使用吧;
-
路由前缀 ; 在实例化路由的时候添加上
prefix
属性就会给路由添加上前缀, 比如上诉代码中如果添加上前缀, 那么再访问 detail 的时候, 完整路径就是http://localhost:3000/user/detail
const router = new Router({ prefix: '/user' })
-
请求方式 ;
koa-router
支持常用的请求方式, 推荐大家使用get
post
put
delete
patch
head
options
// 示例 router.get(...)
-
中间件注册 ; 上述示例中可以看出, 在
app.use
之前 router 和 koa 还是没有关联的, 如果想要他们关联起来就需要使用以下代码app.use(router.routes())
-
redirect
重定向 ; 方法的第一个参数是请求来源,第二个参数是目的地,两者都可以用路径模式的别名代替,还有第三个参数是状态码,默认是 302 这个方法放到了context
上下文中router.get('/home', async ctx => { ctx.redirect('http://baidu.com'); });
-
路由参数
params
和querystring
熟悉路由的同学一定知道, 一般参数有两种情况http://localhost/users/1 // params http://localhost/users/id=1 // querystring
-
params
: 需要注意的是她是一个强路由, 会更改原有的资源地址; 看实例// 在:id代表不确定参数 // 访问时 localhost:3000/user/1 // id可以是任意值, 需要注意url中写什么样的 params 就需要怎样获取,必须保持一致 router.get('/:id', async ctx => { ctx.body = { msg: 'index page', params: ctx.params.id // 通过 ctx.params.id 获取到的是 1 }; });
-
querystring
: 此种方式会在获取路由后面携带的参数user=admin&pwd=234
router.get('/', async ctx => { ctx.body = { msg: 'index page', query: ctx.query // ctx.query会获取url后面携带的参数对象 }; });
-
-
allowedMethod
: 对于这个方法来说 , 它的作用就是用于处理请求的错误 , 假设我们现在实现了一个/user
的接口, 为get
请求方式-
router.get('/user', async ctx => {}); app.use(router.routes()); // 如果没有这一行, 当使用其他请求方式请求user时, 会提示404 // 如果有这一行, 当使用其他请求方式请求user时, 提示405 method not allowed app.use(router.allowedMethod());
-
koa-static (静态web托管服务)
npm install koa-static --save-dev
const staitc = require('koa-static');
// ./static 相对于你当前目录填写, 使其最终拼接成绝对路径
app.use(static(path.join(__dirname, './static')));
此时如果我们想访问这些资源 ; 在启动服务之后就可以通过以下方式访问 ;
http://localhost:3000/xxx.img
或者一些 css js 文件等等
koa-views (模板渲染引擎中间件)
npm intsall koa-views --save-dev
const views = require('koa-views');
// views 相对于当前目录而创建的views目录, 最近内部的文件会作为模板
app.use(views(path.resolve(__dirname, 'views')));
// 挂载此中间件之后, ctx上下文中会挂载一个render函数
app.get('/', async ctx => {
// index 是 views 文件夹内的 index.html 文件
await ctx.render('index')
});
上述代码可能解释的不是很清楚或者还有一些额外需要注意地方
-
render
函数是异步函数 一定要加上await
-
如果是
.html
的模板可以不用写后缀名 , 当然啦还支持一些其他的模板, 需要注册中间件的时候提供第二个参数options
app.use(views(path.resolve(__dirname, 'views')), { extension: 'ejs' // 可以是 pug 或者 nunjucks 记得要使用npm安装对应的依赖 });
如果我们使用的其他的模板那么对应的
render
函数中也要做更改await ctx.render('index.ejs');
-
render
函数也会接受第二个参数, 接收一个对象 ; 然后对应的模板内就可以获取到这个数据 ;await ctx.render('index.ejs', { msg: '这是后端传递的数据' });
关于模板语法, 这里就不做讲解了, 大家可以去对应官网查看 pug ejs
-
模板内引用静态资源, 注意 你已经下载了
koa-static
并且在主文件内注册了这个中间件, 那么static
目录就是你网站的根目录了, 再模板内就不要从static
开始引入了 ; 假设你的目录是这样的+---static | index.css | test.png | \---views | index.ejs | index.js
-
错误的示例
<link rel="stylesheet" href="/static/index.css">
-
正确的示例
<link rel="stylesheet" href="index.css">
-
koa-body (请求body数据处理)
如果我们不使用这个中间件去做一些事情, 比如客户端随便提交点什么数据, 那么我们为了接受这些数据, 服务端的代码可能就是这样的
router.post('/', async ctx => {
let bstr = '';
// 通过 koa 暴露出来的原生方法读取并且合并
ctx.req.on('data', (data) => {
bstr += data
})
ctx.req.on('end', () => {
console.log(bstr)
})
ctx.body = { code: 200 }
})
试想一下如果每次为了接收这些数据监听 data
end
肯定是不合理的 ;
npm install koa-body --save-dev
const bodyparser = require('koa-body');
// 在注册此中间件之后, 会在 ctx 上下文注入 ctx.request.body 属性 用于获取客户端传递来的数据
app.use(bodyparser());
router.post('/', async ctx => {
ctx.body = {
code: 200,
// body 可以获取form表单数据, json数据
msg: ctx.request.body
}
})
如果我们还需要上传文件, 则需要在注册中间的时候增加一些配置
app.use(bodyparser({
multipart: true, // 支持文件上传
formidable: {
// 文件默认保存的默认路径
uploadDir: path.join(__dirname, 'static/uploads'),
// 保留文件拓展名
keepExtensions: true
}
}));
接收数据时可以这样操作
router.post('/', async ctx => {
// files 是上传文件后的对象集合, file是对应传递的 key
const fileInfo= ctx.request.files.file;
// 返回临时路径中的最后一部分作为文件名称
const filePath = path.basename(fileInfo.path);
ctx.body = {
url: `${ctx.origin}/uploads/${filePath}`,
info: ctx.request.files
}
})
@koa/cors (处理跨域中间件)
npm install @koa/cors --save-dev
const cors = require('@koa/cors');
app.use(cors());
koa-json-error (错误处理中间件)
npm install koa-json-error --save-dev
const error = require('koa-json-error');
app.use(error({
// 默认会把堆栈信息也给一起发给客户端, 这样很明显是不合理的所以做了一层简单的处理
postFormat(e, { stack, ...rest }) {
return process.env.NODE_ENV === 'production' ? rest : { stack, ...rest}
}
}));
想要验证此功能, 可以使用 koa
原生提供的抛出错误方法
app.use(async ctx => {
if (ctx.request.body.name !== 'admin') {
// 可以参考下面图片, 信息都是根据状态码生成的, 也可以在第二个参数中自定义一些错误数据
ctx.throw(401, {msg: '鉴权失败'});
}
});
如果是逻辑错误, 比如 a is not defind
也是可以被捕获并且抛出的, 这里就不演示了 ;
koa-parameter (参数校验中间件)
npm install koa-parameter --save-dev
const parameter = require('koa-parameter');
parameter(app);
在经过以上操作之后, ctx
上下文下面就多出了一个校验函数 verifyParams
router.post('/', async ctx => {
// 接收一个对象
ctx.verifyParams({
// 校验的简写形式
username: 'string',
password: { type: 'string', required: true } // 或者也可以这样
})
ctx.body = { code: 1 }
})
简单参数的校验, 可以参考以下方法
当然也提供了一些, 特殊的校验方式
ctx.verifyParams({
// 必须输入 url 格式
address: { type: 'url', required: true },
// 必须输入 email 格式
email: { type: 'email', required: true }
})
如果是比较负责的也是可以校验的 , 当然也是不能覆盖全场景的, 不过这样已经能为我们省去不少力气了
// 某一项是对象, 并且里面某个字段是数组
ctx.verifyParams({
obj: { type: 'object', rule: { children: 'array' } }
})
// 某一项是数组, 里面包含对象
ctx.verifyParams({
// list 是数组, 里面的每一项是object object中必须包含info字段
list: { type: 'array', itemType: 'object', rule: {info: 'string'} }
})
mysql2 (mysql数据库操作)
npm install mysql2 --save-dev
使用层面相对来说也是比较简单的 ;
const mysql = require('mysql2');
// 创建连接
const connection = mysql.createConnection({
host: 'xx.xx.xx.xx',
user: 'root',
password: '123456',
port: 3306,
database: 'test'
});
// 基本的增删改查
const [row] = await connection.promise().query('SELECT * FROM users WHERE username=? AND password=?', ['lisi', 'li']);
const [info] = await connection.promise().query('UPDATE users SET username=? WHERE password=?', ['hello', 'wang']);
const [info] = await connection.promise().query('DELETE FROM users WHERE username=?', ['hello']);
const [info] = await connection.promise().query('INSERT INTO users (id, username, password) VALUES (?, ?, ?)', ['id1', 'username1', 'password1']);
需要注意的是, 当查询的时候你可以使用 [row]
来将结果解构出来, 但是在更新, 修改和删除操作的时候, 并没有返回你删除的某些值, 而是返回一个操作对象, 里面包含了一些必要的信息 ;
JWT 鉴权
npm install koa-jwt jsonwebtoken --save-dev
使用层面主要分两步走, 第一 生成 token, 一般情况下都是登录的时候就返回给用户了 ; 生成 token 使用 sign
方法
生成 token
jwt.sign('要加密的内容 string | Buffer | object', '加密的key', {配置对象})
// 配置对象中 两个比较重要的参数
{
algorithm: "HS256" | "HS384" | "HS512" |
"RS256" | "RS384" | "RS512" |
"ES256" | "ES384" | "ES512" |
"PS256" | "PS384" | "PS512" |
"none"; // 加密方式
expiresIn: "2d" // 可以是数字, 如果是数组就是秒为单位, 或者支持 2d/10h 2天 10小时
}
const jwt = require('jsonwebtoken');
router.post('/', async ctx => {
// 从请求体中结构用户名和密码
const { username, password } = ctx.request.body;
const token = jwt.sign(
{ username, password },
'auth-key',
{
algorithm: 'HS256', // 可以不写
expiresIn: '2d'
}
);
ctx.body = { token };
})
jwt.verify
校验 token
jwt.verify('token', '加密的key', {配置对象})
router.post('/auth', async ctx => {
const token = ctx.header.authorization
try {
// 这个token校验要额外处理 具体可以看下图
jwt.verify(token.split(' ')[1], 'auth-key', { algorithms: 'HS256' });
} catch (e) {
ctx.throw(401);
}
ctx.body = { code: 1 };
})
koa-jwt
校验 token
const koaJwt = require('koa-jwt');
// 如果有配置跨域 一定要写到跨域相关配置的下方
// koaJwt 的第一个参数是一个对象, 与上面介绍到的配置对象内容基本一致,
// path 可以接受 字符串, 数组, 正则表达式,用来过滤某些 url 可以不进行鉴权
app.use(koaJwt({ secret: 'auth-key' }).unless({ path: [/\user/] }))
koa-generator
npm install koa-generator -g
这是一个命令行工具 , 全局安装之后, 可以在命令行使用 koa2 --help 查看相关帮助信息
创建过工程后, 除了 bin 文件夹的下文件有些不解, 其他都还好, 那么咱们就看看吧
图中可出大致归为两类 ;
-
为什么没有后缀 ? 第一行是什么意思 ? 这里引入一篇文章帮助大家解决这个疑问 点这里
-
看到第二个问题, 这个 port 可以在命令行中设置, 但是 window 和 liunx 中命令又是不太一样的 ; 代码都跨端了, 运行个脚本跨不了端就坑了 ; 这里再次推荐一个包
cross-env
npm install cross-env --dev
使用也是非常简单的, 比如之前的启动脚本是这样的
nodemon app.js
那么我们现在改成这样既可
cross-env PORT=3001 nodemon app.js
此时再次运行项目就会在3001端口了当然也可以同时去设置环境变量比如
cross-env NODE_ENV=production PORT=3001 nodemon app.js
完结 撒花?
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!