Ajax(Fetch&Axios)

传统服务器

传统服务器的结构是基于MVC模式
Model -- 数据模型
View -- 视图,用来呈现
Controller -- 控制器,复杂加载数据并选择视图来呈现数据

传统的服务器是直接为客户端返回一个页面,但是传统的服务器并不能适用于现在的应用场景

前后端分离

现在的应用场景,一个应用通常都会有多个客户端(client)存在
web端 移动端(app) pc端
- 如果服务器直接返回html页面,那么服务器就只能为web端提供服务
其他类型的客户端还需要单独开发服务器,这样就提高了开发和维护的成本

如何解决这个问题?
- 传统的服务器需要做两件事情,第一个加载数据,第二个要将模型渲染进视图
- 解决方案就将渲染视图的功能从服务器中剥离出来,
服务器只负责向客户端返回数据,渲染视图的工作由客户端自行完成
- 分离以后,服务器只提供数据,一个服务器可以同时为多种客户端提供服务器
同时将视图渲染的工作交给客户端以后,简化了服务器代码的编写

REST

  • REpresentational State Transfer 表示层状态的传输

    • Rest实际上就是一种服务器的设计风格

    • 它的主要特点就是,服务器只返回数据

    • 服务器和客户端传输数据时通常会使用JSON作为数据格式

  • 请求的方法:

    • GET加载数据
    • POST 新建或添加数据
    • PUT 添加或修改数据
    • PATCH 修改数据
    • DELETE 删除数据
    • OPTION 由浏览器自动发送,检查请求的一些权限

postman
- 这是一个软件,通过它可以帮助向服务器发送各种请求
帮助测试API

REST风格的服务器

yarn init -y

yarn add express

npm install -g nodemon Nodemon当检测到目录中的文件更改时,它会自动重启node应用程序。

编写一个简易服务器

const express = require('express')
const app = express()

// 解析请求体格式的中间件
app.use(express.urlencoded({ extended: true }))
// 解析json格式请求体的中间件
app.use(express.json())

let STU_DATA = [
  { id: "1", name: '孙悟空', age: 20, gender: '男', address: '花果山' },
  { id: "2", name: '猪八戒', age: 20, gender: '男', address: '高老庄' },
  { id: "3", name: '沙和尚', age: 20, gender: '男', address: '流沙河' }
]

app.get('/student', (req, res) => {
  res.send({
    status: 'ok',
    data: STU_DATA
  })
})

app.get('/student/:id', (req, res) => {
  const id = req.params.id
  const stu = STU_DATA.find((item) => {
    return (item.id === id)
  })
  res.send({
    status: 'ok',
    data: stu
  })
})

app.post('/student', (req, res) => {
  console.log(req.body);
  const { name, age, gender, address } = req.body;
  const stu = {
    id: +STU_ARR.at(-1).id + 1 + "", name, age, gender, address
  }
  STU_DATA.push(stu)
  res.send({
    status: 'ok',
    data: stu
  })
})

app.put('/student', (req, res) => {
  const { id, name, age, gender, address } = req.body
  const updataStu = STU_DATA.find(item => item.id === id)
  if (updataStu) {
    updataStu.name = name;
    updataStu.age = age;
    updataStu.gender = gender;
    updataStu.address = address;

    res.send({
      status: 'ok',
      data: updataStu
    })
  } else {
    res.status(403).send({
      status: 'error',
      data: "学生id不存在"
    })
  }
})

app.delete('/student/:id', (req, res) => {
  let delStu = {}
  STU_DATA = STU_DATA.filter((item) => {
    if (item.id === req.params.id) delStu = item
    return item.id !== req.params.id
  })
  if (delStu) {
    res.send({
      status: 'ok',
      data: delStu
    })
  }
  else {
    res.status(403).send({
      status: 'error',
      data: "学生id不存在"
    })
  }
})

app.listen(3000, () => {

})

Ajax

  • A 异步 J JavaScript A 和 X xml

  • 异步的js和xml

  • 它的作用就是通过js向服务器发送请求来加载数据

  • xml是早期AJAX使用的数据格式,目前数据格式都使用json

  • 可以选择的方案:① XMLHTTPRequest(xhr) ② Fetch ③ Axios

CORS (跨域资源共享)

