从零开始,搭建一个简单的购物平台(三)
从零开始,搭建一个简单的购物平台(二):https://blog.csdn.net/time_____/article/details/105408640
项目源码(持续更新):https://gitee.com/DieHunter/myCode/tree/master/shopping
这篇文章在上次的服务端登录基础上增加前端界面
在开始前我们可以简单用postman测试一下token和登录的功能,可以先在后端生成一段token从前端请求
验证token(成功和失败)
紧接着验证一下用户登录(前提事先在数据库添加用户信息,添加过程及加密不做详细说明)
测试成功后我们开始配置编写前端部分(只实现功能,对界面要求不高)
文件结构:
- 配置config.js文件夹,新建config.js文件用于存放配置常量(和服务端config.js一样)
export default class Config { static Agreement = "http://"; static BaseUrl = "127.0.0.1"; static ServerUrl = ""; static ServerPort = ":1024"; static Path = "/"; static CryptoKey = "tokenkey";//加密信息关键字 static FilePath = this.Agreement + this.BaseUrl + this.ServerPort + this.Path; static ServerApi = {//接口名 token: "checkToken", user: { userLogin: "user/userLogin", } }; static StorageName = {//本地缓存localstorage名称 token: "token", userInfo: "userInfo" }; }
- 在utils文件中新建方法,将其放在react.component的原型中,使继承组件可以直接调用
storage:import { Component } from "react"; class Utils { static saveStorage(key, val) {//添加缓存 localStorage.setItem(key, JSON.stringify(val)); } static getStorage(key) {//获取缓存 try { return JSON.parse(localStorage.getItem(key)); } catch (error) {} } static clearStorage(key) {//清除缓存 try { localStorage.removeItem(key); } catch (error) {} } } Component.prototype.$utils = Utils;
axios:
import Config from "../config/config"; import Axios from "axios"; import { Component } from "react"; import { message } from "antd"; Axios.defaults.baseURL = Config.Agreement + Config.BaseUrl + Config.ServerPort + Config.Path; // 添加请求拦截器 Axios.interceptors.request.use( function (config) { // 在发送请求之前做些什么 return config; }, function (error) { // 对请求错误做些什么 return Promise.reject(error); } ); // 添加响应拦截器 Axios.interceptors.response.use( function (response) { // 对响应数据做点什么 if (response.data.result === -999) { //token验证失败 return message.error(response.data.msg); } return response.data; }, function (error) { console.log(error) // 对响应错误做点什么 message.error("操作失败"); return Promise.reject(error); } ); Component.prototype.$axios = Axios;
Crypto:
import * as CryptoJS from "crypto-js"; import { Component } from "react"; import config from "../config/config"; const { CryptoKey } = config; class CryptoTool { /* Crypto加密方法 * @param {object} _data 对用户请求后端的参数进行加密 */ static setCrypto(_data) { let key = CryptoJS.enc.Utf8.parse(CryptoKey); let encrypted = CryptoJS.AES.encrypt(JSON.stringify(_data), key, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 }); return encrypted.toString(); } /* Crypto解密方法 * @param {string} _token 将秘文解密成对象形式 */ static getCrypto(_token) { let key = CryptoJS.enc.Utf8.parse(CryptoKey); let decrypt = CryptoJS.AES.decrypt(_token, key, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 }); return JSON.parse(CryptoJS.enc.Utf8.stringify(decrypt).toString()); } } Component.prototype.$crypto = CryptoTool;
- 在index.js中添加这些函数
import React from "react"; import ReactDOM from "react-dom"; import App from "./App"; import "./util/axios"; import "./util/utils"; import "./util/cryptoTool";
- 随后在page/login中新增login.js,简单配置一下login页面后,在App.js中引入login页面
import React from "react"; import Login from "./page/login/login" function App() { return ( <div className="App"> <Login></Login> </div> ); } export default App;
- 在antd官网找到登录界面实例,直接把大部分复制到login界面,用less微调一下后实现以下界面
- login.js
import React from "react"; import "./login.less"; import { Card, Form, Input, Button, Checkbox, message } from "antd"; import { UserOutlined, LockOutlined } from "@ant-design/icons"; import config from "../../config/config"; const { ServerApi, StorageName } = config; export default class Login extends React.Component { constructor(props) { super(props); this.checkToken(); //验证用户token是否过期 } render() { return ( <div className="cardBox"> <Card title="登录"> <Form name="normal_login" className="login-form" initialValues={{ remember: true }} onFinish={this.sendData} > <Form.Item name="username" rules={[ { required: true, message: "请输入用户名/邮箱", }, ]} > <Input className="infoInput" prefix={<UserOutlined className="site-form-item-icon" />} placeholder="用户名/邮箱" /> </Form.Item> <Form.Item name="password" rules={[ { required: true, message: "请输入密码", }, ]} > <Input className="infoInput" prefix={<LockOutlined className="site-form-item-icon" />} type="password" placeholder="密码" /> </Form.Item> <Form.Item> <Form.Item name="remember" valuePropName="checked" noStyle> <Checkbox>3天内免密</Checkbox> </Form.Item> <a className="login-form-forgot" href="#aaa"> 忘记密码 </a> </Form.Item> <Form.Item> <Button type="primary" htmlType="submit" className="login-form-button" > 登录 </Button> 或者<a href="#aaa">注册</a> </Form.Item> </Form> </Card> </div> ); } checkToken() { let token = this.$utils.getStorage(StorageName.token); if (!token) return; this.$axios .get(ServerApi.token, { params: { token }, }) .then((res) => { switch (res.result) { case 1: message.success(res.msg).then(() => { // this.props.history.push({ // pathname: "/admin/findshop", // query: res, // }); }); break; default: // message.warning(res.msg); break; } }) .catch((err) => { console.log(err); }); } sendData = (data) => { this.$axios .get(ServerApi.user.userLogin, { params: { crypto: this.$crypto.setCrypto(data) }, }) .then((res) => { switch (res.result) { case 1: this.$utils.saveStorage(StorageName.token, res.token); message.success(res.msg); // this.props.history.push({ // pathname: "/admin/findshop", // query: res, // }); break; default: message.warning(res.msg); break; } }) .catch((err) => { console.log(err); }); }; }
- 效果如下:
用户token验证,登录功能前端+后端基本实现
总结
前端与后端的项目搭建顺序可能有所不同,项目周期可能也不同,这时需要灵活使用前后端数据模拟和请求模拟工具,前端可以使用easymock和mockjs生成假数据,而后端最简单的直接放到浏览器url访问(仅限于get),或者使用postman,SoapUI等接口测试工具