前端如何快速为App搭建数据服务

「这是我参与2022首次更文挑战的第4天,活动详情查看:2022首次更文挑战」。

Dear,大家好,我是“前端小鑫同学”,😇长期从事前端开发,安卓开发,热衷技术,在编程路上越走越远~


前言:

作为一个Coder虽然主要在做前端方面的开发,但是为了可以更好的协作开发,还是很有必要学习后端的一些知识,最起码你可以了解到什么东西是真的实现不了😏。

技术栈:

  • 基础项目:eggjs
  • 时间处理:dayjs
  • 数据加密:md5
  • UID生成:uuid
  • 鉴权处理:jsonwebtoken
  • 参数校验:egg-validate
  • 跨域限制:egg-cors
  • 数据存储:egg-mongoose

选择Eggjs原因:

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

我们可以通过eggjs提供的脚手架生成一套完整的项目结构,这对于我们快速学习将是非常有必要的,接下来我们就一起了解一下eggjs基础项目的的结构,对于初次使用我们就只关注如下的目录即可。 image.png

了解第一个Controller:

  • Controller意为控制器,我们主要的后端逻辑处理的地方(当然过多的通用逻辑应该抽取到Service层),我们通过this指针结构到ctx上下文对象,并将要返回的内容赋值给body,接着我们在router.js中增加router.get('/', controller.home.index);就可以启动服务后在浏览器访问IP:PORD得到3号标题的内容了。
'use strict';
const Controller = require('egg').Controller;
class HomeController extends Controller {
async index() {
const { ctx } = this;
ctx.body = '<h3>欢迎使用可追溯查询数据提供服务</h3>';
}
}
module.exports = HomeController;

接下来试着实现用户的基本操作:

  • 这次我们先定义好如下三个路由,分别对应用户的登录,信息获取,登出三种操作。
router.post('/dev-api/user/login', controller.user.login);
router.get('/dev-api/user/info', controller.user.info);
router.post('/dev-api/user/logout', controller.user.logout);
  • 因为会涉及到数据存储,鉴权,跨域,我们将先配置好中间件来避免后续的麻烦,具体的包自行安装就好。
// server\config\plugin.js
exports.mongoose = {
enable: true,
package: 'egg-mongoose',
};
exports.validate = {
enable: true,
package: 'egg-validate',
};
exports.cors = {
enable: true,
package: 'egg-cors',
};
// server\config\config.default.js
module.exports = appInfo => {
...
config.cors = {
origin: '*', // 表示允许的源
allowMethods: 'GET,HEAD,PUT,POST,DELETE,PATCH', // 表示允许的http请求方式
};
return {
...
security: {
csrf: {
enable: false,
},
},
bodyParser: {
jsonLimit: '5mb', // 允许传输内容的大小限制
formLimit: '5mb',
},
mongoose: {
client: {
url: 'mongodb://<这块有时间单独说,各位先百度也行>',
options: {
autoReconnect: true,
reconnectTries: Number.MAX_VALUE,
bufferMaxEntries: 0,
},
},
},
};
};
  • MongoDB对应的用户和Token模型定义:
module.exports = app => {
const mongoose = app.mongoose;
const Schema = mongoose.Schema;
const UserSchema = new Schema({
username: { type: String, required: true },
password: { type: String, required: true },
roles: { type: Array, required: true },
introduction: { type: String, required: true },
avatar: { type: String, required: true },
name: { type: String, required: true },
}, { timestamps: true });
return mongoose.model('User', UserSchema);
};
module.exports = app => {
const mongoose = app.mongoose;
const Schema = mongoose.Schema;
const TokenSchema = new Schema({
token: { type: String, required: true },
}, { timestamps: true });
return mongoose.model('Token', TokenSchema);
};
  • 还准备了一个公共的BaseController,将统一处理接口的返回格式:
'use strict';
const Controller = require('egg').Controller;
class BaseController extends Controller {
success(data) {
this.ctx.body = {
code: 20000,
data,
};
}
message(message) {
this.ctx.body = {
code: 20000,
message,
};
}
error(message, code = -1, errors = {}) {
this.ctx.body = {
code,
message,
errors,
};
}
}
module.exports = BaseController;
  • 总算要开始我们的用户Controller的编写了,继承自BaseController,并导入了加密、鉴权,也定义了操作校验的对象loginUserRule
'use strict';
const BaseController = require('./base');
const KEY = require('../key');
const jwt = require('jsonwebtoken');
const md5 = require('md5');
const loginUserRule = {
username: { type: 'string' },
password: { type: 'string' },
};
class UserController extends BaseController {
async login() {
const { ctx } = this;
}
async info() {
const { ctx } = this;
}
async logout() {
const { ctx } = this;
}
}
module.exports = UserController;
  • 参数校验统一处理参照如下:
const { ctx } = this;
try {
ctx.validate(loginUserRule);
} catch (e) {
return this.error('参数校验失败', -1, e.errors);
}
  • 登录接口编写
    • 首先通过request对象的body属性得到请求中的用户名和密码;
    • 通过用户名在MongoDB中查找用户,成功找到说明用户名正常;
    • 通过将密码进行md5加密与存储的密码比对,成功则说明密码正常;
    • 使用jwt将用户名写入并生成token,存储到MongoDB中;
    • token成功存储后成功响应前端接口数据。
const { username, password } = ctx.request.body;
const ret = await ctx.model.User.findOne({ username });
if (ret.password && ret.password === md5(password)) {
const token = jwt.sign({ username }, KEY.secretOrPrivateKey);
const tokenRet = await ctx.model.Token.create({
token,
});
if (tokenRet._id) {
this.success({ token });
}
} else {
this.error('用户名或密码错误');
}
  • 用户信息获取接口编写
    • 获取用户信息的接口将只需要传递token即可;
    • 我们通过将接收到的token进行Mongo查询,成功查询说明Token正常;
    • 通过验证token正确性得到被写入的用户名;
    • 我们在通过用户名查询Mongo中对应的详细信息,成功查询后相应前端接口数据。
const token = ctx.request.header['x-token'];
const ret = await ctx.model.Token.findOne({ token });
if (ret) {
const { username } = jwt.verify(token, KEY.secretOrPrivateKey);
const userRet = await ctx.model.User.findOne({ username });
if (userRet) {
this.success(userRet);
}
}
  • 登出接口编写
    • 同样通过获取token并查询,成功查询说明token正常;
    • 这时候我们只需要删除token,成功响应前端接口数据即可。
const token = ctx.request.header['x-token'];
const ret = await ctx.model.Token.findOne({ token });
if (ret) {
const tokenRet = await ctx.model.Token.deleteOne({ token });
if (tokenRet && tokenRet.ok === 1) {
this.success('success');
}
} else {
this.error('服务器暂无在线记录');
}

至此我们就已经实现了一个最简单的App中用户的基本操作(登录,信息获取,登出)的功能,当然在实际的业务中将更为复杂。

总结:

这个流程下来,其实涉及的知识点还不少,比如说MongoDB的存取操作,JWT的生成验证,还有统一个数据结构应用的必要等,没有为自己App提供过服务的Coder们,一起来试试吧。


欢迎关注我的公众号“前端小鑫同学”,原创技术文章第一时间推送。

posted @   前端小鑫同学  阅读(5)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示