typescriptとexpressを使用してバックグラウンドプロジェクトを開発する

1.環境構成

  • 1.依存関係をインストールします

    npm i express body-parser bcryptjs jsonwebtoken morgan cors validator helmet dotenv multer -S
    npm install mongoose
    
    npm i typescript  @types/node @types/express @types/mongoose @types/bcryptjs @types/jsonwebtoken  @types/morgan @types/cors @types/validator ts-node-dev nodemon  @types/helmet @types/multer -D
    
  • 2.tsconfig.jsonファイルを生成します(言語に応じて選択します)

    npx tsconfig.json
    
  • 3.package.jsonファイルでスタートアップコマンド設定します

    "scripts": {
          
          
      "build": "tsc",
      "start": "ts-node-dev --respawn src/index.ts",
      "dev": "nodemon --exec ts-node --files src/index.ts"
    },
    
  • 4.src/index.tsファイルにテストコードを記述ます

    import 'dotenv/config';           //这是包的作用是读取.env文件然后写入process.env.JWT_SECRET_KEY
    import path from 'path';
    import express, {
          
           Express } from 'express';    //启服务
    import mongoose from 'mongoose';  //连接数据库的
    import cors from 'cors';          //用来跨域的
    import morgan from 'morgan';      //这是用来输入访问日志的
    import helmet from 'helmet';      //用来进行安全过滤的
    
    const app: Express = express();
    // 使用中间件
    app.use(cors()); // 处理跨域请求
    app.use(morgan('dev')); // 开发环境打印日志
    app.use(helmet());
    // 处理请求
    app.use(express.json());//express.json=bodyParser.json
    app.use(express.urlencoded({
          
           extended: true }));
    
    // 配置静态文件目录,也是上传的目录
    app.use(express.static(path.join(__dirname, 'public')));
    
    // 测试接口
    app.get('/', (_req, res, _next) => {
          
          
      res.json({
          
           code: 0, message: '成功' });
    });
    
    (async function () {
          
          
      await mongoose.set('useNewUrlParser', true);
      await mongoose.set('useUnifiedTopology', true);
      const MONGODB_URL = process.env.MONGODB_URL || `mongodb://localhost/express_ts`;
      await mongoose.connect(MONGODB_URL);
      const PORT = process.env.PORT || 8001;
      app.listen(PORT, () => {
          
          
        console.log(`Running on http://localhost:${
            
            PORT}`);
      })
    })();
    
  • 5.テスト

    npm run start
    # or
    npm run dev
    

2つ目は、エラーインターセプターとエラーミドルウェアを構成することです。

  • 1.インターセプター

    
    class HttpException extends Error {
          
          
      //status HTTP错误状态码  message是错误提示信息 errors 错误对象
      constructor (public status: number, public message: string, public errors: any = {
          
          }) {
          
          
        super(message);
      }
    }
    export default HttpException;
    
  • 2.ミドルウェア

    
    import {
          
           Request, Response, NextFunction } from 'express';
    import HttpException from '../exceptions/HttpException';
    import {
          
           INTERNAL_SERVER_ERROR } from 'http-status-codes';
    const errorMiddleware = (err: HttpException, _req: Request, res: Response, _next: NextFunction) => {
          
          
      let result: any = {
          
          
        success: false,
        message: err.message
      };
      if (err.errors && Object.keys(err.errors).length > 0) {
          
          
        result.errors = err.errors;
      }
      res.status(err.status || INTERNAL_SERVER_ERROR)
        .json(result);
    }
    export default errorMiddleware;
    
  • 3.でsrc/index.ts使用

    ...
    //如果说没有匹配到任何路由,则会创建一个自定义404错误对象并传递给错误处理中间件
    app.use((_req: Request, _res: Response, next: NextFunction) => {
          
          
      const error: HttpException = new HttpException(404, '尚未为此路径分配路由');
      next(error);
    });
    // 使用自定义中间件
    app.use(errorMiddleware);
    ...
    

