koa2,mongoose前后端登录注册

项目编译:VSCode

需要用到的安装项:

# Vue 3 项目,安装最新版 Vant:
npm i vant -S

# Vue 2 项目,安装 Vant 2:
npm i vant@latest-v2 -S

npm install --save axios vue-axios

npm i koa --save  安装koa2

npm install --save mongoose 安装mongoose

npm install glob --save 安装glob

npm install --save bcrypt    安装bcrypt进行加密和解密

npm i koa-router --save 安装koa-router

npm install koa-bodyparser --save  安装koa-bodyparser来处理POST请求参数

npm install koa2-cors --save  安装koa2-cors来解决同源策略,解决跨域

项目目录

image

一、创建项目shopping

用脚手架创建一个项目

vue create shopping

二、在根目录创建serve文件夹并初始化项目

npm init -y

1、在serve文件下创建index.js文件作为程序的主接口

const app = new Koa()

app.use(async(ctx) => {

    ctx.body = '<h2>Hello Word</h2>'

})

app.listen(3000, () => {

    console.log("koa服务已启动,在3000端口");

})

三、MongoDB的安装、使用

1、windows下的安装

下载MongoDB的网址

步骤一:

image.png

步骤二:选择Custom,自定义安装

image.png

选择自己想要的路径安装

image.png

步骤三:

"install mongoDB compass" 不勾选,MongoDB Compass 是一个图形界面管理工具

image.png

如果在自定义安装在进度条提示到starting service的时候报错:

image.png

提示你权限不够,WTF?

这个时候其实MongoDB这个服务已经安装成功。我们不要动这个弹窗,直接打开服务修改如下图,再回来点Retry就可以安装成功了

image.png

还有一个问题就是注意事项就是,如果你原来安装过,一定要重启,不然重装会出一些问题。

把mongodb\bin目录设置到环境变量后命令无效问题:

windows10环境变量设置后可能不会生效,一般设置后建议重启电脑。

2、创建数据目录

MongoDB 将数据目录存储在 db 目录下。但是这个数据目录不会主动创建,我们在安装完成后需要创建它。

请注意,数据目录应该放在根目录下 (如: C:\ 或者 D:\ 等 )。

image.png

3、配置环境变量

image.png

4、命令行下运行 MongoDB 服务器

为了从命令提示符下运行 MongoDB 服务器,你必须从 MongoDB 目录的 bin 目录中执行 mongod.exe 文件。

5、vue中安装MongoDB for VS Code

image.png

四、mongoose的安装使用

1、安装mongoose

npm install --save mongoose

2、启动数据库

在cmd命令行进入C盘目录下的data中的db文件,执行下面的命令

mongod --dbpath C:\data\db

3、连接数据库

我们在项目的serve文件夹下建立一个database文件夹,用来存放和数据库操作有关的文件。
在database文件夹下,建立一个init.js文件,用来作数据库的连接和一些初始化的事情。
/serve/database/init.js

安装glob并引入

3.1安装

npm install glob

3.2 引入glob

const glob = require('glob')
const mongoose = require('mongoose');
const db = 'mongodb://localhost/shopping-db';

//引入glob模块
// glob模块是最简单的模块之一,内容非常少。
//用它可以查找符合特定规则的文件路径名。跟使用windows下的文件搜索差不多
const glob = require('glob');
const { resolve } = require('path');
exports.initSchemas = () => {
    glob.sync(resolve(__dirname, './schema', '**/*.js')).forEach(require);
}

exports.connect = () => {
    // 数据库的连接
    mongoose.connect(db);
    let maxconnectNum = 0;
    return new Promise((resolve, reject) => {
        // 数据库监听操作事件
        mongoose.connection.on('disconnected', function() {
            console.log('断开连接,并尝试重新连接');
            if (maxconnectNum < 4) {
                maxconnectNum++;
                mongoose.connect(db);
            } else {
                reject()
                console.log('多次尝试依然失败');
                throw new Error('数据库异常')
            }
        })
          //数据库出现错误的时候
        mongoose.connection.on('error', function() {
            console.log('数据库连接发生异常,并重连');
            if (maxconnectNum < 4) {
                maxconnectNum++;
                mongoose.connect(db);
            } else {
                reject()
                console.log('多次尝试依然失败');
                throw new Error('数据库异常')
            }
        })
         //连接打开的时候
        mongoose.connection.on('open', function() {
            console.log('数据库连接成功,connection successful ');
            resolve()
        })

    })

}

