认识Koa,实现一个简单的Web服务(入门向、实现向)
一、简介
Koa是基于nodejs的一款Web框架,对于Web应用及api的开发能够提供更为小巧精简的服务形式。Koa通过ES7的async/await语法来替代回调处理,有效避免了上一代Express框架中回调地狱的发生;再者,其独特的中间件流程控制机制(洋葱模型)也确保了中间件定制时的操控粒度与执行时的清晰顺序…
二、实现
话不多说,下面我们开始用Koa实现一个简单的Web服务:
注:Koa 依赖 node v7.6.0 或 ES2015及更高版本和 async 方法支持.
(一)、项目新建与初始化
建立项目文件夹后,先对其进行初始化(意在让其成为“项目”而非仅仅是个文件夹):
npm init
过程中会询问如下信息项:
package name: (koa-hello) // 项目名称,默认为项目文件夹名
version: (1.0.0) // 项目版本,默认从 1.0.0 开始
description: koa demo // 项目描述
entry point: (index.js) // 入口文件,默认为index.js
test command: node app.js // 项目测试(启动)命令,下面将以app.js作为服务程序主文件,所以此处规定为 node app.js
git repository: // git仓库,暂无
keywords: koa // 关键字
author: morilence // 作者
license: (ISC) // 证书,默认为ISC
完成后会创建 package.json 文件来标识你本次的项目(可自行修改),内容如下:
{
"name": "koa-hello",
"version": "1.0.0",
"description": "koa demo",
"main": "index.js",
"scripts": {
"test": "node app.js"
},
"keywords": [
"koa"
],
"author": "morilence",
"license": "ISC",
}
(此部分可先略过)设有团队协作需求,应该再建立git仓库:
git init
并建立 .gitignore 文件来避免不必要的文件(夹)被组织进仓库,内容如下:
.DS_Store
# 此处重在防止将node_modules(以后会越来越大)和public(之后要建立的用于放置静态资源的文件夹)文件夹加进仓库
node_modules
public
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
(二)、安装Koa
npm i koa
安装后会发现项目文件夹下多出如下文件(夹):
|——node_modules/
|——package-lock.json
其中 node_modules/ 用来放置所有安装项(比如刚才安装的Koa);而 package-lock.json 则是用来记录各安装项安装时的来源与版本号(切忌手动改动)。
同时, package.json 文件也已发生变动,多出下面一项,:
"dependencies": {
"koa": "^2.11.0"
}
意即Koa安装成功,并添至生产环境(dependencies),当然对应的还有开发环境(devDependencies),二者详细此处就不再作说明。
(三)、服务初探
在项目根目录下建立app.js:
const http = require('http'); // 引入node内置的http模块
const Koa = require('koa'); // 引入Koa
/* 创建Koa应用实例 */
const app = new Koa();
/* 创建挂载Koa应用程序的http服务 */
const server = http.createServer(app.callback());
/* 开始监听/启动服务(指定3000端口与成功回调) */
server.listen(3000, () => {
console.log('listening on port 3000 ...');
});
其实至此,我们就已真真切切地写好了一个Web服务,接下来我们跑一跑,在项目根目录下的命令行中输入:
node app.js
或者输入:
npm test
二者都可以启动服务(想必到这你也能明白步骤(一)中指定 test command 的作用了),启动成功即可看到命令行中显示的 “listening on port 3000 …”
然而上述写法并不常用,在此贴出是为方便初学node的小伙伴进行理解,下面给出官网标准的等效写法(实质上是个语法糖):
const Koa = require('koa'); // 引入Koa
/* 创建Koa应用实例 */
const app = new Koa();
/* 开始监听/启动服务(指定3000端口与成功回调) */
app.listen(3000, () => {
console.log('listening on port 3000 ...');
});
(四)、添加路由,响应请求
若是仅仅将服务应用实现到这就结束了,肯定有好多小伙伴大为不爽,下面我们将进一步拓展服务至能够响应http请求以及解析静态资源。此处之所以称之为“拓展”,是因为Koa本身是不具有这些能力的,就像官网所说的那样,Koa没有捆绑任何中间件(在Koa中,中间件本质上就是个函数,一个在两个流程状态点间完成某项职能的函数),可以理解为一个十分简洁但拓展性强的“插槽”,所以我们要增加Koa的服务能力,就要为它逐步应用功能专一的中间件。
安装路由中间件——koa-router
npm i koa-router -S
此处安装koa-router的时候后面加上了"-S"(也可写作"–save"),意即指定将该安装项添至生产环境(dependencies)中:
"dependencies": {
"koa": "^2.11.0",
"koa-router": "^8.0.7"
}
使用koa-router中间件
const Koa = require('koa');
const Router = require('koa-router'); // 引入koa-router
const app = new Koa();
/* 创建路由实例 */
const router = new Router();
/* 制定路由规则 */
router
.get('/', ctx => {
/* ctx.body用来返回响应信息 */
ctx.body = "Welcome to here!";
})
/* 可链式调用 */
.get('/api', ctx => {
/* ctx.type用来指定返回的数据格式,默认为 text/plain */
ctx.type = 'json';
ctx.body = {
msg: "hello"
}
});
app
/* 在Koa应用实例上应用制定好的路由规则 */
.use(router.routes())
.use(router.allowedMethods())
app.listen(3000, () => {
console.log('listening on port 3000 ...');
});
代码中的 get() 函数用来响应http中的get请求(当然亦有post、put、delete等其他请求的响应函数),其第一个参数接收请求路由,最后接受的函数即是一个中间件(路由级别的中间件),进行 Request 到 Response 间的处理。
运行检验:
设服务运行在本地,启动成功后,打开浏览器访问 http://127.0.0.1:3000 ,若页面显示 “Welcome to here!” 即说明第一个路由应用成功;
再访问 http://127.0.0.1:3000/api ,若(在json格式数据浏览插件的帮助下)看到指定的json数据,就说明第二个路由也应用成功啦!
(五)、静态资源解析
同理,我们需要一个可实现解析静态资源的中间件——koa-static
npm i koa-static -S
应用之前,我们还要先建立用来存放静态资源的文件夹,在这里我命名为 public ,现在的项目目录为:
|——node_modules/
|——public/
|——.gitignore
|——app.js
|——package-lock.json
|——package.json
然后我们开始应用koa-static:
const path = require('path'); // 引入node内置的path模块
const Koa = require('koa');
const Router = require('koa-router');
const static = require('koa-static'); // 引入koa-static
const app = new Koa();
const router = new Router();
router
.get('/', ctx => {
ctx.body = "Welcome to here!";
})
.get('/api', ctx => {
ctx.type = 'json';
ctx.body = {
msg: "hello"
}
});
app
.use(router.routes())
.use(router.allowedMethods())
/* 指定一个目录作为静态资源的根目录(亦即站点根目录) */
.use(static(path.join(__dirname, './public')))
app.listen(3000, () => {
console.log('listening on port 3000 ...');
});
代码中的path用于拼接路径(path详细便不再赘述)。之后我们在public文件夹中放上一些静态文件(图片、字体、html、css、js等),这里就假设在 public 中放入图片 happy.jpg ,若在本地启动服务后访问 http://127.0.0.1:3000/happy.jpg 可以加载出 happy.jpg 图片的画面,则证明静态资源解析中间件 koa-static 应用成功!
(六)、整合应用
好啦,现在我们就可以用这俩中间件来让我们的Koa服务提供简单页面的展示了,先在public中放入你的前端项目,这里我就假设出如下目录结构(public文件夹):
|——public/
|———assets/
|————…
|———index.html
|———index.css
|———index.js
稍微改写一下服务代码:
const fs = require('fs'); // 引入node内置的fs模块
const path = require('path');
const Koa = require('koa');
const Router = require('koa-router');
const static = require('koa-static');
const app = new Koa();
const router = new Router();
router
.get('/', ctx => {
/* 指定返回的数据格式为 text/html */
ctx.type = 'html';
/* fs创建文件读取流,将页面文件的内容返回 */
ctx.body = fs.createReadStream('./public/index.html');
})
app
.use(router.routes())
.use(router.allowedMethods())
.use(static(path.join(__dirname, './public')))
app.listen(3000, () => {
console.log('listening on port 3000 ...');
});
检验运行:
本地服务启动成功后,访问 http://127.0.0.1:3000 ,若呈现出public中的 index.html 页面即表成功。最后再总结地啰嗦一下流程:koa-router找到匹配的路由规则来响应http请求(此处为"/",即网站根目录),通过fs模块读取 index.html 的文件内容并以"text/html"的数据格式返回给客户端解析,而 index.css、index.js 等其他引用的静态资源的解析则靠的是koa-static中间件。至此,一个简单的Web服务就被我们实现啦。
三、结语
首先,本文成为博主记录自己Koa学习历程的开篇之作啦!🎉🎉
其次如题,本文偏入门,目的也是带领新入node和Koa的朋友们去实现一个简单的Web服务,因此省略掉了一些关于Koa原理机制与示例代码实现细节的讲解。
最后,我会把自己学到的Koa相关的知识、技巧与踩坑逐一分享给大家!