第十一节:|第十一节: Node框架: Koa2
1. 认识Koa
Koa 是基于Node平台的下一代web开发框架
1.1 简介
koa 是由 Express 原班人马打造的,致力于成为一个更小、更富有表现力、更健壮的 Web 框架。使用 koa 编写 web 应用,通过组合不同的 generator,可以免除重复繁琐的回调函数嵌套,并极大地提升错误处理的效率。koa 不在内核方法中绑定任何中间件,它仅仅提供了一个轻量优雅的函数库,使得编写 Web 应用变得得心应手。开发思路和express 差不多, 最大的特点是可以避免异步嵌套
1.2 关于异步
Node.js是一个异步编程的世界, 官方支持的API都是callback形式的异步编程模型,
回调函数处理异步的问题
- callback嵌套,形成的回调地狱问题
- 异步函数中的可能同步调用callback返回值数据
1.3 了解Koa2 Koa v2 的核心是使用面向更新特性的 async 函数的中间件,其正式版本发布前后略有不同。
- 正式版本发布前:支持 3 种中间件的写法(Promise,Generator,async函数)。
- 正式版本发布后:推荐使用 ES2017 的 async 函数作为中间件。
1.4 为什么选择 Koa 为什么要在众多的框架中选择 Koa 呢,基本都是网上找到的选择Koa的理由,供其参考。
- 用 async 函数做异步流程控制时,代码更容易理解。
- 错误处理的干干净净。无论是 async 函数还是 Promise 规范都能很好地处理异常,另外 Koa 继承自 Event 的,结合 ctx 里的一些 API 能够更简单地处理错误。
- 具有优雅的回形针中间件机制。通过更少的代码可以完成更多的工作,尤其是能对响应部分做拦截。
- 性能非常好。(跟 Express 作对比)
- Koa 核心代码量比较少,易于定制,易于在其上开发各种 Web框架。
- 社区生态逐渐完善。
- 国内外很多公司都已经大量应用 Koa 了,目前 Node.js 的首选 Web 架构是 Koa。
- 拥有 Egg.js (基于 Koa 的成熟的企业级 Web 开发框架),拥有庞大的插件生态。
- 拥有 MidwayJS。它基于 Egg.js 生态,使用 TS 编写,提供 IoC 容器,是面向未来的框架。
2. 使用示例 2.1. 初始化化项目 安装koa包
$ npm install --save koa
# or
$ cnpm install --save koa
#or
$ yarn add koa
2.2 编写程序代码
// 1. 引入koa 生成实例
const Koa = require('koa');
// `Koa` 是一个构造函数需要使用new 调用
const app = new Koa;
// 2. 绑定中间件(异步函数use的参数)
// use 就是把中间件绑定到实例上的方法
// 自定义中间件,中间件会有两个参数ctx,next
//Koa把req和res对象汇聚到了ctx上
// use可以注册n个中间件
app.use(async (ctx,next) => {
ctx.body = "hello koa2"
})// 3. 监听端口
app.listen(3000)
2.3 启动应用 通过node 命令启动应用
node app.js
3. 应用程序 3.1 app.use() 挂在中间件
为应用添加中间件,可以注册多个中间件
app.use(async (ctx,next) => {})
app.use(async (ctx,next) => {})
app.use(async (ctx,next) => {})
3.2 app.context 扩展上下文对象
app.context
是从中创建ctx
的原型。 可以通过编辑app.context
向ctx
添加其他属性。app.context.name = 'haha'
app.use((ctx, next) => {
console.log(33)ctx.body = 'hello world'
console.log(55)
})
【第十一节:|第十一节: Node框架: Koa2】
3.4 app.callback() 返回适用于
http.createServer()
方法的回调函数来处理请求。你也可以使用此回调函数将 koa 应用程序挂载到 Connect/Express 应用程序中。http.createServer(app.callback()).listen(3000)
http.createServer(app2.callback()).listen(3001)
3.3 错误处理 如果发生错误时,会触发error事件
app.on('error', err => {
log.error("server error", err)
})
4. Context 上下文 4.1. 了解Context Koa Context 将 node 的
request
和 response
对象封装在一个单独的对象里面,其为编写 web 应用和 API 提供了很多有用的方法。context
在每个 request 请求中被创建,在中间件中作为接收器(receiver)来引用,或者通过 this
标识符来引用:app.use(async ctx => {
ctx;
// is the Context
ctx.request;
// is a koa Request
ctx.response;
// is a koa Response
});
许多 context 的访问器和方法为了便于访问和调用,简单的委托给他们的
ctx.request
和 ctx.response
所对应的等价方法, 比如说 ctx.type
和 ctx.length
代理了 response
对象中对应的方法,ctx.path
和 ctx.method
代理了 request
对象中对应的方法。4.2. Context API
API | 描述 |
---|---|
ctx.req | request 对象. |
ctx.res | response 对象. |
ctx.request | Request 对象. |
ctx.response | Response 对象. |
ctx.cookies.get(name, [options]) | 通过 options 获取 cookie name : |
ctx.cookies.set(name, value, [options]) | 通过 options 设置 cookie name 的 value : |
4.3.Response 别名 以下访问器和 Response 别名等效:
ctx.status
ctx.message
ctx.length
ctx.type
ctx.redirect()
4.4. ctx的属性与方法 4.4.1 向前端返回数据
ctx.body
ctx.body = '返回数据';
//也可以对象ctx.body = {
a:1
}
//Koa会将对象封装成字符串传递个前端
4.4.2 获取前端路径
ctx.url
等同于
ctx.request.url
//比如 网址输入localhost:3000/path?name=wuwei&age=18
//返回 /path?name=wuwei&age=18
app.use(async function(ctx,next){
// 请求路径
console.log(ctx.url);
ctx.body='hello wrold'
})
4.4.3 获取请求的方法
ctx.method
app.use(async function(ctx,next){
// 请求方式
console.log(ctx.method);
ctx.body='htllo world'})
4.4.4 中间件之间的传值 用于通过中间件传递信息和你的前端视图。
ctx.state
4.4.5 获取非查询的路由
ctx.path
//比如 网址输入localhost:3000/path?name=wuwei&age=18ctx.path;
//返回 /path
4.4.6 将路由中的查询部分转成对象
ctx.query
//比如 网址输入localhost:3000/path?name=wuwei&age=18
//返回 { name:wuwei,age:18}
app.use(function(ctx,next){
// 获取querystring解析后的对象
console.log(ctx.query);
ctx.body='hello world'})
4.4.7 只获取路由中查询部分
ctx.querystring
//比如 网址输入localhost:3000/path?name=wuwei&age=18
//返回 name=wuwei&age=18
app.use(function(ctx,next){
//后去queryString
console.log(ctx.querystring);
ctx.body='hello world'
4.4.8. 设置返回给前端的响应头
// 响应状态
ctx.status = 201// 响应类型
ctx.type = 'html'// 返回数据
ctx.body = 'h1'
5. Koa路由 路由(Routing)是一个URL(或者叫路径)和一个特定的HTTP方法(GET,POST等)组成, 涉及到应用如何响应客户端对某个网站节点的访问
简单理解就是路由即根据不同 的URL 地址, 加载不同的页面实现不同的功能
Koa路由和Express 路由有所不同,在Express中直接引入Express就可以配置路由了, 但是在Koa中我们需要安装对应的koa-router路由模块来实现
5.1. 先安装
koa-router
$ npm install --save koa-router
5.2. koa路由的使用 需要引入koa-router模块, 然后实例化
const Koa = require('koa');
// 1. 引入路由模块
const Router = require('koa-router');
const app = new Koa;
// 2.路由实例
const router = new Router;
// 3.定义路由
router.get("/", async ctx => {
ctx.body = "首页"
})// 4.主程序使用路由
app.use(router.routes());
// 启动路由
app.use(router.allowedMethods());
/*
allowedMethods方法的作用
官方推荐用法, 我们可以理解allowedMethods方法用在routes方法之后, 当所有路由中间件最后调用
*/// 监听端口
app.listen(3000)
5.3. 路由设计 5.3.1 get 请求方式 设计
get
请求方式的路由指定路由请求方式为get方式, 请求地址为/ ,当这些匹配 成功后,会触发后面的的中间件
router.get('/' , async (ctx,next)=>{})
5.3.2 post 请求方式 设计
post
请求方式的路由指定路由请求方式为post方式, 请求地址为/ ,当这些匹配 成功后,会触发后面的的中间件
router.post('/' , async ctx,next)=>{})
不同请求方式中的中间件是不会串联的.不会通过
next()
方法转移控制权的5.3.3 为一个路由配置多个中间件 如果想在同一个路由中使用多个中间件,只需要传递多个参数即可,如
router.get('/' , async (ctx,next)=>{
await next();
}, async (ctx,next)=>{}, async (ctx,next)=>{})
//这里的中间件是串联的可以通过`next()`移交控制权
5.4. 父子路由分发 使用
router
分发路由// 主路由
const router =new Router;
// student 子路由
const student =new Router;
student.get("/", ctx =>{
ctx.body ="学生页面"
})// teacher子路由
const teacher =new Router;
teacher.get("/", ctx =>{
ctx.body ="老师页面"
})// 将子路由挂在到主路由上
router.use("/student",student.routes(),student.allowedMethods())
router.use("/teacher",teacher.routes(),teacher.allowedMethods())app.use(router.routes())
app.use(router.allowedMethods())
5.5. 路由传值 5.5.1 get 路由传值 Koa2 中 GET 传值通过request 接受, 接收的方法有两种:query 和 querystring
- query: 返回的是格式化好的参数对象
- querystring: 返回的是请求字符串
router.get("/", async (ctx,next) => {
consol.log(ctx.query) // 推荐使用
consol.log(ctx.querystring)
consol.log(ctx.request.query)
consol.log(ctx.request.querystring)
})
5.5.2 动态路由传值
router.get('/new/:aid', async (ctx,next) => {
console.log(ctx.params) // 获取动态路由的值
})
5.5.3 POST 路由传值 有两种方案
- 原生node获取post数据
- Koa中的koa-bodyparser中间件的使用
// 原生获取post数据的方法
function parsePostData(ctx){
return new Promise((resolve,reject) => {
try{
let datahttps://www.it610.com/article/= ''
ctx.req.on('data', function(chunk){
data += chunk;
})
ctx.req.on('end', function(){
let postdata = https://www.it610.com/article/parseQueryString(data)
resolve(postdata)
})
}catch(error){
reject(error)
}
})
}// 将字符串参数处理成对象
function parseQueryString(querystring){
const queryData = {};
const qsList = querystring.split("&");
for(let [index, qs] of qsList.entries()){
let kvList = qs.split("=");
queryData[kvList[0]] = decodeURIComponent(kvList[1]);
}
return queryData;
};
通过koa-bodyparser 获取post数据
使用流程
- 下载koa-bodyparser包
$ npm install koa-bodyparser --save
- 引入包
const bodyParser = require('koa-bodyparser')
- 使用中间件
app.use(bodyParser())
- 在ctx中request的body属性中获取数据
ctx.request.body
router.get('/', async ctx => {
let postData = https://www.it610.com/article/ctx.request.body;
})
6. 静态资源中间件
koa-static
静态资源中间件 静态web服务6.1 安装
$ npm install koa-static --save
6.2 引入模块
const static = require('koa-static');
6.3 使用static中间件 其实
router
还是static
对于app
来说都是中间件app.use(static())
使用
static
中间件最好在使用router
中间件之前6.4 设置静态资源目录 在使用
static
中间件时,设置静态资源目录需要使用
path
模块下的join()
方法拼接静态资源目录的路由app.use(static(join(__dirname,'static')))
6.5 可以配置多个静态资源目录
app.use(static(join(__dirname,'static')))
app.use(static(join(__dirname,'public')))
6.6 引入静态资源目录里的资源 引入静态资源目录里的资源需要使用绝对路径
7 koa2使用模板引擎 7.1 安装 安装
koa-views
npm install --save koa-views
安装
ejs
npm install --save ejs
7.2 使用模板引擎
// 引入模块
var views = require('koa-views');
// 配置视图模板
app.use(views(__dirname + '/views', {
extension: "ejs"
}));
// 渲染模板引擎
ctx.render("index",{
title:"hello world"
})
8. 解决跨域问题 8.1 通过设置响应头解决跨域
app.use(async (ctx, next) => {
// 允许所有域名请求
ctx.set("Access-Control-Allow-Origin", "*")
//只允许 http://localhost:8080 域名的请求
// ctx.set("Access-Control-Allow-Origin", "http://localhost:8080");
// 设置允许的跨域请求方式
ctx.set("Access-Control-Allow-Methods", "OPTIONS, GET, PUT, POST, DELETE")// 字段是必需的。值一个逗号分隔的字符串,表示服务器所支持的所有头信息字段.
ctx.set("Access-Control-Allow-Headers", "x-requested-with, accept, origin, content-type")// 服务器收到请求以后,检查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以后,确认允许跨源请求,即可做出响应。// Content-Type表示具体请求中的媒体类型信息
ctx.set("Content-Type", "application/json;
charset=utf-8")// 该字段可选。它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。 当设置成允许请求携带凭证cookie时,需要保证"Access-Control-Allow-Origin"是服务器有的域名,而不能是"*";
ctx.set("Access-Control-Allow-Credentials", true);
// 该字段可选,用来指定本次预检请求的有效期,单位为秒。
// 当请求方法是PUT或DELETE等特殊方法或者Content-Type字段的类型是application/json时,服务器会提前发送一次请求进行验证
// 下面的的设置只本次验证的有效时间,即在该时间段内服务端可以不用进行验证
ctx.set("Access-Control-Max-Age", 300);
/*
CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:
Cache-Control、
Content-Language、
Content-Type、
Expires、
Last-Modified、
Pragma。
*/
// 需要获取其他字段时,使用Access-Control-Expose-Headers,
// getResponseHeader('myData')可以返回我们所需的值
ctx.set("Access-Control-Expose-Headers", "myData")await next()
})
8.2 使用包解决跨域 原生的可以设置res解决跨域问题
这里可以通过模块解决跨域问题
- 安装解决跨域的模块
$ npm install @koa/cors@2 --save
- 导入模块
const cors = require('@koa/cors');
- 使用
cors
中间件
app.use(cors())
这样就可以解决跨域啦
9.文件上传 使用
koa-body
处理文件上传9.1
koa-body
处理post数据
const Koa = require("koa");
const Router = require("koa-router");
const koaBody = require('koa-body');
const app = new Koa;
const router = new Router;
// 挂在中间件
app.use(koaBody());
router.post("/upload",ctx => {
ctx.body = `Request Body: ${JSON.stringify(ctx.request.body)}`;
})app.use(router.routes())// 监听端口
app.listen(3000)
9.2
body-body
处理文件上传
koa-body
不仅能处理图片上传,还能处理数据const Koa = require('koa');
const koaBody = require('koa-body');
const { join } = require('path');
const app = new Koa;
app.use(koaBody({
multipart: true,
formidable:{
// 上传存放的路劲
uploadDir: join(__dirname,'upload'),
// 保持后缀名\
keepExtensions: true,
// 文件大小
maxFileSize: 1024,
onFileBegin: (name, file) => {
// 获取后缀, 如: .js.txt
const reg = /\.[A-Za-z]+$/g
const ext = file.name.math(reg)[0]//修改上传文件名
file.path = join(__dirname,"upload/") + Date.now() + ext;
},
onError(err){
console.log(err)
}
}
}))app.use(async (ctx)=>{
// 表单数据在body
console.log(ctx.request.body);
// 文件在files
console.log(ctx.request.files);
ctx.body = '上传成功'
})app.listen(3002)
如果需要处理数据,就不需要给中间件
koaBody()
传任何参数,如果需要传文件,则需要给中间件koaBody({})
传入一个对象.推荐阅读
- 开学第一天(下)
- (二)ES6第一节变量(let|(二)ES6第一节变量(let,const)
- Node.js中readline模块实现终端输入
- 亲子日记第十二天
- C语言学习|第十一届蓝桥杯省赛 大学B组 C/C++ 第一场
- python青少年编程比赛_第十一届蓝桥杯大赛青少年创意编程组比赛细则
- 第十九周
- 欢乐小分队内蒙东北行第六站(第十二天)五大连池印象之(奇特壮观的火山地貌景观)
- 未来丛林历险记
- 第十一节课打卡|第十一节课打卡 杨小风