3、ユーザーモジュール

  • 1.modelsでユーザーのデータモデルを作成します

    import mongoose, {
          
           Schema, Model, Document, HookNextFunction } from 'mongoose';
    import bcryptjs from 'bcryptjs';
    import validator from 'validator';
    // import jwt from 'jsonwebtoken';
    
    // 定义用户的数据模型约束
    export interface UserDocument extends Document {
          
          
      username: string;
      password: string;
      avatar: string;
      email: string;
    }
    
    // 定义mongodb数据模型
    const UserSchema: Schema<UserDocument> = new Schema({
          
          
      username: {
          
          
        type: String,
        required: [true, '用户名不能为空'],
        minlength: [6, '最小长度不能小于6位'],
        maxlength: [12, '最大长度不能大于12位'],
        trim: true,
      },
      password: String,
      avatar: String,
      email: {
          
          
        type: String,
        // 自定义校验器
        validate: {
          
          
          validator: validator.isEmail
        },
        trim: true,
      }
    }, {
          
          
      //使用时间戳 会自动添加两个字段 createdAt updatedAt
      timestamps: true,
      // 自定义返回数据格式
      toJSON: {
          
          
        transform: function (_doc: any, result: any) {
          
          
          result.id = result._id;
          delete result._id;
          delete result.__v;
          delete result.password;
          delete result.createdAt;
          delete result.updatedAt;
          return result;
        }
      }
    })
    
    // 配置mongoose的钩子函数,对密码加密
    UserSchema.pre<UserDocument>('save', async function (next: HookNextFunction) {
          
          
      if (!this.isModified('password')) {
          
          
        return next();
      }
      try {
          
          
        this.password = await bcryptjs.hash(this.password, 10);
        next();
      } catch (error) {
          
          
        next(error);
      }
    })
    
    interface UserModel<T extends Document> extends Model<T> {
          
           }
    
    export const User: UserModel<UserDocument> = mongoose.model<UserDocument, UserModel<UserDocument>>('User', UserSchema);
    
  • 2.ユーザー登録

    import {
          
           Request, Response, NextFunction, } from 'express';
    import {
          
           User, UserDocument } from '../models';
    import {
          
           validateRegisterInput } from '../utils';
    import HttpException from '../exceptions/HttpException';
    import {
          
           UNPROCESSABLE_ENTITY } from 'http-status-codes';
    
    export const register = async (req: Request, res: Response, next: NextFunction) => {
          
          
      let {
          
           username, password, confirmPassword, email } = req.body;
      try {
          
          
        // 先校验用户输入的数据
        let {
          
           valid, errors } = validateRegisterInput(username, password, confirmPassword, email);
        if (!valid) {
          
          
          throw new HttpException(UNPROCESSABLE_ENTITY, '用户提交的数据不正确', errors);
        }
    
        let oldUser: (UserDocument | null) = await User.findOne({
          
           username });
        if (oldUser) {
          
          
          throw new HttpException(UNPROCESSABLE_ENTITY, '用户名重复', errors);
        }
        // 创建用户
        let user: UserDocument = new User({
          
           username, password, confirmPassword, email });
        await user.save();
        // 返回数据
        res.json({
          
          
          success: true,
          data: user.toJSON()
        });
      } catch (error) {
          
          
        next(error);
      };
    }
    
  • 3.ユーザーがログインして戻るtoken

    // models/user.ts文件
    export interface UserPayload {
          
          
      id: string
    }
    
    UserSchema.methods.getAccessToken = function (this: UserDocument): string {
          
          
      //this.id是一个语法糖,或者说快捷方式,指向this._id
      let payload: UserPayload = {
          
           id: this.id };//payload是放在放在jwt token里存放的数据
      return jwt.sign(payload, process.env.JWT_SECRET_KEY || 'abc', {
          
           expiresIn: '1h' });
    }
    
    interface UserModel<T extends Document> extends Model<T> {
          
          
      login: (username: string, password: string) => UserDocument | null
    }
    
    // 扩展一个方法
    UserSchema.static('login', async function (this: any, username: string, password: string): Promise<UserDocument | null> {
          
          
      const user: UserDocument | null = await this.model('User').findOne({
          
           username });
      if (user) {
          
          
        //判断用户输入的密码和数据库里存的密码是否能匹配
        const matched = await bcryptjs.compare(password, user.password);
        if (matched) {
          
          
          return user;
        } else {
          
          
          return null;
        }
      } else {
          
          
        return null;
      }
    })
    
    //controllers/user.ts控制器
    // 用户登录
    export const login = async (req: Request, res: Response, next: NextFunction) => {
          
          
      try {
          
          
        const {
          
           username, password } = req.body;
        // 传递用户名和密码登录
        const user: UserDocument | null = await User.login(username, password);
        if (user) {
          
          
          let access_token = user.getAccessToken();
          res.json({
          
          
            success: true,
            data: access_token
          });
        } else {
          
          
          throw new HttpException(UNAUTHORIZED, '登录失败');
        }
      } catch (error) {
          
          
        next(error);
      }
    }
    

第四に、ファイルをアップロードします

  • 1.依存関係をインストールします

    npm install multer
    
  • 2.アップロードアドレスを指定します

    // 在index.ts文件中
    //指定上传文件的存储空间
    const storage = multer.diskStorage({
          
          
      //指定上传的目录
      destination: path.join(__dirname, 'public', 'uploads'),
      filename(_req: Request, file: Express.Multer.File, callback) {
          
          
        // callback 第二个参数是文件名 时间戳.jpg
        callback(null, Date.now() + path.extname(file.originalname));
      }
    });
    // 在需要上传的地方使用这个
    const upload = multer({
          
           storage });
    
  • 3.アップロードコントローラーを指定します

    //当服务器端接收到上传文件请求的时候,处理单文件上传。字段名avatar  request.file=Express.Multer.File
    app.post('/user/uploadAvatar', upload.single('avatar'), userController.uploadAvatar);
    
  • 4.ファイルをアップロードするためのコントローラー

    // 上传文件
    export const uploadAvatar = async (req: Request, res: Response, next: NextFunction) => {
          
          
      try {
          
          
        const {
          
           userId } = req.body;
        let avatar = `${
            
            req.protocol}://${
            
            req.headers.host}/uploads/${
            
            req.file.filename}`;
        await User.updateOne({
          
           _id: userId }, {
          
           avatar });
        //处理上传的文件,然后更新数据库,更新此用户对应的avatar字段。然后返回真实的图片路径
        res.json({
          
          
          success: true,
          data: avatar
        });
      } catch (error) {
          
          
        next(error);
      }
    }
    

おすすめ

転載: blog.csdn.net/kuangshp128/article/details/108384573