然后在serve文件下的index.js里加入立即执行函数,在使用前记得用require进行引入
connect。

//引入connect
const {connect} = require('./database/init.js')
 
//立即执行函数
;(async () =>{
    await connect()
})()

4、定义schema

在database文件夹中新建一个schema文件夹,定义一个user的Schema,命名为User.js
Schema
schema是mongoose里会用到的一种数据模式,可以理解为表结构的定义;每个schema会映射到mongodb中的一个collection,它不具备操作数据库的能力
实际上,schema就是数据库对象的集合,这个集合包含了各种对象如:表、视图、存储过程、索引等。

//引入mongoose模块
const mongoose = require('mongoose');
//声明Schema
const Schema = mongoose.Schema;
// 声明ObjectId
let ObjectId = Schema.Types.ObjectId;

//创建userSchema
//可以声明 schema type 为某一种 type,或者赋值一个含有 type 属性的对象。
const userSchema = new Schema({
    UserId: { type: ObjectId },
    userName: { unique: true, type: String },//用户名
    password: { type: String },//密码
    createDate: { type: Date, default: Date.now() },//创建时间
    lastLoginTime: { type: Date, default: Date.now() },//最后一次登录时间
},{
     //配置后生成的表格中就不加s
    collection:'user'
})

定义一个Schema就这么简单,指定字段名和类型
Schema Types内置类型如下:
String
Number
Boolean | Bool
Array
Buffer
Date
ObjectId | Oid
Mixed

5、发布模型model

定义好了Schema,接下就是生成Model。
model是由schema生成的模型,可以对数据库的操作
我们对上面的定义的user的schema生成一个User的model并导出,修改后代码如下:
起一个叫User的,对应userSchema数据
就是添加一句 mongoose.model('User', userSchema)

const mongoose = require('mongoose');
const Schema = mongoose.Schema;
let ObjectId = Schema.Types.ObjectId;

//创建userSchema
const userSchema = new Schema({
    UserId: { type: ObjectId },
    userName: { unique: true, type: String },//用户名
    password: { type: String },//密码
    createDate: { type: Date, default: Date.now() },//创建时间
    lastLoginTime: { type: Date, default: Date.now() },//最后一次登录时间
})
//发布模型
mongoose.model('User', userSchema)

6、数据库的增删改查

增加

user.save().then((result)=>{
    console.log("成功的回调")
},()=>{
    console.log("失败的回调")
})

删除

1、删除指定数据
User.remove({userName:"张三"}).then((result)=>{
    console.log(result)
})

result:是一个对象 返回值是受影响条数


2、删除所有数据
User.remove({}).then((result)=>{
             console.log(result)
})

//删除指定ID
3、User.findByIdAndRemove(id值).then((result)=>{

})

修改

