大前端 - nodejs - egg.js 企业级框架实战 - eggJS综合案例

本文详细介绍了如何模仿实现一个YouTube克隆项目,包括前后端架构分离、选用Egg.js和Vue.js作为技术栈、数据库设计(如用户、视频、评论等模型)、身份验证、数据验证、错误处理、用户注册和登录、订阅功能、阿里云视频点播服务集成等。同时,文章涵盖了接口设计、数据库操作、用户权限验证以及数据安全等方面,为构建类似平台提供了完整的步骤和代码示例。
摘要由CSDN通过智能技术生成

案例介绍

前后端架构分离

  • 先做服务接口,再做客户端应用

  • 后端技术选型:
    web框架:eggjs
    数据库:MongoDB
    ORM框架:mongoose
    身份认证:JWT

  • 客户端选型:vue3系列技术栈

接口设计

https://www.yuque.com/books/share/6eb0a508-d745-4e75-8631-8eb127b7b7ca?#

使用 Yapi 管理接口

项目初始化

npm i create-egg -g

create-egg youtube-clone-eggjs (smaple)

cd youtube-clone-eggjs

npm install

npm run dev

初始化mongoose配置

npm i egg-mongoose --save

/config/config.default.js

/* eslint valid-jsdoc: "off" */

'use strict';

/**
 * @param {Egg.EggAppInfo} appInfo app info
 */
module.exports = appInfo => {
   
  /**
   * built-in config
   * @type {Egg.EggAppConfig}
   **/
  const config = exports = {
   };

  // use for cookie sign key, should change to your own and keep security
  config.keys = appInfo.name + '_1649334278876_9943';

  // add your middleware config here
  config.middleware = [];

  // add your user config here
  const userConfig = {
   
    // myAppName: 'egg',
  };

  // 加入mongoose
+  config.mongoose = {
   
    client: {
   
      url: 'mongodb://127.0.0.1/youtube-clone',
      options: {
   },
      plugins: [],
    },
  };

  return {
   
    ...config,
    ...userConfig,
  };
};

/config/plugin.js

'use strict';

/** @type Egg.EggPlugin */
// module.exports = {
   
//   // had enabled by egg
//   // static: {
   
//   //   enable: true,
//   // }

// };

+ exports.mongoose = {
   
  enable: true,
  package: 'egg-mongoose'
}

数据模型设计

// app/model/user.js
module.exports = app => {
   
  const mongoose = app.mongoose
  const Schema = mongoose.Schema

  const userSchema = new Schema({
   
    username: {
    // 用户名
      type: String,
      required: true
    },
    email: {
    // 邮箱
      type: String,
      required: true
    },
    password: {
    // 密码
      type: String,
      select: false, // 查询中不包含该字段
      required: true
    },
    avatar: {
    // 头像
      type: String,
      default: null
    },
    cover: {
   
      type: String, // 封面
      default: null
    },
    channelDescription: {
    // 频道介绍
      type: String,
      default: null
    },
    subscribersCount: {
   
      type: Number,
      default: 0
    },
    createdAt: {
    // 创建时间
      type: Date,
      default: Date.now
    },
    updatedAt: {
    // 更新时间
      type: Date,
      default: Date.now
    }
  })

  return mongoose.model('User', userSchema)
}

视频:

// app/model/video.js
module.exports = app => {
   
  const mongoose = app.mongoose
  const Schema = mongoose.Schema

  const videoSchema = new Schema({
   
    title: {
    // 视频标题
      type: String,
      required: true
    },
    description: {
    // 视频介绍
      type: String,
      required: true
    },
    vodVideoId: {
    // VOD 视频 ID
      type: String,
      required: true
    },
    cover: {
    // 视频封面
      type: String,
      required: true
    },
    user: {
   
      type: mongoose.ObjectId, // 视频作者
      required: true,
      ref: 'User'
    },
    commentsCount: {
    // 评论数量
      type: Number,
      default: 0
    },
    dislikesCount: {
    // 不喜欢数量
      type: Number,
      default: 0
    },
    likesCount: {
    // 喜欢数量
      type: Number,
      default: 0
    },
    viewsCount: {
    // 观看次数
      type: Number,
      default: 0
    },
    createdAt: {
    // 创建时间
      type: Date,
      default: Date.now
    },
    updatedAt: {
    // 更新时间
      type: Date,
      default: Date.now
    }
  })

  return mongoose.model('Video', videoSchema)
}

视频点赞:

// app/model/video_like.js
module.exports = app => {
   
  const mongoose = app.mongoose
  const Schema = mongoose.Schema

  const likeSchema = new Schema({
   
    like: {
    // 点赞状态
      type: Number,
      enum: [1, -1], // 喜欢 1,不喜欢 -1
      required: true
    },
    user: {
    // 点赞用户
      type: mongoose.ObjectId,
      ref: 'User', // 关联到User表
      required: true
    },
    video: {
    // 点赞视频
      type: mongoose.ObjectId,
      ref: 'Video',
      required: true
    },
    createdAt: {
    // 创建时间
      type: Date,
      default: Date.now
    },
    updatedAt: {
    // 更新时间
      type: Date,
      default: Date.now
    }
  })

  return mongoose.model('VideoLike', likeSchema)
}

视频评论:

