前端的几种网络请求方式

网络请求

node编写接口

这里用到的几个包的作用

  • express:基于 Node.js 平台,快速、开放、极简的 Web 开发框架,官网:https://www.expressjs.com.cn/
  • cors:用来解决跨域问题
  • body-parser:可以通过 req.body 获取post请求传递过来的json数据
  • multer:用于文件上传
const express = require('express');
const fs = require("fs");
const path = require("path")
const app = express()
const multer = require("multer");
const cors = require("cors")
const bodyParser = require("body-parser")
app.use(cors())
app.use(bodyParser.json())
// 设置上传目录和文件名
const storage = multer.diskStorage({
    destination: function (req, file, cb) {
        cb(null, 'uploads/')
    },
    filename: function (req, file, cb) {
        console.log(file)
        cb(null, Date.now() + path.extname(file.originalname))
    }
})

const upload = multer({ storage: storage });


app.get("/name",(req,res)=>{
    res.send({
        code:200,
        msg:"成功"
    })
})


app.post("/add",(req,res)=>{
    console.log(req.body)
    // 使用req.body接收前端传递过来的post参数
    let data = req.body
    res.send({
        code:200,
        data:data
    })
})

app.post("/upload", upload.single("file"), (req, res) => {
    if (!req.file) {
        return res.status(400).send('No file uploaded.');
    }
    // 输出文件信息
    console.log('Uploaded: ' + req.file.filename);
    res.send({
        code:200,
        data:'文件上传成功'
    })
});

app.get("/sse", (req, res) => {
    // 声明链接方式是sse
    res.writeHead(200,{
        "Content-Type":"text/event-stream"
    })
    const text = fs.readFileSync("./read.txt","utf8")
    const arr = text.split("")
    let current = 0
    // 设置每隔20毫秒主动发送一次数据
    let timer = setInterval(()=>{
        if(current >= arr.length){
            clearInterval(timer)
        }else{
            // 发送消息必须是 data: 开头,\n\n 结尾。中间是发送的内容,内容必须是一个字符串
            res.write(`data:${arr[current]}\n\n`)
            current++
        }
    },20)
})

app.listen(10086, () =>{
    console.log("启动成功: http://localhost:10086")
})

Ajax请求

发送GET请求

let xhr = new XMLHttpRequest()
function sendGet(){
  xhr.open("GET","http://127.0.0.1:10086/name")
  // 当接口相应数据后会触发onload回调
  xhr.onload = function (){
    if(xhr.status === 200){
        console.log(JSON.parse(xhr.responseText))
    }
  }
  xhr.send()
}

前端的几种网络请求方式_第1张图片

发送POST请求

let xhr = new XMLHttpRequest()
function sendPost(){
    xhr.open("POST","http://127.0.0.1:10086/add")
    xhr.setRequestHeader("Content-Type","application/json")
    xhr.onload = function (){
        if(xhr.status === 200){
            console.log(JSON.parse(xhr.responseText))
        }
    }
    // 定义发送给后端的数据
    let data = {
        name:"张三",
        age:18
    }
    // 把数据放在send中进行发送,需要使用JSON.stringify进行序列化
    xhr.send(JSON.stringify(data))
}

前端的几种网络请求方式_第2张图片

上传文件

var fileEl = document.getElementById("file");
fileEl.addEventListener("change",event=>{
    let file = event.target.files[0]
    xhr.open("POST","http://127.0.0.1:10086/upload")
    xhr.onload = function (){
        if(xhr.status === 200){
            console.log(JSON.parse(xhr.responseText))
        }
    }
    let data = new FormData()
    data.append("file",file)
    xhr.send(data)
})

前端的几种网络请求方式_第3张图片

前端的几种网络请求方式_第4张图片

设置超时时间

function sendGet() {
    xhr.open("GET", "http://127.0.0.1:10086/name")
    xhr.setRequestHeader("Content-Type", "application/json")
    // 设置超时时间1000毫秒
    xhr.timeout = 1000
    xhr.addEventListener("timeout", function () {
        console.log("请求超时")
    })
    xhr.onload = function () {
        if (xhr.status === 200) {
            console.log(JSON.parse(xhr.responseText))
        }
    }
    xhr.send()
}

