• 前端发起请求,后端响应请求的整个过程



    本文分为:前端、后端两部分讲诉。以下是前端发送请求到服务器,服务器响应前端,的整个过程的图解:
    在这里插入图片描述

    前端

    前端主要是发起请求,本文使用自定义的axios请求方法,分为注册页面模块axios 请求方法模块两部分。

    前端运行地址: http://localhost:8080/

    注册页面

    该页面主要是通过点击提交按钮,触发点击事件,发起注册请求。

    目标端口是:http://localhost:3000/api/login,协议、域名、端口号之中,存在端口号与前端运行地址不一样,满足跨域请求的条件。后端部分会讲到:如何解决跨域问题。

    <el-button type="primary" @click="submitForm('loginForm')">提交el-button>
    
    • 1

    点击提交,触发并传递给submitForm方法参数loginForm。其中,loginForm数据形式是

    loginForm:{
        username:"",  
        pass:"",
    }
    
    • 1
    • 2
    • 3
    • 4

    第 7 行,submitForm发起请求,调用自定义的axios方法:api.login(){}

    import api from "@/api"
    
    submitForm(formName) {
        this.$refs[formName].validate((valid)=> {
            if(valid){
                if(this.currentIndex==='login'){
                    api.login(this.loginForm).then(res=>{
                        // 用户登陆成功
                        if(res.data.status === 200){
                            //用户信息存储到 vuex 和 localstorage
                            this.setUser(res.data)
                            localStorage.setItem('hp',JSON.stringify(res.data))
                            this.$notify({
                                title: '登录成功',
                                type: 'success'
                            });
                            // 用户登陆成功跳转到home页面
                            this.$router.push('/')
                        }else{
                            this.$notify.error({
                                title: '登录失败',
                                message: '请重新登录'
                            });
                        }
                    })
                }
                if(this.currentIndex==='register'){
                    api.register(this.registerForm).then(res =>{
                        console.log(res)
                        if(res.data.status === 200){
                            this.$notify({
                                title: '注册成功',
    
    
                                type: 'success'
                            });
                        }
                    })
                }
            }else{
                return ;
            }
        })
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    axios 请求方法

    为了请求方法的应用性更强,选择自定义请求的方式编写封装请求。

    考虑到模块开发、代码可读性的好处,将请求方法封装模块分为路径配置自定义 axiosAPI封装三部分。

    URL 路径配置

    每次请求都使用以下预定义的路径,该文件向外导出base对象

    const base = {
      baseUrl: "http://localhost:3000",
      register: "/api/register",//用户的接口
      login: "/api/login",
      selectTbItemAllByPage: '/api/backend/item/selectTbItemAllByPage',//商品列表请求路径
      total: "/api/total",//商品总条数
      search: "/api/search" //商品 模糊查询
    }
    export default base
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    自定义 axios

    自定义axios 易用、简洁且高效的http库,对发起的axios请求和响应进行拦截

    // /api/utils/request.js 文件
    import axios from "axios
    // 引入qs模块,用来序列化post类型的数据
    import qs from 'qs'
    import router from "@/router"
    
    //主要步骤:三步
    // 1.创建一个axios实例
    // 2.拦截器--请求拦截
    // 3.拦截器--响应拦截
    
    // 1.创建一个axios实例
    const instance = axios.create({
        timeout: 5000,  //超时处理,超过时间告诉用户超时
        // baseURL: "http://localhost:3000",
    })
    
    instance.all = axios.all;
    instance.spread = axios.spread
    // 设置post的请求头
    instance.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
    // interceptors 拦截器
    // 2.请求拦截 做一个逻辑后再把请求发送,可以用于配置公用的逻辑,就不用每个请求都配一遍。
    
    instance.interceptors.request.use(
        // 请求拦截配置  
        // 拦截成功,返回非promise实例对象config
        config => {
            if (config.method === 'post') {
                config.data = qs.stringify(config.data);
            }
            return config;
    
            // 判断token是否存在,存在就添加到请求头上
            // const token = store.state.loginModule.user.token;
            // if(token){
            //     config.headers.authorization = store.state.loginModule.user.token;
            // }
        },
        // 拦截失败时,返回promise
        error => Promise.reject(error)
    )
    
    const toLogin = () => {
        router.push("/login")
    }
    // errorHandle打印失败状态码对应的 描述和路由去向
    const errorHandle = (status, info) => {
        switch (status) {
            case 400:
                console.log("服务器收到客户端通过PUT或者POST请求提交的表示,表示的格式正确,但服务器不懂它什么意思");
                toLogin();
                break;
            case 401:
                console.log("客户端试图对一个受保护的资源进行操作,却又没有提供正确的认证证书");
                toLogin();
                break;
            case 403:
                console.log("客户端请求的结构正确,但是服务器不想处理它");
                toLogin();
                break;
            case 404:
                console.log("资源被围定义(网络请求地址错误)");
                break;
            case 500:
                console.log("执行请求处理代码时遇到了异常,它们就发送此响应代码");
                break;
            case 503:
                console.log("最可能的原因是资源不足:服务器突然收到太多请求,以至于无法全部处理");
                break;
            default:
                console.log(info);
                break;
        }
    }
    // 3.interceptors拦截器配置response响应拦截
    instance.interceptors.response.use(
        // 成功时
        response => response.status === 200 ? Promise.resolve(response) : Promise.reject(response),
        // 失败时
        error => {
            const { response } = error;
            if (response) {
                errorHandle(response.status, response.data);
                return Promise.reject(response);
            } else {
                console.log("请求被中断");
            }
        }
    )
    // 封装get请求
    export function get(url, params) {
        return new Promise((resolve, reject) => {
            instance.get(url, params).then(res => {
                //请求回调成功
                console.log('封装这里,', params)
                resolve(res.data);
            }).catch(err => {
                reject(err.data);
            })
        })
    }
    // 封装post请求
    
    export function post(url, params) {
        return new Promise((resolve, reject) => {
            instance.post(url, params).then(res => {
                //请求回调成功
                resolve(res.data)
            }).catch(err => {
                reject(err.data)
            })
        })
    }
    
    export default instance
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116

    方法封装

    // api/index.js 文件
    // 入口文件
    // 导入自定义ajax封装库axios: myaxios 
    import myaxios from "@/api/utils/request"
    // 导入自定义路径配置,模块化开发 简化url 
    import base from "./base"
    
    // 定义前端的发送请求方法 
    const api = {
      // 注册请求
      register(params) {
        // 调用自定义asiox(myaxios)封装的post方法
        return myaxios.post(base.baseUrl + base.register, params)
      }
     }
    }
    // 定义的请求方法全部导出
    export default api;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    后端

    本文采用:node.js + express+MySQL,创建web 服务器,构建后端。后端编写分为服务器模块接口模块连接数据库模块三部分,都是server文件夹下的 js 文件。

    后端运行地址:http://127.0.0.1:3000/

    创建服务器模块

    因此,需要完成获取数据请求必须解决跨域问题,跨域问题可以在前端或者后端解决。本文采取后端解决,使用 CORS同源安全策略 默认阻止“跨域”获取资源。但是 CORS 给了 web 服务器这样的权限,即服务器可以选择,允许跨域请求访问到它们的资源。

    //接口服务器 server/index.js文件
    // 1.导入express
    const express = require("express")
    //跨域请求处理 后台处理
    const cors = require("cors")
    //post传参问题
    const bodyParser = require("body-parser")
    // 导入自定义路由
    const router = require("./router")
    
    // 2.创建web服务器
    const app = express()
    
    // 注册中间件
    app.use(cors());
    // app.use(express.json());
    app.use(bodyParser.urlencoded({ extended: false }))
    //路由访问前缀
    app.use('/api', router)
    
    // 3.启动服务器
    app.listen(3000, () => {
      console.log('express server running at http://127.0.0.1')
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    其中,web服务器服务器对象 app,通过导入接口方法模块,并使用app.use('/api', router)实现接口方法应用。

    接口方法模块

    注册的步骤:主要是实现服务器获取请求体的数据,然后将数据插入数据库,最后给客户端响应。

    // server/router.js文件
    // 设置路由,定义对应post,URL的处理函数
    const express = require("express")
    // 创建路由对象
    const router = express.Router();
    //导入数据库配置对象
    const sqlClient = require('./dbconfig')
    //导入JWT生成token
    const JWT = require("jsonwebtoken")
    //导入JWT解密
    const expressJWT = require("express-jwt")
    // 请求post和url=localhost:3000/api/register 的注册路由
    const url = require("url");
    const { send } = require("process");
    
    
    router.post("/register", (req, res) => {
      //接收请求对象携带的数据
      const { username, pass, email } = req.body;
      //sqlClient实现连接数据库,并将用户数据插入数据库 并回调函数响应数据
      sqlClient("insert into user values(null,?,?,?)", [username, pass, email], result => {
        //插入成功 并响应对象数据给客户端
        if (result.affectedRows > 0) {
          res.send({
            status: 200,
            msg: "注册成功"
          })
        } else {
          res.send({
            statu: 401,
            msg: "注册失败"
          })
        }
      })
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    连接数据库

    只需要导入mysql库,即可以实现数据库连接,并向外导出操作数据库的方法对象,该函数返回一个操作结果。

    // server/dbconfig.js文件
    const mysql = require("mysql")
    
    //定义连接对象
    const client = mysql.createConnection({
      host: "localhost",
      user: "root",
      password: "root",
      database: "vue_mall"
    })
    //定义操作数据库的方法,参数为sql语句,数组数据,回调函数
    const sqlClient = (sql, arr, callback) => {
      client.query(sql, arr, (error, result) => {
        if (error) {
          //发生错误,返回错误信息
          console.log(error)
          return
        }
        //成功,则调用回调函数返回操作的结果
        callback(result)
      })
    }
    module.exports = sqlClient
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    使用 mysql 对象的createConnection方法创建连接数据库对象 client,然后 定义一个sqlClient对象,其中 sqlClient 需要参数sql语句数组数据,便会调用回调函数,并返回sql语句数据操作数据库的结果。

  • 相关阅读:
    微服务—RabbitMQ高级(业务在各方面的可靠性)
    Python数据权限的管理通常涉及到几个关键组件:身份验证,、授权和访问控制。这通常是通过使用数据库、ORM(对象关系映射)框架、API框架和中间件
    动态规划——474. 一和零
    《码处高效:Java开发手册》之代码风格
    为了大厂得码住学起来~Java架构师进阶之路:Java核心框架指导
    机器学习——异常检测
    ORACLE not available如何解决
    数商云SCM系统订单收货场景介绍,探索采购新模式,提升汽车服务企业运营水平
    微服务架构
    ES6之Symbol.hasInstance
  • 原文地址:https://blog.csdn.net/puhuihui/article/details/126292449