Il y a quelque temps, je discutais de son parcours professionnel avec un collègue de l'entreprise qui était passé de la technologie au produit, et il a dit quelque chose en lequel je croyais profondément :
" Ne vous limitez pas à un certain domaine. La transformation de la technologie en produit est avant tout un changement de pensée. Vous avez travaillé sur le front-end. Lorsque vous interagissez avec des données, vous savez seulement comment les saisir, mais vous Je ne sais pas comment ça se passe. C'est une limitation.
C'était comme une illumination. Lorsque j'apprenais Vue, j'ai vu un projet d'enregistrement et de connexion, j'ai simplement emboîté le pas et j'ai lancé un projet Vue, introduit koa et mongodb et implémenté l'ensemble du processus de soumission-serveur de base de données. .
Ce projet est construit sur la base de vue-cli, utilise la méthode des jetons pour vérifier la connexion des utilisateurs et implémente des fonctions telles que l'enregistrement dans la base de données, la lecture des utilisateurs et la suppression d'utilisateurs. L'article suppose que les lecteurs ont une certaine base en node et en vue, les parties de base ne seront donc pas décrites en détail.
Environnement système : MacOS 10.13.3
Installer à l'aide du miroir Taobao
$ npm install -g cnpm --registry=https://registry.npm.taobao.org
Ensuite, remplacez toutes les installations npm par cnpm install
Afin de rendre plus claires les idées du projet et les technologies sélectionnées, un diagramme est dessiné pour faciliter la compréhension.
1.Initialiser le projet
$ npm install
2. Démarrez le projet
$ npm run dev
3. Démarrez MongoDB
$ mongod --dbpath XXX
xxx est le chemin d'accès au dossier de données dans le projet (vous pouvez également en créer un nouveau, la base de données est utilisée pour stocker les données), ou vous pouvez le faire glisser directement dans le terminal.
4. Démarrez le serveur
$ node server.js
J'ai choisi Element-UI d'Ele.me comme bibliothèque d'interface utilisateur préférée pour vue. D'autres, comme iview et vue-strap, ne semblent pas aussi complets qu'ele.
$ npm i element-ui -S
//在项目里的mian.js里增加下列代码
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
Utilisez le changement d'onglet dans l'interface utilisateur pour basculer entre les interfaces d'enregistrement et de connexion, utilisez le composant de connexion comme interface principale de l'ensemble du système de connexion et le composant de registre comme composant indépendant. Veuillez vous référer au site officiel pour la méthode de composition d'Element-UI, la validation de formulaire et d'autres API.
//login组件
<template>
<div class="login">
<el-tabs v-model="activeName" @tab-click="handleClick">
<el-tab-pane label="登录" name="first">
<el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
<el-form-item label="名称" prop="name">
<el-input v-model="ruleForm.name"></el-input>
</el-form-item>
<el-form-item label="密码" prop="pass">
<el-input type="password" v-model="ruleForm.pass" auto-complete="off"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm('ruleForm')">登录</el-button>
<el-button @click="resetForm('ruleForm')">重置</el-button>
</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane label="注册" name="second">
<register></register>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script>
import register from '@/components/register'
export default {
data() {
var validatePass = (rule, value, callback) => {
if (value === '') {
callback(new Error('请输入密码'));
} else {
if (this.ruleForm.checkPass !== '') {
this.$refs.ruleForm.validateField('checkPass');
}
callback();
}
};
return {
activeName: 'first',
ruleForm: {
name: '',
pass: '',
checkPass: '',
},
rules: {
name: [
{ required: true, message: '请输入您的名称', trigger: 'blur' },
{ min: 2, max: 5, message: '长度在 2 到 5 个字符', trigger: 'blur' }
],
pass: [
{ required: true, validator: validatePass, trigger: 'blur' }
]
},
};
},
methods: {
//选项卡切换
handleClick(tab, event) {
},
//重置表单
resetForm(formName) {
this.$refs[formName].resetFields();
},
//提交表单
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
this.$message({
type: 'success',
message: '登录成功'
});
this.$router.push('HelloWorld');
} else {
console.log('error submit!!');
return false;
}
});
},
},
components: {
register
}
}
</script>
<style rel="stylesheet/scss" lang="scss">
.login {
width: 400px;
margin: 0 auto;
}
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
.el-tabs__item {
text-align: center;
width: 60px;
}
</style>
Ensuite, il faut enregistrer le composant
//register组件
<template>
<el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
<el-form-item label="名称" prop="name">
<el-input v-model="ruleForm.name"></el-input>
</el-form-item>
<el-form-item label="密码" prop="pass">
<el-input type="password" v-model="ruleForm.pass" auto-complete="off"></el-input>
</el-form-item>
<el-form-item label="确认密码" prop="checkPass">
<el-input type="password" v-model="ruleForm.checkPass" auto-complete="off"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm('ruleForm')">注册</el-button>
<el-button @click="resetForm('ruleForm')">重置</el-button>
</el-form-item>
</el-form>
</template>
<script>
export default {
data() {
var validatePass = (rule, value, callback) => {
if (value === '') {
callback(new Error('请输入密码'));
} else {
if (this.ruleForm.checkPass !== '') {
this.$refs.ruleForm.validateField('checkPass');
}
callback();
}
};
var validatePass2 = (rule, value, callback) => {
if (value === '') {
callback(new Error('请再次输入密码'));
} else if (value !== this.ruleForm.pass) {
callback(new Error('两次输入密码不一致!'));
} else {
callback();
}
};
return {
activeName: 'second',
ruleForm: {
name: '',
pass: '',
checkPass: '',
},
rules: {
name: [
{ required: true, message: '请输入您的名称', trigger: 'blur' },
{ min: 2, max: 5, message: '长度在 2 到 5 个字符', trigger: 'blur' }
],
pass: [
{ required: true, validator: validatePass, trigger: 'blur' }
],
checkPass: [
{ required: true, validator: validatePass2, trigger: 'blur' }
],
}
};
},
methods: {
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
this.$message({
type: 'success',
message: '注册成功'
});
// this.activeName: 'first',
} else {
console.log('error submit!!');
return false;
}
});
},
resetForm(formName) {
this.$refs[formName].resetFields();
}
}
}
</script>
vue-router est le cœur de Vue pour créer des projets d'une seule page. Les applications peuvent être composées en combinant des composants. Ce que nous devons faire est de mapper les composants aux routes, puis d'indiquer à vue-router où les rendre. Le code ci-dessus implique déjà quelques changements de routage. Améliorons maintenant le routage :
$ cnpm i vue-router
import Router from 'vue-router'
Vue.use(Router)
Créez un nouveau routeur (dossier)/index.js sous le dossier src. Nous avons introduit trois composants :
Page d'affichage de HelloWorld après la connexion
connexion interface principale de connexion
composant de registre de registre
Utilisez router.beforeEach router guard pour définir la page qui doit être connectée en premier. Utilisez le champ requireAuth pour déterminer si la route nécessite des autorisations de connexion. Les routes qui nécessitent des autorisations seront interceptées, puis il sera déterminé s'il existe un jeton (le jeton sera discuté ci-dessous). Si tel est le cas, connectez-vous directement. passez à la page de connexion.
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
import login from '@/components/login'
import register from '@/components/register'
Vue.use(Router)
const router = new Router({
mode: 'history',
routes: [{
path: '/',
name: 'home',
component: HelloWorld,
meta: {
requiresAuth: true
}
},
{
path: '/HelloWorld',
name: 'HelloWorld',
component: HelloWorld,
},
{
path: '/login',
name: 'login',
component: login,
},
{
path: '/register',
name: 'register',
component: register,
},
]
});
//注册全局钩子用来拦截导航
router.beforeEach((to, from, next) => {
//获取store里面的token
let token = store.state.token;
//判断要去的路由有没有requiresAuth
if (to.meta.requiresAuth) {
if (token) {
next();
} else {
next({
path: '/login',
query: { redirect: to.fullPath } // 将刚刚要去的路由path作为参数,方便登录成功后直接跳转到该路由
});
}
} else {
next();
}
});
export default router;
Nous pouvons voir que le jeton dans le garde de routage est obtenu à partir du magasin, ce qui signifie que nous stockons les différents états du jeton dans le magasin et effectuons des opérations telles que la récupération, la mise à jour et la suppression. Cela nécessite l'introduction de la gestion des états vuex. .
Expliquez pourquoi une simple page d'enregistrement et de connexion nécessite l'utilisation de vuex : Le fonctionnement de chacun de nos composants dans le projet nécessite essentiellement l'obtention d'un jeton pour vérification. Si le composant A stocke un jeton, l'acquisition du jeton par le composant B implique une communication entre les composants. ce qui serait très fastidieux. Avec l'introduction de vuex, ce n'est plus la communication entre les composants, mais la communication entre les composants et le magasin qui est simple et pratique.
$ cnpm i vuex --S
Introduisez le magasin dans main.js et ajoutez le magasin à l'instance de vue.
//引入store
import store from './store'
Puis introduisez-le dans les composants qui doivent utiliser vuex
//store index.js
import Vuex from 'vuex'
Vue.use(Vuex)
Créez un nouveau magasin (dossier)/index.js sous le dossier src
//store index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
//初始化时用sessionStore.getItem('token'),这样子刷新页面就无需重新登录
const state = {
token: window.sessionStorage.getItem('token'),
username: ''
};
const mutations = {
LOGIN: (state, data) => {
//更改token的值
state.token = data;
window.sessionStorage.setItem('token', data);
},
LOGOUT: (state) => {
//登出的时候要清除token
state.token = null;
window.sessionStorage.removeItem('token');
},
USERNAME: (state, data) => {
//把用户名存起来
state.username = data;
window.sessionStorage.setItem('username', data);
}
};
const actions = {
UserLogin({ commit }, data){
commit('LOGIN', data);
},
UserLogout({ commit }){
commit('LOGOUT');
},
UserName({ commit }, data){
commit('USERNAME', data);
}
};
export default new Vuex.Store({
state,
mutations,
actions
});
Vous pouvez voir que nous soumettons des mutations par le biais d'actions, modifions les jetons, effaçons les jetons et stockons les noms d'utilisateur.
Lorsque vous démarrez le projet à ce moment, vous pouvez voir l'interface d'inscription et de connexion préliminaire. Cliquez sur le bouton d'inscription ou de connexion pour passer à l'interface correspondante, et il y a une vérification de base du formulaire. Après vous être connecté, vous entrerez dans la page helloworld.
Nous avons écrit l'interface de base et l'étape suivante consiste à envoyer les données du formulaire en arrière-plan et à effectuer une série de traitements. Peu importe s'il n'y a pas encore d'interface back-end. Écrivons d'abord la requête axios frontale.
La communication de Vue utilisait auparavant vue-resource , et il y avait de nombreux pièges. En attendant que vue2.0 arrive, abandonnez simplement vue-resource et utilisez axios .
Encapsulez ajax, utilisé pour envoyer des requêtes et obtenir des données de manière asynchrone. Client HTTP basé sur des promesses, adapté pour : navigateur et node.js.
Description spécifique de l'API en chinois : https://www.kancloud.cn/yunye/axios/234845
$ cnpm i -S axios
import axios from 'axios'
Dans le cadre de la configuration de vue-router, des gardes de routage sont ajoutés pour intercepter les routes qui nécessitent une connexion, mais cette méthode n'est qu'un simple contrôle de routage frontal et ne peut pas vraiment empêcher les utilisateurs d'accéder aux routes qui nécessitent des autorisations de connexion. Lorsque le jeton expire, il est toujours enregistré localement. À ce stade, lorsque vous accédez à un itinéraire qui nécessite des autorisations de connexion, vous devez en fait demander à l'utilisateur de se reconnecter. A ce stade, les intercepteurs + le code d'état http renvoyé par l'interface backend sont nécessaires pour juger.
Créez un nouveau axios.js sous le dossier src (même niveau que App.vue)
//axios.js
import axios from 'axios'
import store from './store'
import router from './router'
//创建axios实例
var instance = axios.create({
timeout: 5000, //请求超过5秒即超时返回错误
headers: { 'Content-Type': 'application/json;charset=UTF-8' },
});
//request拦截器
instance.interceptors.request.use(
config => {
//判断是否存在token,如果存在的话,则每个http header都加上token
if (store.state.token) {
config.headers.Authorization = `token ${store.state.token}`;
}
return config;
}
);
//respone拦截器
instance.interceptors.response.use(
response => {
return response;
},
error => { //默认除了2XX之外的都是错误的,就会走这里
if (error.response) {
switch (error.response.status) {
case 401:
router.replace({ //跳转到登录页面
path: 'login',
query: { redirect: router.currentRoute.fullPath } // 将跳转的路由path作为参数,登录成功后跳转到该路由
});
}
}
return Promise.reject(error.response);
}
);
export default {
//用户注册
userRegister(data){
return instance.post('/api/register', data);
},
//用户登录
userLogin(data){
return instance.post('/api/login', data);
},
//获取用户
getUser(){
return instance.get('/api/user');
},
//删除用户
delUser(data){
return instance.post('/api/delUser', data);
}
}
Le code expose enfin quatre méthodes de requête, qui correspondent à l'enregistrement, à la connexion, à l'obtention (utilisateur) et à la suppression (delUser), et elles se trouvent toutes sous /api. Les quatre interfaces de requête sont :
http://localhost:8080/api/login
http://localhost:8080/api/register
http://localhost:8080/api/user
http://localhost:8080/api/delUser
Plus tard, nous utiliserons ces quatre méthodes pour écrire l'interface backend correspondante.
L'article commence à partir d'ici côté serveur. Étant donné que le côté serveur doit être construit avec la base de données et la communication sécurisée http (jwt), veuillez lire cette section en conjonction avec les chapitres sur la base de données et jwt ci-dessous.
koa2 peut utiliser la syntaxe async/await pour éviter l'imbrication répétée et fastidieuse des fonctions de rappel, et utiliser ctx pour accéder à l'objet Context.
Nous utilisons maintenant koa2 pour écrire l'interface de service API du projet.
$ cnpm i koa
$ cnpm i koa-router -S //koa路由中间件
$ cnpm i koa-bodyparser -S //处理post请求,并把koa2上下文的表单数据解析到ctx.request.body中
const Koa = require('koa');
Créez un nouveau server.js sous le répertoire racine du projet comme entrée de démarrage pour l'ensemble du serveur.
//server.js
const Koa = require('koa');
const app = new Koa();
//router
const Router = require('koa-router');
//父路由
const router = new Router();
//bodyparser:该中间件用于post请求的数据
const bodyParser = require('koa-bodyparser');
app.use(bodyParser());
//引入数据库操作方法
const UserController = require('./server/controller/user.js');
//checkToken作为中间件存在
const checkToken = require('./server/token/checkToken.js');
//登录
const loginRouter = new Router();
loginRouter.post('/login', UserController.Login);
//注册
const registerRouter = new Router();
registerRouter.post('/register', UserController.Reg);
//获取所有用户
const userRouter = new Router();
userRouter.get('/user', checkToken, UserController.GetAllUsers);
//删除某个用户
const delUserRouter = new Router();
delUserRouter.post('/delUser', checkToken, UserController.DelUser);
//装载上面四个子路由
router.use('/api',loginRouter.routes(),loginRouter.allowedMethods());
router.use('/api',registerRouter.routes(),registerRouter.allowedMethods());
router.use('/api',userRouter.routes(),userRouter.allowedMethods());
router.use('/api',delUserRouter.routes(),delUserRouter.allowedMethods());
//加载路由中间件
app.use(router.routes()).use(router.allowedMethods());
app.listen(8888, () => {
console.log('The server is running at http://localhost:' + 8888);
});
Comme on peut le voir dans le code, l'obtention et la suppression d'utilisateurs nécessitent des jetons de vérification (voir le chapitre jwt ci-dessous pour plus de détails), et nous avons suspendu les quatre interfaces sur /api, ce qui est cohérent avec le chemin de requête précédent d'axios.
De plus, puisque le port de démarrage de notre projet est 8080 et que le port surveillé par l'interface koa est 8888, nous devons ajouter ce qui suit à la configuration de développement dans le fichier config/index.js :
proxyTable: {
'/api': {
target: 'http://localhost:8888',
changeOrigin: true
}
},
JWT peut nous aider à effectuer l'authentification d'identité lors de la communication HTTP.
Pour des détails spécifiques sur l'API, veuillez consulter : https://segmentfault.com/a/1190000009494020
1. Le client se connecte au serveur via son nom d'utilisateur et son mot de passe ;
2. Le serveur vérifie l'identité du client ;
3. Le serveur génère un jeton pour l'utilisateur et le renvoie au client ;
4. Le client enregistre le token sur le navigateur local, généralement dans un cookie (cet article utilise sessionStorage, cela dépend de la situation) ;
5. Lorsque le client lance une demande, il doit transporter le jeton ;
6. Après avoir reçu la demande, le serveur vérifie d'abord le jeton puis renvoie les données. Le serveur n'a pas besoin de sauvegarder le Token, il lui suffit de vérifier les informations contenues dans le Token. Quel que soit le serveur auquel le client accède en arrière-plan, à condition qu'il puisse réussir la vérification des informations utilisateur.
Dans le dossier du serveur, créez un nouveau /token (dossier) ci-dessous et ajoutez checkToken.js et createToken.js pour placer respectivement les méthodes de vérification et d'ajout de jetons.
$ cnpm i jsonwebtoken -S
const jwt = require('jsonwebtoken');
module.exports = function(user_id){
const token = jwt.sign({user_id: user_id}, 'zhangzhongjie', {expiresIn: '60s'
});
return token;
};
Lors de la création du jeton, nous utilisons le nom d'utilisateur comme attribut de la charge utile JWT, définissons la clé sur « zhangzhongjie » et définissons le délai d'expiration du jeton sur 60 secondes. Cela signifie qu'après la connexion, l'actualisation de la page dans les 60 secondes ne nécessite pas de se reconnecter.
const jwt = require('jsonwebtoken');
//检查token是否过期
module.exports = async ( ctx, next ) => {
//拿到token
const authorization = ctx.get('Authorization');
if (authorization === '') {
ctx.throw(401, 'no token detected in http headerAuthorization');
}
const token = authorization.split(' ')[1];
let tokenContent;
try {
tokenContent = await jwt.verify(token, 'zhangzhongjie');//如果token过期或验证失败,将抛出错误
} catch (err) {
ctx.throw(401, 'invalid token');
}
await next();
};
Obtenez d'abord le jeton, puis utilisez jwt.verify pour le vérifier. Notez que la clé doit correspondre à la clé « zhangzhongjie » de createToken.js. Si le jeton est vide, expire ou échoue à la vérification, une erreur 401 sera générée, vous obligeant à vous reconnecter.
MongoDB est un système de gestion de bases de données orienté documents conçu pour fournir des solutions de stockage de données évolutives et performantes pour les applications WEB. Il est très pratique d'utiliser un nœud pour se connecter à MongoDB.
$ cnpm i mongoose -S
Il existe plusieurs façons de se connecter à MongoDB. Ici, nous utilisons la connexion. connection est la référence par défaut du module mongoose et renvoie un objet Connection.
Créez un nouveau db.js dans le dossier du serveur comme entrée de connexion à la base de données.
//db.js
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/vue-login');
let db = mongoose.connection;
// 防止Mongoose: mpromise 错误
mongoose.Promise = global.Promise;
db.on('error', function(){
console.log('数据库连接出错!');
});
db.on('open', function(){
console.log('数据库连接成功!');
});
//声明schema
const userSchema = mongoose.Schema({
username: String,
password: String,
token: String,
create_time: Date
});
//根据schema生成model
const User = mongoose.model('User', userSchema)
module.exports = User;
En plus de la connexion que nous utilisons, il existe également les méthodes de connexion *connect() et createConnection()*.
Schema définit le modèle du tableau pour que ce type de document ait une composition et un mode de stockage spécifiques dans la base de données. Mais cela définit uniquement à quoi ressemble le document. Quant à la génération du document et à l'exécution de diverses opérations sur le document (ajout, suppression, modification et vérification), cela se fait via le modèle correspondant. Nous devons ensuite convertir le schéma utilisateur en quelque chose. nous pouvons utiliser le modèle, c'est-à-dire que le modèle est la poignée sur laquelle nous pouvons opérer.
Après avoir compilé le modèle, nous obtenons un modèle nommé User .
Faites attention à la table de schéma que vous définissez ici. Le stockage des données doit correspondre à cette table lors de son écriture et de son enregistrement ultérieur dans la base de données.
Créez un nouveau contrôleur (dossier)/user.js sous le dossier du serveur pour stocker les méthodes de fonctionnement de la base de données.
Installez d'abord quelques plug-ins fonctionnels
$ cnpm i moment -s //用于生成时间
$ cnpm i objectid-to-timestamp -s //用于生成时间
$ cnpm i sha1 -s //安全哈希算法,用于密码加密
//user.js
const User = require('../db.js').User;
//下面这两个包用来生成时间
const moment = require('moment');
const objectIdToTimestamp = require('objectid-to-timestamp');
//用于密码加密
const sha1 = require('sha1');
//createToken
const createToken = require('../token/createToken.js');
//数据库的操作
//根据用户名查找用户
const findUser = (username) => {
return new Promise((resolve, reject) => {
User.findOne({ username }, (err, doc) => {
if(err){
reject(err);
}
resolve(doc);
});
});
};
//找到所有用户
const findAllUsers = () => {
return new Promise((resolve, reject) => {
User.find({}, (err, doc) => {
if(err){
reject(err);
}
resolve(doc);
});
});
};
//删除某个用户
const delUser = function(id){
return new Promise(( resolve, reject) => {
User.findOneAndRemove({ _id: id }, err => {
if(err){
reject(err);
}
console.log('删除用户成功');
resolve();
});
});
};
//登录
const Login = async ( ctx ) => {
//拿到账号和密码
let username = ctx.request.body.name;
let password = sha1(ctx.request.body.pass);//解密
let doc = await findUser(username);
if(!doc){
console.log('检查到用户名不存在');
ctx.status = 200;
ctx.body = {
info: false
}
}else if(doc.password === password){
console.log('密码一致!');
//生成一个新的token,并存到数据库
let token = createToken(username);
console.log(token);
doc.token = token;
await new Promise((resolve, reject) => {
doc.save((err) => {
if(err){
reject(err);
}
resolve();
});
});
ctx.status = 200;
ctx.body = {
success: true,
username,
token, //登录成功要创建一个新的token,应该存入数据库
create_time: doc.create_time
};
}else{
console.log('密码错误!');
ctx.status = 200;
ctx.body = {
success: false
};
}
};
//注册
const Reg = async ( ctx ) => {
let user = new User({
username: ctx.request.body.name,
password: sha1(ctx.request.body.pass), //加密
token: createToken(this.username), //创建token并存入数据库
create_time: moment(objectIdToTimestamp(user._id)).format('YYYY-MM-DD HH:mm:ss'),//将objectid转换为用户创建时间
});
//将objectid转换为用户创建时间(可以不用)
user.create_time = moment(objectIdToTimestamp(user._id)).format('YYYY-MM-DD HH:mm:ss');
let doc = await findUser(user.username);
if(doc){
console.log('用户名已经存在');
ctx.status = 200;
ctx.body = {
success: false
};
}else{
await new Promise((resolve, reject) => {
user.save((err) => {
if(err){
reject(err);
}
resolve();
});
});
console.log('注册成功');
ctx.status = 200;
ctx.body = {
success: true
}
}
};
//获得所有用户信息
const GetAllUsers = async( ctx ) => {
//查询所有用户信息
let doc = await findAllUsers();
ctx.status = 200;
ctx.body = {
succsess: '成功',
result: doc
};
};
//删除某个用户
const DelUser = async( ctx ) => {
//拿到要删除的用户id
let id = ctx.request.body.id;
await delUser(id);
ctx.status = 200;
ctx.body = {
success: '删除成功'
};
};
module.exports = {
Login,
Reg,
GetAllUsers,
DelUser
};
Les méthodes ci-dessus constituent le cœur des opérations de base de données dans le projet.
Tout d’abord, trois méthodes de base courantes sont définies : findUser, findAllUsers et delUser. Parmi eux, findUser doit transmettre le paramètre username et delUser doit transmettre le paramètre id .
Obtenez les informations du formulaire soumises par la publication de l'utilisateur, le nouvel utilisateur qui a été précédemment conçu selon la base de données et compilé dans un modèle, ainsi que le nom d'utilisateur obtenu, le mot de passe (doit être crypté avec le hachage sha1), le jeton (en utilisant le créé précédemment. createToken et utilisez le nom d'utilisateur comme paramètre de charge utile de jwt) et économisez le temps de génération.
À ce stade, vous devez d'abord rechercher dans la base de données pour voir si le nom d'utilisateur existe. S'il existe, il renverra un échec. Sinon, l'utilisateur sera stocké dans la base de données et le succès sera renvoyé.
Obtenez les informations du formulaire concernant la publication, le nom d'utilisateur et le mot de passe de l'utilisateur (l'enregistrement est haché et doit être déchiffré à ce moment). Recherchez le nom d'utilisateur dans la base de données pour déterminer si le nom d'utilisateur existe. S'il n'y a pas d'erreur de retour, déterminez si le mot de passe stocké dans la base de données est cohérent avec le mot de passe soumis par l'utilisateur. S'ils sont cohérents, un nouveau jeton sera généré pour. l'utilisateur et stocké dans la base de données. Renvoie le succès.
Encapsulez simplement la méthode publique findAllUsers ci-dessus et mettez les informations dans le résultat, afin que la page helloworld puisse obtenir ces données et les afficher plus tard.
Notez que vous devez d'abord obtenir l'ID utilisateur qui doit être supprimé et le transmettre en tant que paramètre.
Après avoir écrit ces méthodes, vous pouvez améliorer les fonctions d'enregistrement et de connexion qui n'étaient pas parfaites auparavant.
Lorsque nous complétons l'enregistrement et que les données sont stockées dans la base de données, nous souhaitons vérifier les données qui viennent d'être enregistrées et stockées dans la base de données, et nous devons utiliser des outils de visualisation de base de données. J'utilise MongoBooster , qui est facile à utiliser.
Comme vous pouvez le voir sur la figure ci-dessous, les deux données enregistrées dans l'exemple incluent l'identifiant, le nom d'utilisateur, le mot de passe, le jeton et l'heure. Cette longue chaîne de mots de passe est compilée grâce au cryptage par hachage.
Ajoutez le code suivant après validation du formulaire dans register.vue
//register.vue
if (valid) {
axios.userRegister(this.ruleForm)
.then(({}) => {
if (data.success) {
this.$message({
type: 'success',
message: '注册成功'
});
} else {
this.$message({
type: 'info',
message: '用户名已经存在'
});
}
})
}
Nous n'avons soumis aucune donnée auparavant dans le composant de connexion. Nous ajoutons maintenant une série de méthodes pour terminer l'opération de connexion après une vérification réussie : Présentation d'axios.
import axios from '../axios.js'
Ajoutez ensuite le code suivant après vérification du formulaire dans login.vue
//login.vue
if (valid) {
axios.userLogin(this.ruleForm)
.then(({ data }) => {
//账号不存在
if (data.info === false) {
this.$message({
type: 'info',
message: '账号不存在'
});
return;
}
//账号存在
if (data.success) {
this.$message({
type: 'success',
message: '登录成功'
});
//拿到返回的token和username,并存到store
let token = data.token;
let username = data.username;
this.$store.dispatch('UserLogin', token);
this.$store.dispatch('UserName', username);
//跳到目标页
this.$router.push('HelloWorld');
}
});
}
Soumettez les données du formulaire en arrière-plan, renvoyez l'état des données et déterminez si le compte existe ou non. Après une connexion réussie, vous devez obtenir le jeton et le nom d'utilisateur renvoyés, les enregistrer dans le magasin et accéder à la page HelloWorld cible.
Après une inscription et une connexion réussies, nous sommes finalement arrivés à la page d'affichage réelle : helloworld !
Améliorons ce composant afin qu'il affiche tous les noms d'utilisateurs actuellement enregistrés et donne un bouton de suppression.
//Helloworld.vue
<template>
<div class="hello">
<ul>
<li v-for="(item,index) in users" :key="item._id">
{{ index + 1 }}.{{ item.username }}
<el-button @click="del_user(index)">删除</el-button>
</li>
</ul>
<el-button type="primary" @click="logout()">注销</el-button>
</div>
</template>
<script>
import axios from '../axios.js'
export default {
name: 'HelloWorld',
data () {
return {
users:''
}
},
created(){
axios.getUser().then((response) => {
if(response.status === 401){
//不成功跳转回登录页
this.$router.push('/login');
//并且清除掉这个token
this.$store.dispatch('UserLogout');
}else{
//成功了就把data.result里的数据放入users,在页面展示
this.users = response.data.result;
}
})
},
methods:{
del_user(index, event){
let thisID = {
id:this.users[index]._id
}
axios.delUser(thisID)
.then(response => {
this.$message({
type: 'success',
message: '删除成功'
});
//移除节点
this.users.splice(index, 1);
}).catch((err) => {
console.log(err);
});
},
logout(){
//清除token
this.$store.dispatch('UserLogout');
if (!this.$store.state.token) {
this.$router.push('/login')
this.$message({
type: 'success',
message: '注销成功'
})
} else {
this.$message({
type: 'info',
message: '注销失败'
})
}
},
}
}
</script>
<style scoped>
h1, h2 {
font-weight: normal;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
.hello {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
width: 400px;
margin: 60px auto 0 auto;
}
</style>
La page de sortie est relativement simple. Voici quelques points clés :
1. L'interface getUser() doit être demandée immédiatement après la création de l'instance ( created() ). Si la demande échoue, le jeton doit être effacé. Si la demande réussit, les données de retour doivent être transmises à l'utilisateur pour la page. rendu.
2. thisID doit être écrit au format objet, sinon une erreur sera signalée
3. Effacez le jeton lors de la déconnexion
Changer la façon de penser des gens est en effet la chose la plus difficile. Selon le processus, koa devrait d'abord concevoir l'interface, puis le front-end fera des requêtes basées sur cette interface. Mais à l'inverse, j'écris d'abord les requêtes front-end, puis je formule l'interface en fonction de cette requête.
Bien sûr, j'ai également rencontré de nombreuses difficultés : lorsque j'ai terminé la page d'affichage du front-end et qu'axios a été écrit, j'ai été longtemps bloqué dans l'utilisation de koa pour écrire l'interface, je n'en avais aucune idée comme mentionné dans la préface. , "Je sais seulement comment saisir les données, pas comment les saisir." Savoir comment en sortir. Ensuite, j'ai rencontré une erreur d'interface de 500 et je l'ai débogué pendant longtemps. La raison principale était que je n'avais aucune idée du débogage de l'interface. En fin de compte, le patron de l'entreprise, Langya, a aidé à résoudre le problème.