Koa2 from pit to abandon


For what it’s worth, the original Express team was built smaller, more robust, and more expressive

Has been very want to study koA2, recently empty, plus their own time, finally into the pit KOA2. Having had some experience with Express before, I developed some things on the back end. So THINK KOA is still very good to use, but use it found meng forced, although roughly the structure is similar, but some of the details of the method is still some differences. The major difference is response. In addition, ES6 grammar is adopted, which makes the writing style more elegant. In order to avoid just into the pit friends can not climb out, so tidy up this article.

The project build


The directory structure is described as follows

. ├ ─ ─ the README. Md project description ├ ─ ─ app business side code │ ├ ─ ─ the controller associated with routing API methods │ └ ─ ─ modal data model ├ ─ ─ app. Js file entrance ├ ─ ─ bin nodemon │ ├ ─ ─ ├─ WWW ├─ config config │ ├─ dbconfig.js database config │ ├─ logconfig.js logConfig │ ├─ serverconfig.js service config ├── ├─ error ├─ response │ ├─ ├─ error │ ├─ response ├── Public │ ├─ exercises │ ├─ routes │ ├─ allRoute.js │ ├ ─ ─ files. Js modules routing configuration │ ├ ─ ─ index. The js │ └ ─ ─ the users. The js ├ ─ ─ uploads the upload folder │ └ ─ ─ the 2017-8-29 ├ ─ ─ utils public methods │ ├ ─ ─ logUtil. Js │ ├─ ├─ ├─ download.txt └─ ├─ download.txt │ ├─ download.txt └─ download.txt └─ download.txt -I "node_modules" brew install tree || apt-get install treeCopy the code
  • Tree -d displays only folders;
  • Tree-l n Displays the level of an item. N indicates the number of tiers. For example, if you want to display the three-tier structure of the project, you can use tree-l 3;
  • Tree -I pattern Filters files or folders that do not want to be displayed. For example, if you want to filter the node_modules folder in your project, use tree -i “node_modules”;
  • Tree > tree.md Outputs the project structure to tree.md.

The first is the writing


Koa2 uses the new features of ES6 and 7, and uses the Gospel of Nodemon Babelrc to automatically transcode. Babelrc does not need to configure. Babelrc, and does not need to install a number of bable transcodes.

Write asynchronous

It used to be the various callbacks in the.then method

exports.getUserList = function() { user.find({ _id: id, }, arr, function(e, numberAffected, raw) { if(e){ respondata={ "code":"9900", "message":"error" }; }else{ respondata={ "code":"0000", "message":"success" }; }}); }Copy the code

Now you can use async await

exports.getUserList = async (ctx, next) => { try { let list = await user.find(); let respon = { code: '0000', message: 'success', data: list } return respon; } catch (err) { let respon = { code: '9999', message: 'error', data: err } return respon; }}Copy the code

Since many of the back-end operations, such as files and databases, are asynchronous, the change from asynchronous to synchronous writing greatly improves the readability of the code.

The Route of routing


Koa – the route is restful design patterns, you can refer to nguyen other teacher the restful API design guide www.ruanyifeng.com/blog/2014/0…

The modular routing rule for routing is domain name + module + method

For example: localhost: 8080 / users/getUser

<allroute.js> const router = require('koa-router')(); const index = require('./index'); const users = require('./users'); const files = require('./files'); router.use('/', index.routes(), index.allowedMethods()); router.use('/users', users.routes(), users.allowedMethods()); router.use('/files', files.routes(), files.allowedMethods()); module.exports = router; <users.js> const router = require('koa-router')(); import {getUserList, register, removeUser} from '.. /app/controller/user' router.get('/', function (ctx, next) { ctx.body = 'this a users response! '; }); router.get('/getUser', async (ctx, next) => { ctx.body = await getUserList(ctx, next); }); router.post('/register', async (ctx, next) => { console.log(ctx.request.body); let reqBody = ctx.request.body; ctx.body = await register(reqBody); }); router.del('/removeUser', async (ctx, next) => { console.log(ctx.request.body); let reqBody = ctx.request.body; ctx.body = await removeUser(reqBody); }); module.exports = router;Copy the code

Reseful routing, if your request is not a get | post | del, or do not match, with unified return 404 not found

Middleware Middleware


Middleware is something like a filter, a way of handling requests and responses between a client and an application.

.middleware1 {
  // (1) do some stuff
  .middleware2 {
    // (2) do some other stuff
    .middleware3 {
      // (3) NO next yield !
      // this.body = 'hello world'
    }
    // (4) do some other stuff later
  }
  // (5) do some stuff lastest and return
}Copy the code

Middleware execution is like an onion, but rather than layer by layer, it is bounded by next, executing the parts of the layer before Next, and executing the parts of the layer after Next when the next layer is finished.

let koa = require('koa'); let app = new koa(); app.use((ctx, next) => { console.log(1) next(); Console. log(5)}); app.use((ctx, next) => { console.log(2) next(); console.log(4) }); app.use((ctx, next) => { console.log(3) ctx.body = 'Hello World'; }); app.listen(3000); // Print 1, 2, 3, 4, 5Copy the code

The above simple application prints out 1, 2, 3, 4, 5. This is the core of KOA middleware control, an onion structure, coming in layer by layer from top to bottom, and going back layer by layer from bottom to top. At first glance, it looks very complicated, why not just go down layer by layer, like Express/Connect? We just want next to go to the next middleware, why come back?

This is a cascade of code designed to deal with frequent callbacks in complex applications, not passing control directly to the next middleware, but to next to the next middleware, and then to next when all else is done

