如何用Egg.js+Mongodb完成jwt登录和一套增删查改

·  阅读 736
如何用Egg.js+Mongodb完成jwt登录和一套增删查改

前言

大家好,我叫竹业。今天主要来了解一下用Egg.js编写一套增删查改,以及登陆注册接口。

Egg.js 为企业级框架和应用而生,我们希望由 Egg.js 孕育出更多上层框架,帮助开发团队和开发人员降低开发和维护成本。 ---来自官方的介绍

git代码的地址

egg工程搭建

新建一个文件,通过下面命令,一路默认回车,搭建工程

npm init egg --type=simple

安装依赖

npm i

本地启动服务

npm run dev

文件主要目录

image.png

一个简单的get请求

虽然一个简单get的请求,用express也很容易做到,但是egg让我们结构更加的清晰,遵守MVC模式,每一层

router路由配置

当访问localhost:7001/hello的路径时,会执行controller文件夹下面,home.js文件里面的index方法

// router.js
module.exports = (app) => {
  const { router } = app;
  router.get("/hello", controller.home.index);
};

controller层

image.png

直接返回结果(也可以在这里调用service方法,获取数据库数据)

"use strict";
const Controller = require("egg").Controller;
class HomeController extends Controller {
  async index() {
    const { ctx } = this;
    ctx.body = "hello egg";
  }
}
module.exports = HomeController;

通过postman访问接口

image.png

如何实现一套增删查改

我们实现一套标签的增删查找

配置路由

使用egg提供的RESTful风格的URL定义

MethodPathRoute NameController.Action
GET/postspostsapp.controllers.posts.index
GET/posts/newnew_postapp.controllers.posts.new
GET/posts/:idpostapp.controllers.posts.show
GET/posts/:id/editedit_postapp.controllers.posts.edit
POST/postspostsapp.controllers.posts.create
PUT/posts/:idpostapp.controllers.posts.update
DELETE/posts/:idpostapp.controllers.posts.destroy

在router.js中一行代码搞定

module.exports = (app) => {
  const { router, controller, jwt } = app;
  // jwt的使用,往下翻
  router.resources("tags","/api/v1/tags", jwt, controller.tags);  // 标签
};

model定义

定义一些标签相关字段,还可以定义校验规则,无需导入,在service层,可以通过ctx.model.Tags来访问

mongoose的使用的egg-mongoose,下面会说使用方法

app/model/tags.js
module.exports = (app) => {
  const mongoose = app.mongoose;
  const Schema = mongoose.Schema;
  const TagsSchema = new Schema(
    {
      name: {
        type: String,
        min: 2,
        max: 20,
        match: /^[\u4e00-\u9fa5A-Za-z0-9_]{2,20}$/,
      },
      createTime: {
        type: Date,
        default: Date.now(),
      },
      updateTime: {
        type: Date,
        default: Date.now(),
      },
      avatar: {
        type: String,
      },
      articleNum: {
        type: "number",
        default: 0,
      },
      status: {
        type: "boolean",
        default: true,
      },
    },
    { versionKey: false }
  );
  return mongoose.model("Tags", TagsSchema, "tags");
  // User是指定查找的入口,随便取;UserSchema是参数;user是你数据集合表的名称
};

service的方法

// app/service/tags.js
"use strict";
const Service = require("egg").Service;
class TagsService extends Service {
  // 分页查询
  async index(params = {}) {
    const { ctx } = this;
    try {
      const page = params.page * 1;
      const pageSize = params.pageSize * 1;
      const queryCon = params.name
        ? {
            name: {
              $regex: new RegExp(params.name, "i"),
            },
          }
        : {};
      const totalCount = await ctx.model.Tags.find(queryCon).countDocuments();
      const results = await ctx.model.Tags.find(queryCon)
        .sort({
          updateTime: -1,
        })
        .skip((page - 1) * pageSize)
        .limit(pageSize);

      return {
        data: {
          page,
          pageSize,
          totalCount,
          list: results,
        },
      };
    } catch (error) {
      return {
        code: 500,
        msg: JSON.stringify(error),
      };
    }
  }
   
  // 根据字段搜索
  async find(params = {}) {
    try {
      const { ctx } = this;
      const results = await ctx.model.Tags.find(params);
      return results;
    } catch (error) {
      return {
        code: 500,
        msg: JSON.stringify(error),
      };
    }
  }

