平时在有道云上做笔记,直接发到这里格式全乱了,现在改成Markdown的,效果还凑合,大家也可查看有道云链接: http://note.youdao.com/noteshare?id=3a3ed5e59748451d1e606f384a229e32&sub=F643D721EB1D41D7A6DA8E54CEE6AE48
验证码发送流程:
- 启动项目,启动redis,填写用户名和邮箱,点击发送验证码按钮。
- 校验用户名,不通过return false。
this.$refs['ruleForm'].validateField('name', (value) => {
usernamePass = !value
})
if (!usernamePass) { return false }
- 校验邮箱。
this.$refs['ruleForm'].validateField('email', (value) => {
emailPass = !value
})
- 如果通过,通过axios发送请求,带参数用户名和邮箱,其中用户名可能有中文,所以要用encodeURIComponent编码(把参数中能编码的全部编了,含http协议中的字符,而encodeURI只把问号后面的参数进行编码)。
this.$axios.post('/users/verify', {
username: encodeURIComponent(this.ruleForm.name),
email: this.ruleForm.email
}).then()
- 接口收到请求后,取出用户名,先在存于Redis的session中查询验证码过期时间,并与当前时间比较;
如果还没过期就返回“操作过于频繁”的提示。
如果不存在或者过期(不存在会转成0比较),进行下一步。
const username = ctx.request.body.username
// session存入Redis
const saveExpire = await Store.hget(`nodemail:${username}`, 'expire')
if (new Date().getTime() < saveExpire) {
ctx.body = {
code: -1,
msg: '请求过于频繁,1分钟内只能发1次'
}
return false
}
- 用nodeMailer创建一个邮件发送器(这里的源码和视频上不一致,被坑了很久):
let transporter = nodeMailer.createTransport({
host: Email.smtp.host, // 主机,腾讯的是smtp.qq.com
post: 587, // 端口
secure: false,
auth: {
user: Email.smtp.user, // 发送服务器的账号
pass: Email.smtp.pass // 发送服务器的授权码
}
})
- 创建发邮件相关的对象ko和邮件发送选项
const ko = {
code: Email.smtp.code,
expire: Email.smtp.expire(),
user: username,
email: ctx.request.body.email
}
const options = {
from: `认证邮件:<${Email.smtp.user}>`,
to: ko.email,
subject: '关中刀客在青岛做网站练习的验证码',
html: '<p>谢谢测试!</p>验证码是:' + Email.smtp.code()
}
- 发送邮件,注意前面有await。发送成功后将相关信息写入session存到redis,如果不成功打印错误信息
await transporter.sendMail(options, (error, info) => {
if (error) { // 发邮件出错,打印并停止
return console.log(error, info)
} else {
// Redis中取session
Store.hmset(`nodemail:${ko.user}`, 'code', ko.code, 'email', ko.email, 'expire', ko.expire)
}
})
- 设置发送成功标志code
ctx.body = {
code: 0,
msg: '验证码发送成功,有效期1分钟。'
}
- 在页面的then中,判断页面响应成功并收到成功标志后,设置60秒倒计时的定时器。如果不成功就显示相应信息。
if (status === 200 && data.code === 0) {
let counter = 60
const timer = setInterval(() => {
this.statusMsg = `验证码已发送,请查收! ${--counter}秒后重发`
if (!counter) { clearInterval(timer)}
}, 1000)
说明
- 当验证码还没过期时点击按钮,则服务器返回“操作频繁”的提示,但因页面有定时器,这个提示最多看到1秒钟就会被倒计时替换。
- 代码仅供参考,可能有很多原因会导致照搬不成功。
其他注意事项
- 首先揭示一个大坑,坑了我一个多小时:
源码中的结构不对,不能用,返回错误信息 connect ETIMEDOUT 123.129.254.12:587 at TCPConnectWrap.afterConnect [as oncomplete] ,后来按视频中讲的去配置才可以!
// 错的!
let transporter = nodeMailer.createTransport({
service: 'qq',
auth: {
user: Email.smtp.user,
pass: Email.smtp.pass
}
})
// 对的!
let transporter = nodeMailer.createTransport({
host:Email.smtp.host,
post:587,
secure:false,
auth:{
user:Email.smtp.user,
pass: Email.smtp.pass
}
})
- elementUI布局中的列宽span前面要加上冒号,否则控制台警告:
Invalid prop: type check failed for prop "span". Expected Number with value 3, got String……
- server/index.js 中use路由的位置是下面两行代码功能之间,否则连接口都找不到。
if (config.dev) {
const builder = new Builder(nuxt)
await builder.build()
} else {
await nuxt.ready()
}
app.use(users.routes()).use(users.allowedMethods())
app.use((ctx) => {
ctx.status = 200
ctx.respond = false // Bypass Koa's built-in response handling
ctx.req.ctx = ctx // This might be useful later on, e.g. in nuxtServerInit or with nuxt-stash
nuxt.render(ctx.req, ctx.res)
})
- 必须引进koa-bodyparser,否则接口从请求头拿不到任何数据。使用位置在start()之前,方法如下:
app.use(bodyparser({
extendTypes:['json','form','text']
}))
环境版本:
"dependencies": {
"@nuxtjs/axios": "^5.3.6",
"cross-env": "^5.2.0",
"element-ui": "^2.4.11",
"koa": "^2.6.2",
"nuxt": "^2.0.0"
},
"devDependencies": {
"@nuxtjs/eslint-config": "^1.0.1",
"@nuxtjs/eslint-module": "^1.0.0",
"babel-cli": "^6.26.0",
"babel-eslint": "^10.0.1",
"babel-preset-env": "^1.7.0",
"eslint": "^6.1.0",
"eslint-plugin-nuxt": ">=0.4.2",
"koa-bodyparser": "^4.2.1",
"koa-passport": "^4.1.3",
"koa-redis": "^4.0.0",
"koa-router": "^7.4.0",
"mongoose": "^5.6.13",
"node-sass": "^4.12.0",
"nodemailer": "^6.3.0",
"nodemon": "^1.18.9",
"passport-local": "^1.0.0",
"sass-loader": "^8.0.0"
}