User.update({userName:"张三"},{$set:{userName:"李四"}},{multi:true}).then((result)=>{
             console.log(result)
})
multi:true  表示修改多条数据
//更新指定id值的数据
User.findByIdAndUpdate(id值,{$set:{需要修改的内容}}.then((result)=>{
     console.log(result)
})

查询

1、查询符合条件的所有数据
User.find({userName:"张三"}).then((result)=>{
    console.log(result)
})
result是查到的数据

2、查询所有数据
User.find().then((result)=>{
             console.log(result)
})

3、查询单条数据
User.findOne({userName:"张三"}).then((result)=>{
             console.log(result);
 })
 
 4、条件查询:
 $lt(小于) $lte(小于等于) $gt(大于) $gte(大于等于) $ne(不等于);

User.find({"age":{"$lt":20}}).then((result)=>{
             console.log(result);
 })

User.find({"age":{"$lte":20}}).then((result)=>{
             console.log(result);
 })

 User.find({"age":{"$gt":20}}).then((result)=>{
             console.log(result)
})

User.find({"age":{"$gte":20}}).then((result)=>{
             console.log(result)
})

User.find({"age":{"$ne":19}}).then((result)=>{
             console.log(result)
})

7、加密(哈希和加盐)bcrypt

1、安装

npm install --save  bcrypt

2、用pre每次进行保存时都进行加盐加密

let SALT_WORK_FACTOR = 5;

//密码加密
userSchema.pre('save', function(next) {
        var myuser = this;
        console.log(this);
        //产生密码hash当密码有更改的时候(或者是新密码)
        // if (!myuser.isModified('password')) return next();

        // 产生一个salt
        bcrypt.genSalt(SALT_WORK_FACTOR, function(err, salt) {
            if (err) return next(err);

            //  结合salt产生新的hash
            bcrypt.hash(myuser.password, salt, function(err, hash) {
                if (err) return next(err);

                // 使用hash覆盖明文密码
                myuser.password = hash;
                next();
            });
        });
    })

3、密码对比comparePwd方法

//密码对比
userSchema.methods = {
    comparePwd: (_password, password) => {
        return new Promise((resolve, reject) => {
            bcrypt.compare(_password, password, (err, isMatch) => {
                console.log('对比方法');
                console.log(_password);
                console.log(password);
                console.log(err);
                console.log(isMatch);
                console.log('对比方法结束');
                if (!err) {
                    resolve(isMatch);
                } else {
                    reject(err)
                }
            });
        })
    }
};

五、关联前后端

koa-router

1、安装koa-router

npm i koa-router

2、在serve下新建appApi文件夹,新建user.js用来处理注册登录请求

const Router = require('koa-router');
let router = new Router();
const mongoose = require('mongoose');
//连接数据库初始化
const { connect, initSchemas } = require('../database/init.js');

//立即执行函数
(async() => {
    await connect();
    initSchemas();
    // const User = mongoose.model('User');
    // let deleteUser = await User.remove({}).exec();
    // console.log('删除成功');

})()

// 注册
router.post('/register', async(ctx) => {

    const User = mongoose.model('User');
    let newUser = new User(ctx.request.body);
    await newUser.save().then(() => {
            ctx.body = {
                code: 200,
                messsage: '注册成功'
            }
        }).catch(error => {
            ctx.body = {
                code: 500,
                messsage: '服务器异常'
            }
        })
})
//登录
router.post('/login', async(ctx) => {
    //从网页获取到的数据
    let loginUser = ctx.request.body;
    // 输入框拿到的用户名
    let userName = loginUser.userName;
    // 输入框拿到的密码
    let password = loginUser.password;
    console.log(userName);
    console.log(password);
    //引入User的model
    const User = mongoose.model('User');
    // 根据用户名去数据库查找相对应的数据
    await User.findOne({ userName: userName }).exec().then(async(result) => {
        // 如果查询到了,就打印出来整条数据
        console.log(result);
        // 如果查询到了
        if (result) {
            //当用户名存在时,开始比对密码
            let newUser = new User(); //因为是实例方法,所以要new出对象,才能调用
            // 进行密码的解析和对比
            // comparePwd是解密的方法
            // result.password是数据库中的密码
            await newUser.comparePwd(password, result.password).then((isMatch) => {
                console.log('对比成功');
                //返回比对结果
                ctx.body = {
                    code: 200,
                    messsage: isMatch
                }
            }).catch(err => {
                console.log(err);
            })
        } else {
            ctx.body = {
                code: 201,
                messsage: '用户不存在'
            }
        }
    })
})
module.exports = router

3、在index.js下引入koa-router模块

var Router = require('koa-router');
var router = new Router();

app.use(router.routes())
app.use(router.allowedMethods());

koa-bodyparser中间件

利用koa-bodyparser来处理POST请求参数
1、安装
使用npm进行安装,需要注意的是我们这里要用–save,因为它在生产环境中需要使用。

npm install koa-bodyparser

2、引入使用:在index.js引入koa-bodyparser模块
安装完成后,需要在代码中引入并使用。我们在代码顶部用require进行引入。

const bodyParser = require('koa-bodyparser');

3、然后进行使用,如果不使用是没办法调用的,使用代码如下。

app.use(bodyParser());

4、完整写法

const bodyParser = require('koa-bodyparser');
app.use(bodyParser());

5、在代码中使用后,直接可以用ctx.request.body进行获取POST请求参数,中间件自动给我们作了解析。

6、更改Register.vue文件和Login.vue文件用来处理post请求(简单的做了一些非空和长度的处理)
serveAPI.config.js中将URL地址进行封装

const LOCALURL = 'http://localhost:3000';
const URL = {
    registerUser: LOCALURL + '/user/register',
    loginUser: LOCALURL + '/user/login'
}
module.exports = URL

注册界面Register.vue

<template>
<div>
    <div>
        <van-nav-bar title="用户注册" left-text="返回" left-arrow @click-left="onClickLeft" />
    </div>
    <div>
        <van-cell-group>
            <!-- :error-message是密码错误时候的提示 -->
            <van-field v-model="userName" required label="用户名" type="text" placeholder="请输入用户名" :error-message="userNameErrorMsg" />
            <van-field v-model="password" required label="密码" type="password" placeholder="请输入密码" :error-message="passwordErrorMsg"/>
        </van-cell-group>
        <!-- loading的作用是防止用户重复操作和用户体验 -->
        <van-button type="primary" :loading="openLoading" id="btnRegister"  @click="registerMethod" block>马上注册</van-button>
    </div>
</div>
</template>

<script>
import url from '@/serveAPI.config.js'
import { Toast } from 'vant';
export default {
    data() {
        return {
            userName: '',
            password: '',
            openLoading:false,
            userNameErrorMsg:'',
            passwordErrorMsg:'',
        }
    },
    methods: {
        // 非空判断和密码校验
        chekform(){
            let isOk =true;
            if (this.userName==' '|| this.userName.length==0) {
                this.userNameErrorMsg="用户名不能为空或空字符";
                isOk=false;
            } else {
                this.userNameErrorMsg='';
            }
            if (this.password.length<6) {
                this.passwordErrorMsg="用户名不能为空或空字符";
                isOk=false;
            } else {
                this.passwordErrorMsg='';
            }
            return isOk;
        },
        onClickLeft() {
            //用来回退界面
            this.$router.go(-1);
        },
        registerMethod(){
            this.chekform && this.register();
        },
        register() {
            if (this.chekform()) {
                this.openLoading=true;
            this.axios.post(url.registerUser, {
                userName: this.userName,
                password: this.password
            }).then((response) => {
                  this.openLoading=false;
                //Toast轻提示
                if (response.data.code==200) {
                    
                    Toast.success('注册成功');
                }else{
                    Toast.fail('注册失败'+response.data.messsage);
                }
                console.log(response);
            }).catch((error) =>{
                  this.openLoading=false;
                console.log(error);
            })
            }
            
            
        }
    }
}
</script>

登录界面Login.vue

<template>
<div>
    <div>
        <van-nav-bar title="用户登录" />
    </div>
    <div>
        <van-cell-group>
            <van-field v-model="userName" required label="用户名" placeholder="请输入用户名" />
            <van-field v-model="password" required label="密码" placeholder="请输入密码" />
        </van-cell-group>
        <van-button type="primary" @click="login" block>登录</van-button>
        <div class="sign"><a href="/register">注册</a></div>

    </div>
</div>
</template>

<script>
import url from '@/serveAPI.config.js'
import { Toast } from 'vant'
export default {
    data() {
        return {
            userName: '',
            password: ''
        }
    },
    methods: {
        login() {
            console.log(url.loginUser);
                this.axios.post(url.loginUser, {
                    userName: this.userName,
                    password: this.password
                }).then((response) => {
                    if (response.data.code==200) {
                        Toast.success('登录成功');
                    } else{
                        Toast.fail('登陆失败'+response.data.message);
                    }
                  
                    console.log(response);
                }).catch((error)=>{
                    console.log(error);
                })
            
        },
       
    }
}
</script>

<style scoped>

</style>

koa2-cors中间件解决同源策略,解决跨域

1、安装

npm install koa2-cors --save

2、在serve文件夹下的index.js中写入同源策略解决代码

//引入koa2-cors中间件
const cors = require('koa2-cors');
//注册(使用)中间件
app.use(cors());

主要接口index.js 最终代码

const Koa = require('koa');
const app = new Koa();
const mongoose = require('mongoose');
// const { connect, initSchemas } = require('./database/init.js');
const Router = require('koa-router');
// 处理post请求,把 koa2 上下文的表单数据解析到 ctx.request.body 中
const bodyParser = require('koa-bodyparser');
app.use(bodyParser());
// 解决同源策略以及跨域问题用koa2-cors
const cors = require('koa2-cors');
app.use(cors());
let user = require('./appApi/user.js');

// 装载所有子路由
let router = new Router();
router.use('/user', user.routes());
// 加载路由的中间件
app.use(router.routes());
app.use(router.allowedMethods());
// 监听端口号,启动服务
app.listen(3000, () => {
    console.log("koa服务已启动,在3000端口");
})

根据自己的想法一步步写出来的,过程中查阅了不少文档,算是比较详细的
作为一个菜鸟,想记录一下自己的学习成长进度,有什么不足的地方,欢迎批评指正

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,294评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,780评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,001评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,593评论 1 289
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,687评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,679评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,667评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,426评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,872评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,180评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,346评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,019评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,658评论 3 323
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,268评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,495评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,275评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,207评论 2 352

推荐阅读更多精彩内容