3. Koa.js + mongodb

1,694 阅读6分钟

官网地址 >>

一、概述

koa 是由 Express 原班人马打造的,致力于成为一个更小、更富有表现力、更健壮的 Web 框架。使用 koa 编写 web 应用,通过组合不同的 generator,可以免除重复繁琐的回调函数嵌套,并极大地提升错误处理的效率。koa 不在内核方法中绑定任何中间件,它仅仅提供了一个轻量优雅的函数库,使得编写 Web 应用变得得心应手。

Koa 应用是一个包含一系列中间件 generator 函数的对象。 这些中间件函数基于 request 请求以一个类似于栈的结构组成并依次执行。

Koa 的核心设计思路是为中间件层提供高级语法糖封装,以增强其互用性和健壮性,并使得编写中间件变得相当有趣。

Koa 包含了像 content-negotiation(内容协商)、cache freshness(缓存刷新)、proxy support(代理支持)和 redirection(重定向)等常用任务方法。

二、下载使用

# NPM
$ npm install koa
# Yarn
$ yarn add koa

代码示例:

const Koa = require('koa');
const app = new Koa();
// 对于任何请求,app将调用该异步函数处理请
app.use(async ctx => {
  ctx.body = 'Hello World';
});

app.listen(3000);
console.log("Server running at http://127.0.0.1:3000");

浏览器访问:“http://127.0.0.1:3000”,页面出现“Hello world!”。

app.listen(...) 实际上是以下代码的语法糖:

const http = require('http');
const Koa = require('koa');
const app = new Koa();
http.createServer(app.callback()).listen(3000);

这意味着您可以同时支持 HTTPS 和 HTTPS,或者在多个端口监听同一个应用。

const http = require('http');
const https = require('https');
const Koa = require('koa');
const app = new Koa();
http.createServer(app.callback()).listen(3000);
https.createServer(app.callback()).listen(3001);

三、上下文

Koa Context 将 node 的 requestresponse 对象封装在一个单独的对象里面。context 在每个 request 请求中被创建,在中间件中作为接收器(receiver)来引用,或者通过 this 标识符来引用:

app.use(async ctx => {
  ctx; // is the Context
  ctx.request; // is a koa Request
  ctx.response; // is a koa Response
});

Request API >>

Response API >>

四、处理跨域

中间件 => koa2-cors

>> 下载:

# NPM
$ npm install koa2-cors
# YARN 
$ yarn add koa2-cors

>> 使用:

const cors = require('koa2-cors');
app.use(cors());

五、处理静态资源

中间件:koa-static

>> 下载:

# NPM
$ npm install koa-static
# YARN
$ yarn add koa-static

>> 使用:

// 1. 引入
const path = require('path');
const static = require('koa-static');
// 2. 使用
app.use(static(
    path.join(__dirname, "./www")
));
// 3. 浏览器访问
// => http://localhost:3000/images/koa.jpeg

六、处理路由

路由是由一个URI和一个特定的HTTP 方法(GET、POST 等)组成,涉及到应用如何响应客户端对某个网站节点的访问。通俗的讲 => 路由就是根据不同的URL 地址,加载不同的页面实现不同的功能。 Koa 中,我们使用 koa-router 实现路由配置。

1. 基础示例

# NPM
$ npm install koa-router
# YARN
$ yarn add koa-router

代码示例:

const Koa = require('koa');
const router = require('koa-router')(); // 注意:引入的方式
const app = new Koa();

router.get('/', (ctx, next) => {
	ctx.body = "Hello koa!";
})

router.get('/info', (ctx, next) => {
	ctx.body = JSON.stringify({
		name: "木子李",
		job: "前端工程师",
		email: "lihy_online@163.com",
		origin: "四川成都"
	});
});
app.use(router.routes()); 
app.use(router.allowedMethods());
/* 作用: 这是官方文档的推荐用法,我们可以看到router.allowedMethods()用在了路由匹配
router.routes()之后,目的在于:根据ctx.status 设置 response 响应头
*/
app.listen(3000, () => {
	console.log('server running at http://localhost:3000');
});

2. GET

在koa2 中GET 传值通过request 接收,但是接收的方法有两种:queryquerystring

  • query:返回的是格式化好的参数对象。
  • querystring:返回的是请求字符串。

代码示例:

router.get('/info', (ctx, next) => {
	let url = ctx.url;
	// 从request 中获取GET 请求
	let request = ctx.request;
	let req_query = request.query;
	let req_querystring = request.querystring;
	// 从context 中直接获取
	let ctx_query = ctx.query;
	let ctx_querystring = ctx.querystring;
	ctx.body = {
		url,
		req_query,
		req_querystring,
		ctx_query,
		ctx_querystring
	}
});

效果显示:

动态路由:

// 请求方式 => http://localhost/product/123
router.get('/product/:aid', async (ctx) => {
	console.log(ctx.params); // { aid: '123' } // 获取动态路由的数据
	ctx.body = '商品页面';
});

客户端代码:

<!DOCTYPE html>
<html lang="zh-CN">
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>	
		<img src="http://localhost:3000/images/koa.jpeg" >
		<script>
			// 1. 创建请求对象
			let xhr = new XMLHttpRequest();
			// 2. 配置请求
			xhr.open("POST", "http://127.0.0.1:3000/login", true);
			xhr.setRequestHeader("Content-Type", "application/json");
			xhr.responseType = "json";
			// 3. 发送请求
			xhr.send(JSON.stringify({
			    username: "admin",
			    password: "123"
			}));
			// 4. 监听请求
			xhr.onload = function() {
			    if(xhr.readyState == 4 && xhr.status == 200) {
			        console.log(xhr.response);
			    }else {
			        console.log("err");
			    }
			}
		</script>
	</body>
