preface
This article describes how to use MERN technology stack to develop a front and back end of a separate e-commerce project, level limited, shortcomings, please point out, Github.
The backend development
Install and start mongodb and create a new back-end project directory with the following structure
npm init
npm i express mongoose multer validator jsonwebtoken dotenv cors bcrypt -S
Image upload multer, validate form data Validator, configure environment variable Dotenv, and handle CORS across domains
Create a. Env file and configure parameters such as database in it
DB_HOST=localhost
DB_PORT=27017
DB_NAME=cake-shop
JWT_KEY=my_jwt_key
PORT=9090
HOSTNAME=http://localhost
Copy the code
You then define the data model product.js under the Models directory to represent the product, and so on
// product.js
const mongoose = require('mongoose')
const Schema = mongoose.Schema
const productSchema = new Schema(
{
name: {
type: String.required: true.default: ' '
},
description: {
type: String.default: ' '
},
price: {
type: Number.required: true
},
stock: {
type: Number.default: 0
},
imgList: {
type: Array.default: ' '
},
category: {
type: Array
},
top: {
type: Boolean.default: false
},
rate: {
type: Number.default: 5.0
},
publish: {
type: Boolean.default: false}}, {timestamps: { createdAt: 'created_at'.updatedAt: 'updated_at'}})module.exports = mongoose.model('Product', productSchema)
Copy the code
Create product.js in the routes directory to define the product-related route. Create index.js to export the product.js route. Then create product.js in the controllers directory to handle product-specific routes and use it in the entry file
// routes/product.js
const express = require('express')
const router = express.Router()
const auth = require('.. /middleware/auth')
const controller = require('.. /controllers/product')
router.get('/', controller.getAllProducts)
router.get('/top', controller.getTopProducts)
router.get('/recommend', controller.getRecommendProducts)
router.get('/detail/:productId', controller.getProductDetail)
router.get('/:sort', controller.getProducts)
router.post('/', controller.addProduct)
router
.route('/:productId')
.put(auth, controller.updateProduct)
.delete(auth, controller.deleteProduct)
module.exports = router
Copy the code
// index.js
const routes = require('./routes')... app.use('/api/product', routes.product)
Copy the code
Write the logical code corresponding to the route in the controller, run the code logic with Postman, verify the field when the user registers, encrypt the user password, and return the token generated by JsonWENToken to the front end when the user logs in
// controllers/user.js
/ / register
async function signUp(req, res, next) {
let { name, email, password } = req.body
if(! isVerifiedField(name)) {return res.status(400).json({ success: false.message: 'Please enter a user name greater than 4 characters in length'})}if(! isVerifiedEmail(email)) {return res.status(400).json({ success: false.message: 'Please enter the correct email address'})}if(! isVerifiedField(password)) {return res.status(400).json({ success: false.message: 'Please set a password of at least 4 characters.'})}const oldUser = await User.findOne({ email }).exec() // Verify that the user already exists
if(oldUser ! = =null) {
return res.status(409).json({ success: false.message: 'User already exists' })
}
password = await bcrypt.hash(password, 10) // Encrypt the user password
const newUser = new User({ name, email, password })
newUser
.save()
.then(result= > {
return res.status(201).json({ success: true, result })
})
.catch(error= > {
return res.status(500).json({ success: false, error })
})
}
/ / login
async function login(req, res, next) {
const { name, email, password } = req.body
if (name) {
checkField({ name })
}
if (email) {
checkField({ email })
}
async function checkField(field) {
const user = await User.findOne(field).exec()
if (user === null) {
return res.status(404).json({ success: false.message: 'User does not exist'})}const isMatch = await bcrypt.compare(password, user.password)
if (isMatch) {
const token = jwt.sign({ field, id: user._id }, process.env.JWT_KEY) / / token is generated
return res.status(200).json({ success: true.message: 'Login successful', token }) / / returns a token
} else {
return res.status(401).json({ success: false.message: 'Password error'})}}}Copy the code
Management background
Create a project using create-react-app, and install dependency packages using YARN
npx create-react-app you-project
yarn add antd react-router-dom axios
Configure the CSS preprocessor according to the User Guide of create-React-app
Establish the project directory structure
Configuration axios
import axios from 'axios'
const token = localStorage.getItem('CAKE_SHOP_AUTH_TOKEN')
const Request = axios.create({
baseURL: 'http://localhost:9090/api'.timeout: 5000.headers: {
authorization: token ? token : ' ' // If you have a token, put it in the request headers}})export default Request
Copy the code
A page for administrator registration and login is set up in the Pages directory. After the user logs in, the token returned by the backend is stored in localStorage and the user jumps to the workbench home page
// pages/Login
import React, { Component } from 'react'
import { withRouter } from 'react-router-dom'
import { Layout, Form, Icon, Input, Button, Checkbox, message as Message } from 'antd'
import '.. /account.css'
import { ManagerContext } from '.. /.. /store/manager'
import ManagerService from '.. /.. /services/manager'
import { CAKE_SHOP_AUTH_TOKEN, CAKE_SHOP_USER_INFO } from '.. /.. /constant'
const { Item } = Form
const { Content, Footer } = Layout
class Login extends Component {
handleSubmit = e= > {
e.preventDefault()
this.props.form.validateFields(async (err, values) => {
if(! err) {const { name, password } = values
ManagerService.login(name, password) // Request the login interface
.then(res= > {
const { history, login } = this.props // The history object comes from react-router-dom
const { message, token, manager } = res.data
Message.success(message)
localStorage.setItem(CAKE_SHOP_AUTH_TOKEN, `Bearer ${token}`)
localStorage.setItem(CAKE_SHOP_USER_INFO, JSON.stringify(manager))
login(manager)
history.push('/dashboard')
})
.catch(error= > {
const { data } = error.response
Message.error(data.message)
})
}
})
}
render() {
const { getFieldDecorator } = this.props.form
return( <ManagerContext.Consumer> {login => ( <Layout className="account" login={login}> <Content ClassName ="account__content"> <h1 className="account__title"> Store Management system </ H1 > <sub className="account__sub-title"> Login </sub> <Form className="account__form" onSubmit={this.handleSubmit}> <Item> {getFieldDecorator('name', { rules: [{ required: }]})(<Input prefix={<Icon type="user" style={{color: RGB (51, 51, 51); 'rgba(0,0,0,.25)'}} />} placeholder="Username admin" />)} </Item> <Item> {getFieldDecorator('password', {rules: [{required: true, message: 'Please enter your password!'}]})(<Input prefix={<Icon type="lock" style={{color: 'rgba(0,0,0,.25)'}} />} type="password" placeholder=" password admin" />) </Button> </Item> <Item> {getFieldDecorator('remember', {valuePropName: 'checked', initialValue: True})(<Checkbox> remember me </Checkbox>)} </Item> </Form> </Content> <Footer className="account__footer"> <a className="account__link" href="https://xrr2016.github/io" target="_blank" rel="noopener noreferrer" > <Icon type="github" /> </a> <a className="account__link" href="mailto:[email protected]"> <Icon type="mail" /> </a> </Footer> </Layout> )} </ManagerContext.Consumer> ) } } export default withRouter(Form.create()(Login))Copy the code
Create a subroute page in the workbench in the Routes directory, write the corresponding logic, and process different services. Finally, define the react-router-dom route in the entry file
The front page
Use create-react-app to create a project and use YARN to install required dependency packages
npx create-react-app you-project
yarn add antd-mobile react-router-dom axios
Establish the project directory structure
The main difference between the logic here and the management background is that the data interface of the request is different, as well as the UI of the page is different, and the specific implementation, UI interaction and so on depends on the individual.
The source address
The deployment of
After the function is developed, use Yanr build to package the front-end and management background projects, upload the code to the server, configure different domain names, use Nginx reverse proxy, prevent the refresh browser 404.
location / {
try_files $uri $uri/ /index.html;
}
Copy the code