app.use((req, res,next) => {
    // 设置响应头
    res.setHeader("Access-Control-Allow-Origin", "*")
    res.setHeader("Access-Control-Allow-Methods", "GET,POST")
    res.setHeader("Access-Control-Allow-Headers", "Content-type")
    // Access-Control-Allow-Origin 设置指定值时只能设置一个
    // res.setHeader("Access-Control-Allow-Origin", "http://127.0.0.1:5500")
    // Access-Control-Allow-Methods 允许的请求的方式
    // Access-Control-Allow-Headers 允许传递的请求头
    next()
})

Fetch

  • fetch是xhr的升级版,采用的是Promise API

  • 作用和AJAX是一样的,但是使用起来更加友好

  • fetch原生js就支持的一种ajax请求的方式

fetch("http://localhost:3000/students", {
  method: "post",
  headers: {
    // application/x-www-form-urlencoded 表单项
    //用来指定发送的数据是什么类型 JSON类型数据
    "Content-type": "application/json"
  },
  // 通过body去发送数据时,必须通过请求头来指定数据的类型
  body: JSON.stringify({
    name: "白骨精",
    age: 16,
    gender: "女",
    address: "白骨洞"
  })
}).then((res) => res.json())
  .then((res) => {
     if (res.status !== "ok") {
        throw new Error("用户名或密码错误")
     }})
  .catch((err) => {
     console.log("出错了!", err)
  })

请求终止

const controller = new AbortController()
fetch("http://localhost:3000/test", {
    signal: controller.signal
})
    .then((res) => console.log(res))
    .catch((err) => console.log("出错了", err))
setTimeout(() => {
    controller && controller.abort()
}, 3000)

关于用户登录

登录成功以后,需要保持用户的登录的状态,需要将用户的信息存储到某个地方

需要将用户信息存储到本地存储

所谓的本地存储就是指浏览器自身的存储空间,可以将用户的数据存储到浏览器内部

  • sessionStorage 中存储的数据 页面一关闭就会丢失
  • localStorage 存储的时间比较长

setItem() 用来存储数据
getItem() 用来获取数据
removeItem() 删除数据
clear() 清空数据

但是登录以后直接将用户信息存储到localStorage存在两个问题:

1.数据安全问题 2.服务器不知道用户有没有登录

jwt

对数据进行加密,加密以后在发送给客户端保存,这样即可避免数据的泄露

在node中可以直接使用jsonwebtoken这个包来对数据进行加密

jsonwebtoken(jwt) --> 通过对json加密后,生成一个web中使用的令牌

// 引入jwt
const jwt = require("jsonwebtoken")
// 创建一个对象
const obj = {
    name: "swk",
    age: 18,
    gender: "男"
}
// 使用jwt来对json数据进行加密
const token = jwt.sign(obj, "hellohellohowyou", {
    expiresIn: "1"// 单位秒,过期时间
})
try {
    //服务器收到客户端的token后进行解密
    const decodeData = jwt.verify(token, "hellohellohowyou")
    console.log(decodeData)
} catch (e) {
    // 说明token解码失败,说明token
    console.log("无效的token")
}
// 定义一个登录的路由
app.post("/login", (req, res) => {
    // 获取用户输入的用户名和密码
    const { username, password } = req.body
    // 验证用户名和密码
    if (username === "admin" && password === "123123") {
        // 登录成功,生成token
        const token = jwt.sign(
            {
                id: "12345",
                username: "admin",
                nickname: "超级管理员"
            },
            "chaojianquanmima",
            {
                expiresIn: "1d"
            }
        )
        // 登录成功
        res.send({
            status: "ok",
            data: {
                token,
                nickname: "超级管理员"
            }
        })
    } else {
        // 登录失败
        res.status(403).send({
            status: "error",
            data: "用户名或密码错误"
        })
    }
})
const token = localStorage.getItem("token")
fetch("http://localhost:3000/students", {
    headers: {
        // "Bearer xxxxxx"
        "Authorization": `Bearer ${token}`
    }
}).then((res) => {
    if (res.status === 200) {
        // res.json() 可以用来读取json格式的数据
        return res.json()
    } else {
        throw new Error("加载失败!")
    }
}).then((res) => {

})
app.get("/students", (req, res) => {
    try {
        // 这个路由必须在用户登录后才能访问
        // 需要检查用户是否登录
        // 读取请求头
        const token = req.get("Authorization").split(" ")[1]

        // 对token进行解码
        const decodeToken = jwt.verify(token, "chaojianquanmima")
        console.log(decodeToken)
        // 解码成功,token有效,返回学生信息
        res.send({
            status: "ok",
            data: STU_ARR
        })
    } catch (e) {
        // 解码错误,用户token无效
        res.status(403).send({
            status: "error",
            data: "token无效"
        })
    }
})