</html>

3. POST

中间件 => koa-bodyparser

>> 下载:

# NPM
$ npm install koa-bodyparser
# YARN
$ yarn add koa-bodyparser

>> 使用:

// 1. 引入
const bodyParser = require('koa-bodyparser');
// 2. 使用
app.use(bodyParser());
// 3. 读取
router.post('/login', async (ctx) => {
	console.log(ctx.request.body);
});

七、模块化配置

"dependencies": {
  "koa": "^2.11.0",
  "koa-bodyparser": "^4.2.1",
  "koa-router": "^7.4.0",
  "koa-static": "^5.0.0",
  "koa2-cors": "^2.0.6"
}

用户接口:./router/user.js

// => 导入路由模块
const router = require('koa-router')();
// => 处理路由
router.post('/login', (ctx, next) => {
    console.log(`「登录接口」 被调用!`)
    ctx.body = {
        code: 200,
        message: "用户登录!"
    };
    next();
});
router.post('/register', (ctx, next) => {
    console.log(`「注册接口」 被调用!`)
    ctx.body = {
        code: 200,
        message: "用户注册!"
    };
    next();
});
// => 导出路由配置
module.exports = router.routes();

订单接口:./router/orders.js

// => 导入路由模块
const router = require('koa-router')();
// => 处理路由
router.get('/', (ctx, next) => {
    console.log(`「查询订单接口」 被调用!`)
    ctx.body = {
        code: 200,
        message: "查询订单"
    };
    next();
});
router.get('/delete', (ctx, next) => {
    console.log(`「删除订单接口」 被调用!`)
    ctx.body = {
        code: 200,
        message: "删除订单"
    };
    next();
});
// => 导出路由配置
module.exports = router.routes();

合并接口:./router/index.js

const router = require('koa-router')();
const user = require('./user');
const orders = require('./orders');

router.use('/user', user);
router.use('/orders', orders);

module.exports = router;

调用接口:app.js

// => 导入模块
const Koa = require('koa'); 
const cors = require('koa2-cors'); 
const bodyParser = require('koa-bodyparser'); 
const path = require('path'); 
const static = require('koa-static');
const router = require('./router');
const app = new Koa();

// => 处理跨域
app.use(cors());
// => 解析POST参数
app.use(bodyParser());
// => 处理路由
app.use(router.routes()).use(router.allowedMethods());
// => 处理静态资源
app.use(static(
    path.join(__dirname, "./www")
))

// => 监听
app.listen(3000, () => {
	console.log('server running at http://localhost:3000');
});

页面部署参考 >>

八、访问数据库

Mongoose >>

MongoDB >>

访问数据库之前,首先创建数据库,然后在该数据库下创建对应的用户并设置权限。

1. Schema

数据库中的 Schema, 为数据库对象的集合。 schema 是 mongoose 里会用到的一种数据模式, 可以理解为表结构的定义; 每个 schema 会映射到 mongodb 中的一个 collection, 它不具备 操作数据库的能力。

const heroSchema = new mongoose.Schema({
	name: string,
	age: number
}, {
	collection: "heros"
});

2. Model

const HeroModel = mongoose.model("HeroModel", heroSchema, "heros");

3. 代码示例

> 建立连接

// 文件位置:./db/index.js
// => 导入模块
const mongoose = require('mongoose');
// => URI > mongodb://用户名:密码@主机:端口/数据库名称
const MONGODB_URI = "mongodb://root:123@localhost:27017/mongo";
// => 连接数据库
mongoose.connect(MONGODB_URI, { useNewUrlParser: true }, (err) => {
	if (err) {
		console.log('mongodb connect fail!');
		return;
	}
	console.log("mongodb connect success!")
});
module.exports = mongoose;

> 构建Model

// 文件位置:./models/hero.js
const mongoose = require("../db");
const heroSchema = new mongoose.Schema({
	name: {
		type: String,
		required: true,
		index: true,
		unique: true
	},
	age: {
		type: Number,
		required: true,
		min: 18,
		max: 65
	},
	tel: {
		type: String,
		required: true
	},
	gender: {
		type: String,
		default: "保密"
	}
}, {
	collection: "heros"
});

const Hero = mongoose.model("Hero", heroSchema, "heros");

module.exports = Hero;

提示:你可以根据此模式构建多个Model

> 操作数据

// 文件位置:app.js
const HeroModel = require('./models/heros');
// => 存储数据
let hero = new HeroModel({
	name: "木子李",
	age: 27,
	tel: "17398888669",
	gender: "男"
});
hero.save((err, res) => {
	if(err) {
		console.log(err);
		return;
	}
	console.log(res);
})

// => 查询数据
HeroModel.find((err, res) => {
	if(err) {
		console.log(err);
		return;
	}
	console.log(res);
});

// => 修改数据
HeroModel.updateOne({name: "木子李"}, {age: 30}, (err, res) => {
	if(err) {
		console.log(err);
		return;
	}
	console.log("修改成功!");
});

// => 删除数据
HeroModel.deleteOne({name: "木子李"}, (err, res) => {
	if(err) {
		console.log(err);
		return;
	}
	console.log("删除成功!");
})