  // 创建标签
  async create(params) {
    try {
      const { ctx } = this;
      const results = await ctx.model.Tags.create(Object.assign({}, params));
      return results;
    } catch (error) {
      return {
        code: 500,
        msg: JSON.stringify(error),
      };
    }
  }
    
  // 更新标签
  async update(params) {
    const { ctx } = this;
    try {
      const results = await ctx.model.Tags.updateOne(
        {
          _id: params._id,
        },
        Object.assign({}, params)
      );
      return results;
    } catch (error) {
      return {
        code: 500,
        msg: JSON.stringify(error),
      };
    }
  }
  
  // 删除标签
  async destroy(params) {
    const { ctx } = this;
    try {
      const results = await ctx.model.Tags.deleteOne({
        _id: params.id,
      });
      return results;
    } catch (err) {
      return {
        code: 500,
        msg: JSON.stringify(err),
      };
    }
  }
}
module.exports = TagsService;

controller层方法

实现增删查改的一套方法,访问名字遵循RESTful风格约定的方法

// app/controller/tags.js
"use strict";

const Controller = require("egg").Controller;
class TagsController extends Controller {
  get createRule() {
    return {
      name: {
        type: "string",
        min: 2,
        max: 20,
        match: /^[\u4e00-\u9fa5A-Za-z0-9_]{2,20}$/,
      },
    };
  }
  // 条件查询
  async index() {
    const { ctx } = this;
    const { pageSize = 10, page = 1, name = "" } = ctx.query || {};
    const res = await ctx.service.tags.index({ page, pageSize, name });
    if (res.code === 500) {
      return ctx.helper.error(res);
    }
    ctx.helper.success({ res });
  }

  // /tags/:id 查询
  async show() {
    const { ctx } = this;
    const { pageSize = 10, page = 1, name = "" } = ctx.query || {};
    const res = await ctx.service.tags.index({ page, pageSize, name });
    if (res.code === 500) {
      return ctx.helper.error(res);
    }
    ctx.helper.success({ res });
  }

  async hasTag() {
    const { ctx } = this;
    ctx.validate(this.createRule);
    let tag = await ctx.service.tags.find({ name: ctx.request.body.name });
    if (tag.length) {
      ctx.helper.success({ msg: "该标签已存在" });
      return true;
    }
    return false;
  }

  // 创建
  async create() {
    const { ctx } = this;
    if (await this.hasTag()) return;
    let res = await ctx.service.tags.create(ctx.request.body);
    if (res.code === 500) {
      return ctx.helper.error(res);
    }
    ctx.helper.success({ res });
  }

  // 	/tags/:id 更新
  async update() {
    const { ctx } = this;
    if (await this.hasTag()) return;
    let res = await ctx.service.tags.update({
      ...ctx.request.body,
      updateTime: Date.now(),
      _id: ctx.params.id,
    });
    if (res.code === 500) {
      return ctx.helper.error(res);
    }
    ctx.helper.success({ res });
  }

  // 销毁
  async destroy() {
    const { ctx } = this;
    const res = await ctx.service.tags.destroy(ctx.params);
    if (res.code === 500) {
      return ctx.helper.error(res);
    }
    ctx.helper.success({ res });
  }
}

module.exports = TagsController;

账号注册与登录(jwt使用)

egg-mongoose使用

用于连接和操作mongodb数据库,这里就不再描述怎么安装Mongodb数据库,需要在本地电脑安装,可以直接网上查找。一般安装完的数据库默认地址都是mongodb://127.0.0.1:27017

安装egg-mongoose

npm i egg-mongoose egg-jwt

在plugin中配置

// config/plugin.js
"use strict";
module.exports = {
  mongoose: {
    enable: true,
    package: "egg-mongoose",
  },
  jwt: {
    enable: true,
    package: "egg-jwt",
  }
};

// config/config.default.js
"use strict";
module.exports = (appInfo) => {
  const config = (exports = {});
  // 连接数据数据库
  config.mongoose = {
    client: {
      url: "mongodb://127.0.0.1:27017/test", // test表示数据库名称
      options: {
        useNewUrlParser: true,
      },
    },
  };
  
  config.jwt = {
    secret: "admin"
  };
  
  return {
    ...config
  };
};

