koa需要node v7.6.0以上(因为需要ES6)
npm install koa --save
习惯性加上save,不加也可以。
koa的hello world示例
const Koa = require('koa');
const app = new Koa();
app.use(async(ctx)=>{
ctx.body = 'hello koa!';
})
app.listen(3000);
这个示例很简单,从node_modules中导入Koa类,然后实例化给app,通过app.use(中间件处理函数)来触发接受请求(每当有请求的时候就会按编写顺序和next堆栈方式触发这些所有的中间件函数),通过koa语法糖创建监听端口.
ctx.body即是我们显示在网页上的内容。
官网的表述:
Koa 的中间件通过一种更加传统(您也许会很熟悉)的方式进行级联,摒弃了以往 node 频繁的回调函数造成的复杂代码逻辑。 然而,使用异步函数,我们可以实现"真正" 的中间件。与之不同,当执行到 yield next 语句时,Koa 暂停了该中间件,继续执行下一个符合请求的中间件(‘downstrem’),然后控制权再逐级返回给上层中间件(‘upstream’)。
下面的例子在页面中返回 “Hello World”,然而当请求开始时,请求先经过 x-response-time 和 logging 中间件,并记录中间件执行起始时间。 然后将控制权交给 reponse 中间件。当一个中间件调用next()函数时,函数挂起并控件传递给定义的下一个中间件。在没有更多的中间件执行下游之后,堆栈将退出,并且每个中间件被恢复以执行其上游行为。
// 中间件函数参数 第一个context上下文对象 第二个next函数,用于控制koa的执行顺序
// 接下来我们编写三个中间件使得koa能够反馈响应并打印到控制台上
// 打印请求方式和请求地址
const Koa = require('koa');
const app = new Koa();
// 大致意义就是堆栈类似的调用方式:x-response-time -> logger -> response ->logger ->x-response-time
app.use(async (ctx, next) => {
const start = Date.now();
console.log("x-response-time was used at 1")
await next();
console.log("x-response-time was used at 2")
const ms = Date.now() - start;
// 这句话的意义是设置返回头 response headers
ctx.set('X-Response-Time', `${
ms}ms`);
});
// logger
app.use(async (ctx, next) => {
const start = Date.now();
console.log("logger was used at 1")
await next();
console.log("logger was used at 2")
const ms = Date.now() - start;
console.log(`${
ctx.method} ${
ctx.url} - ${
ms}`);
});
// response
app.use(async ctx => {
console.log("response was used at 1")
ctx.body = 'Hello World';
});
app.listen(3000);
打印结果:
x-response-time was used at 1
logger was used at 1
response was used at 1
logger was used at 2
GET / - 12
x-response-time was used at 2
大致意义就是堆栈类似的调用方式:x-response-time,next之前的内容 -> logger,next之前的内容 -> response,无next,全部调用 ->logger,next之后的内容 ->x-response-time,next之后的内容->结束
get请求:
我们可以通过ctx.request
属性获取统一的request对象,获取其中url/query的内容
const Koa = require("koa");
const app = new Koa();
app.use(async (ctx) => {
let url = ctx.url;
let request = ctx.request;
// query返回的是格式化好的参数对象,querystring则是请求字符串,
let req_query = request.query;
let req_query_string = request.querystring;
// 也可以直接通过ctx获取
let ctx_query = ctx.query;
let ctx_querystring = ctx.querystring;
ctx.body = {
url,
req_query,
req_query_string,
ctx_query,
ctx_querystring
};
console.log(url);
});
app.listen(3000);
console.log("server start at http://127.0.0.1:3000");
post请求:
1.使用第三方的koa-bodyparser中间件,在使用后从ctx.request.body中获取post的数据(对象)
const Koa = require("koa");
const app = new Koa();
const bodyparser = require("koa-bodyparser");
// koa-bodyparser需要先引用
app.use(bodyparser());
app.use(async (ctx) => {
if (ctx.url === "/" && ctx.method === "GET") {
// 显示表单界面
let html = `
AX Koa2 request post
`;
ctx.body = html;
} else if (ctx.url === "/" && ctx.method === "POST") {
// 导入bodyparser后,直接从body中拿取post
let postData = ctx.request.body;
ctx.body = postData;
} else {
ctx.body = "404!
";
}
});
app.listen(3000, () => {
console.log("server started at http:127.0.0.1:3000/");
});
手写解析(原理剖析,非重点)
注意这里的
ctx.req.addListener("data", (data) => {
postData += data;
});
ctx.req.on("end", function () {
let parseData = parseQueryStr(postData);
resolve(parseData);
});
这两种监听,都是通过ctx.req对象实现,ctx.req就是原生的nodejs对象里的http/https板块内容
const Koa = require("koa");
const app = new Koa();
app.use(async (ctx) => {
if (ctx.url === "/" && ctx.method === "GET") {
// 显示表单界面
let html = `
AX Koa2 request post
`;
ctx.body = html;
} else if (ctx.url === "/" && ctx.method === "POST") {
let postData = await parsePostData(ctx);
ctx.body = postData;
} else {
ctx.body = "404!
";
}
});
function parsePostData(ctx) {
return new Promise((resolve, reject) => {
try {
let postData = "";
// 原生node设置数据监听
//当node.js后台收到post请求时,
//会以buffer的形式将数据缓存起来。
//Koa2中通过ctx.req.addListener('data', ...)这个方法监听这个buffer。
ctx.req.addListener("data", (data) => {
postData += data;
});
// buffer流结束触发
ctx.req.on("end", function () {
let parseData = parseQueryStr(postData);
resolve(parseData);
});
} catch (error) {
reject(error);
}
});
}
function parseQueryStr(queryStr) {
let queryData = {
};
let queryStrList = queryStr.split("&");
console.log(queryStrList);
console.log(queryStrList.entries());
for (let [index, queryStr] of queryStrList.entries()) {
let itemList = queryStr.split("=");
console.log(itemList);
// 原生方法decodeURIComponent
queryData[itemList[0]] = decodeURIComponent(itemList[1]);
}
return queryData;
}
app.listen(3000, () => {
console.log("server started at http:127.0.0.1:3000/");
});
首先我们可以通过原生手段,解析url从而来实现不同页面的分发:
准备三个html文件:
基本框架类似
const Koa = require("koa");
const fs = require("fs");
const app = new Koa();
async function render(page) {
return new Promise((resolve, reject) => {
let pageUrl = `./page/${
page}`;
// 使用fs,用utf-8指定读取编码
fs.readFile(pageUrl, "utf-8", (err, data) => {
if (err) {
reject(err);
} 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;
default:
break;
}
let html = await render(page);
return html;
}
app.use(async (ctx) => {
let url = ctx.request.url;
let html = await route(url);
ctx.body = html;
});
app.listen(3000);
显然这种方式比较缓慢,于是我们使用koa-router来实现路由的分发:
大致上分为以下步骤
导入koa-router
实例化koa-router
通过实例化对象的get/post/put/delete等方法(指定请求方式)来创建路由,第一个参数为路由(字符串),第二个参数为触发的中间件async函数。
挂载到实例化的app上,通过app.use
const Koa = require('koa')
const Router = require('koa-router')
const app = new Koa();
const router = new Router();
router
.get('/', (ctx, next) => {
ctx.body = 'hello ax';
})
.get('/todo', (ctx, next) => {
ctx.body = 'todo page';
})
// 确定路由请求
app
.use(router.routes())
.use(router.allowedMethods());
app.listen(3000, () => {
console.log("starting at port 3000")
})
通过koa-router实现多级路由:
添加层级:比如将/ax,/todo变为/home/ax,/home/todo
我们可以通过实例化router对象的时候,在参数中加入prefix
const Koa = require('koa')
const Router = require('koa-router')
const app = new Koa();
// 程序层级
const router = new Router(
{
// 前缀
prefix: '/ax'
}
);
router
.get('/', (ctx, next) => {
ctx.body = 'hello ax';
})
.get('/todo', (ctx, next) => {
ctx.body = 'todo page';
})
// 确定路由请求
app
.use(router.routes())
.use(router.allowedMethods());
app.listen(3000, () => {
console.log("starting at port 3000")
})
多级路由和多前缀实现
创建父路由并将子路由挂载到其下
const Koa = require('koa')
const Router = require('koa-router')
const app = new Koa();
// 子路由
let home = new Router();
home.get('/ax', async (ctx) => {
ctx.body = "home ax page";
})
.get('/todo', async (ctx) => {
ctx.body = "home toDo page";
})
let page = new Router();
page.get('/ax', async (ctx) => {
ctx.body = "page ax page";
})
.get('/todo', async (ctx) => {
ctx.body = "page toDo page";
})
// 父级路由
let router = new Router();
router.use('/home', home.routes(), home.allowedMethods());
router.use('/page', page.routes(), page.allowedMethods());
// 装载中间件
app
.use(router.routes())
.use(router.allowedMethods());
app.listen(3000)
我们可以通过koa-static第三方中间件实现静态资源的加载
使用步骤:
导入koa-static
作为中间件调用导入的函数并且将其挂载到示例(以要开辟的空间的路径使用
const Koa = require('koa')
const path = require('path')
// 导入koa-static
const static = require('koa-static')
const app = new Koa();
const staticPath = './static';
// 路径字符串作为初始化值
app.use(static(path.join(__dirname, staticPath)))
app.use(async (ctx) => {
console.log(path.join(__dirname, staticPath))
ctx.body = "hello world"
})
app.listen(3000)
到此koa的基本内容结束,关于cookies,ejs等其他前后不分离的开发形式请自行参考官方文档