请求中断

xhr.abort()
// 请求中断回调
xhr.addEventListener("abort",function (){
    console.log("请求中断")
})

监听上传进度

// 监听上传进度
xhr.addEventListener("progress",function (event){
    console.log(`${(event.loaded / event.total * 100).toFixed(2)}%`)
})

Axios使用

Axios 是对 Ajax 的封装,可以帮助我们更好的发送请求,官方文档:http://www.axios-js.com/zh-cn/docs/

下面是分别发送get和post的示例

<script src="https://unpkg.com/axios/dist/axios.min.js"></script>

function axiosGet(){
    axios.get("http://127.0.0.1:10086/name").then(res=>{
        console.log(res.data)
    })
}

function axiosPost(){
    axios.post("http://127.0.0.1:10086/add",{name:"张三"}).then(res=>{
        console.log(res.data)
    })
}

fetch请求

Fetch是一种网络通信协议,用于在客户端和服务器之间传输数据。该协议使用HTTP请求和响应进行通信,与传统的AJAX方式相比,Fetch更加简单易用,并提供了许多现代化的功能。

使用Fetch可以方便地向服务器发送请求,并将响应返回给客户端。你可以使用Fetch获取文本、JSON、图像和文件等数据,并进行各种处理。Fetch还支持流式传输和取消请求等高级功能,使得处理大型数据集和长时间运行的操作变得更加简单和可靠。

Fetch API也是Javascript中常用的API之一,它提供了一组方法和属性,可以在浏览器端与服务器进行通信。通过Fetch API,你可以轻松地使用Fetch协议进行数据传输,并对请求和响应进行操作和处理。

fetch 对比 xhr

fetchXMLHttpRequest(XHR)都是前端与服务器进行数据交互的常用方式,它们各有优缺点,下面是它们的比较:

  1. API 设计和使用方式

fetch 的 API 设计更加现代化、简洁和易于使用,使用起来更加直观和方便。相比之下,XHR 的 API 设计比较繁琐,需要进行多个参数的配置和回调函数的处理。

  1. 支持的请求方法

fetch API 默认只支持 GET 和 POST 请求方法,而 XHR 则支持所有标准的 HTTP 请求方法。

  1. 请求头部

fetch 中设置请求头部的方式更加清晰和直接,可以通过 Headers 对象进行设置,而 XHR 的方式相对较为繁琐。

  1. 请求体

在发送 POST 请求时,fetch API 要求将请求体数据作为参数传递给 fetch 方法中的 options 对象,而 XHR 可以直接在 send() 方法中设置请求体数据。

  1. 支持的数据类型

在解析响应数据时,fetch API 提供了多种方法,包括 .json(), .blob(), .arrayBuffer() 等,而 XHR 只支持文本和二进制数据两种数据类型。

  1. 跨域请求

在进行跨域请求时,fetch API 提供了一种简单而强大的解决方案——使用 CORS(跨域资源共享)头部实现跨域请求,而 XHR 则使用了一个叫做 XMLHttpRequest Level 2 的规范,在代码编写上相对较为繁琐。

总的来说,fetch API 与 XHR 各有优缺点,具体选择哪种方式还需要根据具体情况进行考虑。平时开发中使用较多的是 fetch ,因为它使用方便、API 简洁、语法清晰,同时也支持了大多数常用的功能,可以有效地简化前端开发流程。

fetch 返回格式

  1. text(): 将响应体解析为纯文本字符串并返回。
  2. json(): 将响应体解析为JSON格式并返回一个JavaScript对象。
  3. blob(): 将响应体解析为二进制数据并返回一个Blob对象。
  4. arrayBuffer(): 将响应体解析为二进制数据并返回一个ArrayBuffer对象。
  5. formData(): 将响应体解析为FormData对象。

发送GET请求

function fetchGet() {
    fetch("http://localhost:10086/name").then(res => res.json()).then(res => {
        console.log(res)
    })
}

发送POST请求

function fetchPost() {
    fetch("http://localhost:10086/add", {
        method: "post",
        headers:{
            'Content-Type':'application/json'
        },
        body: JSON.stringify({
            name: "张三",
            age: 18
        })
    }).then(res => res.json()).then(res => {
        console.log(res)
    })
}