在model中定义用户的数据模型

app/model/user.js

定义字段的名字和类型,用户注册登陆

module.exports = (app) => {
  const mongoose = app.mongoose; // 使用的插件可以通过app直接访问
  const Schema = mongoose.Schema;
  const UserSchema = new Schema(
    {
      username: {
        type: String,
        required: true,
      },
      password: {
        type: String,
        required: true,
      },
      email: {
        type: String,
        required: true,
      },
      avatar: {
        type: String,
      },
      date: {
        type: Date,
        default: Date.now(),
      },
      name: {
        type: String,
        required: true,
      },
    },
    { versionKey: false }
  );
  return mongoose.model("User", UserSchema, "user");
  // User是指定查找的入口;UserSchema是参数;user是你数据集合表的名称
};

查找用户和注册用户

app/service/user.js 在service定义查找和新增用户的方法

"use strict";
const Service = require("egg").Service;
class UserService extends Service {
  // 根据邮箱查找用户
  async find(params) {
    const { ctx } = this;
    try {
      const results = await ctx.model.User.findOne({ email: params.email });
      return results;
    } catch (err) {
      return {
        code: 500,
        msg: JSON.stringify(err),
      };
    }
  }
  
  // 注册用户
  async create(params) {
    const { ctx } = this;
    try {
      const results = await ctx.model.User.create(Object.assign({}, params));
      return results;
    } catch (err) {
      return {
        code: 500,
        msg: JSON.stringify(err),
      };
    }
  }


}
module.exports = UserService;

实现注册和登录

app/controller/user.js

设置controller层的方法,路由匹配之后,执行对应的方法

"use strict";

const Controller = require("egg").Controller;
const utility = require("utility");
class UserController extends Controller {
  // 注册
  async register() {
    const { ctx } = this;
    const body = Object.assign({}, ctx.request.body);
    // 1.调用查找服务,查询邮箱是否被注册
    let user = await ctx.service.user.find(body);
    if (user && user.email) {
      ctx.body = {
        code: 0,
        data: null,
        msg: "邮箱已被注册"
      }
      return;
    }
    body.password = utility.md5(body.password);
    // 2.邮箱不存在的话,注册账号
    const res = await ctx.service.user.create(body);
    ctx.body = {
        code: 0,
        data: res,
        msg: "注册成功"
    };
  }

  // jwt登录,返回token
  async login() {
    const { ctx, app } = this;
    const body = Object.assign({}, ctx.request.body);
    // 1.根据邮箱,查找账号是否存在
    let user = await ctx.service.user.find(body);
    if (user && user.password === utility.md5(body.password)) {
      // 2.密码正确,将通过jwt插件生成token,返回给前端
      const token = app.jwt.sign(
        { username: body.username },
        app.config.jwt.secret,
        {
          expiresIn: "1h",
        }
      );
      ctx.body = {
        code: 0,
        data: {
            token,
            username: body.username
        }
        msg: "登录成功"
      }
      return;
    }
    ctx.body = {
        code: 0,
        data: null,
        msg: "邮箱或密码错误"
    }
  }
}

module.exports = UserController;

配置路由

app/router.js 路由设置controller层对应的方法

"use strict";
module.exports = (app) => {
  const { router, controller } = app;
  router.post("/register", controller.user.register);
  router.post("/login", controller.user.login);
  router.get("/hello", jwt, controller.home.index);
};

使用jwt

在router中的,传入的路径参数和传入控制层方法参数之间,传入的参数都会被作为中间件使用

下面使用了jwt,会在请求头中校验token

"use strict";
module.exports = (app) => {
  const { router, controller, jwt } = app;
  router.get("/hello", jwt, controller.home.index);
};

接口返回401

image.png

传入token

添加请求头,值是Bearer空格后面拼接登录返回的token Authorization: Bearer ${token}

image.png

总结

egg+mongodb实现一套小型项目的后台接口是完全没问题,大大的提升了我们的效率。Egg 奉行约定优于配置,按照一套统一的约定进行应用开发。所以减少了很多重复导入工作,开发只需专注代码逻辑。

最后

这篇文章只是一个初级的使用,参考的网上一些资料视频和官网做的一个小的demo,如果有进阶的用法或者项目,大家可以在评论区留言,欢迎大家点赞,周末愉快~

分类:
前端
标签:
分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改