# koa-node开发实战
**Repository Path**: javafdx/koa-node-development-practice
## Basic Information
- **Project Name**: koa-node开发实战
- **Description**: No description available
- **Primary Language**: NodeJS
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2021-06-10
- **Last Updated**: 2021-06-26
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
源码地址
https://github.com/ikcamp
### koa-bodyparser中间件(解析post请求体参数)
npm install koa-bodyparser --save
const bodyParser = require('koa-bodyparser');
app.use(bodyParser()); // 加载koa-bodyparser中间件
解释: 把POST请求的参数解析到ctx.request.body中
当POST请求时,中间件koa-bodyparser解析post表单里的数据
{“key”:"value",“key1”:"value1"}
模拟post请求
curl -d 'userName=4' http://127.0.0.1:3000
否则就要是用原生Node自己封装解析的方法
exports.getPostData=function(ctx){
return new Promise((resolve,reject)=>{
try {
let params = '';
ctx.req.on('data', (chunk) => {
params += chunk;
})
ctx.req.on('end', (chunk) => {
resolve(params)
})
} catch (error) {
console.log("获取post提交的数据错误")
reject(error)
}
})
}
### koa-router中间件
npm install --save koa-router
替换:
app.use(async (ctx) => {
if(ctx.url === '/' && ctx.method === 'GET'){}
else if (ctx.url === '/' && ctx.method === 'POST'){}
})
用法:
const Router = require('koa-router');
const router = new Router(); // 初始化koa-router中间件
app
.use(router.routes())
.use(router.allowedMethods()); // 对异常状态码的处理
router.get('/', (cyx, next) => {})
router.post('/', (ctx, next) => {})
### koa-static中间件与koa-views中间件
koa-static是专门用于加载静态资源的中间件,通过它可以请求加载CSS,JavaScript等静态资源;
koa-views用于加载HTML模板文件
依赖:
npm i koa-static -S
npm i koa-views --save
替换:
ctx.type = 'html';
let html = `
登录
`
ctx.body = html;
改写:
const views = require('koa-views');
const static = require('koa-static');
const path = require('path');
app.use(views(__dirname + '/views', { // 加载模板引擎
map: { html: 'ejs'}
}))
app.use(staic( // 加载静态资源
path.join(__dirname, '/static')
))
router.get('/', (ctx, next) => {
await ctx.render('index') // 渲染模板
})
// all()方法一般设置请求头,如设置过期时间,cors等(都会执行这个)
router.all('/*', async (ctx, next) => {
// 符号*代表允许来自所有域名的请求
ctx.set("Access-Control-Allow-Origin", "https://www.cctalk.com");
await next();
})
//命名路由
// 设置此路由的名称为user
router.get('user', '/users/:id', function (ctx, next) {
});
// 路由前缀
let router = new Router({
prefix: '/users'
})
### 接口控制
token验证,权限控制
npm install jsonwebtoken --save
npm install koa-jwt
### HTTP
querystring模块
由Node.js原生提供,包含解析和格式化工具
const querystring = require('querystring');
#### 1.escape,对传入的 字符串进行编码:
querystring.escape("id=1") // 返回id%3D1
该方法类似于浏览器中window对象的encodeURIComponent方法;
#### 2.unescape,解码
querystring.unescape("id%3D1"); // 返回id=1
该方法类似于window对象的decodeURIComponent
#### 3.parse,将传入的字符串反序列化为对象
querystring.parse("type=1&status=0"); // 返回{ type:'1', status: '0' }
#### 4.stringify,将传入的对象序列化为对象
querystring.stringify({ type:'1', status: '0' });
### koa-router中的querystring(接收参数,用于解析与格式化url)
属性:query(返回对象) querystring(返回查询字符串)
router.get('/.home', async(ctx, next) => {
console.log(ctx.request.query);
console.log(ctx.request.querystring);
})
请求:
http://localhost:3000/home?id=12&name=kk;
打印:
{ id: '12', name: 'kk'}
id=12&name=kk
特殊场景(常会用到)参数放在url中:
http://localhost:3000/home/12/kk
router.get('/home/:id/:name', async (ctx, next) => {
console.log(ctx.params);
})
打印:
{ id: '12', name: 'kk'}
### 分离router
新建router.js
` const router = require('koa-router')();
module.exports = (app) => {
app.use(router.routes())
.use(router.allowedMethods());
router.get('/', async(ctx, next) => {
})
} `
在app.js中引入
const router = require('./router');
router(app);
### 分离controller(对router.js再次优化)
新建controller--home.js文件
module.exports = {
index: async (ctx, next) => {},
home: async (ctx, next) => {},
homeParams: async (ctx, next) => {},
user: async (ctx, next) => {},
login: async (ctx, next) => {},
}
在router.js中引入controller/home.js
const HomeController = require('./controller/home');
router.get('/', HomeController.index);
router.get('/home', HomeController.home);
router.get('/home/:id/:name', HomeController.homeParams);
router.get('/user', HomeController.user);
router.post('/user/login', HomeController.login);
### 分离Service(需要进行数据访问层的操作,数据库,对controller优化)
新建service--home.js
module.exports = {
login: async (name, pwd) => {
let data;
if (name == 'ikcamp' && pwd == '123456') {
data = `Hello, ${name}!`;
} else {
data = '账号信息错误';
}
return data;
}
}
在controller/home.js中引入
const HomeService = require('../service/home');
login: async (ctx, next) => {
let { name, password } = ctx.request.body;
let data = await HomeService.login(name, password);
ctx.response.body = data;
},
### 模板引擎
Nunjucks
1.文件扩展名:.njk
2.变量:{{username}}
3.注释 {#666#}
3.标签
{% if vaariable %} kk {% endif %}
{% for item in items %} {{item.title}} {% else %} ll {% endfor %}
引入依赖:
npm install koa-nunjucks-2 --save
新建views(存放视图)
在app.js中引入:
const nunjucks a= require('koa-nunjucks-2');
app.use(nunjucks({
ext: 'html', // 指定视图文件默认后缀
path: path.join(__dirname, 'views'), // 指定视图目录
nunjucksConfig: {
trimBlocks: true // 开启转义,防止xss攻击
}
}));
新建views--home--login.html
改写controller--home.js
login: async (ctx, next) => {
await ctx.render('home/login', {
btnName: '点击登录'
})
}
### koa-static(处理静态资源)
npm install koa-static --save
演示:
app.use(require('koa-static'(root,opts)))
root:指定静态资源的相对路径
opts:配置
maxage: 最大缓存时长(毫秒),默认0,不启用缓存
hidden: 是否允许传输隐藏的文件,默认false
index: 默认的文件名,index.html
defer: 是否延迟响应
gzip: 支持压缩文件
setHeaders: 设置请求头函数
extensions: 匹配资源
使用:
新建pubilc(存放就是,css,图片和文字)
配置app.js
const staticFiles = require('koa-static');
app.use(staticFiles(path.resolve(__dirname, "./public"), { // 指定静态资源目录
maxage: 30 *24 *60 *60 *1000 // 指定缓存时长
}));
增加样式文件public/home/main.css
增加公用视图layout.html
### 集中处理中间件
新建middleware--mi-send--index.js(自定义设置请求头中间件)
新建middleware--index.js(集中处理中间件)
### 文件上传
npm install koa-multer --save
app.use(multer({dest: '路径'}))
dest属性:用来设置上传文件的存储地址,如果省略,上传文件将保存在内存中,不会写入磁盘。
1.upload.sing(fieldname)
接收一个以fieldname参数命名的问阿金,文件信息存储在req.files
2.upload.array(fieldname[,maxCount])
限制上传的最大数量
3.upload.fields(fields)
街火速指定fields的混合文件
4.upload.none()
5.upload.and()
接收所有类型。
### 数据库(Sequelize)
在node.js中,一般采用Sequelize这个ORM类库来操作数据库。Sequelize支持多数据库。
如:PostgreSQL,MySQL,SQLite
依赖:
npm install sequelize --save
const Sequelize = require('sequelize');
const sequelize = new Sequelize('databaseName', 'userName', 'password', {
host: 'localhost', // 数据库服务地址
dialect:'mysql' // SQL语言类型
});
sequelize.authenticate().then(()=>{
console.log('数据库已连接');
}).catch(err=>{
console.error('数据库连接失败');
})
定义模型:
const Project = sequelize.define('project', { // 定义Project模型
name: { // 定义name字段
type: Sequelize.STRING, // 定义类型为String
allowNull: false, // 不能为空
unique: true // 必须唯一,不允许重复
},
date: {
type: Sequelizee.DATE,
defaultValue: Sequelize.NOW, // 默认值为当前时间
}
})
配置数据表:
Sequelize会默认创建'createdAt'和'updatedAt'字段;
const Product = sequelize.define('product', {
name: Sequelize.STRING
},{
timestamps: false, // 禁止创建CreateAt和UpdateAt字段
updatedAt: 'ipdateTimestamp', // 创建updateTimestamp字段,替代UpdateAt字段
tableName: 'my_product' // 修改创建的数据表名称为my_product
})
定义Getter和Setter
const Custom = sequelize.define('custom', {
name: {
type: Sequelize.STRING,
get () { // 定Getter,可以自定义获取数据
const title = this.getDateValue('title');
return `${this.getDataValue('name')} (${title})`
}
},
title: {
title: Sequelize.STRING,
set (val) { // 定义Setter,可以在写入数据前处理数据
this.setDataValue('title', val.toUpperCase())
}
}
})
### 将定义的模型同步到数据库
sequelize.sync().then(()=>{ // 同步全部模型
//done
}).catch(error=>{
// some error thrown
})
Product.sync(); // 同步单个数据表
Project.sync({ force: true }); // 强制同步,当数据库中已经处在表时,清除已经存在的表
### 查询数据
await Product.findAll();
查询某些字段(attributes)
await Project.findAll({ // 查询name和date字段
attributes: ['name', 'date']
})
通过where查询(查询字段name以t开头的所有记录)
const { Op } = require('sequelize'); // 引入操作枚举Op
await Project.findAll({
where: { // 通过where配置查询条件
name: { // 根据name字段查询
[Op.like]: 't%' // 查询操作为like,值为't'
}
}
})
### 多条件查询
AND关系:
await Project.findAll({
where: { // 通过where配置查询条件
createAt: {
[Op.lt]: new Date(),
[Op.gt]: new Date(new Date() - 24 * 60 * 60 * 1000)
}
}
})
OR关系:
await Project.findAll({
where: { // 通过where配置查询条件
[Op.or]: {
name: {
[Op.eq]: 'test'
},
createdAt: {
[Op.gte]: new Date(new Date() - 24 * 60 * 60 * 1000)
}
}
}
})
### 使用RESTful数据库接口
const { getAllCustomers, getCustomerById, getCustomerByName } = require(./db)
router.get('/customer', async (context) => {
// 查询所有数据
const customers = await getAllCustomers();
})
### 安装MongoDB
### 建立连接
const mongoose = require('mongoose')
mongoose.connect('mongodb://localhost/test', {
user: 'username',
pase: 'password',
poolSize: 10 // 数据库连接池大小
})
const db = mongoose.connection; // 获取连接对象
db.on('error', err => {
// 连接失败
console.error(err)
})
db.on('open', () => {
// 连接成功
console.log('mongodb连接成功')
})
### 定义数据模型
const categorySchema = new mongoose.Schema({
// 创建一个Schema
name: String, // 简单描述类型
description: String,
createdAt: { // 描述复杂定义
type: Date, // 定义类型
default: Date.now // 定义默认值
}
})
### 操作数据
// 得到模型
const Category = mongoose.model('Category', categorySchema)
//实例化一个新的对象来新增数据
const category = new Category({
name: 'test',
description: 'test category'
})
// 通过save方法,保存对象到数据库中
category.save(error => {
// 保存失败的错误
if(error) {
console.error(error)
return;
}
console.log('保存成功')
})
// 也可以直接通过模型的create方法新增数据
Category.create({
name: 'test',
description: 'test category'
}, (error, category) => {
// 在回调中处理操作结果
if(error) {
console.error(error)
} else {
console.log(category) // 输出新增的对象
}
})
// 查询
Category.find({
name: 'test',
},(err, res) => {
if(err){
} else {
console.log(res) // 输出查询结果
}
})
Category.find({
name: '^t' // 正则表达式,模糊查询
}).then(res => {
}).catch(err => {
})
Category.where('createdAt') // 通过where方法对指定字段查询
.lt(new Date()) // 通过lt对where指定的字段继续限定查询条件
.select('name, description') // 指定查询输出结果的字段
.sort({createdAt:1}) // 指定排序规则
.limit(10) // 限定查询10条数据
.exec((err, res) => { // 执行查询表
})
// 删除数据
Category.remove({
name: 'test' // 删除数据的条件
}).then(() => {
})
Category.update({ // 更新数据
name: 'test' // 筛选出需要的数据
}, {
name: 'test1', // 更新的数据
description: 'test1'
}).then(() => {
})
### Redis使用