Node.js+Koa2+MySQL implement small program interface
File directory
Install dependencies
koa koa-bodyparser koa-router koa-static require-directory
validator lodash jsonwebtoken sequelize mysql2 bcryptjs
basic-auth axios
Copy the code
Automatic route registration
- Define entry methods for registering global properties, methods, or exception handling, etc
- Rely on require-directory dependencies for automatic route registration
const Router = require('koa-router')
const requireDirectory = require('require-directory')
class InitManager {
static initCore(app) {
// The entry method
InitManager.app = app;
InitManager.initLoadRouters()
}
// Load all routes
static initLoadRouters() {
// Absolute path
const apiDirectory = `${process.cwd()}/app/api`
// Routes are automatically loaded
requireDirectory(module, apiDirectory, {
visit: whenLoadModule
})
// Determine whether the module loaded by requireDirectory is a route
function whenLoadModule(obj) {
if (obj instanceof Router) {
InitManager.app.use(obj.routes())
}
}
}
}
// use the entry method in app.js
InitManager.initCore(app)
Copy the code
Global exception handling
- Http-exception.js defines different exception type classes
class HttpException extends Error {
constructor(msg = 'Server exception', errorCode = 10000, code = 400) {
super(a)this.errorCode = errorCode
this.code = code
this.msg = msg
}
}
class ParameterException extends HttpException {
constructor(msg, errorCode) {
super(a)this.code = 400
this.msg = msg || 'Parameter error'
this.errorCode = errorCode || 10000}}Copy the code
- Exception. Js uses onion routing for error handling (route registration comes first)
const {HttpException} = require('.. /core/http-exception')
const catchError = async (ctx, next) => {
try {
await next()
} catch (error) {
// Development environment
const isHttpException = error instanceof HttpException
const isDev = global.config.environment === 'dev'
if(isDev && ! isHttpException) {throw error
}
// Build the environment
if (isHttpException) {
ctx.body = {
msg: error.msg,
error_code: error.errorCode,
request: `${ctx.method} ${ctx.path}`
}
ctx.status = error.code
} else {
ctx.body = {
msg: "Unknown error!".error_code: 9999.request: `${ctx.method} ${ctx.path}`
}
ctx.status = 500}}}Copy the code
validators
// Define rules using lin-validator
const { Rule, LinValidator } = require('.. /.. /core/lin-validator-v2')
class PositiveIntegerValidator extends LinValidator {
constructor() {
super(a)this.id = [
new Rule('isInt'.I need a positive integer., {min: 1}}})]Copy the code
// Use checksum
router.get('/v1/book/:index/latest'.async (ctx, next) => {
const v = await new PositiveIntegerValidator().validate(ctx, {
id: 'index'
})
const index = v.get('path.index'); // Get the checksum field (automatic conversion exists)
})
Copy the code
Database operations
Connecting to a Database
Sequelize official document
// db.js
// Connect to the database (sequelize)
const sequelize = new Sequelize(database, username, password,{host,port}) // Official document specific parameters
sequelize.sync({
force: false // Whether to re-establish the database
})
Copy the code
Build a user model
//user.js
// Define the user model
class User extends Model{
Static methods can be defined to implement specific functions, such as login authentication
}
// Initialize the user model
User,init({
// Table field, see official website
id: {
type: Sequelize.INTEGER,
primaryKey: true./ / the primary key
autoIncrement: true // Auto-increment
},
password: {
type: Sequelize.STRING,
set(val) {
/ / encryption
const salt = bcrypt.genSaltSync(10) / / bcryptjs library
// Generate an encrypted password
const psw = bcrypt.hashSync(val, salt)
this.setDataValue("password", psw)
}
},
....
},{
sequelize,
tableName: 'user' / / the name of the table
})
Copy the code
Database operations
// See the official website for details
User.create()
User.destroy()
User.update()
User.findAll()
Copy the code
Login verification (different login methods)
Account login
// Account password verification, database comparison (bcryptJS library) decryption (registered account saved to the database password encrypted)
const correct = bcrypt.compareSync(plainPassword, user.password)
Copy the code
// Issue a token to obtain a token
const generateToken = function (uid, scope) {
const secretKey = global.config.security.secretKey
const expiresIn = global.config.security.expiresIn
const token = jwt.sign({
uid, / / user id
scope // Define user permissions
}, secretKey, {
expiresIn
})
return token
}
Copy the code
Applets login
// The applets API is called mainly to get the user openID
const url = util.format(global.config.wx.loginUrl,
global.config.wx.appId,
global.config.wx.appSecret,
code)
const result = await axios.get(url)
// Check whether wechat user OpenDID exists in the database
let user = await User.getUserByOpenid(result.data.openid)
// If not, create a wechat applet user
if(! user) { user =await User.createUserByOpenid(result.data.openid)
}
// Issue a token to obtain a token (as above).Copy the code
Interface authorization (Middleware)
- Use the middleware in front of each interface, and if the user has insufficient permissions, the interface will not be accessible
const basicAuth = require('basic-auth')
const jwt = require('jsonwebtoken')
class Auth {
constructor(level) {
this.level = level || 1 // Pass in the interface permissions
// Assign permissions to users
Auth.AUSE = 8
Auth.ADMIN = 16
Auth.SPUSER_ADMIN = 32
}
get m() {
// HTTP specifies the authentication mechanism HttpBasicAuth
return async (ctx, next) => {
const tokenToken = basicAuth(ctx.req)
let errMsg = "Token is illegal"
// If no token is required
if(! tokenToken || ! tokenToken.name) {throw new global.errs.Forbidden(errMsg)
}
//
try {
var decode = jwt.verify(tokenToken.name, global.config.security.secretKey)
} catch (error) {
// Token is invalid and expires
if (error.name === 'TokenExpiredError') {
errMsg = "Token has expired"
}
throw new global.errs.Forbidden(errMsg)
}
// Check whether the user has permission to access the interface
if (decode.scope <= this.level) {
errMsg = "Insufficient authority"
throw new global.errs.Forbidden(errMsg)
}
ctx.auth = {
uid: decode.uid,
scope: decode.scope
}
}
}
// Verify the token is valid
static verifyToken(token) {
try {
jwt.verify(token, global.config.security.secretKey)
return true;
} catch (e) {
return false}}}module.exports = {
Auth
}
Copy the code