Koa 是一个新的 web 框架,由 Express 幕后的原班人马打造, 致力于成为 web 应用和 API 开发领域中的一个更小、更富有表现力、更健壮的基石。 通过利用 async 函数,Koa 帮你丢弃回调函数,并有力地增强错误处理。 Koa 并没有捆绑任何中间件, 而是提供了一套优雅的方法,帮助您快速而愉快地编写服务端应用程序。
Koa 依赖 node v7.6.0 或 ES2015及更高版本和 async 方法支持.
你可以使用自己喜欢的版本管理器快速安装支持的 node 版本:
$ nvm install 7
$ npm i koa
只要三行代码,就可以用 Koa 架设一个 HTTP 服务。
const Koa = require('koa');
const app = new Koa();
app.listen(3000);
Koa 提供一个 Context 对象,表示一次对话的上下文(包括 HTTP 请求和 HTTP 回复)。通过加工这个对象,就可以控制返回给用户的内容。
Context.response.body
属性就是发送给用户的内容。
const Koa = require('koa');
const app = new Koa();
const main = ctx => {
ctx.response.body = 'Hello World';
};
app.use(main);
app.listen(3000);
上面代码中,main
函数用来设置ctx.response.body
。然后,使用app.use
方法加载main
函数。
你可能已经猜到了,ctx.response
代表 HTTP Response。同样地,ctx.request
代表 HTTP Request。
Koa 默认的返回类型是text/plain
,如果想返回其他类型的内容,可以先用ctx.request.accepts
判断一下,客户端希望接受什么数据(根据 HTTP Request 的Accept
字段),然后使用ctx.response.type
指定返回类型。
// demos/03.js
const main = ctx => {
if (ctx.request.accepts('xml')) {
ctx.response.type = 'xml';
ctx.response.body = '<data>Hello World</data>';
} else if (ctx.request.accepts('json')) {
ctx.response.type = 'json';
ctx.response.body = {
data: 'Hello World' };
} else if (ctx.request.accepts('html')) {
ctx.response.type = 'html';
ctx.response.body = '<p>Hello World</p>';
} else {
ctx.response.type = 'text';
ctx.response.body = 'Hello World';
}
};
实际开发中,返回给用户的网页往往都写成模板文件。我们可以让 Koa 先读取模板文件,然后将这个模板返回给用户。
const Koa = require('koa');
const app = new Koa();
const fs = require('fs');
const path = require('path');
const main = (ctx) => {
ctx.response.type = 'html';
ctx.response.body = fs.createReadStream(path.resolve(path.join(__dirname, './demo.html')));
}
app.use(main);
app.listen(3000);
网站一般都有多个页面。通过ctx.request.path
可以获取用户请求的路径,由此实现简单的路由。
const main = ctx => {
if (ctx.request.path !== '/') {
ctx.response.type = 'html';
ctx.response.body = '<a href="/">Index Page</a>';
} else {
ctx.response.body = 'Hello World';
}
};
原生路由用起来不太方便,我们可以使用封装好的koa-route
模块。
const route = require('koa-route');
const about = ctx => {
ctx.response.type = 'html';
ctx.response.body = '<a href="/">Index Page</a>';
};
const main = ctx => {
ctx.response.body = 'Hello World';
};
app.use(route.get('/', main));
app.use(route.get('/about', about));
如果网站提供静态资源(图片、字体、样式表、脚本…),为它们一个个写路由就很麻烦,也没必要。koa-static
模块封装了这部分的请求。
const path = require('path');
const serve = require('koa-static');
const main = serve(path.join(__dirname));
app.use(main);
有些场合,服务器需要重定向(redirect)访问请求。比如,用户登陆以后,将他重定向到登陆前的页面。ctx.response.redirect()
方法可以发出一个302跳转,将用户导向另一个路由。
const redirect = ctx => {
ctx.response.redirect('/');
ctx.response.body = '<a href="/">Index Page</a>';
};
app.use(route.get('/redirect', redirect));
Koa 的最大特色,也是最重要的一个设计,就是中间件(middleware)。为了理解中间件,我们先看一下 Logger (打印日志)功能的实现。
最简单的写法就是在main
函数里面增加一行。
const main = ctx => {
console.log(`${
Date.now()} ${
ctx.request.method} ${
ctx.request.url}`);
ctx.response.body = 'Hello World';
};
上一个例子里面的 Logger 功能,可以拆分成一个独立函数。
const logger = (ctx, next) => {
console.log(`${
Date.now()} ${
ctx.request.method} ${
ctx.request.url}`);
next();
}
app.use(logger);
const one = (ctx, next) => {
console.log('>> one');
next();
console.log('<< one');
}
const two = (ctx, next) => {
console.log('>> two');
next();
console.log('<< two');
}
const three = (ctx, next) => {
console.log('>> three');
next();
console.log('<< three');
}
app.use(one);
app.use(two);
app.use(three);
结果如下:
>> one
>> two
>> three
<< three
<< two
<< one
洋葱模型
其实就是想向我们表达,调用next的时候,中间件的代码执行顺序是什么。
[外链图片转存中…(img-yd8D74Df-1567503613036)]
迄今为止,所有例子的中间件都是同步的,不包含异步操作。如果有异步操作(比如读取数据库),中间件就必须写成 async 函数。请看
const fs = require('fs.promised');
const Koa = require('koa');
const app = new Koa();
const main = async function (ctx, next) {
ctx.response.type = 'html';
ctx.response.body = await fs.readFile('./demos/template.html', 'utf8');
};
app.use(main);
app.listen(3000);
koa-compose
模块可以将多个中间件合成为一个。
const compose = require('koa-compose');
const logger = (ctx, next) => {
console.log(`${
Date.now()} ${
ctx.request.method} ${
ctx.request.url}`);
next();
}
const main = ctx => {
ctx.response.body = 'Hello World';
};
const middlewares = compose([logger, main]);
app.use(middlewares);
var greeting = (firstName, lastName) => 'hello, ' + firstName + ' ' + lastName
var toUpper = str => str.toUpperCase()
var fn = compose(toUpper, greeting)
console.log(fn('jack', 'smith')) // Hello Jack Smith
compose
的参数是函数,返回的也是一个函数多元
的,而其他函数的接受值是一元
的compsoe
函数可以接受任意的参数,所有的参数都是函数,且执行方向是自右向左
的,初始函数一定放到参数的最右面
参考链接: https://segmentfault.com/a/1190000008394749
下面是常见的HTTP状态码:
[外链图片转存中…(img-Y0cBAfPt-1567503613043)]
如果代码运行过程中发生错误,我们需要把错误信息返回给用户。HTTP 协定约定这时要返回500状态码。Koa 提供了ctx.throw()
方法,用来抛出错误,ctx.throw(500)
就是抛出500错误。
const main = ctx => {
ctx.throw(500);
};
const main = ctx => {
ctx.response.status = 404;
ctx.response.body = 'Page Not Found';
};
为了方便处理错误,最好使用try...catch
将其捕获。但是,为每个中间件都写try...catch
太麻烦,我们可以让最外层的中间件,负责所有中间件的错误处理。
const handler = async (ctx, next) => {
try {
await next();
} catch (err) {
ctx.response.status = err.statusCode || err.status || 500;
ctx.response.body = {
message: err.message
};
}
};
const main = ctx => {
ctx.throw(500);
};
app.use(handler);
app.use(main);
运行过程中一旦出错,Koa 会触发一个error
事件。监听这个事件,也可以处理错误。
const main = ctx => {
ctx.throw(500);
};
app.on('error', (err, ctx) =>
console.error('server error', err);
);
需要注意的是,如果错误被try...catch
捕获,就不会触发error
事件。这时,必须调用ctx.app.emit()
,手动释放error
事件,才能让监听函数生效。
const handler = async (ctx, next) => {
try {
await next();
} catch (err) {
ctx.response.status = err.statusCode || err.status || 500;
ctx.response.type = 'html';
ctx.response.body = '<p>Something wrong, please contact administrator.</p>';
ctx.app.emit('error', err, ctx);
}
};
const main = ctx => {
ctx.throw(500);
};
app.on('error', function(err) {
console.log('logging error ', err.message);
console.log(err);
});
说明app是继承自nodejs的EventEmitter对象。
参考链接:https://www.runoob.com/nodejs/nodejs-event.html
Web 应用离不开处理表单。本质上,表单就是 POST 方法发送到服务器的键值对。koa-body
模块可以用来从 POST 请求的数据体里面提取键值对。
var koa = require('koa');
var app = new koa();
// var route = require('koa-route');
const main = (ctx) => {
var dataArr = [];
ctx.req.addListener('data', (data) => {
dataArr.push(data);
});
ctx.req.addListener('end', () => {
// console.log(jsonBodyparser(str));
let data = Buffer.concat(dataArr).toString();
console.log(data)
});
ctx.response.body = 'hello world';
}
app.use(route.post('/', main)); // 1. 路径 2. ctx函数
app.listen(3000); // 起服务 , 监听3000端口
koa-body
模块还可以用来处理文件上传。
const os = require('os');
const path = require('path');
const koaBody = require('koa-body');
var route = require('koa-route');
var koa = require('koa');
var app = new koa();
var fs = require('fs');
const main = async function(ctx) {
const tmpdir = os.tmpdir();
const filePaths = [];
const files = ctx.request.files || {
};
for (let key in files) {
const file = files[key];
const filePath = path.join(tmpdir, file.name);
const reader = fs.createReadStream(file.path);
const writer = fs.createWriteStream(filePath);
reader.pipe(writer);
filePaths.push(filePath);
}
// console.log('xxxxxxxx', filePaths)
ctx.body = filePaths;
};
app.use(koaBody({
multipart: true })); // 代表我们上传的是文件
app.use(route.post('/upload', main));
app.listen(3000); // 起服务 , 监听3000端口
最后推荐一个 js常用的utils合集,帮我点个star吧~
证明最大匹配数==最小覆盖点数点击打开链接#include&lt;bits/stdc++.h&gt;using namespace std;typedef long long ll;const int mx = 1e2 + 10;int n,m,xi[mx*mx],yi[mx*mx],cp[mx];bool vis[mx][mx],vic[mx];bool pipei(int x){...
FlashFXP v3.6.0 Build 1240 汉化版 By 烈火http://www.crsky.com/soft/1111.html CuteFTP Pro v8.3.2 Build 09.02.2008.1 汉化版h...
就同一个Service类中,一个方法调用另外一个有事务的方法的思考写这篇博客的缘由:我在link.评论过,有人说我杠,这里就写篇实际操作的博客证明下我的评论现在库里的数据如下图所示:现在我在同一个Service中一个事务的方法调用另外一个事务方法:目的是 将id为1与2 的记录中的name属性值Jack与Rose 分别改为Tom与Jerry如下图代码所示:package com.wqf.service.flowable.impl;import com.wqf.dao.flowable
./configure \--prefix=/usr/local/ffmpeg-3520D \--cross-prefix=/opt/hisi-linux-nptl/arm-hisiv100-linux/target/bin/ \--cc=/opt/hisi-linux-nptl/arm-hisiv100-linux/target/bin/arm-hisiv100nptl-linux-gcc \-...
上篇文章《python+selenium+pytesseract识别图片验证码》对验证码进行识别后,应用到具体实例过程中,发现该算法识别可能会出现识别错误或者没有输出结果的情况,所以在具体应用时需要对验证码输入代码进行优化。一、剪切验证码图片将验证码剪切部分提出成一个单独的方法,方便调用test_cut.pyfrom io import BytesIOfrom test_ceis.testBefore.testDriver import driverfrom PIL import Image
递归算法将待排元素分成大小大致相同的两个子集合,分别对这两个集合进行排序,最终将排好序的子集合合并。#include&lt;iostream&gt; #include&lt;cstdio&gt; void Merge(int s[],int t[],int b,int m,int e){//将s[b...m]与s[m+1...e]合并 int i=b;//i-&gt;前一...
#ifndef __iom16v_h#define __iom16v_h#define uchar unsigned char#define uint unsigned int#ifndef BIT#define BIT(x) (1 #endif /* ATmega16 header file for * ImageCraft ICCAVR compi
20个强大的CSS3文字效果展示8月 19, 2015评论 (5)Sponsor今天为大家分享20个强大的CSS3文字效果,这些文字效果都不是用PS或是AI等设计软件制作出来,全部用代码实现,只有你想不到,没有做不到!来一起看看这些优秀的CSS3文字案例,可以作为灵感参考,也可点进去学习这些代码的实现方法。注:如果用RSS阅读的用户,请返回设计达人网查看原文才能看到效果哦!多款CSS文字HOVER...
在写代码时我们常常会遇到"……is not a function"的问题,这样的问题往往是因为使用该方法需要的js包未引入成功,最简单的检验js是否导入成功的方法是打开浏览器开发者工具的Source栏目中查找js是否存在,如果存在则说明导入成功。但有时检查到该目录下存在我们所需的js包,但却仍然会报is not a function的问题,这个时候需要检查下自己的jsp页面中是否使用了&l...
什么是图结构图(Graph)结构也是一种非线性数据结构,并且每个数据元素之间可以任意关联。正是任意关联性,导致了图结构中数据关系的复杂性。一个典型的图结构包括如下两个部分:顶点(Vertex):图中的数据元素。 边(Edge):图中连接这些顶点的线。无向图:如果一个图结构中所有的边都没有方向性,这称为无向图。有向图:如果一个图结构,边是有方向性的,这称为有向图。权:...
看了王泽宾博客的一篇文章(http://blog.csdn.net/wanghao72214/archive/2009/02/13/3888090.aspx),故事虽小,很有寓意,以下是转载全文,共享之: 联合利华引进了一条香皂包装生产线,结果发现这条生产线有个缺陷:常常会有盒子里没装入香皂。总不能把空盒子卖给顾客啊,他们只好请了一个学自动化的博士后设计一个方案来分拣空的香皂
Attribute Description ...