Axios

基本用法

axios({
    method: "post",
    url: "http://localhost:3000/students",
    data: {
        name: "唐僧",
        age: 18,
        gender: "男",
        address: "女儿国"
    } // 请求参数
    // data:"name=swk&age=18"
})
    .then((result) => {
        // result是axios封装过
        console.log(result.data)
    })
    .catch((err) => {
        console.log("出错了!", err)
    })

请求的配置对象

axios({
    // baseURL 指定服务器的根目录(路径的前缀)
    baseURL: "http://localhost:3000",
    // 请求地址
    url: "test",

    // 请求方法,默认是get
    method: "get",

    // 指定请求头
    // headers:{"Content-type":"application/json"}

    // 请求体 表单形式发送encode
    // data:"name=唐僧&age=16"
    data: {
        name: "唐僧",
        age: 18,
        gender: "男",
        address: "女儿国"
    },

    // params 用来指定路径中的查询字符串
    params: {
        id: 1,
        name: "swk"
    },

    //timeout 过期时间
    timeout: 1000,

    // transformRequest 可以用来手动处理请求数据(data),正常的axios都是自动配置好的
    // 它需要一个数组作为参数,数组可以接收多个函数,请求发送时多个函数会按照顺序执行
    // 函数在执行时,会接收到两个参数data和headers
    // transformRequest:[function(data, headers){
    //     // 可以在函数中对data和headers进行修改
    //     data.name = "猪八戒"
    //     headers["Content-Type"] = "application/json"
    //     return data
    // }, function(data, headers){
    //     // 最后一个函数必须返回一个字符串,才能使得数据有效
    //     return JSON.stringify(data)
    // }]
})
    .then((result) => {
        // result是axios封装过
        console.log(result.data)
    })
    .catch((err) => {
        console.log("出错了!", err)
    })

响应数据的结构

{
    //服务器响应数据
	data:{},
	//服务器响应状态码
	status:200,
	//服务器响应的http状态信息
    statusText:'ok',
    headers:{},
    //请求的配置对象
    config:{},
    request:{}
}

默认配置

axios.defaults.baseURL = "http://localhost:3000"
axios.defaults.headers.common[
    "Authorization"
] = `Bearer ${localStorage.getItem("token")}`

axios({
    url: "students",
    method: "post",
    data: {
        name: "唐僧",
        age: 18,
        gender: "男",
        address: "女儿国"
    },
    timeout: 1000
})
    .then((result) => {
        console.log(result.data)
    })
    .catch((err) => {
        console.log("出错了!", err)
    })

axios实例

axios实例相当于是axios的一个副本,它的功能和axios一样

axios的默认配置在实例也同样会生效,但是可以单独修改axios实例的默认配置

const instance = axios.create({
    baseURL: "http://localhost:4000"
})

const instance = axios.create()
instance.defaults.baseURL = "xxx"

instance.get("students")
    .then((res) => console.log(res.data))
    .catch((err) => {
       console.log("出错了", err)
    })

axios拦截器

axios的拦截器可以对请求或响应进行拦截,在请求发送前和响应读取前处理数据(请求要发出去还没发出去的时候,位于请求和服务器之间)

拦截器只对当前的实例有效,如果定义了新的实例,就无效

  • 两个参数 :参数一对响应数据做点什么,参数二对响应错误做点什么

config是axios中的配置对象

import axios from 'axios'
import qs from 'qs';

axios.defaults.baseURL = 'http://127.0.0.1:3007'

const myAxios =axios.create({
  headers: { 'content-type': 'application/x-www-form-urlencoded' },
  transformRequest: [function (data) {
    return qs.stringify({
      ...data
    })
  }]
});

axios.interceptors.request.use((config) => {
  config.headers.Authorization = sessionStorage.getItem('token');
  return config;
})

/* 添加请求拦截器 */
axios.interceptors.request.use((config) => {
  if (config.method === 'post') {
    config.headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8';
    config.transformRequest = [function (data, headers) {
      return qs.stringify(data);
    }];
  }
  return config;
}, (err) => {
  return Promise.reject(err);
});

posted @   Lu西西  阅读(44)  评论(0编辑  收藏  举报
相关博文:
点击右上角即可分享
微信分享提示