// app/model/video_comment.js
module.exports = app => {
   
  const mongoose = app.mongoose
  const Schema = mongoose.Schema

  const commentSchema = new Schema({
   
    content: {
    // 评论内容
      type: String,
      required: true
    },
    user: {
    // 评论用户
      type: mongoose.ObjectId,
      ref: 'User',
      required: true
    },
    video: {
    // 评论视频
      type: mongoose.ObjectId,
      ref: 'Video',
      required: true
    },
    createdAt: {
    // 创建时间
      type: Date,
      default: Date.now
    },
    updatedAt: {
    // 更新时间
      type: Date,
      default: Date.now
    }
  })

  return mongoose.model('Comment', commentSchema)
}

频道(用户)订阅

// app/model/subscription.js
module.exports = app => {
   
  const mongoose = app.mongoose
  const Schema = mongoose.Schema

  const subscriptionSchema = new Schema({
   
    user: {
    // 订阅用户
      type: mongoose.ObjectId,
      ref: 'User',
      required: true
    },
    channel: {
    // 订阅频道
      type: mongoose.ObjectId,
      ref: 'User',
      required: true
    },
    createdAt: {
    // 创建时间
      type: Date,
      default: Date.now
    },
    updatedAt: {
    // 更新时间
      type: Date,
      default: Date.now
    }
  })

  return mongoose.model('Subscription', subscriptionSchema)
}

观看历史:

// app/model/video_view.js
module.exports = app => {
   
  const mongoose = app.mongoose
  const Schema = mongoose.Schema

  const viewSchema = new Schema({
   
    user: {
    // 用户
      type: mongoose.ObjectId,
      ref: 'User',
      required: true
    },
    video: {
    // 视频
      type: mongoose.ObjectId,
      ref: 'Video',
      required: true
    },
    createdAt: {
    // 创建时间
      type: Date,
      default: Date.now
    },
    updatedAt: {
    // 更新时间
      type: Date,
      default: Date.now
    }
  })

  return mongoose.model('View', viewSchema)
}

用户注册-准备

app/router.js

'use strict';

/**
 * @param {Egg.Application} app - egg application
 */
module.exports = app => {
   
  const {
    router, controller } = app;
  router.prefix('/api/v1') // 设置基础路由
  router.get('/users', controller.user.create)
};

app/controller/user.js

'use strict';

const Controller = require('egg').Controller;

class UserController extends Controller {
   
  async create() {
   
    const {
    ctx } = this;
    ctx.body = 'UserController'
  }
}

module.exports = UserController;

config/config.default.js

/* eslint valid-jsdoc: "off" */

'use strict'

/**
 * @param {Egg.EggAppInfo} appInfo app info
 */
module.exports = appInfo => {
   
  /**
   * built-in config
   * @type {Egg.EggAppConfig}
   **/
  const config = {
   }

  // use for cookie sign key, should change to your own and keep security
  config.keys = appInfo.name + '_1611716016238_6422'

  // add your middleware config here
  config.middleware = ['errorHandler']

  // add your user config here
  const userConfig = {
   
    // myAppName: 'egg',
  }

  config.mongoose = {
   
    client: {
   
      url: 'mongodb://127.0.0.1/youtube-clone',
      options: {
   
        useUnifiedTopology: true
      },
      // mongoose global plugins, expected a function or an array of function and options
      plugins: []
    }
  }
	
	// 解决403问题,没有权限问题。关闭权限的验证
+  config.security = {
   
    csrf: {
   
      enable: false
    }
  }

  return {
   
    ...config,
    ...userConfig
  }
}

用户注册-数据验证介绍

参数校验 :https://www.eggjs.org/zh-CN/basics/controller#%E5%8F%82%E6%95%B0%E6%A0%A1%E9%AA%8C

https://github.com/eggjs/egg-validate

用户注册-数据验证

npm i egg-validate --save

// config/plugin.js
exports.validate = {
   
  enable: true,
  package: 'egg-validate',
};
// controller/user.js
'use strict';

const Controller = require('egg').Controller;

class UserController extends Controller {
   
  async create() {
   
    const {
    ctx } = this;
    // 1.数据验证
+    this.ctx.validate({
   
      username: {
    type: string },
      email: {
    type: email },
      
    })
    // 2.保存用户
    // 3.生成token
    // 4.发送响应
  }
}

module.exports = UserController;

用户注册-自定义异常处理

2种方式
1.try catch
2.框架层统一处理异常:https://www.eggjs.org/zh-CN/core/error-handling
https://www.eggjs.org/zh-CN/tutorials/restful#%E7%BB%9F%E4%B8%80%E9%94%99%E8%AF%AF%E5%A4%84%E7%90%86

通一错误处理

// app/middleware/error_handler.js
// 外层函数负责接收参数
module.exports = () => {
   
	// 返回一个中间件处理函数
  return async function errorHandler(ctx, next) {
   
    try {
   
      await next();
    } catch (err) {
   
      // 所有的异常都在 app 上触发一个 error 事件,框架会记录一条错误日志
      ctx.app.emit('error', err, ctx);

      const status = err.status || 500;
      // 生产环境时 500 错误的详细错误内容不返回给客户端,因为可能包含敏感信息
      const error =
        status === 500 && ctx.app.config.env === 'prod'
          ? 'Internal Server Error'
          : err.message;

      // 从 error 对象上读出各个属性,设置到响应中
      ctx.body = {
    error };
      if (status === 422) {
   
        ctx.body.detail = err.errors;
      }
      ctx.status = status;
    }
  };
};

config/config.default.js

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值