【Node】之那些社区常用框架:express、koa、egg 该怎么上手?
写这篇文章是由于我之前有接触过一段时间的
Node
,之前做前端一直是在用别人的接口,当试着自己写接口,从另一个角度看待前端和后端后,感觉蛮有意思。最近离职了,时间比较充裕就把这三个框架都过一遍,毕竟都说前端的尽头是全栈
,哈哈哈,整个学习过程,让我对于服务器、浏览器的理解加深了,也便于自己对项目的全局理解。
本篇文章不过多介绍关于这几个框架的理论知识,我一贯认为:能上手写尽量不啰嗦
,关于你想了解更多,这里奉献上几个框架的官网地址express、 koa、 egg
如果你想了解原生node
的一些使用操作,请点击这里
一、Node
框架之 express
上手简单,自带路由,新手可以使用
express
找感觉
下载:
npm i express -D
下载完成后,我们来看看具体的使用:
1. 自带路由
const express = require('express');
let server = express();
server.listen("8080");
// send 方法用来向服务器发送内容
server.get('/a', (req, res, next) => {
res.send('qq');
});
启动服务:
node server.js
浏览器端:
2. 同一个请求可以进行多次处理 next()
需要手动调用
next
const express = require('express');
let server = express();
server.listen("8080");
// 自带路由
server.get('/a', (req, res, next) => {
console.log('a');
// 根据情况,手动调用 next 即可向后继续调用
next();
});
server.get('/a', (req,res,next) => {
console.log("b");
})
启动服务,控制台正常打印:
3. 支持 get 、post 、 use
的请求
支持
get
post
的请求,如果不确定什么请求,可以用use
get('url',fn);
post('url',fn);
use('url',fn);
// 另一种传参方式,会拦截所有的请求,一起进行处理
get(fn);
post(fn);
use(fn);
4. 处理文件
在处理数据时,大部分需要使用
中间件
,这个就做个简述,中间件:顾名思义就是帮我们处理一些小的操作或者数据解析之类的小玩意,在后面我们用到时候会具体介绍。
1.html 文件
server.js
const express = require('express');
let server = express();
server.listen("8080");
// 自带路由
server.get('/a', (req, res, next) => {
res.send('aaa');
});
server.get('/b', (req,res,next) => {
res.send('bbb');
})
// 处理文件
server.use(express.static('./static/'));
启动服务器,查看资源已经被拿到了:
get
请求的数据处理
const express = require('express');
let server = express();
server.listen("8080");
server.get('/a', (req, res, next) => {
console.log(req.query);
res.send('aaa');
});
启动服务器,打开浏览器端:
打开控制台,请求的数据已经被解析:
post
请求处理数据
下载中间件:
npm i body-parser -D
示例表单:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form action="http://localhost:8080/reg" method="post">
<input type="text" name='user' value="">
<input type="password" name='pass' value="">
<input type="submit" name='' value="注册">
</form>
</body>
</html>
处理js:
const express = require('express');
const body = require('body-parser');
let server = express();
server.listen("8080");
server.use(body.urlencoded())
server.post('/reg', (req, res) => {
console.log(req.body);
});
启动服务,打开浏览器输入信息:
打开控制台,数据已经被解析:
模拟实现body-parser
:
如果不考虑文件上传,我们来模拟一下这个
body-parser
,看一下它具体的实现原理是怎样的:
const express = require('express');
const querstring = require('querystringify');
let server = express();
server.listen("8080");
server.use((req, res, next) => {
let arr = [];
req.on('data', buffer => {
arr.push(buffer);
})
req.on('end', () => {
let post =querstring.parse(Buffer.concat(arr).toString());
req.body = post;
next();
})
});
server.post('/reg', (req, res) => {
console.log(req.body);
});
页面仍然使用这个提交页面:
查看控制台的解析,成功!!!:
再进一步封装:
body-parser.js
const querstring = require('querystringify');
module.exports = {
urlencoded() {
return (req, res, next) => {
let arr = [];
req.on('data', buffer => {
arr.push(buffer);
})
req.on('end', () => {
let post = querstring.parse(Buffer.concat(arr).toString());
req.body = post;
next();
})
};
}
}
使用页面:
const express = require('express');
const body = require('./static/body-parser');
let server = express();
server.listen("8080");
server.use(body.urlencoded());
server.post('/reg', (req, res) => {
console.log(req.body);
});
启动服务器测试,效果一样,封装成功!:
处理文件上传 multer
npm i multer -D
1.html 上传页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form action="http://localhost:8080/reg" method="post" enctype="multipart/form-data">
<input type="file" name='f1' value="">
<input type="submit" name='' value="上传">
</form>
</body>
</html>
处理数据:
const express = require('express');
const multer = require('multer');
let server = express();
server.listen("8080");
// 找一个位置来存上传后的文件
let obj = multer({ dest: './static/upload' })
// 这里可以限制文件类型、大小
server.use(obj.any());
server.post('/reg', (req, res) => {
console.log(req.files);
res.end('upload successed');
});
启动服务,浏览器上传文件:
查看控制台输出:
浏览器处理完的成功提示:
查看上传完成的文件:
cookie 不跨域的
子域名可以访问主域名
只能向上,不能向下
domain:
5. 处理cookie
先去下载一个中间件:
cnpm i cookie-parser -D
普通的存取cookie
:
const express = require('express');
const cookieParser = require('cookie-parser');
let server = express();
server.listen("8080");
server.use(cookieParser());
server.get('/a', (req, res) => {
// 存储cookie,可根据情况配置参数
res.cookie('age', 18, {
// domain:'a.com',
//path:'/',
maxAge:14*86400*1000
});
// 读取cookie
console.log(req.cookies);
res.send('ok');
})
重启服务,我们在浏览器看到已经存好了,且过期时间也设置上了:
控制台看一下获取cookie:
带签名的cookie
:
我们知道cookie 是非常不安全的,在浏览器里是可以像这样手动随意修改的,那么有没有一个办法去限制修改呢,那就要说签名这个话题了。
如何来设置签名:
const express = require('express');
const cookieParser = require('cookie-parser');
let server = express();
server.listen("8080");
// welmxxfoiweorkpwk9834534nsd8j8u 这个签名是你自己编写的,别人不知道的
server.use(cookieParser(
"welmxxfoiweorkpwk9834534nsd8j8u"
));
server.get('/a', (req, res) => {
// 存储cookie,可根据情况配置参数
res.cookie('age', 18, {
maxAge: 14 * 86400 * 1000,
// httpOnly:true, // 只能服务器修改,前台修改失效
// secure: true, // 只认https的请求
signed:true // 是否是签名的
});
// 读取cookie
console.log("普通的cookie",req.cookies);
console.log("签名的cookie",req.signedCookies);
res.send('ok');
})
看一下浏览器端:
我们看到我们的值被存储为一堆我们不认识的编码,那么它到底是什么?
s%3A18.Vbc4AX1PPW%2FeDBZexBICvp3lNB9Xx8qpE5Gm9UROIu0
我们来解析一下:
s
代表我们的值是签名过的
通过 decodeURIComponent('%3A')
,'%3A
解析出来是 :
冒号
Vbc4AX1PPW%2FeDBZexBICvp3lNB9Xx8qpE5Gm9UROIu0
这是使用我们之前给服务器的welmxxfoiweorkpwk9834534nsd8j8u
这个编码又重新编译的值。
此时我们再看下控制台输出的结果,也是可以正常打印出来的:
我们再去浏览器修改一下,重新刷新浏览器看到值被还原了,也就是没有修改成功,且控制台提示我们这个值不被认可:
此时我们的目的已经达到了,签名也在一定程度上提高了cookie 的安全性。
6.处理session
session
一般用来放用户登录信息,具体的信息存储在服务器,是不加密的:
const express = require('express');
const cookieSession= require('cookie-session');
let server = express();
server.listen("8080");
server.use(cookieSession({
keys: ['sdfgherwr23sds', 'oi884jxkcoifpgd', '2kcoer043kclcu'], // 循环密钥用于生成签名
maxAge:20*60*10000 // 一般设置20分钟
}))
server.get('/a', (req, res) => {
if (!req.session['view']) {
req.session['view'] = 1;
} else {
req.session['view']++;
}
console.log(req.session['view']);
res.send(`欢迎你第${req.session['view']}次访问!`);
})
启动服务器,看到浏览器已经把我的session
存上了,且我们发现除了session
,还多了一个session.sig
,这个就是使用我们的循环密钥生成的签名。
我们尝试来存一些敏感数据:
const express = require('express');
const cookieSession= require('cookie-session');
let server = express();
server.listen("8080");
server.use(cookieSession({
keys: ['sdfgherwr23sds', 'oi884jxkcoifpgd', '2kcoer043kclcu'], // 循环密钥
maxAge:20*60*10000 // 一般设置20分钟
}))
server.get('/a', (req, res) => {
if (!req.session['view']) {
req.session['view'] = 1;
} else {
req.session['view']++;
}
req.session['amount'] = 199;
console.log(req.session['view']);
res.send(`欢迎你第${req.session['view']}次访问!`);
})
我们看到前台并不会新增字段
,因为它只会存储一个session
的id
,用来识别身份,所以比较安全:
express
就介绍到这里,下面我们来看一下express
的升级版 koa
二、 Node
框架之Koa
koa
和express
同一个团队开发的,本身区别不是很大,思想有些区别,然后就是写法上面的不同 koa@v1 / v2/ v3
express 基于回调的思想
koa: 利用promise
的思想
v1: generator
v2: 过渡版本,generator && async / await
v3: async / await
下载(koa 不带路由,所以需要再下载一个路由):
cnpm i koa -D
cnpm i koa-router -D
来个示例:
const koa = require('koa');
const Router = require('koa-router');
let server = new koa();
server.listen(8080);
// 路由需要自己new
let router = new Router();
//ctx 里包含 req & res
router.get('/a', async (ctx, next) => {
ctx.body = 'qqq';
});
// 将路由添加到 koa 上
server.use(router.routes());
启动服务器,看一下浏览器是否已经有内容:
嵌套路由
我们在上面知道koa 的路由是需要单独下载的,那么把路由抽离出来的目的其实也是为了方便路由在项目中的使用,这一部分就来分析一下多层嵌套路由的使用:
const koa = require('koa');
const Router = require('koa-router');
let server = new koa();
server.listen(8080);
//主路由
let router = new Router();
// cart
let cartRouter = new Router();
cartRouter.get('/b', async ctx => {
ctx.body = 'cart的b';
})
// 一级路由
router.use('/cart', cartRouter.routes());
// user
let userRouter = new Router();
userRouter.get('/', async ctx => {
ctx.body = 'user根目录';
});
// 一级路由
router.use('/user', userRouter.routes());
//user - company
let company = new Router();
company.get('/a', async ctx => {
ctx.body = '企业的a';
});
// 二级路由
userRouter.use('/company',company.routes());
//user - admin
let admin = new Router();
admin.get('/a', async ctx => {
ctx.body = '管理员的a';
});
// 二级路由
userRouter.use('/admin',admin.routes());
// 将路由添加到koa
server.use(router.routes());
下面就是我们示例中嵌套层级,如果是实际开放项目中,路由可以单独抽离一个文件来管理,看起来就不会这么凌乱:
一级路由 | 二级路由 | 请求接口 | 最终路由 |
---|---|---|---|
user | company | a | user/company/a |
admin | a | user/admin/a | |
cart | b | cart/b |
路由带参
const Koa = require('koa');
const Router = require('koa-router');
let server = new Koa();
server.listen(8080);
let router = new Router();
router.get('/news/:id', async ctx => {
let { id } = ctx.params;
ctx.body = '新闻' + id;
})
router.get('/news/:id/:id2/:id3', async ctx => {
let { id, id2, id3 } = ctx.params;
ctx.body = `新闻 ${id}-${id2}-${id3}`;
})
// 将路由添加到koa
server.use(router.routes());
启动服务,查看浏览器如何传参:
当有一个参数时:
当有三个参数时:
当传参方式用 ?
的形式,用query
来取参:
const Koa = require('koa');
const Router = require('koa-router');
let server = new Koa();
server.listen(8080);
let router = new Router();
router.get('/news', async ctx => {
let { id } = ctx.query;
ctx.body = id;
})
server.use(router.routes());
server.context
相当于ctx 的proptype,在server.context 挂的参数是可以在ctx 上取的,适用于挂一些全局使用的内容
const Koa = require('koa');
const Router = require('koa-router');
let server = new Koa();
server.listen(8080);
server.context.qq = 232323;
let router = new Router();
router.get('/news', async ctx => {
ctx.body = ctx.qq;
})
server.use(router.routes());
ctx.throw
用于抛错
...
router.get('/login',async ctx=>{
if(!ctx.query.user|| !ctx.query.pass){
ctx.throw(400,'user and password is required')
}esle{
// 正常的逻辑
}
})
ctx.assert
...
router.get('/login',async ctx=>{
ctx.assert(ctx.query.user,400,'username is required');
ctx.assert(ctx.query.pass,400,'password is required')
// 正常的逻辑
})
ctx.state 状态码
...
router.get('/login',async ctx=>{
ctx.state=404;
})
ctx.redirect 重定向
...
router.get('/login',async ctx=>{
ctx.redirect='/news';
})
koa-static
访问静态资源
基本使用:
创建一个static 文件,存放静态资源;
当前有一个1.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class='box'>
gjktyurtxvfgerte34535645647
</div>
</body>
</html>
server.js
const Koa = require('koa');
const Router = require('koa-router');
const static = require('koa-static');
let server = new Koa();
server.listen(8080);
server.use(static('./static'))
启动服务去请求静态资源,资源被成功拿到:
当然我们koa 的static 是可以设置缓存时间的,在一定程度上减轻服务器的压力,下面我们一起来试试吧:
maxage 设置缓存时间
server.use(static('./static', {
maxage: 84600 * 1000 * 10,
}));
index 设置默认文件
server.use(static('./static', {
index:'1.html'
}));
可根据文件类型设置不同缓存时间:
const Koa = require('koa');
const Router = require('koa-router');
const static = require('koa-static');
let server = new Koa();
server.listen(8080);
let staticRouter = new Router();
staticRouter.all(/(\.jpg|\.png|\.gif)$/i, static('./static', {
maxage: 86400 * 1000 * 60
}));
staticRouter.all(/(\.css)$/i, static('./static', {
maxage: 86400 * 1000 * 20
}));
staticRouter.all(/(\.html|\.hml|\.shtml)$/i, static('./static', {
maxage: 86400 * 1000 * 10
}));
staticRouter.all('',static('./static', {
maxage: 86400 * 1000 * 30
}));
server.use(staticRouter.routes());
cookie
koa 是自带cookie的,所以我们就可以直接用
设置cookie:
const Koa = require('koa');
let server = new Koa();
server.listen(8080);
server.keys = ['2323rfdf3434', '23sds32rfex', 'sd4ds4434'];
server.use(async ctx => {
ctx.cookies.set('user', '222s', {
maxAge: 86400 * 1000 * 7,
signed:true
});
ctx.body = '232';
})
获取cookie:
const Koa = require('koa');
let server = new Koa();
server.listen(8080);
server.keys = ['2323rfdf3434', '23sds32rfex', 'sd4ds4434'];
server.use(async ctx => {
ctx.cookies.get('user', {
signed:true
});
ctx.body = '232';
})
koa-session
const Koa = require('koa');
const session = require('koa-session');
let server = new Koa();
server.listen(8080);
server.keys = ['2323rfdf3434', '23sds32rfex', 'sd4ds4434'];
server.use(session({
maxAge: 86400 * 1000, // 有效期
renew: true // 自动续期
},server));
server.use(async ctx => {
if (!ctx.session['view']) {
ctx.session['view'] = 0;
}
ctx.session['view']++;
ctx.body = `欢迎你第${ctx.session.view}次来访`;
})
三、Node
框架之Egg
Egg.js
是阿里旗下的一个基于nodejs
和koa2
的企业级应用框架,基于es6
,es7
和nodejs
。之前项目有用到egg 来搭建一个简单的服务,下面就来分享一下我的使用感受。
特性
Koa 是一个新的
web
框架,由Express
幕后的原班人马打造, 致力于成为web
应用和API
开发领域中的一个更小、更富有表现力、更健壮的基石。而Egg
选择了Koa
作为其基础框架,在它的模型基础上,进一步对它进行了一些增强。
所以吧,大概就是这么个关系:
----施主别走,加个关注,我还在持续补充中
可能会有人问了:现在egg
都已经可以替代 koa
和 express
了,那为啥还要学旧的呢?
我其实是想说,框架出现的目的,是使我们可以更便捷的写代码,去实现业务的需求,所以学习哪个框架他们是不关心的,对我我们个人来说,当你知道一个框架的演变过程后,你会对它有更深的理解,是思维的扩展。
转载自:https://juejin.cn/post/7186938802147950647