终止请求

需要使用 AbortController 构造器

<button onclick="fetchGet()">get请求button>
<button onclick="stop()">终止请求button>

发送请求

const abort = new AbortController()
function fetchGet() {
    fetch("http://localhost:10086/name",{
        signal:abort.signal
    }).then(res => res.json()).then(res => {
        console.log(res)
    })
}

调用终止方法

function stop(){
    abort.abort()
}

请求超时

fetch 本身没有超时定义,需要我们自己封装一个计时器,到时间后调用 abort.abort() 方法

const abort = new AbortController()
function fetchGet() {
    setTimeOutFn()
    fetch("http://localhost:10086/name",{
        signal:abort.signal
    }).then(res => res.json()).then(res => {
        console.log(res)
    })
}

function setTimeOutFn(time = 2000){
    setTimeout(()=>{
        abort.abort()
    },time)
}

SSE

概述

SSE(Server-Sent Events)是一种用于实现服务器主动向客户端推送数据的技术,也被称为“事件流”(Event Stream)。它基于 HTTP 协议,利用了其长连接特性,在客户端与服务器之间建立一条持久化连接,并通过这条连接实现服务器向客户端的实时数据推送。

SSE 和 Socket 区别

SSE(Server-Sent Events)和 WebSocket 都是实现服务器向客户端实时推送数据的技术,但它们在某些方面还是有一定的区别。

  1. 技术实现

SSE 基于 HTTP 协议,利用了其长连接特性,通过浏览器向服务器发送一个 HTTP 请求,建立一条持久化的连接。而 WebSocket 则是通过特殊的升级协议(HTTP/1.1 Upgrade 或者 HTTP/2)建立新的 TCP 连接,与传统 HTTP 连接不同。

  1. 数据格式

SSE 可以传输文本和二进制格式的数据,但只支持单向数据流,即只能由服务器向客户端推送数据。WebSocket 支持双向数据流,客户端和服务器可以互相发送消息,并且没有消息大小限制。

  1. 连接状态

SSE 的连接状态仅有三种:已连接、连接中、已断开。连接状态是由浏览器自动维护的,客户端无法手动关闭或重新打开连接。而 WebSocket 连接的状态更灵活,可以手动打开、关闭、重连等。

  1. 兼容性

SSE 是标准的 Web API,可以在大部分现代浏览器和移动设备上使用。但如果需要兼容老版本的浏览器(如 IE6/7/8),则需要使用 polyfill 库进行兼容。而 WebSocket 在一些老版本 Android 手机上可能存在兼容性问题,需要使用一些特殊的 API 进行处理。

  1. 安全性

SSE 的实现比较简单,都是基于 HTTP 协议的,与普通的 Web 应用没有太大差异,因此风险相对较低。WebSocket 则需要通过额外的安全措施(如 SSL/TLS 加密)来确保数据传输的安全性,避免被窃听和篡改,否则可能会带来安全隐患。

总体来说,SSE 和 WebSocket 都有各自的优缺点,适用于不同的场景和需求。如果只需要服务器向客户端单向推送数据,并且应用在前端的浏览器环境中,则 SSE 是一个更加轻量级、易于实现和维护的选择。而如果需要双向传输数据、支持自定义协议、或者在更加复杂的网络环境中应用,则 WebSocket 可能更加适合。

适用于场景

chatGPT 返回的数据 就是使用的SSE 技术

实时数据大屏 如果只是需要展示 实时的数据可以使用SSE技术 而不是非要使用webSocket

使用

调用node端写好的 sse 接口,这个接口会每隔20毫秒主动向前端发送一次数据

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Titletitle>
head>
<style>
    .content{
        width: 400px;
        height: 400px;
        border: 1px solid #ddd;
    }
style>
<body>
    <div class="content" id="message">div>
    <button onclick="start()">开始button>
    <button onclick="close()">关闭button>