What basis is there for addressing frequent pullbacks? For a simple example, if we need to know the time across middleware, we can easily write it with KOA, but with Express, we can look at the source code of Express Reponse-time. It can only count the time by listening for the header to be written out and then triggering the callback function, but koA doesn’t need to write a callback at all, we just need to add a few lines of code after next. Then ()

Logs log


Log4js access and usage

let log4js = require('log4js'); let logConfig = require('.. /config/logConfig'); Log4js. configure(logConfig); let logUtil = {}; let errorLogger = log4js.getLogger('error'); // Categories let resLogger = log4js.getLogger('response'); Logutil. logError = function (CTX, error, resTime) {if (CTX && error) {errorLogger.error(formatError(CTX, error, resTime)); }}; Logutil. logResponse = function (CTX, resTime) {if (CTX) {reslogger.info (formatRes(CTX, resTime)); }}; Config :{"appenders":{error: {"category":"errorLogger", // Logger name" type": "dateFile", //log type" filename": ErrorLogPath, // Log output position "alwaysIncludePattern":true, // Whether there is always a suffix "pattern": "-YYYY-MM-DD-hh. log", // suffix, create a new log file every hour "path": errorPath}, response: {"category":"resLogger", "type": "dateFile", "filename": responseLogPath, "alwaysIncludePattern":true, "pattern": "-yyyy-MM-dd-hh.log", "path": responsePath, } }, "categories" : { error: { appenders: ['error'], level: 'error' }, response: { appenders: ['response'], level: 'info' }, default: { appenders: ['response'], level: 'info' }, } }Copy the code

File File system


Nodejs file I/O is a simple wrapper around standard POSIX functions. Use this module by require(‘fs’). All methods have asynchronous and synchronous forms.

The last argument to an asynchronous method is a callback function. The arguments passed to the callback function depend on the method, but the first argument of the callback function is always reserved for the exception. If the operation completes successfully, the first argument will be null or undefined.

When using the synchronous method, any exceptions are thrown immediately. You can use try/catch to handle exceptions, or bubble them up.

For example, to do a picture upload and picture display function, need to use the following methods

ExistsSync Checks whether a file exists (synchronous method) mkdirsSync creates a directory (synchronous method) readFileSync reads a file createWriteStream Creates a write stream createReadStream creates a read stream UnlinkSync file deletion (sync method)Copy the code

Procedure for Uploading files

  1. Get the uploaded file object
  2. Specify the file storage path
  3. Create a write stream for the destination path and a read stream for file.path(cache path)
  4. Put into a write stream based on a read stream
  5. Delete files from the cache path
  6. Database record
file = ctx.request.body.files targetInfo = getFileInfo(type); tmpPath = file.path; type = file.type; targetInfo = getFileInfo(type); // targetInfo contains {targetName file name,targetPaths full path target directory, resultPath plus file name target directory, RelativePath relativePath target directory} mkdirs.mkdirssync (targetinfo.targetpaths); // stream = fs.createWritestream (targetInfo.resultPath); // Stream = fs.createWritestream (targetInfo.resultPath); // Create a writable stream fs.createreadStream (tmpPath).pipe(stream); unlinkStatus = fs.unlinkSync(tmpPath);Copy the code

Get the file as Buffer with readFileSync

Filepath = files.find({_id: id}); Ctx. body = fs.readfilesync (filepath); ctx.res.writeHead(200, {'Content-Type': 'image/png'});Copy the code

Mongodb CRUD database


Connect Database connection

let dbName = "nodeapi"; let dbHost = "mongodb://localhost/"; let mongoose = require("mongoose"); exports.connect = function(request, response) { mongoose.connect(dbHost + dbName, { useMongoClient: true }); // useMongoClient to prevent error let db = mongoose. Connection; db.on('error', console.error.bind(console, 'connection error:')); db.once('open', function (callback) { console.log('connet success! '); }); }Copy the code

Mongoose.Schema Object Schema of the field

Add, delete, change and check modal

let mongoose = require("mongoose"); let Schema = mongoose.Schema; let FilesSchema = new Schema({ fileName: String, filePath: String, content: String, createTime: { type: Date, dafault: Date.now() }, updateTime: { type: Date, dafault: Date.now() }, }) FilesSchema.pre('save', function(next) { if (this.isNew) { this.createTime = this.updateTime = Date.now() } else { this.updateTime = Date.now() } next() }) class Files{ constructor() { this.files = mongoose.model("files", FilesSchema); } find(dataArr={}) { const self = this; return new Promise(function (resolve, reject){ self.files.find(dataArr, function(e, docs) { if(e){ console.log('e:',e); reject(e); }else{ resolve(docs); } }) }) } create(dataArr) { const self = this; return new Promise(function (resolve, reject){ let user = new self.files({ fileName: dataArr.fileName, filePath: dataArr.filePath, content: dataArr.content, }); user.save(function(e, data, numberAffected) { // if (e) response.send(e.message); if(e){ reject(e); }else{ resolve(data); }}); }) } delete(dataArr) { const self = this; return new Promise(function (resolve, reject){ self.files.remove({ _id: dataArr.id }, function(e, data) { if(e){ reject(e); }else{ resolve(data); }}); }) } } let files = new Files() export {files}Copy the code

Encapsulation in the form of modules is more convenient for external calls

Async Asynchronously writes data to a database

import {files} from '../modal/files'
readFile =  async (id) => {
    try {
        let list = await files.find({_id: id});
        console.log(list)
        if(list && list.length > 0) {
            return fs.readFileSync(list[0].content);   
        } else {
            return errdata(null,'9999', 'can not find file')
        }
    } catch (err) {
        return errdata(err);
    }
}Copy the code

Write in the last

This project is only for your study and reference, welcome to exchange more ~ wechat

Koa2 Learning address reference

Github.com/guo-yu/koa-…