# 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使用