body>
<script>
    let sse = null
    function start(){
        sse = new EventSource("http://localhost:10086/ssh")
        // 链接成功回调
        sse.onopen = ()=>{
            console.log("open")
        }

        // 监听消息
        sse.onmessage = (event)=>{
            console.log(event)
            // 默认监听message,这个可以定义
            // 默认数据也是在event的data属性中返回
            document.getElementById("message").innerHTML += event.data
        }

        // 链接失败回调
        sse.onerror = ()=>{
            console.log("onerror")
        }
    }

    function close(){
        console.log("关闭")
        sse.close()
    }
script>
html>

前端的几种网络请求方式_第5张图片

WebSocket

WebSocket是双向通信,客户端可以随时向服务端发送消息,反过来服务端也可以随时向客户端发送消息

双向通信

使用node定义接口

安装

npm install ws

编写接口

const ws = require("ws")

// 创建服务
const wss = new ws.Server({port:8888},()=>{
    console.log("socket服务启动成功")
})

// 监听链接成功提示
wss.on("connection",(socket)=>{
    console.log("客户端链接成功")
    socket.on("message",e=>{
        // 通过e.toString()获取前端传递过来的东西
        socket.send("我是服务器,我收到你的消息了,内容是:"+ e.toString())
    })
})

调用

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Titletitle>
head>
<body>
<input type="text" id="msg"/>
<button onclick="send()">发送button>
<ul id="ul">ul>
body>
<script>
    let msg = document.querySelector("#msg")
    let ul = document.querySelector("#ul")
    // 链接
    let wss = new WebSocket("ws://localhost:8888")
    // 监听消息
    wss.addEventListener("message", e => {
        console.log(e.data)
        let li = document.createElement("li")
        li.innerText = e.data
        ul.appendChild(li)
    })

    function send() {
        // 发送消息
        wss.send(msg.value)
        msg.value = ""
    }
script>
html>

前端的几种网络请求方式_第6张图片

消息广播

默认一个链接只会收到自己与服务器之间的消息,不能收到其他链接接收的消息,我们通过如下方式来实现

// 监听链接成功提示
wss.on("connection",(socket)=>{
    console.log("客户端链接成功")
    socket.on("message",e=>{
        // 广播消息 wss.clients 存放了所有链接信息,遍历这个链接信息,给所有链接者发送消息
        wss.clients.forEach(client=>{
            // 通过e.toString()获取前端传递过来的东西
            client.send("我是服务器,我收到你的消息了,内容是:"+ e.toString())
        })
    })
})

前端的几种网络请求方式_第7张图片

心跳检测

在长时间没有发送消息交互,或者网络不好的情况下,websocket 会出现断掉的情况,我们可以通过心跳检测的机制,定时发送一次消息,实现链接保活

const ws = require("ws")

// 创建服务
const wss = new ws.Server({port: 8888}, () => {
    console.log("socket服务启动成功")
})

// 消息类型
const STATE = {
    HEART:1,
    MESSAGE:2
}

// 监听链接成功提示
wss.on("connection", (socket) => {
    console.log("客户端链接成功")
    socket.on("message", e => {
        // 广播消息
        wss.clients.forEach(client => {
            // 通过e.toString()获取前端传递过来的东西
            client.send(JSON.stringify({
                type:STATE.MESSAGE,
                data:"我是服务器,我收到你的消息了,内容是:" + e.toString()
            }))
        })
    })
    // 添加心跳检测
    let headInterval = null
    let headCheck = () => {
        if(socket.readyState === ws.OPEN){
            socket.send(JSON.stringify({
                type:STATE.HEART,
                data:"我是心跳包"
            }))
        }else{
            clearInterval(headInterval)
        }
    }
    // 每隔500毫秒检测一次
    setInterval(headCheck,500)
})

同时前端需要区分不同的消息类型

// 监听消息
wss.addEventListener("message", e => {
    let li = document.createElement("li")
    let data = JSON.parse(e.data)
    if(data.type === 2){
        li.innerText = data.data
        ul.appendChild(li)
    }
})

navigator.sendBeacon

使用 navigator.sendBeacon 实现高效的数据上报

