为什么选择 express
刚学习node,express比较轻量,几乎没有内置任何的额外功能,灵活度较高
为什么选择 TS
主要是参数约束接口,规定调用方法的传参类型,避免出现数据类型不一致导致JS直接卡死在浏览器的语法错误
配置TS
npm install typescript -g
配置 tsconfig.config
{
"compilerOptions": {
"module": "commonjs",
"noImplicitAny": true,
"declaration": true,
"removeComments": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"target": "es2017",
"preserveConstEnums": true,
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
"incremental": true
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules",
"**/*.spec.ts",
"dist"
]
}
安装 tsc-watch 监听ts文件,自动编译
npm install tsc-watch -D
package.json文件
{
"name": "user-link-tracking",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "tsc-watch --onSuccess \"node dist/main.js\"",
"format": "prettier --write \"src/**/*.ts\""
},
"author": "",
"license": "ISC",
"dependencies": {
"axios": "^0.24.0",
"express": "^4.17.1",
"mysql2": "^2.3.3"
},
"devDependencies": {
"@types/express": "^4.17.13",
"@types/mysql2": "github:types/mysql2",
"@types/node": "^16.11.7",
"dotenv": "^10.0.0",
"nodemon": "^2.0.15",
"prettier": "^2.4.1",
"tsc-watch": "^4.5.0",
"typescript": "^4.4.4"
}
}
搭建项目并启动服务
const express = require("express")
const app = express()
const port = 3000
app.listen(3000, () => {
console.log('服务启动')
})
数据库连接
mysql.ts
import mysql from 'mysql2'
import {
MYSQL_HOST,
MYSQL_PORT,
MYSQL_USER,
MYSQL_PASSWORD,
MYSQL_DATABASE
} from '../../app/app.config'
export const connection = mysql.createConnection({
host: MYSQL_HOST,
port: parseInt(MYSQL_PORT),
user: MYSQL_USER,
password: MYSQL_PASSWORD,
database: MYSQL_DATABASE
})
业务模型分层
model层:定义数据格式
export class PostModel{
id?:number;
title?:string;
content?:string;
}
service层:处理数据逻辑
import { connection } from '../app/database/mysql'
import { PostModel } from './post.model'
export const getPosts = async () => {
const statement = `SELECT post.id,post.title,post.content, JSON_OBJECT(
'id',user.id,
'name',user.name
) as woker
FROM post
LEFT JOIN user ON post.userId=user.id`
const [data] = await connection.promise().query(statement)
console.log(data)
return data
}
export const createPost = async (post: PostModel) => {
const statement = `
INSERT INTO post
set ?
`
const [data] = await connection.promise().query(statement,post)
return data
}
controller层:处理逻辑,加工数据
import { Request, Response, NextFunction, request } from "express";
import { getPosts, createPost } from './post.service'
export const index = async (request: Request, response: Response, next: NextFunction) => {
try {
const posts = await getPosts()
response.send(posts)
} catch (error) {
next(error)
}
}
export const store = async (request: Request, response: Response, next: NextFunction) => {
const { content, title } = request.body
try {
const data = await createPost({ content, title })
response.status(200).send(data)
} catch (error) {
next(error)
}
}
router层: 接收请求路由
import express from 'express';
import * as postController from './post.controller'
import { requestUrl } from '../app/app.middleware'
const router = express.Router()
/**
* 内容接口
*/
router.get('/posts', requestUrl, postController.index)
router.post('/posts', postController.store)
export default router
中间件
参数解析中间件
app/index.ts
import express from 'express';
/**
* 处理json
*/
app.use(express.json());
请求异常处理中间件
app.middleware.ts
import { Request, Response, NextFunction, request } from 'express'
/**
* 异常处理器
*/
export const defaultErrorHandle = (error: any, request: Request, response: Response, next: NextFunction) => {
if (error) {
console.log(error.message)
}
let statusCode: number, message: string;
switch (error.message) {
default:
statusCode = 500;
message = '出错了';
break;
}
response.status(statusCode).send({ message })
}
app/index.ts
import { defaultErrorHandle } from './app.middleware'
app.use(defaultErrorHandle)
路由处理
app/index.ts
import postRouter from '../post/post.router';
import skyRouter from '../skyLog/log.router';
app.use(postRouter)
app.use(skyRouter)
总结
其他更多的公共逻辑都可以放在中间件层面去做,例如登录校验、权限校验等。到目前为止上面细节上还有很多问题没来得及做处理,比如接口转发,文件处理,后期还会对很多细节进行优化。