校园社区卡片墙
new-wall-ui(在3a写好的react静态组件页面,react-cli、ant-design)
根据/user/login和/user/register接口实现注册与登入功能
使用到axios
cd new-wall/client
npm i axios
mkdir api
touch ajax.js
new-wall/client/api/ajax.js
封装了axios函数
但是还没有编写有传递token的情况,后面会更新该封装函数
touch index.js
new-wall/client/api/index.js
index.js为封装的众多ajax请求函数
每次onChange后都会进行重名验证
提交按钮进行了防重复提交
代码如下
new-wall/client/src/components/register/register.jsx
import React , { useState , useEffect } from 'react' ;
import { useHistory } from 'react-router-dom'
import {
Form ,
Input ,
Tooltip ,
Button ,
message ,
Switch ,
} from 'antd' ;
import {
QuestionCircleOutlined ,
ManOutlined ,
WomanOutlined ,
LoadingOutlined ,
CheckCircleFilled ,
CloseCircleFilled ,
} from '@ant-design/icons' ;
import Cookies from 'js-cookie'
import {
registerAjax ,
usernameCheckAjax ,
nicknameCheckAjax ,
} from '../../../api/index'
import './register.css'
const RegistrationForm = ( ) => {
let history = useHistory ( ) ;
const [ form ] = Form . useForm ( ) ;
const [ usernameChecking , setUsernameChecking ] = useState ( false )
const [ usernameChecked , setUsernameChecked ] = useState ( false )
const [ usernameCanUse , setUsernameCanUse ] = useState ( true )
const [ nicknameChecking , setNicknameChecking ] = useState ( false )
const [ nicknameChecked , setNicknameChecked ] = useState ( false )
const [ nicknameCanUse , setNicknameCanUse ] = useState ( true )
const [ submiting , setSubmiting ] = useState ( false )
const onFinish = values => {
if ( nicknameCanUse && usernameCanUse && ! submiting ) { //额外判断nicknameCanUse和usernameCanUser
setSubmiting ( true ) //提交中状态
registerAjax ( values )
. then ( val => {
message . success ( "注册成功🤓自动登入" )
let { token} = val . data . data //注册后就获得了token
localStorage . setItem ( "token" , token ) //保存token
history . push ( "/" ) //react hook的history路由跳转
} )
. catch ( err => {
message . error ( "服务器出问题了,稍等一下🥴" )
setSubmiting ( false ) //提交完毕状态
} )
}
} ;
//用户名重名判断函数
const usernameCheck = ( e ) => {
if ( e . target . value ) {
//传递e.target.value去验证是否已经被使用
setUsernameChecking ( true )
setUsernameChecked ( false )
let data = {
username :e . target . value
}
usernameCheckAjax ( data )
. then ( val => {
setUsernameChecked ( true ) //检查完毕
val . data . code == 200 //判断是否可用
?
setUsernameCanUse ( true )
:
setUsernameCanUse ( false )
} )
. catch ( err => {
message . error ( "服务器出问题了,稍等一下🥴" )
} )
} else {
//删除为空时更新可用否图标状态
setUsernameChecking ( false )
setUsernameChecked ( false )
}
}
//nickname重名判断函数
const nicknameCheck = ( e ) => {
if ( e . target . value ) {
//传递e.target.value去验证是否已经被使用
setNicknameChecking ( true )
setNicknameChecked ( false )
let data = {
nickname :e . target . value
}
nicknameCheckAjax ( data )
. then ( val => {
setNicknameChecked ( true ) //检查完毕
val . data . code == 200 //判断是否可用
?
setNicknameCanUse ( true )
:
setNicknameCanUse ( false )
} )
. catch ( err => {
message . error ( "服务器出问题了,稍等一下🥴" )
} )
} else {
//删除为空时更新可用否图标状态
setNicknameChecking ( false )
setNicknameChecked ( false )
}
}
return (
< div className = "register-box" >
< Form
form = { form }
name = "register"
onFinish = { onFinish }
scrollToFirstError
>
< Form . Item
validateStatus = { usernameCanUse ? "" : "error" } //usernameCanUse来决定input边框颜色
help = { usernameCanUse ? null : "用户名已经被抢先注册啦🙈" } //usernameCanUse为false时提示文字
name = "username"
label = {
< span >
用户名
< / span >
}
rules = { [ { required : true , message : '不能为空哦' , whitespace : true } ] }
>
< Input
onChange = { usernameCheck }
suffix = {
usernameChecking
?
< Tooltip title = "重名判断" >
{
usernameChecked
?
usernameCanUse ? < CheckCircleFilled style = { { color : '#52c41a' } } / > : < CloseCircleFilled style = { { color : '#fe7171' } } / >
:
< LoadingOutlined style = { { color : '#fddb3a' } } / >
}
< / Tooltip >
:
< span > < / span >
}
/ >
< / Form . Item >
< Form . Item
name = "password"
label = "密码"
rules = { [
{
required : true ,
message : 'Please input your password!' ,
} ,
] }
hasFeedback
>
< Input . Password / >
< / Form . Item >
< Form . Item
name = "confirm"
label = "确认密码"
dependencies = { [ 'password' ] }
hasFeedback
rules = { [
{
required : true ,
message : 'Please confirm your password!' ,
} ,
( { getFieldValue } ) => ( {
validator ( rule , value ) {
if ( ! value || getFieldValue ( 'password' ) === value ) {
return Promise . resolve ( ) ;
}
return Promise . reject ( 'The two passwords that you entered do not match!' ) ;
} ,
} ) ,
] }
>
< Input . Password / >
< / Form . Item >
< Form . Item
validateStatus = { nicknameCanUse ? "" : "error" } //nicknameCanUse来决定input边框颜色
help = { nicknameCanUse ? null : "好听的昵称已经被抢先注册啦🙈" } //nicknameCanUse为false时提示文字
name = "nickname"
label = {
< span >
昵称
< Tooltip title = "社区代号嘻嘻嘻" >
< QuestionCircleOutlined / >
< / Tooltip >
< / span >
}
rules = { [ { required : true , message : '不能为空哦' , whitespace : true } ] }
>
< Input
onChange = { nicknameCheck }
suffix = {
nicknameChecking
?
< Tooltip title = "重名判断" >
{
nicknameChecked
?
nicknameCanUse ? < CheckCircleFilled style = { { color : '#52c41a' } } / > : < CloseCircleFilled style = { { color : '#fe7171' } } / >
:
< LoadingOutlined style = { { color : '#fddb3a' } } / >
}
< / Tooltip >
:
< span > < / span >
}
/ >
< / Form . Item >
< Form . Item
name = "gender"
label = "性别"
initialValue = { true }
rules = { [ { required : true } ] }
>
< Switch
defaultChecked
checkedChildren = { < ManOutlined / > }
unCheckedChildren = { < WomanOutlined / > }
/ >
< / Form . Item >
< Form . Item >
< Button className = "register-box-btn" type = "primary" htmlType = "submit" >
{
//状态判断
submiting
?
< LoadingOutlined style = { { color : 'white' } } / >
:
"注册"
}
< / Button >
< / Form . Item >
< / Form >
< / div >
) ;
} ;
export default RegistrationForm
这里防抖的使用原本思路是传递event对象获取event.target.value来获取表单值
但是防抖的写法是传递的静态参数,此处采用了很low的dom操作来获取最新value😧😧😧
日后有好的解决方法会进行更新
编写防抖函数
使用防抖函数
src/components/register.jsx
效果:
在停止输入username的500ms后才进行ajax重复验证(有options预检测请求),而不是之前改变一个字母就会发送一个ajax请求
new-wall//client/src/User/Login/login.jsx
import React , { Component } from 'react'
import Cookies from 'js-cookie'
import { withRouter } from 'react-router-dom'
import { Form ,
Input ,
Button ,
message } from 'antd' ;
import {
loginAjax ,
} from '../../../api/index'
import './login.css'
class Login extends Component {
constructor ( props ) {
super ( props )
}
layout = {
labelCol : { span : 8 } ,
wrapperCol : { span : 16 } ,
} ;
tailLayout = {
wrapperCol : { offset : 8 , span : 16 } ,
} ;
onFinish = values => {
loginAjax ( values )
. then ( ( val ) => {
console . log ( val )
if ( val . data . code == 200 ) {
let { token} = val . data . data
localStorage . setItem ( "token" , token )
message . success ( "欢迎回来" )
this . props . history . push ( "/" )
} else if ( val . data . code == 100 ) {
message . warn ( val . data . msg )
}
} )
. catch ( ( err ) => {
console . log ( err )
message . warn ( "错误啦!" )
} )
} ;
onFinishFailed = errorInfo => {
console . log ( 'Failed:' , errorInfo ) ;
} ;
render ( ) {
return (
< div className = "login-box" >
< Form
name = "basic"
initialValues = { { remember : true } }
onFinish = { this . onFinish }
onFinishFailed = { this . onFinishFailed }
>
< Form . Item
label = "用户名"
name = "username"
rules = { [ { required : true , message : 'Please input your username!' } ] }
>
< Input / >
< / Form . Item >
< Form . Item
label = "密码"
name = "password"
rules = { [ { required : true , message : 'Please input your password!' } ] }
>
< Input . Password / >
< / Form . Item >
< Form . Item >
< Button className = "login-box-btn" type = "primary" htmlType = "submit" >
登入
< / Button >
< / Form . Item >
< / Form >
< / div >
)
}
}
export default withRouter ( Login )
注册完毕后,后端返回token,前端保存,即实现了自动登入
现在我们要做的是将index页面的用户信息面板改为动态
需要使用到/use/getInfo接口了
首先编写获取用户信息的api
这里我们需要在请求头部中增加token字段了,
进行token过期的判断,userbox显示"?",message发送请重新登入提示
因为我想让用户未登入的情况也可以查看main卡片墙,所以token过期不强制跳转login.jsx
如需跳转的话这里还涉及到非路由组件进行编程式重定向的知识点(withRouter()、useHistory)
这里在userbox的右上角增加了退出按钮
退出按钮确定函数的内容:
删除localStorage中的token字段
清空userbox组件的state的userInfo对象
退出按钮代码
userbox.jsx目前整体代码如下
import React , { Component } from 'react'
import { withRouter } from 'react-router-dom'
import PropTypes from 'prop-types'
import {
message ,
Popover ,
Upload ,
Tooltip ,
Popconfirm ,
} from 'antd'
import ImgCrop from 'antd-img-crop' ;
import {
PlusOutlined ,
QuestionOutlined ,
DeleteOutlined ,
} from "@ant-design/icons"
import {
getUserInfoAjax ,
} from '../../api/index'
import "./userbox.css"
class Userbox extends Component {
constructor ( props ) {
super ( props )
}
state = {
userInfo :{ } ,
}
componentDidMount ( ) {
let token = localStorage . getItem ( "token" )
getUserInfoAjax ( token ) //获取用户信息接口
. then ( val => {
if ( val . data . code == 100 ) { //token过期
message . warning ( "还未进行用户登入噢🙊" )
// this.props.history.push("/login")
} else {
this . setState ( { userInfo :val . data . userInfo } ) //更新state
}
} )
. catch ( err => {
message . error ( "服务器宕机啦!请稍候再试" )
} )
}
gotoLogin = ( ) => {
console . log ( this )
this . props . history . push ( "/login" )
}
{ /* ---------------------本次重要代码start----------------- */ }
logoutConfirm = ( ) => {
//删除localStorage中存储的token
localStorage . removeItem ( "token" )
//更新state中userInfo的状态
this . setState ( { userInfo :{ } } ) //清除userInfo
message . success ( "退出成功" )
}
{ /* ---------------------本次重要代码end----------------- */ }
render ( ) {
const props = {
name : 'file' ,
action : 'http://localhost:3030/user/avatar' ,
headers : {
authorization : 'authorization-text' ,
} ,
onChange ( info ) {
if ( info . file . status !== 'uploading' ) {
console . log ( info . file , info . fileList ) ;
}
if ( info . file . status === 'done' ) {
message . success ( `${ info . file . name } file uploaded successfully` ) ;
} else if ( info . file . status === 'error' ) {
message . error ( `${ info . file . name } file upload failed.` ) ;
}
} ,
} ;
return (
< div className = "user-box" >
{ /* ---------------------本次重要代码start----------------- */ }
{
Object . keys ( this . state . userInfo ) . length
?
< Popconfirm
title = "确定要退出该账户?"
onConfirm = { this . logoutConfirm }
okText = "Yes"
cancelText = "No"
>
< img className = "exit-btn" src = "exit.png" alt = "" / >
< / Popconfirm >
:
null
}
{ /* ---------------------本次重要代码end----------------- */ }
< div className = "user-avatar" >
{
Object . keys ( this . state . userInfo ) . length
?
< ImgCrop rotate >
< Upload { ...props } >
{ this . state . userInfo . avatar }
< / Upload >
< / ImgCrop >
?
< ImgCrop rotate >
< Upload { ...props } >
< img className = "user-avatar-img" src = { this . state . userInfo . avatar } alt = "" / >
< / Upload >
< / ImgCrop >
:
< ImgCrop rotate >
< Upload { ...props } >
< p className = "user-avatar-img-logo" >
{ this . state . userInfo . nickname ? this . state . userInfo . nickname . slice ( 0 , 1 ) : null }
< / p >
< / Upload >
< / ImgCrop >
:
//未登入
< p className = "default-avatar" onClick = { this . gotoLogin } >
< QuestionOutlined / >
< / p >
}
< / div >
< p className = "nickName" >
{ this . state . userInfo . nickname }
< / p >
< div className = "user-tag-box" >
< Popover
overlayClassName = "userbox-popover"
content = { < DeleteOutlined / > }
color = "red"
title = "" >
< span className = "tag" > 萌新< / span >
< / Popover >
{ /* <span className="tag">二次元</span>
<span className="tag">zzuli</span>
<span className="tag">萌新</span>
<span className="tag">二次元</span>
<span className="tag">zzuli</span>
<span className="tag">萌新</span>
<span className="tag">二次元</span>
<span className="tag">zzuli</span> */ }
< span className = "tag tag-add-btn" > < PlusOutlined / > < / span >
< / div >
< / div >
)
}
}
//props验证
Userbox . propTypes = {
userInfo :PropTypes . object
}
export default withRouter ( Userbox )
首先复制粘贴antd的上传组件中的可剪裁部分(想说很久了,例图肯定是阿里程序员的女朋友)
Uploads组件的options配置对象onChange函数
上传完毕需要再次调用getUserInfoAjax来获取新的userInfo
效果如下:
首先实现userbox的个性标签的样式
这里使用到的就是@keyframes
动画帧组合实现自己想要的效果
UI效果如下
首先编写tag操作相关的ajax
new-wall/client/api/index.js
实现tag添加功能
双向绑定input
submit button调用tag相关的ajax
这里又遇到个小问题就是关于dom获取问题
获取antd的popover需要在其显示之后,我将他们都保存至state的userpopvers中,每次userpopvers的末尾dom就是还未绑定单机事件的,我为其进行绑定。
并且进行删除操作的代码编写
注意这里的span使用onmouseenter事件,鼠标进入元素后激活一次,使用onmousemove还要手写防抖
成品效果图如下:
完美
cd new-wall
cd server
npm init
npm install koa
touch index.js
new-wall/server/index.js
const Koa = require ( "koa" )
const app = new Koa ( )
app . use ( async ( ctx ) => {
ctx . body = "Hello Koa2"
} )
app . listen ( 3030 )
console . log ( "koa2 running in 3030!!!" )
本机开启mongod
npm install mongoose
mkdir database
const mongoose = require ( "mongoose" )
const db = "mongodb://localhost:27017/new-wall"
module . exports . connect = ( ) => {
mongoose . connect ( db , { useNewUrlParser : true } )
let maxReconnectCount = 0
return new Promise ( ( ( resolve , reject ) => {
//绑定事件
mongoose . connection . on ( "disconnected" , ( ) => {
if ( maxReconnectCount < 3 ) {
//可进行重连
mongoose . connect ( db , { useNewUrlParser : true } )
return 1
} else {
//超出最大连接限制
reject ( "连接失败!超出最大重连次数限制!" )
}
} )
mongoose . connection . on ( "error" , ( ) => {
if ( maxReconnectCount < 3 ) {
//可进行重连
mongoose . connect ( db , { useNewUrlParser : true } )
return 1
} else {
//超出最大连接限制
reject ( "连接失败!超出最大重连次数限制!" )
}
} )
mongoose . connection . on ( "open" , ( ) => {
resolve ( "连接数据库成功!🤓" )
} )
} ) )
}
该文件引入mongoose来定义Schema和Model并导出
new-wall/server/database/schema&module.js
const mongoose = require ( "mongoose" )
const Schema = mongoose . Schema
//定义Schema
const UserSchema = new Schema ( {
userId :Schema . Types . ObjectId ,
username :String ,
password :String ,
nickname :String ,
gender :String ,
avatar :String ,
tags :Array //个人标签字段
} )
//定义Model
const UserModel = new mongoose . model ( "user" , UserSchema )
//导出是Model
exports . UserModel = UserModel
cd new-wall/server
npm install koa-router
mkdir routes
cd routes
touch user.js
touch router.js //用于装载众多routes
new-wall/server/routes/user.js
new-wall/server/routes/router.js
new-wall/server/index.js (app使用router.js中间件)
下图写法也可以,还是对配置中间件有点迷惑...
子路由访问结果:
使用token (这里疑惑的话再去看js笔记里的jwt部分)
cd new-wall/server
npm install jsonwebtoken
mkdir utils
touch jwt.js //编写工具类jwt.js
new-wall/server/utils/jwt.js
const jwt = require ( "jsonwebtoken" )
class Jwt {
constructor ( data ) {
this . data = data
this . sercet = "hello token"
}
generToken ( ) {
let data = this . data
let secret = this . sercet
let endTime = Math . floor ( Date . now ( ) / 1000 )
console . log ( endTime )
// let token = jwt.sign(data,secret,{expiresIn:endTime + 10})
let token = jwt . sign ( { data, exp :endTime + 60 } , secret )
return token
}
verifyToken ( token ) {
let secret = this . sercet
let nowTime = Math . floor ( Date . now ( ) / 1000 )
let result = jwt . verify ( token , secret , { } , ( error , decoded ) => {
if ( error ) {
//error有值有两种情况,
//exp过期时间已经小于当前时间。
//token传递有误。
return { code :100 , msg :"token已过期" }
}
return { code :200 , data :decoded }
} )
return result
}
}
module . exports = Jwt
touch tokenVerify.js //编写验证token的中间件
new-wall/server/utils/tokenVerify.js
const Jwt = require ( "./jwt" )
function tokenVerify ( ctx , next ) {
let urlArr = [ "/user/info" ] //需要token的url数组
if ( urlArr . indexOf ( ctx . request . url ) !== - 1 ) {
//该url在需要token的url数组中
let { token} = ctx . request . headers
let jwt = new Jwt ( { } ) , result = null
token ? result = jwt . verifyToken ( token ) : ctx . throw ( 401 , "未携带token" )
console . log ( result )
if ( result . code === 200 ) {
next ( )
} else if ( result . code === 100 ) {
ctx . throw ( 401 , "token过期!" )
}
} else {
//请求url不需要token直接next()
next ( )
}
}
module . exports = tokenVerify
在index.js暂时直接生成token,配置中间件
真实环境的token是应该在用户登入后生成,而不是直接生成
const Koa = require ( "koa" )
const router = require ( "./routes/router" ) //装载了众多子路由的router.js
const Jwt = require ( "./utils/jwt" ) //jsonwebtoken的生成与验证
const tokenVerify = require ( "./utils/tokenVerify" )
const db = require ( "./database/connect" ) //mongodb数据库的连接操作
const { UserModel, } = require ( "./database/schema&model" ) //mongoose的schema和model
const app = new Koa ( )
//app加载路由中间件
app . use ( router . routes ( ) , router . allowedMethods ( ) )
//-------------重要代码start---------------------
//token测试
let jwt = new Jwt ( { username :"ttttjh" , password :"tttjh" } , "Hello Token" )
let token = jwt . generToken ( )
console . log ( token )
let result = jwt . verifyToken ( token )
console . log ( result )
//-------------重要代码end---------------------
//连接数据库
db . connect ( )
. then ( val => {
console . log ( val )
} )
. catch ( err => {
console . log ( err )
} )
//-------------重要代码start---------------------
app . use ( ( ctx , next ) => tokenVerify ( ctx , next ) )
//-------------重要代码end---------------------
app . listen ( 3030 )
console . log ( "koa2 running in 3030!!!" )
此时访问/user/info就需要携带token了
本次注册要求传递的字段有:
username
password
nickname
gender
tags
使用到koa-bodyparser中间件(解析post请求数据)
npm i koa-bodyparser
new-wall/server/routes/user.js
接着在user.js中导入UserModel
因为我们要在user.js路由中对mongodb有所操作
UserModel为我们提供了操作mongodb的user表的途径
写到这里我觉得,应该在用户注册时,填完一个input后,就应该进行一次检查,是否重复,进行相应提示。
所以在register注册路由编写之前,再对应编写几个字段检查路由
下图为用户名检查路由
使用UserModel来对user-collection来进行username查询
注意UserModel的操作是异步的,需要使用async await来使其表现同步化,否则回调函数中的ctx.body的赋值代码无法起到正确作用。
后来使用检查路由时发现回调函数的写法有时会not found,所以还是要采用Promise链式调用的方法
回调函数的写法会not found
//nickname检查路由
回调函数的写法会not found
由于我们在用户每次输入input后都进行了是否重复可用检查,所以register路由执行时代表传递数据直接可用。
这里我们需要使用的npm上的md5包
npm i md5
md5包用于对传递来的password字段进行加密存储进数据库
用户登入时传递的password也进行md5加密然后和数据库中加密过的password字段比较
注意:
这里我们使用Model.save()方法的链式调用方式 来获取数据库执行结果
因为回调函数 的写法,在回调函数内部ctx.body无法设置 使得路由查询结果为not found (写了async await的情况 )
更新/user/register接口为注册后即生成token并返回
使用/user/register接口
-
- 数据添加成功
-
下图的写法ctx.body设置无效 (还不知道是为啥🥴🥴🥴)
在登入路由中验证用户名和密码成功后我们需要使用之前便携的Jwt类生成token并返回
//--------------用户登入路由--------------------
user . post ( "/login" , async ( ctx ) => {
let { username, password} = ctx . request . body
password = md5 ( password )
//数据库查询
await UserModel . findOne ( { "username" :username } )
. then ( val => {
if ( val && ( password === val . password ) ) { //username和password都正确
let data = { //token包含的数据
userId :val . _id ,
username :val . username ,
nickname :val . nickname ,
gender :val . gender
}
let jwt = new Jwt ( data , "user-secret" ) //实例化Jwt类
let token = jwt . generToken ( ) //调用jwt的生成token的方法
ctx . body = { code :200 , data :{ token} }
} else if ( val && ( password !== val . password ) ) {
ctx . body = { code :100 , msg :"密码错了哦" }
} else if ( ! val ) {
ctx . body = { code :100 , msg :"该账户不存在哦" }
}
} )
. catch ( ( err ) => {
ctx . body = { code :100 , msg :"服务器过累啦,稍候再试" }
} )
} )
因为当前koa的端口在3030,react前端的端口在3000,出现跨域,需要后端设置CROS解决
koa设置跨域
直接使用koa-cors实现
npm i koa-cors
注意中间件设置顺序问题
const Koa = require ( "koa" )
const cors = require ( "koa-cors" )
const router = require ( "./routes/router" ) //装载了众多子路由的router.js
const Jwt = require ( "./utils/jwt" ) //jsonwebtoken的生成与验证
const tokenVerify = require ( "./utils/tokenVerify" )
const db = require ( "./database/connect" ) //mongodb数据库的连接操作
const { UserModel, } = require ( "./database/schema&model" ) //mongoose的schema和model
const app = new Koa ( )
app . use ( cors ( ) ) //允许全部跨域
//app加载路由中间件
app . use ( router . routes ( ) , router . allowedMethods ( ) )
//-------------重要代码start---------------------
//token测试
// let jwt = new Jwt({username:"ttttjh",password:"tttjh"},"Hello Token")
// let token = jwt.generToken()
// console.log(token)
// let result = jwt.verifyToken(token)
// console.log(result)
//-------------重要代码end---------------------
//连接数据库
db . connect ( )
. then ( val => {
console . log ( val )
} )
. catch ( err => {
console . log ( err )
} )
//-------------重要代码start---------------------
app . use ( ( ctx , next ) => tokenVerify ( ctx , next ) ) //启用编写的token验证中间件
//-------------重要代码end---------------------
app . listen ( 3030 )
console . log ( "koa2 running in 3030!!!" )
现在应该去编写login和register的ui界面。☝☝☝
该路由用于返回用户信息
通过前端请求头中传递过来的token来进行token解析,里面包含有用户信息,主要看token有没有过期。
注意开启token验证中间件
new-wall/server/index.js
new-wall/server/utils/tokenVerify.js
new-wall/server/routes/user.js
没有使用UserModel,总感觉之后还会改。
芜湖
这里我们使用到的一个模块叫做multiparty,用于图片的接收
该接口需要进行token验证,前端使用antd的图片上传,在Uploads组件的props中设置了headers的token字段进行token携带
图片存放路径为__dirname+"/uploads"
这里还需要设koa的静态文件目录,使用到了koa-static模块,本来想用nodejs的方式直接写出一个koa中间件来作为图片服务器路由,但是有问题,所以直接使用了koa-static进行静态目录设置
在接受图片完毕后获取图片的名称进行数据库内的用户的avatar字段更新
此处的avatar接口仍存在问题,无论怎样都会返回code:200,msg:success,后续需要修改
new-wall/server/routes/user.js
主要的avatar部分
//-----------------user子路由模块-----------------------
const Router = require ( "koa-router" )
const md5 = require ( "md5" ) //md5包用于password字段加密
const koaBodyParser = require ( "koa-bodyparser" ) //post传递数据解析中间件
const fs = require ( "fs" ) //nodejs的文件模块
const multiparty = require ( "multiparty" ) //解析文件上传模块
const util = require ( "util" ) //工具模块
const Jwt = require ( "../utils/jwt" )
const { UserModel, } = require ( "../database/schema&model" ) //导入Model对象
const user = new Router ( )
user . use ( koaBodyParser ( ) )
//--------------获取用户个人信息路由--------------------
user . get ( "/getInfo" , async ( ctx ) => {
let { token} = ctx . request . headers
let jwt = new Jwt ( ) //实例化一个jwt对象来进行token验证
let tokenVerifyResult = jwt . verifyToken ( token )
if ( tokenVerifyResult . code == 200 ) { //token验证通过
//数据库查询
await UserModel . findOne ( { "username" :tokenVerifyResult . data . username } )
. then ( val => {
let userInfo = {
userId :val . _id ,
username :val . username ,
nickname :val . nickname ,
gender :val . gender ,
avatar :val . avatar ,
tags :val . tags
}
ctx . body = { code :200 , data :userInfo }
} )
} else { //token验证失败
ctx . body = { code :100 , msg :"token验证失败" }
}
} )
//--------------用户登入路由--------------------
user . post ( "/login" , async ( ctx ) => {
let { username, password} = ctx . request . body
password = md5 ( password )
//数据库查询
await UserModel . findOne ( { "username" :username } )
. then ( val => {
if ( val && ( password === val . password ) ) { //username和password都正确
let data = {
userId :val . _id ,
username :val . username ,
nickname :val . nickname ,
gender :val . gender ,
avatar :val . avatar ,
tags :val . tags
}
let jwt = new Jwt ( data , "user-secret" )
let token = jwt . generToken ( )
ctx . body = { code :200 , data :{ token} }
} else if ( val && ( password !== val . password ) ) {
ctx . body = { code :100 , msg :"密码错了哦" }
} else if ( ! val ) {
ctx . body = { code :100 , msg :"该账户不存在哦" }
}
} )
. catch ( ( err ) => {
console . log ( err )
ctx . body = { code :100 , msg :"服务器过累啦,稍候再试" }
} )
} )
//--------------测试路由--------------------
user . get ( "/images" , async ( ctx ) => {
console . log ( "/images被访问" )
ctx . body = [ "people1" , "people1" , "people2" , "people2" , "people3" , "people3" ]
} )
//--------------用户注册路由-------------------------
user . post ( "/register" , async ( ctx ) => {
let { username, password, nickname, gender} = ctx . request . body
let user = new UserModel ( {
username,
password :md5 ( password ) , //进行字段加密
nickname,
gender
} )
//这里使用Model.save()的Promise链式调用方法,因为回调函数的写法在函数内部无法进行ctx.body使得路由返回结果为not found
await user . save ( )
. then ( ( val ) => {
console . log ( val )
let data = {
userId :val . _id ,
username :val . username ,
nickname :val . nickname ,
gender :val . gender ,
avatar :"" ,
}
let jwt = new Jwt ( data , "user-secret" )
let token = jwt . generToken ( )
ctx . body = { code :200 , data :{ token} }
} )
. catch ( ( ) => {
ctx . body = { code :100 , msg :"服务器暂停营业,请稍候" }
} )
} )
//---------------用户名检查路由----------------------
user . post ( "/username_check" , async ( ctx ) => {
let { username} = ctx . request . body
//进行数据库操作
await UserModel . findOne ( { "username" :username } )
. then ( val => {
if ( ! val ) {
ctx . body = { code :200 , msg :"该用户名可以使用" }
} else {
ctx . body = { code :100 , msg :"该用户名已经被注册,换一个吧" }
}
} )
. catch ( err => {
ctx . body = { code :100 , msg :"服务器出错🚫🚫🚫" }
} )
} )
//----------------昵称检查------------------------
user . post ( "/nickname_check" , async ( ctx ) => {
let { nickname} = ctx . request . body
await UserModel . findOne ( { "nickname" :nickname } )
. then ( val => {
if ( ! val ) {
ctx . body = { code :200 , msg :"该昵称还没被使用,快注册!" }
} else {
ctx . body = { code :100 , msg :"这个好听的昵称已经被人抢先注册啦!" }
}
} )
. catch ( err => {
ctx . body = { code :100 , msg :"服务器出错🚫🚫🚫" }
} )
} )
//------------头像上传路由-------------------
user . post ( '/avatar' , async ( ctx , next ) => {
//token解析操作
let { token} = ctx . request . headers
let jwt = new Jwt ( ) //实例化一个jwt对象来进行token验证
let userInfo = jwt . verifyToken ( token ) . data
//multiparty模块的使用
let form = new multiparty . Form ( { uploadDir : './uploads' } )
form . parse ( ctx . req , async ( err , fields , files ) => {
if ( err ) {
ctx . body = { code :100 , msg :"上传失败!" }
}
let imageName = files . file [ 0 ] . path . split ( "/" ) [ 1 ]
//进行数据库更新
await UserModel . findByIdAndUpdate ( userInfo . userId , { avatar : imageName } , ( err , doc ) => {
if ( err ) throw err
} )
} )
ctx . body = { code :200 , msg :"成功上传:)" }
} ) ;
//-----------子路由导出----------------------
module . exports = user
该路由需要携带token来访问,所以在tokenVerify中进行路由添加
该路由通过前端传递data来判断是tag的增加操作还是减少操作
// 个性tag增删路由
user . post ( "/tag" , async ( ctx , next ) => {
//token解析
let { token} = ctx . request . headers
let jwt = new Jwt ( ) //实例化一个jwt对象来进行token验证
let userInfo = jwt . verifyToken ( token ) . data
let query = ctx . request . body //post数据获取
if ( query . addTagValue ) {
//增加tag操作
//数据库操作
await UserModel . findById ( userInfo . userId )
. then ( async val => {
let { tags} = val
tags . push ( query . addTagValue )
await UserModel . findByIdAndUpdate ( userInfo . userId , { tags} , ( err , doc ) => {
if ( err ) {
ctx . body = { code :100 , msg :"服务器出错🚫🚫🚫" }
} else {
ctx . body = { code :200 , msg :"添加成功" }
}
} )
} )
. catch ( ( err ) => {
ctx . body = { code :100 , msg :"服务器出错🚫🚫🚫" }
} )
} else {
//删除tag操作
//数据库操作
await UserModel . findById ( userInfo . userId )
. then ( async val => {
let { tags} = val
tags . splice ( query . delTagIndex , 1 )
await UserModel . findByIdAndUpdate ( userInfo . userId , { tags} , ( err , doc ) => {
if ( err ) {
ctx . body = { code :100 , msg :"服务器出错🚫🚫🚫" }
} else {
ctx . body = { code :200 , msg :"删除成功" }
}
} )
} )
. catch ( ( err ) => {
ctx . body = { code :100 , msg :"服务器出错🚫🚫🚫" }
} )
}
} )