しばらく前、私はテクノロジーからプロダクトに転向した社内の同僚と彼のキャリアパスについて話し合っていたのですが、彼は私が深く信じていることを言いました。
「特定の分野に限定しないでください。テクノロジーから製品への変革は、まず第一に考え方の変化です。あなたはフロントエンドで働いてきました。データを扱うとき、あなたはデータの入力方法しか知りませんが、それがどのように出てくるかはわかりません。」
Vue を学習していたときに、登録とログインのプロジェクトを見て、それに倣って Vue プロジェクトを開始し、クライアントの送信 - サーバー受信 - 入力のプロセス全体を実装したようなものでした。 。
このプロジェクトは vue-cli をベースに構築されており、トークン方式でユーザーのログインを検証し、データベースへの登録、ユーザーの読み取り、ユーザーの削除などの機能を実装しています。この記事は読者がnodeとvueについて一定の基礎を持っていることを前提としているため、基本的な部分については詳しく説明しません。
システム環境:MacOS 10.13.3
タオバオミラーを使用してインストールする
$ npm install -g cnpm --registry=https://registry.npm.taobao.org
次に、すべてのnpm install をcnpm installに変更します。
プロジェクトのアイデアや選定した技術をより明確にするために、わかりやすく図を描きます。
1.プロジェクトの初期化
$ npm install
2. プロジェクトを開始する
$ npm run dev
3.MongoDBを起動する
$ mongod --dbpath XXX
xxx はプロジェクト内のデータフォルダーへのパスです (新しいフォルダーを作成することもできます。データベースはデータの保存に使用されます)。または、ターミナルに直接ドラッグすることもできます。
4. サーバーを起動します
$ node server.js
私は Ele.me の Element-UI を vue の優先UIライブラリとして選択しました。iview やvue-strapなどは ele ほど包括的ではないようです。
$ npm i element-ui -S
//在项目里的mian.js里增加下列代码
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
UI のタブ切り替えを使用して登録インターフェイスとログイン インターフェイスを切り替え、ログイン コンポーネントをログイン システム全体のメイン インターフェイスとして使用し、登録コンポーネントを独立したコンポーネントとして使用します。 Element-UIの構成方法やフォームバリデーション、その他の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>
次にコンポーネントを登録します
//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 は、単一ページのプロジェクトを作成するための Vue の中核です。コンポーネントを組み合わせてアプリケーションを構成できます。コンポーネントをルートにマッピングし、 vue-router にそれらをレンダリングする場所を指示する必要があります。 上記のコードにはすでにルーティングの切り替えが含まれています。ここでルーティングを改善しましょう。
$ cnpm i vue-router
import Router from 'vue-router'
Vue.use(Router)
src フォルダーの下に新しい router (フォルダー)/index.js を作成します。 以下の 3 つのコンポーネントを導入しました。
ログイン後のHelloWorld表示ページ
ログイン ログイン メインインターフェイス
レジスタレジスタコンポーネント
router.beforeEachルーティング ガードを使用して、最初にログインする必要があるページを設定します。 requireAuthフィールドを使用して、ルートにログイン許可が必要かどうかを判断し、トークンがあるかどうかを判断します (トークンがある場合は、直接ログインします)。ログインページにジャンプします。
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;
ルーティング ガード内のトークンがストアから取得されていることがわかります。これは、トークンのさまざまな状態をストアに保存し、取得、更新、削除などの操作を実行することを意味します。これには、vuex 状態管理の導入が必要です。 。
簡単な登録とログイン ページに vuex の使用が必要な理由を説明します。プロジェクト内の各コンポーネントの操作では、基本的に検証用のトークンを取得する必要があります。コンポーネント A がトークンを保存している場合、コンポーネント B のトークンの取得にはコンポーネント通信が含まれます。それはとても面倒なことでしょう。 vuex の導入により、コンポーネント間の通信ではなく、コンポーネントとストア間の通信が行われるようになり、シンプルで便利になりました。
$ cnpm i vuex --S
main.jsにstoreを導入し、vueインスタンスにstoreを追加します。
//引入store
import store from './store'
次に、vuex を使用する必要があるコンポーネントにそれを導入します。
//store index.js
import Vuex from 'vuex'
Vue.use(Vuex)
srcフォルダーの下に新しいストア(フォルダー)/index.jsを作成します
//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
});
アクションを通じてミューテーションを送信し、トークンを変更し、トークンをクリアし、ユーザー名を保存していることがわかります。
この時点でプロジェクトを開始すると、事前登録およびログイン インターフェイスが表示されます。登録またはログイン ボタンをクリックすると、対応するインターフェイスに切り替わります。ログイン後、helloworld ページが表示されます。
基本的なインターフェースは記述できました。次はフォームデータをバックグラウンドに送信して一連の処理を実行します。バックエンド インターフェイスがまだなくても構いません。最初にフロントエンドの axios リクエストを作成しましょう。
Vue の通信は以前はvue-resource を使用していましたが、多くの落とし穴がありました。 vue2.0 が登場するまでは、 vue-resource を放棄してaxios を使用してください。
リクエストの送信とデータの非同期取得に使用される ajax をカプセル化します。 Promise ベースの HTTP クライアント。ブラウザおよび Node.js に適しています。
中国語での具体的な API の説明: https://www.kancloud.cn/yunye/axios/234845
$ cnpm i -S axios
import axios from 'axios'
vue-router のセットアップの一部では、ログインを必要とするルートを遮断するためにルーティング ガードが追加されますが、この方法は単純なフロントエンド ルーティング制御にすぎず、ログイン許可が必要なルートへのユーザーのアクセスを実際に防ぐことはできません。トークンの有効期限が切れても、トークンは引き続きローカルに保存されます。このとき、ログイン権限が必要なルートにアクセスする場合は、実際にはユーザーに再度ログインを求める必要があります。このとき、インターセプタ+バックエンドインターフェースから返されるhttpステータスコードが判定に必要となります。
src フォルダー (App.vue と同じレベル) の下に新しい axios.js を作成します。
//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);
}
}
このコードは最終的に、ユーザーの登録、ログイン、取得 (user)、および削除 (delUser) に対応する 4 つの要求メソッドを公開します。これらはすべて /api の下にあります。 4 つの要求インターフェイスは次のとおりです。
http://localhost:8080/api/login
http://localhost:8080/api/register
http://localhost:8080/api/user
http://localhost:8080/api/delUser
後で、これら 4 つのメソッドを使用して、対応するバックエンド インターフェイスを作成します。
この記事はここからサーバー側について始まります。サーバー側はデータベースと http セキュア通信 (jwt) とともに構築する必要があるため、このセクションは以下のデータベースと jwt の章と併せて読んでください。
koa2 は、async/await 構文を使用して、繰り返しの煩雑なコールバック関数のネストを回避し、ctx を使用して Context オブジェクトにアクセスできます。
ここで、koa2 を使用してプロジェクトの API サービス インターフェイスを作成します。
$ cnpm i koa
$ cnpm i koa-router -S //koa路由中间件
$ cnpm i koa-bodyparser -S //处理post请求,并把koa2上下文的表单数据解析到ctx.request.body中
const Koa = require('koa');
サーバー全体の起動エントリとして、プロジェクトのルート ディレクトリに新しいserver.jsを作成します。
//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);
});
コードに見られるように、ユーザーの取得とユーザーの削除の両方に検証トークンが必要です (詳細については、以下の jwt の章を参照してください)。また、axios の以前のリクエスト パスと一致する /api に 4 つのインターフェイスをハングしました。
さらに、プロジェクトの起動ポートは 8080 で、koa インターフェイスによって監視されるポートは 8888 であるため、config/index.js ファイルの dev 構成に次の内容を追加する必要があります。
proxyTable: {
'/api': {
target: 'http://localhost:8888',
changeOrigin: true
}
},
JWT は、HTTP 通信中に ID 認証を実行するのに役立ちます。
特定の API の詳細については、https://segmentfault.com/a/1190000009494020 を参照してください。
1. クライアントはユーザー名とパスワードを使用してサーバーにログインします。
2. サーバーはクライアントの身元を確認します。
3. サーバーはユーザーのトークンを生成し、クライアントに返します。
4. クライアントはトークンをローカル ブラウザに、通常は Cookie に保存します (この記事では sessionStorage を使用しますが、状況によって異なります)。
5. クライアントがリクエストを開始するとき、トークンを運ぶ必要があります。
6. リクエストを受信した後、サーバーはまずトークンを検証してからデータを返します。サーバーはトークンを保存する必要はなく、トークンに含まれる情報を検証するだけで済みます。ユーザー情報の検証を通過できる限り、クライアントがバックグラウンドでどのサーバーにアクセスしても問題ありません。
サーバーフォルダーに、以下に新しい /token (フォルダー) を作成し、トークンの確認と追加をそれぞれ行うためのメソッドを配置する checkToken.js と createToken.js を追加します。
$ 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;
};
トークンを作成するとき、JWT ペイロードの属性としてユーザー名を使用し、キーを「zhangzhongjie」に設定し、トークンの有効期限を 60 秒に設定します。つまり、ログイン後、60 秒以内にページを更新すれば、再度ログインする必要はありません。
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();
};
最初にトークンを取得してから、jwt.verify を使用して検証します。キーは createToken.js のキー 'zhangzhongjie' に対応している必要があることに注意してください。トークンが空であるか、期限切れであるか、検証に失敗した場合は、401 エラーがスローされ、再度ログインする必要があります。
MongoDB は、Web アプリケーションにスケーラブルで高性能なデータ ストレージ ソリューションを提供するように設計されたドキュメント指向のデータベース管理システムです。 MongoDB への接続にはノードを使用すると非常に便利です。
$ cnpm i mongoose -S
MongoDB に接続するにはいくつかの方法があります。ここでは接続を使用します。 connection は mongoose モジュールのデフォルトの参照であり、Connection オブジェクトを返します。
サーバー フォルダーにデータベース接続エントリとして新しい db.js を作成します。
//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;
私たちが使用する接続に加えて、*connect()およびcreateConnection()* 接続メソッドもあります。
スキーマは、このタイプのドキュメントがデータベース内で特定の構成と保存モードを持つようにテーブルのテンプレートを定義します。ただし、それはドキュメントの外観を定義するだけであり、ドキュメントの生成とドキュメントに対するさまざまな操作 (追加、削除、変更、確認) は、対応するモデルを通じて行われます。次に、userSchema を何かに変換する必要があります。モデル、つまりモデルは操作できるハンドルです。
モデルをコンパイルすると、 Userという名前のモデルが得られます。
ここで定義するスキーマ テーブルは、後でデータベースに書き込んで登録するときに、このテーブルに対応している必要があることに注意してください。
データベースの操作メソッドを格納するコントローラー(フォルダー)/user.jsをサーバーフォルダー配下に新規作成します。
最初にいくつかの機能的なプラグインをインストールします
$ 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
};
上記のメソッドは、プロジェクトにおけるデータベース操作の核となります。これらを分析してみましょう。
まず、3 つの一般的な基本メソッド、findUser、findAllUsers、および delUser が定義されています。このうち、findUser はusernameパラメーターを渡す必要があり、delUser はidパラメーターを渡す必要があります。
ユーザーの投稿によって送信されたフォーム情報、データベースに従って事前に設計されモデルにコンパイルされた新しいユーザー、および取得したユーザー名、パスワード (sha1 ハッシュで暗号化する必要がある)、トークン (以前に作成したものを使用) を取得します。 createToken メソッド、およびユーザー名を jwt のペイロード パラメーターとして使用し、生成時間を節約します。
このとき、まずデータベースを検索してユーザー名が存在するかどうかを確認する必要があります。存在する場合は失敗が返され、存在しない場合はユーザーがデータベースに保存され、成功が返されます。
ユーザーの投稿、ユーザー名、パスワードのフォーム情報を取得します (登録はハッシュ化されているため、この時点で復号化する必要があります)。データベースからユーザー名を検索して、ユーザー名が存在するかどうかを確認します。戻りエラーがない場合は、データベースに保存されているパスワードがユーザーによって送信されたパスワードと一致するかどうかを確認します。一致する場合は、新しいトークンが生成されます。ユーザーを返し、データベースに保存されます。
上記のパブリック findAllUsers メソッドをカプセル化し、結果に情報を含めるだけで、helloworld ページがこのデータを取得して後で表示できるようになります。
まず、削除する必要があるユーザー ID を取得し、それをパラメーターとして渡す必要があることに注意してください。
これらのメソッドを作成した後は、以前は完璧ではなかった登録およびログイン機能を改善できます。
登録が完了し、データベースにデータが保存された後、登録してデータベースに保存されたデータを確認したい場合は、データベース可視化ツールを使用する必要があります。私は操作が簡単なMongoBooster を使用しています。
以下の図からわかるように、例で登録される 2 つのデータには、ID、ユーザー名、パスワード、トークン、時刻が含まれます。この長いパスワード文字列は、ハッシュ暗号化によってコンパイルされます。
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: '用户名已经存在'
});
}
})
}
以前はログイン コンポーネントでデータを送信していませんでしたが、検証が成功した後にログイン操作を完了するための一連のメソッドを追加します。 axios の紹介。
import axios from '../axios.js'
次に、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');
}
});
}
フォームデータをバックグラウンドに送信し、データのステータスを返し、アカウントが存在するかどうかを判断します。ログインに成功したら、返されたトークンとユーザー名を取得してストアに保存し、ターゲットの HelloWorld ページにジャンプする必要があります。
登録とログインに成功すると、最終的に実際の表示ページ、helloworld! に到達しました。
このコンポーネントを改良して、現在登録されているすべてのユーザー名を表示し、削除ボタンを表示できるようにしましょう。
//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>
出力ページは比較的シンプルです。いくつかの重要なポイントを示します。
1. getUser() インターフェースは、インスタンスの作成 ( created() ) の直後にリクエストする必要があります。リクエストが失敗した場合は、トークンをクリアする必要があります。リクエストが成功した場合は、戻りデータをページのユーザーに入力する必要があります。レンダリング。
2. thisID はオブジェクト形式で記述する必要があります。そうでない場合はエラーが報告されます。
3. ログアウト時にトークンをクリアする
人々の考え方を変えることは確かに最も難しいことです。プロセスによれば、koa は最初にインターフェイスを設計し、次にフロントエンドがこのインターフェイスに基づいてリクエストを作成します。しかし、逆に、私は最初にフロントエンドのリクエストを作成し、次にこのリクエストに基づいてインターフェイスを作成します。
もちろん、私も多くの困難に遭遇しました。フロントエンドの表示ページが完成し、axios が作成されたとき、序文で述べたように、koa を使用してインターフェイスを作成することに長い間行き詰まりました。 「私はデータの入力方法を知っているだけで、データを取り出す方法を知りません。」その後、インターフェイス エラー 500 が発生し、長い間デバッグしました。主な理由は、インターフェイスをデバッグするという概念がなかったためです。最終的には、会社の上司の Langya が問題を解決してくれました。