项目地址:github.com/Nealyang/Re…html
本想等项目作完再连载一波系列博客,随着开发的进行,也是的确遇到了很多坑,请教了很多人。遂想,何不一边记录踩坑,一边分享收获呢。分享固然是好的,
若是能作到集思广益,那岂不是更美。咱们的口号是:坚定不会烂尾前端
本博客为连载代码博客同步更新博客,随着项目日后开发可能会遇到前面写的不合适的地方会再回头修改。若有不妥~欢迎兄弟们不啬赐教。谢谢!react
接上篇,咱们登陆界面已经画完了,登陆功能,涉及到异步请求。因此大体我须要须要以下几个action。请求发起action,请求结束action,错误信息提醒action,登陆action,注册action以及后面免登录咱们用到的自动登陆action。git
由于该登陆功能涉及到的都是全局的信息,因此这里咱们放到index的reducer中处理github
const initialState = {
isFetching: true,
msg: {
type: 1,//0失败 1成功
content: ''
},
userInfo: {}
};
export const actionsTypes = {
FETCH_START: "FETCH_START",
FETCH_END: "FETCH_END",
USER_LOGIN: "USER_LOGIN",
USER_REGISTER: "USER_REGISTER",
RESPONSE_USER_INFO: "RESPONSE_USER_INFO",
SET_MESSAGE: "SET_MESSAGE",
USER_AUTH:"USER_AUTH"
};
export const actions = {
get_login: function (username, password) {
return {
type: actionsTypes.USER_LOGIN,
username,
password
}
},
get_register: function (data) {
return {
type: actionsTypes.USER_REGISTER,
data
}
},
clear_msg: function () {
return {
type: actionsTypes.SET_MESSAGE,
msgType: 1,
msgContent: ''
}
},
user_auth:function () {
return{
type:actionsTypes.USER_AUTH
}
}
};
export function reducer(state = initialState, action) {
switch (action.type) {
case actionsTypes.FETCH_START:
return {
...state, isFetching: true
};
case actionsTypes.FETCH_END:
return {
...state, isFetching: false
};
case actionsTypes.SET_MESSAGE:
return {
...state,
isFetching: false,
msg: {
type: action.msgType,
content: action.msgContent
}
};
case actionsTypes.RESPONSE_USER_INFO:
return {
...state, userInfo: action.data
};
default:
return state
}
}复制代码
class LoginFormCom extends Component {
constructor(props) {
super(props);
}
handleLogin = (e) => {
e.preventDefault();
this.props.form.validateFields((err, values) => {
if (!err) {
this.props.login(values.userName,values.password)
}
});
};
render() {
const {getFieldDecorator} = this.props.form;
return (
<Form onSubmit={this.handleLogin} className={style.formStyle}>
<FormItem>
{getFieldDecorator('userName', {
rules: [{required: true, message: '请输入用户名!'}],
})(
<Input prefix={<Icon type="user" style={{fontSize: 13}}/>} placeholder="Username"/>
)}
</FormItem>
<FormItem>
{getFieldDecorator('password', {
rules: [{required: true, message: '请输入密码!'}],
})(
<Input prefix={<Icon type="lock" style={{fontSize: 13}}/>} type="password"
placeholder="Password"/>
)}
</FormItem>
<FormItem>
<Button className={style.loginButton} type="primary" htmlType="submit">
登陆
</Button>
</FormItem>
</Form>
)
}
}
const LoginForm = Form.create()(LoginFormCom);
export default LoginForm复制代码
如上代码,在handleLogin中,咱们调用父组件传进来的login方法。可能得说是爷爷组件吧。罢了,就是其容器组件。数据库
而容器组件Home.js中的代码以下:express
Home.defaultProps = {
userInfo:{}
};
Home.propsTypes = {
userInfo:PropTypes.object.isRequired
};
function mapStateToProps(state) {
return{
userInfo:state.globalState.userInfo
}
}
function mapDispatchToProps(dispatch) {
return{
login:bindActionCreators(actions.get_login,dispatch),
register:bindActionCreators(actions.get_register,dispatch)
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Home);复制代码
如上,咱们已经定义了login和register。分别为登陆和注册两个方法。在登陆部分咱们如上写。固然,注册功能也是如上。json
由于登陆和注册都是异步的,因此这里咱们须要saga去监听这个action的发起。而后对应的去处理。redux
export function* register (data) {
yield put({type:IndexActionTypes.FETCH_START});
try {
return yield call(post, '/user/register', data)
} catch (error) {
yield put({type:IndexActionTypes.SET_MESSAGE,msgContent:'注册失败',msgType:0});
} finally {
yield put({type: IndexActionTypes.FETCH_END});
}
}
export function* registerFlow () {
while(true){
let request = yield take(IndexActionTypes.USER_REGISTER);
let response = yield call(register, request.data);
if(response&&response.code === 0){
yield put({type:IndexActionTypes.SET_MESSAGE,msgContent:'注册成功!',msgType:1});
yield put({type:IndexActionTypes.RESPONSE_USER_INFO,data:response.data})
}
}
}复制代码
这里咱们就举例说下registerFlow吧,其实也就是监听USER_REGISTER的action。而后调用register方法,发送请求开始action(界面出现Loading),而后请求结束action。接收到请求后,拿出数据,发送拿到数据后的action后端
基本思路如上,代码如上,你们研究研究哈,不明白的地方,直接issue。
router.post('/register', (req, res) => {
let {userName, password, passwordRe} = req.body;
if (!userName) {
responseClient(res, 400, 2, '用户名不可为空');
return;
}
if (!password) {
responseClient(res, 400, 2, '密码不可为空');
return;
}
if (password !== passwordRe) {
responseClient(res, 400, 2, '两次密码不一致');
return;
}
//验证用户是否已经在数据库中
User.findOne({username: userName})
.then(data => {
if (data) {
responseClient(res, 200, 1, '用户名已存在');
return;
}
//保存到数据库
let user = new User({
username: userName,
password: md5(password + MD5_SUFFIX),
type: 'user'
});
user.save()
.then(function () {
User.findOne({username: userName})
.then(userInfo=>{
let data = {};
data.username = userInfo.username;
data.userType = userInfo.type;
data.userId = userInfo._id;
responseClient(res, 200, 0, '注册成功', data);
return;
});
})
}).catch(err => {
responseClient(res);
return;
});
});复制代码
后端这边其实都差很少,咱们拿注册举例子。简单解释下上面代码
responseClient是封装的一个方法。代码以下:
module.exports = {
MD5_SUFFIX: 'eiowafnajkdlfjsdkfj大姐夫文姐到了困难额我积分那看到你@#¥%……&)(*&……)',
md5: function (pwd) {
let md5 = crypto.createHash('md5');
return md5.update(pwd).digest('hex')
},
responseClient(res,httpCode = 500, code = 3,message='服务端异常',data={}) {
let responseData = {};
responseData.code = code;
responseData.message = message;
responseData.data = data;
res.status(httpCode).json(responseData)
}
}复制代码
让你简写不少代码。而后判断用户名、密码是否为空以及两次密码是否一致。(虽然这些部分在前端也应该作判断,可是后端也尽可能保障一下)。
验证用户是否已经在数据库中。若是不存在,则存储下,而后将用户信息返回。若是存在,则返回给客户端相应的信息。
注意这里咱们由于用的saga,因此只要是http请求三次握手成功的,咱们都是返回200.对于用户名重复、别的错误,咱们统一在返回的数据中给个状态码标识。
存储的时候咱们用md5加密,为了防止md5解密,咱们在后面添加了一个随机的字符串。在登陆的时候,拿到用户的登陆密码,而后加上随机字符串,进行md5加密再与数据库数据记性比较也就OK了。
后端实现基本思路就是这些,而后对于mongoose的基本操做这里就不赘述,你们可自行查看文档。
router.post('/login', (req, res) => {
let {username, password} = req.body;
if (!username) {
responseClient(res, 400, 2, '用户名不可为空');
return;
}
if (!password) {
responseClient(res, 400, 2, '密码不可为空');
return;
}
User.findOne({
username,
password: md5(password + MD5_SUFFIX)
}).then(userInfo => {
if (userInfo) {
//登陆成功
let data = {};
data.username = userInfo.username;
data.userType = userInfo.type;
data.userId = userInfo._id;
//登陆成功后设置session
req.session.userInfo = data;
responseClient(res, 200, 0, '登陆成功', data);
return;
}
responseClient(res, 400, 1, '用户名密码错误');
}).catch(err => {
responseClient(res);
})
});复制代码
基本到这,就是实现了一个查和增的过程。也实现了先后端的基本交互。你们感觉下哈~
而后你们确定也是发现了,登陆了之后,貌似每次刷新咱们都要再从新登陆,这并非咱们想要的。固然,这部分功能,咱们将在下一篇博客中介绍。
实战react技术栈+express先后端博客项目(17)-- 收工
假若有哪里说的不是很明白,或者有什么须要与我交流,欢迎各位提issue。或者加群联系我~
扫码关注个人我的微信公众号,直接回复,必有回应。分享更多原创文章。点击交流学习加我微信、qq群。一块儿学习,一块儿进步
欢迎兄弟们加入:
Node.js技术交流群:209530601
React技术栈:398240621
前端技术杂谈:604953717 (新建)