在 web 开发中,我们经常需要将用户行为或性能数据上报到服务器。为了不影响用户体验,开发者通常会在页面卸载时进行数据上报。然而,传统的数据上报方式,如 XMLHttpRequestFetch API,容易受到页面卸载过程中的阻塞,导致数据丢失。为了解决这个问题,navigator.sendBeacon API 被引入,它可以在页面卸载时安全、可靠地发送数据。

navigator.sendBeacon 对比 Ajax fetch

优点

  1. 不受页面卸载过程的影响,确保数据可靠发送。
  2. 异步执行,不阻塞页面关闭或跳转。
  3. 能够发送跨域请求。

缺点

  1. fetch 和 ajax 都可以发送任意请求 而 sendBeacon 只能发送POST
  2. fetch 和 ajax 可以传输任意字节数据 而 sendBeacon 只能传送少量数据(64KB 以内)
  3. fetch 和 ajax 可以定义任意请求头 而 sendBeacon 无法自定义请求头
  4. sendBeacon 只能传输 ArrayBufferArrayBufferViewBlobDOMStringFormDataURLSearchParams 类型的数据
  5. 如果处于危险的网络环境,或者开启了广告屏蔽插件 此请求将无效

navigator.sendBeacon 应用场景

  1. 发送心跳包:可以使用 navigator.sendBeacon 发送心跳包,以保持与服务器的长连接,避免因为长时间没有网络请求而导致连接被关闭。
  2. 埋点:可以使用 navigator.sendBeacon 在页面关闭或卸载时记录用户在线时间,pv uv,以及错误日志上报 按钮点击次数。
  3. 发送用户反馈:可以使用 navigator.sendBeacon 发送用户反馈信息,如用户意见、bug 报告等,以便进行产品优化和改进

其他注意事项 type

ping请求 是html5 新增的 并且是sendBeacon 特有的 ping 请求 只能携带少量数据,并且不需要等待服务端响应,因此非常适合做埋点统计,以及日志统计相关功能。

使用方法

编写一个接口

const multer = require("multer");

app.post("/ping",multer().none(),(req,res)=>{
    console.log(req.body.data)
    res.send("ok")
})

前端发送 navigation.sendBeacon 请求

function send() {
    let data = new FormData()
    data.append("data", JSON.stringify({
        name: "张三",
        age: 1
    }))
    navigator.sendBeacon("http://localhost:10086/ping", data)
}

后端接收到文件

前端的几种网络请求方式_第8张图片

浏览器相应数据

前端的几种网络请求方式_第9张图片

JWT鉴权

首先安装相关依赖

npm install jsonwebtoken express cors
  • jsonwebtoken:用于生成token和验证token
  • cors:处理跨域问题

接口编写

const express = require("express")
const cors = require("cors")
const jwt = require("jsonwebtoken")
const app = express()

// 处理跨域问题
app.use(cors())
// 可以使用 req.body 获取post请求传递的json数据
app.use(express.json())
// token加盐
const KEY = "123456"


// 白名单,包含不需要验证token的路径
const whitelist = ['/login'];
// 验证token的中间件
app.use((req, res, next) => {
    // 检查路径是否在白名单中
    if (whitelist.includes(req.path)) {
        console.log("3333")
        // 在白名单中,直接进入下一个中间件或请求处理函数
        next();
    } else {
        // 不在白名单中,需要验证token
        const token = req.headers['authorization'];
        jwt.verify(token, KEY, (err, decode) =>{
            if(err){
                res.status(401).send('无效的token')
            }else{
                next()
            }
        })
    }
});


// 登录接口
app.post("/login", (req, res) => {
    let data = req.body
    console.log(data)

    if(data.name === "admin" && data.pwd === "123456"){
        res.send({
            id:1,
            // 生成token,第一个参数是token携带的内容,第二是KEY,第三个是过期时间
            token:jwt.sign({id:1,name:data.name}, KEY, {expiresIn: "1h"})
        })
    }else{
        res.send({
            code:500,
            msg:"用户名或密码错误"
        })
    }
})

// 获取集合信息
app.get("/list",(req,res)=>{
    res.send([
        {name:"lisi",age:1},
        {name:"wangwu",age:2},
    ])
})


app.listen(3000, () => {
    console.log("服务启动成功:http://localhost:3000")
})

前端调用

