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 (跨域资源共享)
- 跨域请求
- 如果两个网站的完整的域名不相同
- a网站:http://haha.com
- b网站:http://heihei.com
- 跨域需要检查三个东西:协议 域名 端口号
- http://localhost:5000
- http://127.0.0.1:5000
- 三个只要有一个不同,就算跨域
- 当我们通过AJAX去发送跨域请求时,浏览器为了服务器的安全,会阻止JS读取到服务器的数据
- 如果两个网站的完整的域名不相同
- 解决方案
- 在服务器中设置一个允许跨域的头
- Access-Control-Allow-Origin
- 允许那些客户端访问我们的服务器 https://developer.mozilla.org/en-US/docs/Web/HTTP/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);
});
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步