Koa2自学笔记
对应的代码示例:https://codesandbox.io/s/reverent-worker-egree
现在使用node写后端应用的话,使用Koa2是一个非常不错的选择,非常的小巧,并且对应的扩展性很强,编程方式也比较方便非常好用,由此决定写一些关于Koa2的一些内容!
- 通过Koa2编写对应的Web应用配合不同的generator可以避免重复繁琐的回调函数嵌套,并且大大提升错误的处理效率! 一个Koa应用就是一个对象,包含了一个middleware数组,对应的数组由一组generator函数组成!
- 对应的generator负责对HTTP请求进行处理,对应的生成缓存,指定代理,或者说请求重定向等等,中间件韩书记与request请求以一个类似于栈(后进先出)的结构组成并且依次运行!
01|开发环境的搭建
其实对应的还是比较简单地,因为是基于node环境,自然少不了对应的node和NPM了,因此我们使用Koa的前提条件是需要有对应的node的环境!
安装好了对应的node环境,因为是自带了NPM,因此我们可以使用NPM安装对应的koa的依赖!
mkdir Koa2 && cd koa2
npm init -y
npm install -D koa
然后我们写下第一行Koa2的代码吧:
const koa = require("koa");
const app = new koa();
app.use(async (context)=>{
context.body = "Hello Koa2";
}).listen(3000);
写好了对应的Koa的代码之后,我们就可以通过分配的主机和端口就可以看到对应的内容 Hello Koa2
了!
02|async/await的使用方法
async/await其实就是现在最新的对于异步的处理,使对异步的操作更加的方便,其中包括两个关键词:
- async:声明一个方法是异步的
- await:表示对 异步方法的等待
其中比较重要的也是我们需要注意的点就是:await必须在async的函数下才能够使用,await本身就会造成程序的阻塞,因此着重强调在async的函数中才能够使用!
哪么随即的问题又来了,async到底起什么作用啊? await所谓的等待又在等待什么?
- async到底起什么作用啊?
async function testAsync() {
return "Hello Async";
}
const result = testAsync();
console.log(result);//Promise {"Hello Async"}
拖过以上的代码就可以知道对应的返回的对象是一个Promise对象!
- await到底在等待什么?
await一般在等待async方法执行完毕,但是对应的await等待的其实只是一个表达式,该表达式在官方文档中说的就是一个Promise对象! 对应的可以接收普通值 我们通过简单的代码来予以验证!
async function s1(){
return "SomeThing1";
}
async function s2(){
return "SomeThing2";
}
async function test(){
const some1 = await s1();
const some2 = await s2();
console.log(some1,some2);
}
test();//SomeThing1 SomeThing2
通过await promise拿到的内容并且打印出来之后对应的结果就是 SomeThing1 SomeThing2
了!
但是如果说只是在test函数中简单的打印s1和s2结果显然就不同了!
async function test(){
console.log(s1(),s2());// Promise {: "SomeThing1"} Promise {: "SomeThing2"}
}
对应的就是Promise对象,await就是把Promise解析成为异步函数中返回的值!
对应的如果说你还不能够明白的话,其实也不要紧,我们用一个例子简单阐述一下:
function takeLongTime() {
return new Promise(resolve => {
setTimeout(resolve("longTimeValue"), 1000);
});
}
async function test1() {
const v = await takeLongTime();
console.log(v);
}
test1();
我们提供了一个takeLongTime函数,返回的是一个Promise对象,对象中在1秒钟之后就会resolve对应的一个值!
我们在另外一个测试函数中,通过await一个Promise对象并且返回的结果值打印出来便可以知道打印的是一个Promise对象中resolve出来的值!
03|GET请求的接收
前后端配合的时候,都会告知你请求时候的请求方法,一般都是Get和Post请求方法居多,在前端开发当中也最为常见,接下来我们简单说说请求在Koa2中是如何接受并且处理的!
- query和queryString的区别
在Koa2中Get请求通过request参数,但是接受的方法分别为:query和queryString
- query:返回的是格式化好的参数对象
- queryString:返回的是请求字符串
我们通过例子来说明吧:
const koa = require("koa");
const app = new koa();
app.use(async(context)=>{
let url = context.url;
let request = context.request;
let req_query = request.query;
let req_query_string = request.querystring;
context.body = {url,req_query,req_query_string};
}).listen(3000,()=>{console.log('This server running at the port:3000!')});
我们通过对应链接加上查询参数如下所示:
https://127.0.0.1:3000/?user=ProbeDream&age=22
对应的返回的是这么一串JSON字符串:
{
url: "/?user=ProbeDream&age=22",
request_query: {
user: "ProbeDream",
age: "22"
},
request_queryString: "user=ProbeDream&age=22"
}
从上面的代码不难看出拿到的值都是从request中获取到的查询字符串,如果说在context中的查询字符串又是如何的呢?
const koa = require("koa");
const app = new koa();
app.use(async(context)=>{
let url = context.url;
let request = context.request;
let req_query = request.query;
let req_query_string = request.querystring;
let context_query = context.query;
let context_query_string = context.querystring;
context.body = {url,req_query,req_query_string,context_query,context_query_string};
}).listen(3000,()=>{console.log('This server running at the port:3000!')});
这样一来加上了context部分拿到的JSON字符串如何呢?
{
url: "/?user=ProbeDream&age=22",
request_query: {
user: "ProbeDream",
age: "22"
},
request_query_string: "user=ProbeDream&age=22",
context_query: {
user: "ProbeDream",age: "22"
},
context_query_string: "user=ProbeDream&age=22"
}
因此Get请求的方式有两种:
- 从request中获取
- 从对应的上下文中获取
04|POST请求如何接收01
在Koa中没有封装方便的获取参数的方法,需要通过解析上下文context中的原生nodejs请求对象req来获取!
对应的获取Post请求的步骤如下所示:
- 解析上下文中context的原生node对象req
- 将POST表单数据解析成requeststring字符串,就像之前所说的 user=ProbeDream&age=22
- 将字符串转换为JSON格式
那么对应的context.request和context.req的区别在哪里?
- context.request是Koa2中context经过封装的请求对象,用起来更加的直观和简单
- context.req是conotext提供的node.js原生HTTP请求的对象,不那么直观但是可以得到更多的内容,适合深度变成!
对应的context.method可以拿到请求的类型/方法,接下来我们通过代码演示通过不同的请求类型获得不同的内容进行演示操作!
const koa = require("koa");
const app = new koa();
app
.use(async context => {
if (context.url === "/" && context.method === "GET") {
const HTML = `
Koa2 request post demo
`;
context.body = HTML;
} else if (context.url === "/" && context.method === "POST") {
context.body = "收到客户端请求!";
} else {
context.body = "404!
";
}
})
.listen(3000, () => {
console.log("This Server running at the port:3000");
});
其实上面的代码还是比较容易理解的:
- 第一次访问通过的GET请求返回对应的表单
- 通过表单提交进去因为是POST请求 由此 页面响应内容为
收到客户端请求
05|POST请求如何接受02
01|解析Node原生POST参数
const koa = require("koa");
const app = new koa();
app.use(async context => {
if (context.url === "/" && context.method === "GET") {
const HTML = `
Koa2 request post demo
`;
context.body = HTML;
} else if (context.url === "/" && context.method === "POST") {
//await parsePostData(context); 其实实际上就是获取返回的表单数据! 而不是Promise!
context.body = await parsePostData(context);
} else {
context.body = "404!
";
}
}).listen(3000, () => {
console.log("This Server running at the port:3000");
});
对应的其中用到了两个参数分别为:
function parsePostData(context){
return new Promise((resolve,reject)=>{
try{
let postData = "";
context.req.on("data",data=>{postData += data;});
context.req.addListener("end",()=>{
resolve(postData);
})
}catch(error){
reject(error);
}
})
}
对应的因为之后拿到的返回的数据是字符串形式的,如果说能够以JSON形式的呈现出来可能会好很多!
function parseQueryString(queryString){
let parseData = {};
let queryList = queryString.split("&");
for(let [index,queryString] of queryList.entries()){
let itemList = queryString.split("=");
parseData[itemList[0]] = encodeURIComponent(itemList[1]);
}
return parseData;
}
最后在parsePostData方法中在对应的end事件回调函数中resolve(postData)更换为以下代码就可以了! resolve(parseQueryString(postData));
这样的话返回的就是一个JSON格式的内容了!
其中最主要的是 Array.prototype.entries的使用! 返回的是一个Array Iterator对象! 该对象包含数组中每个索引的键和值!
这里就可以简单讲讲entries的使用!
let arr = ['a','b','c'];
let Iterator = arr.entries();
console.log(Iterator.next().value);//["0","a"]
最后的效果简单尝试之后返回的效果是这样的:
{"userName":"123123","age":"123123","webSite":"[email protected]"}
06|使用koa-bodyparser中间件
在以上的代码中我们简单学会了使用代码编写并且解析Post请求,但是其实对应的我们可以不用自己写,有对应的轮子给我们使用! koa-bodyparser就是一个造好的轮子! 我们在Koa中叫做中间件!
- 对于POST请求的处理,我们只需要把koa上下文中的formData数据解析到context.request.body中即可!
对应的我们先安装 koa-bodyparser
中间件,然后在主程序中引入该依赖就行了,之后将 context.request.body
替替换掉之前的 await parsePostData
就行了!
npm install --save koa-bodyparser
const koa = require("koa");
const bodyParser = require("koa-bodyparser");
const app = new koa();
app.use(bodyParser());
app
.use(async context => {
if (context.url === "/" && context.method === "GET") {
const HTML = `
Koa2 request post demo
`;
context.body = HTML;
} else if (context.url === "/" && context.method === "POST") {
context.body = context.request.body;
} else {
context.body = "404!
";
}
})
.listen(3000, () => {
console.log("This Server running at the port:3000");
});
虽然说只是引入对应的中间件,但是前面讲的内容还是比较重要的,偏向于原理性,因此我们不能够只了解使用中间件即可,还需要对原理进行理解才能够提高我们的编码水平和开发效率!
07|Koa2原生路由实现
如果说要想实现对应的路由功能的话,就需要根据不同的地址进行跳转,使用对应的context.request.url就能够实现! 我们接下来通过简单例子进行说明:
原生的路由实现需要引入对应的fs模块读取文件,根据路由的路径去读取,最后返回给页面,进行渲染!
const koa = require("koa");
const fs = require("fs");
let app = new koa();
function render(page){
return new Promise((resolve,reject)=>{
let pageUrl = `./page/${page}`;
fs.readFile(pageUrl,(error,data)=>{
if(error){
reject(error);
}else{
resolve(data);
}
})
});
}
async function route(url){
let page = '404.html';
switch(url){
case "/":
page = "index.html";break;
case "/index":
page = "index.html";break;
case "/todo":
page = "todo.html";break;
case "/404":
page = "404.html";break;
default : break;
}
return await render(page);
}
app.use(async(context)=>{
let url = context.request.url;
context.body = await route(url);
}).listen(3000,()=>{console.log('This server running at the 3000 port!')});
我们通过node main.js之后 根据分配的端口,访问对应的路径就可以跳转到对应的路由!
比如说我们访问 127.0.0.1:3000/index
对应的就匹配到了当前文件夹page里面的index.html文件!
08|Koa-router中间件入门
以上的代码虽然说不够优雅不够完美,但是是能够非常好的帮助我们学习原生路由! 但是实际开发中用到的都是相对来讲比较成熟的中间件,由此引除了Koa的中间件 Koa-router,能够帮助我们更好的编写路由功能!
- 我们先安装对应的koa-router
yarn add koa-router
编写对应的代码:
const Koa = require('koa');
const Router = require('koa-router');
let app = new Koa();
let router = new Router();
/* 使用koa-router */
router
.get('/', (context, next) => {
context.body = 'Hello ! This is about home page!
';
})
.get('/todo', (context, next) => {
context.body = 'Hello ! This is about todo page!
';
});
app
.use(router.routes())
.use(router.allowedMethods())
.listen(3000, () => {
console.log('This Server running at the 127.0.0.1:3000');
});
我们通过分配出来的地址加上路由地址访问就可以发现不同的内容了!
09|Koa-router中间件2 层级
在对路由有初步的了解之后,有些时候路由的设计还是比较复杂的,很多时候哦你会发现路由表的设计并不是那么的单纯,由此便引出这张讲的内容,层级!
其中有对应的常见的两种写法:
- 添加前缀
const router = new Router({
prefix:"/probedream"
})
但是对应的前缀一般都是全局的,为了更好的拆封路由功能等等,这样的分层意义不大,由此引出第二种!
- 使用
router.use("/",page.routes(),page.allowedMethods())
进行分层
const koa = require("koa");
const Router = require("koa-router");
let app = new koa();
let home = new Router();
home
.get("/probedream", (context, next) => {
context.body = " home probedream
";
})
.get("/todo", (context, next) => {
context.body = " home todo
";
});
let page = new Router();
page
.get("/probedream", (context, next) => {
context.body = " page probedream
";
})
.get("/todo", (context, next) => {
context.body = " page todo
";
});
let router = new Router();
/* 整合所有的子路由 */
router.use("/home", home.routes(), home.allowedMethods());
router.use("/page", page.routes(), page.allowedMethods());
/* 路由中间件的加载 */
app.use(router.routes(), router.allowedMethods());
app.listen(3000, () => {
console.log("This server running at the http://127.0.0.1:3000/");
});
这样通过分层就拆分除了四个路由地址:
- /home/probedream
- /home/todo
- /page/probedream
- /page/todo
这样一来的结构就清楚很多了!
10|Koa-router中间件参数传递
其实这一点还是比较清楚的,就和之前讲到的第三部分关于参数的接收:
- context.query
- context.querystring
- context.request.query
- context.request.querystring
都可以赋值给context.body进行参数的传递操作:
const koa = require("koa");
const Router = require("koa-router");
let app = new koa();
let home = new Router();
home
.get("/probedream", (context, next) => {
context.body = context.query;
})
.get("/todo", (context, next) => {
context.body = " home todo
";
});
let page = new Router();
page
.get("/probedream", (context, next) => {
context.body = " page probedream
";
})
.get("/todo", (context, next) => {
context.body = " page todo
";
});
let router = new Router();
/* 整合所有的子路由 */
router.use("/home", home.routes(), home.allowedMethods());
router.use("/page", page.routes(), page.allowedMethods());
/* 路由中间件的加载 */
app.use(router.routes(), router.allowedMethods());
app.listen(3000, () => {
console.log("This server running at the http://127.0.0.1:3000/");
});
看代码这样一来,我们通过 http://127.0.0.1:3000/home/probedream?age=20
拿到的响应值就可下面一样!
{
age:"20"
}
11|koa中使用cookie
在日常开发中的登录和用户信息的保存一般都是在本地保存的,最常用的就是cookie操作,一般我们在做一个登录功能的时候都希望一周之内都可以不用再次重新登录便可以访问资源,由此涉及到了Cookie完成这个功能!
- Koa的上下文提供了读取和写入Cookie的方法!
ctx.cookies.get(name, [options]);
ctx.cookies.set(name, value, [options]);
这两个方法其实都还是比较好理解的,在上下文中读取/写入Cookie!
我们通过一个简单的小例子设置Cookie吧:
const Koa = require("koa");
let app = new Koa();
app
.use(async context => {
if (context.url === "/") {
context.cookies.set("name", "probedream");
context.body = "Cookie setting status is ok!";
} else {
context.body = "Hello probeDream!";
}
})
.listen(3000, () => {
console.log("This server running at the 127.0.0.1:3000");
});
通过访问 127.0.0.1:3000
就能看到 Cookie setting status is ok!
的字样了!
对应的涉及到的还有Cookie的option选项的信息:
- domain:写入cookie所在的域名
- path:写入cookie所在的路径
- maxAge:Cookie最大有效时长
- expires:cookie失效时间
- httpOnly:是否只用http请求中获得
- overwirte:是否允许重写
由此我们可以是这配置一下Cookie
const Koa = require("koa");
let app = new Koa();
app
.use(async context => {
if (context.url === "/index") {
context.cookies.set("name", "probedream", {
domain: "https://egree.sse.codesandbox.io/",
path: "/index",
maxAge: 1000 * 60 * 2,
overwrite: false
});
context.body = "Cookie setting status is ok!";
} else {
if (context.cookies.get("name")) {
context.body = context.cookies.get("name");
} else {
context.body = "Cookie is Empty!";
}
}
})
.listen(3000, () => {
console.log("This server running at the 127.0.0.1:3000");
});
- 访问index的时候,显示:
Cookie setting status is ok!
- 访问/的时候,显示:
probedream
12|模板引擎的使用ejs
在使用Koa的时候不可能把所有的HTML代码写在JS里面,因此此时就需要借助一个工具,叫做模板引擎的东西来帮助我们做开发,Koa中使用模板引擎或者说模板的话,也需要依赖中间件,我们这里使用了koa-views
- 安装koa-views
npm install koa-views
装好了对应的依赖我们就可以直接写代码和对应的模板文件了!
const Koa = require("koa");
const views = require("koa-views");
const path = require("path");
let app = new Koa();
/* 加载ejs */
app.use(
views(path.join(__dirname, "./views"), {
extension: "ejs"
})
);
app
.use(async context => {
let title = "Hello ProbeDream";
await context.render("index", { title });
})
.listen(3000, () => {
console.log("This server running at the 127.0.0.1:3000");
});
对应的ejs格式如下所示:
<%= title %> http://github.com/probedream
<%= title %>
EJS Welcome to <%= title %>
将对应的ejs文件的文件夹views和index.js文件保持同一目录即可!
13|koa-static静态资源中间件
对应的后台服务并不仅仅只是做简单的逻辑,在有些时候,我们需要报录一些资源给前端使用或者说给用户使用,因此在这里就需要介绍到koa-static中间件了!
这个中间件件如其名,就是对外暴露静态资源使用的!
现在我们进行简单使用:
npm install koa-static
编写对应的逻辑代码
const Koa = require("koa");
const path = require("path");
const server = require("koa-static");
let app = new Koa();
/* 加载ejs */
app.use(server(path.join(__dirname, "../static")));
app
.use(async context => {
context.body = "Hello ProbeDream!";
})
.listen(3000, () => {
console.log("This server running at the 127.0.0.1:3000");
});
我在最外层创建了一个文件夹并且创建了一个文件 1.txt
内容为你好啊!
https://egree.sse.codesandbox.io/1.txt
就可以看到对应的信息!
关于Koa的自学就到这里啦,感谢您看到这里!