前端在发送请求时需要在请求头中携带token

login.html

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Titletitle>
    <script src="https://unpkg.com/axios/dist/axios.min.js">script>
head>
<body>
<div>
    用户名: <input placeholder="用户名" id="name"/>
    密码: <input placeholder="密码" id="pwd"/>
    <button onclick="login()">登录button>
div>
<script>
    function login() {
        let name = document.getElementById("name").value;
        let pwd = document.getElementById("pwd").value;
        axios.post("http://localhost:3000/login",{
            name,
            pwd
        }).then(res=>{
            if(res.data.token){
                alert("登录成功")
                localStorage.setItem("token",res.data.token)
                location.href = "./list.html"
            }else{
                alert("登录失败")
                localStorage.removeItem("token")
            }
        })
    }
script>
body>
html>

list.html

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Titletitle>
  <script src="https://unpkg.com/axios/dist/axios.min.js">script>
head>
<body>
<button onclick="list()">获取listbutton>
body>
<script>
  function list(){
    axios.get("http://localhost:3000/list",{
      headers: {
        'authorization': localStorage.getItem("token"),
      }
    }).then(res=>{
      console.log(res.data)
    })
  }
script>
html>

判断网络状态

获取是否联网

// 是否联网,返回true或者false,true表示已经联网,false表示没有联网
console.log(navigator.onLine)

获取网络环境

前端还可以判断用户当前所在网络的好坏

// 获取网络环境
console.log(navigator.connection)

返回的是一个 NetworkInformation 对象

前端的几种网络请求方式_第10张图片

前端的几种网络请求方式_第11张图片

XSS跨站脚本攻击

随着互联网的快速普及,越来越多的敏感信息被存储在网络上,例如个人身份信息、财务信息等。在当前数字化时代,这些安全问题变得更加突出。作为开发者,我们必须采取适当的防范措施,以确保用户数据的安全性。本文将着重探讨跨站脚本攻击(Cross-site scripting,XSS)这一常见的网络攻击方式,包括其定义、原理、危害、分类和防范措施,以帮助大家更好地预防此类安全风险。

概述

**定义:**跨站点脚本攻击,简称XSS,是指攻击者利用网站存在的漏洞,通过在网站中注入恶意脚本代码,从而使得用户在访问该网站时受到攻击。这些恶意脚本代码通常是JavaScript 代码,它们可以窃取用户的敏感信息,如用户名、密码等,并将这些信息发送到攻击者的服务器。

原理

XSS攻击的本质是利用Web应用程序中的漏洞,向网页注入恶意脚本代码,然后将这些代码嵌入到网页中,当其他用户访问这个网页时,恶意脚本将会被执行。

攻击者通常会在Web应用程序的输入框、评论框、搜索框等可输入内容的地方输入特定的脚本代码,这些代码可以被Web应用程序直接插入到网页中,导致网页上的所有用户都会受到攻击。

XSS攻击的原理包括以下几个步骤:

1、攻击者在Web应用程序的输入框、评论框等可输入内容的地方输入包含script标签的恶意脚本代码,例如:

<script>
// 在这里插入恶意脚本代码
</script>

2、Web应用程序将恶意脚本代码保存到数据库中或直接将其插入到网页的HTML代码中。

3、当其他用户访问这个网页时,浏览器会执行其中的恶意脚本代码。恶意脚本可以窃取用户的敏感信息,如登录凭证、浏览器历史记录、Cookie等,或者通过控制用户的浏览器来进行更多的攻击。

例如,以下是一段可以窃取用户Cookie的恶意脚本代码:

<script>
let cookieValue = document.cookie;
// 将cookieValue发送到攻击者的服务器
</script>

4、攻击者获取到用户的敏感信息后,可以进行更进一步的攻击,例如重定向到恶意网站、发起钓鱼攻击等。

预防工具

使用第三方库来预防,这里使用 xss,官网文档:https://www.npmjs.com/package/xss

<script src="https://rawgit.com/leizongmin/js-xss/master/dist/xss.js">script>
<script>
  // apply function filterXSS in the same way
  var html = filterXSS('
                    
                    

你可能感兴趣的:(nodejs,前端,网络)