Jacky's blog Jacky's blog
首页
  • 编码专题
  • 深入浅出 Vite
  • 深入浅出 babel
  • 快速上手API
  • 深入浅出 react
  • Node

    • code-notebook
  • 状态管理

    • redux
  • 前端工程化

    • Wepack
  • React源码

    • React源码
  • 组件库封装

    • 组件库
  • 开发工具

    • Vscode 插件
  • 项目展示
  • 案例中心 (opens new window)
  • First Project
  • 基础算法题
  • 链表题
  • 动态规划
  • 双指针
  • 递归
  • 数据结构
  • 前端学习计划 (opens new window)
  • 技术随笔
  • 转载文章
  • 包管理工具
  • 前端学习周报
  • VSCode插件
  • Promise 专题
  • 函数技巧
  • React 专题
  • 配置文件

    • TSCONFIG-配置 (opens new window)
    • NGINX-配置 (opens new window)
    • 正则规则查询手册 (opens new window)
    • Lint 配置 (opens new window)
  • 教程

    • GIT-教程
    • NPM SCRIPTS-工作流 (opens new window)
    • DOCKER-教程 (opens new window)
    • LERNA-教程 (opens new window)
    • GIT-常用操作整理 (opens new window)
  • VSCode

    • LAUNCH.JSON (opens new window)
  • 指令

    • NPM 指令 (opens new window)
    • NVM 指令 (opens new window)
    • Nginx 指令 (opens new window)
    • YARN 指令 (opens new window)
    • PNPM 指令 (opens new window)
  • 库

    • FS-EXTRA 库 (opens new window)
    • NODE 库-PATH (opens new window)
  • 永远的神

    • 魔法师卡颂-自顶向下学 React 源码 (opens new window)
    • 全栈潇晨 (opens new window)
    • 博客-程序员山月-Daily (opens new window)
    • 淘系前端:冴羽 (opens new window)
  • 系列文章

    • 《图解HTTP》 (opens new window)
    • 《ES6标准入门》 (opens new window)
    • 《现代JavaScript教程》 (opens new window)
    • 《深入浅出Webpack》 (opens new window)
    • VSCode 插件系列:小茗同学 (opens new window)
    • JEST 教程 (opens new window)
    • 前端精读周刊:各种精读系列 (opens new window)
    • 一文吃透系列 (opens new window)
    • 图解 REACT 原理 (opens new window)
  • 实用网站

    • MDN (opens new window)
    • CAN I USE (opens new window)
    • TYPESCRIPT-ESLint-RULES (opens new window)
    • ESLint-RULES (opens new window)
    • FRONT-END TREND (opens new window)
    • NPM TREND (opens new window)
    • 在线分析 Node 依赖 (opens new window)
    • FIND NPM (opens new window)
    • CODE PEN (opens new window)
    • 印记中文 (opens new window)
    • TOOL.LU (opens new window)
    • 阮一峰-网道 (opens new window)
    • DIGITAL OCEAN (opens new window)
    • DEVDOCS.IO (opens new window)
    • JOI (opens new window)
  • 算法

    • 小浩算法 (opens new window)
    • LABULADONG 的算法小抄 (opens new window)
    • 力扣 SOLUTION (opens new window)
    • HACKER RANK (opens new window)
    • 代码随想录 (opens new window)
  • 博客系列

    • 美团大佬 (opens new window)
    • 蜡笔小伟 (opens new window)
    • 优秀博客1 (opens new window)
    • 优秀博客2-umi (opens new window)
    • 优质博客 (opens new window)
  • CSS

    • CSS-EASING 库 (opens new window)
    • ROUGH.JS (opens new window)
    • CSS 网站收集
    • UNOCSS (opens new window)
  • 前端

    • PROMISE (opens new window)
    • UNDERSCORE.JS (opens new window)
    • study with BGM (opens new window)
    • nginx【B站视频】 (opens new window)
    • 机器学习
    • Js基础
  • 掘金已购课程

    • 前端自动化测试精讲 (opens new window)
    • 深入浅出 Vite (opens new window)
    • 现代 Web 布局 (opens new window)
    • 前端算法与数据结构 (opens new window)
    • 基于 Vite 的 SSG 框架开发实战 (opens new window)
    • SSR 实战:官网开发指南 (opens new window)
    • WebGL 入门与实践 (opens new window)
    • 玩转 CSS 的艺术之美 (opens new window)
    • 前端调试通关秘籍 (opens new window)
    • React 进阶实践指南 (opens new window)
    • TypeScript 全面进阶指南 (opens new window)
    • 前端缓存技术与方案解析 (opens new window)
    • npm scripts 前端工作流 (opens new window)
    • Webpack5 核心原理与应用实践 (opens new window)
  • 购物车

    • 张鑫旭-技术写作指南 (opens new window)
    • 深入剖析 Node.js 底层原理 (opens new window)
    • 前端开发者的现代 C++ 课 (opens new window)
    • 从前端到全栈 (opens new window)
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Jacky Wang

行到水穷处,坐看云起时
首页
  • 编码专题
  • 深入浅出 Vite
  • 深入浅出 babel
  • 快速上手API
  • 深入浅出 react
  • Node

    • code-notebook
  • 状态管理

    • redux
  • 前端工程化

    • Wepack
  • React源码

    • React源码
  • 组件库封装

    • 组件库
  • 开发工具

    • Vscode 插件
  • 项目展示
  • 案例中心 (opens new window)
  • First Project
  • 基础算法题
  • 链表题
  • 动态规划
  • 双指针
  • 递归
  • 数据结构
  • 前端学习计划 (opens new window)
  • 技术随笔
  • 转载文章
  • 包管理工具
  • 前端学习周报
  • VSCode插件
  • Promise 专题
  • 函数技巧
  • React 专题
  • 配置文件

    • TSCONFIG-配置 (opens new window)
    • NGINX-配置 (opens new window)
    • 正则规则查询手册 (opens new window)
    • Lint 配置 (opens new window)
  • 教程

    • GIT-教程
    • NPM SCRIPTS-工作流 (opens new window)
    • DOCKER-教程 (opens new window)
    • LERNA-教程 (opens new window)
    • GIT-常用操作整理 (opens new window)
  • VSCode

    • LAUNCH.JSON (opens new window)
  • 指令

    • NPM 指令 (opens new window)
    • NVM 指令 (opens new window)
    • Nginx 指令 (opens new window)
    • YARN 指令 (opens new window)
    • PNPM 指令 (opens new window)
  • 库

    • FS-EXTRA 库 (opens new window)
    • NODE 库-PATH (opens new window)
  • 永远的神

    • 魔法师卡颂-自顶向下学 React 源码 (opens new window)
    • 全栈潇晨 (opens new window)
    • 博客-程序员山月-Daily (opens new window)
    • 淘系前端:冴羽 (opens new window)
  • 系列文章

    • 《图解HTTP》 (opens new window)
    • 《ES6标准入门》 (opens new window)
    • 《现代JavaScript教程》 (opens new window)
    • 《深入浅出Webpack》 (opens new window)
    • VSCode 插件系列:小茗同学 (opens new window)
    • JEST 教程 (opens new window)
    • 前端精读周刊:各种精读系列 (opens new window)
    • 一文吃透系列 (opens new window)
    • 图解 REACT 原理 (opens new window)
  • 实用网站

    • MDN (opens new window)
    • CAN I USE (opens new window)
    • TYPESCRIPT-ESLint-RULES (opens new window)
    • ESLint-RULES (opens new window)
    • FRONT-END TREND (opens new window)
    • NPM TREND (opens new window)
    • 在线分析 Node 依赖 (opens new window)
    • FIND NPM (opens new window)
    • CODE PEN (opens new window)
    • 印记中文 (opens new window)
    • TOOL.LU (opens new window)
    • 阮一峰-网道 (opens new window)
    • DIGITAL OCEAN (opens new window)
    • DEVDOCS.IO (opens new window)
    • JOI (opens new window)
  • 算法

    • 小浩算法 (opens new window)
    • LABULADONG 的算法小抄 (opens new window)
    • 力扣 SOLUTION (opens new window)
    • HACKER RANK (opens new window)
    • 代码随想录 (opens new window)
  • 博客系列

    • 美团大佬 (opens new window)
    • 蜡笔小伟 (opens new window)
    • 优秀博客1 (opens new window)
    • 优秀博客2-umi (opens new window)
    • 优质博客 (opens new window)
  • CSS

    • CSS-EASING 库 (opens new window)
    • ROUGH.JS (opens new window)
    • CSS 网站收集
    • UNOCSS (opens new window)
  • 前端

    • PROMISE (opens new window)
    • UNDERSCORE.JS (opens new window)
    • study with BGM (opens new window)
    • nginx【B站视频】 (opens new window)
    • 机器学习
    • Js基础
  • 掘金已购课程

    • 前端自动化测试精讲 (opens new window)
    • 深入浅出 Vite (opens new window)
    • 现代 Web 布局 (opens new window)
    • 前端算法与数据结构 (opens new window)
    • 基于 Vite 的 SSG 框架开发实战 (opens new window)
    • SSR 实战:官网开发指南 (opens new window)
    • WebGL 入门与实践 (opens new window)
    • 玩转 CSS 的艺术之美 (opens new window)
    • 前端调试通关秘籍 (opens new window)
    • React 进阶实践指南 (opens new window)
    • TypeScript 全面进阶指南 (opens new window)
    • 前端缓存技术与方案解析 (opens new window)
    • npm scripts 前端工作流 (opens new window)
    • Webpack5 核心原理与应用实践 (opens new window)
  • 购物车

    • 张鑫旭-技术写作指南 (opens new window)
    • 深入剖析 Node.js 底层原理 (opens new window)
    • 前端开发者的现代 C++ 课 (opens new window)
    • 从前端到全栈 (opens new window)
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 技术随笔

    • git教程
    • 前端模块化:CommonJS,AMD,CMD,ES6
    • 如何动态加载本地图片
    • GitHub自动部署脚本
    • React生命周期详解
    • 将父组件的props作为组件的初始state的几种方案
    • webpack基础入门
    • StackOver记录:如何在function中使用redux
    • React中使用onClick时的三种性能优化
    • 思考React状态管理
    • forEach/for..in/of
    • styled-components使用记录
    • 深入理解forEach与forof
    • Node技术架构
    • Redux、Mobx状态管理杂谈
    • JS中的类型检测
    • 如何判断图片是否在可视区域
    • 如何使用图片的懒加载操作
    • JavaScript模块化:从CommonJS到AMD
    • git查看:修改用户名、密码
    • 保持数据Immutable的几种写法
    • 如何实现一个Promise.all
    • resolve(Promise对象)会多出两个微任务
    • todo_基于Mocha的测试驱动开发
    • 浏览器页面渲染机制
    • todo-响应式编程之数据劫持
    • todo-immer库的介绍
    • React-Hooks 基础原理解析
    • JS基本功
    • 函数闭包与this指针
    • 收集 DUMI 配置过程中遇到的问题
    • typescript 类型工具
    • 统一接口的导入导出
    • 网络安全:如何预防 XSS 攻击
    • 浏览器页面渲染机制【2023】
    • 跨域资源请求
    • 浏览器事件循环机制
    • 如何理解浏览器的 user agent 用户代理的含义?
      • 0.前言
      • 1. 浏览器的自动发送请求功能
        • 1.1 地址栏行为
        • 1.2 原生 HTML 标签行为
      • 2. 根据 Content-Type 响应结果
      • 3. 综合案例实践
      • 4.常见错误
  • 转载文章

  • Mac相关

  • 前端学习周报

  • 包管理工具

  • 技术随笔
  • 技术随笔
wangjiasheng
2023-11-05
目录

如何理解浏览器的 user agent 用户代理的含义?

# 0.前言

本篇博客对应 issue:https://github.com/wangjs-jacky/examples-collection/issues/2 (opens new window)

user agenet 顾名思义为 用户代理,能帮用户发送网络请求的工具很多,如 VSCode 插件 REST client 、客户端 postman 、国产客户端 apifox 等。

这里 用户代理 在网络层面主要值的是两大核心能力:

  1. 发的能力:代替用户发送 http 请求。
  2. 收的能力:根据 Content-Type 解析响应结果。

# 1. 浏览器的自动发送请求功能

浏览器的发送方式有多种多样,大致分为两类:其一、地址栏行为。其二、原生 html 标签内置行为。

# 1.1 地址栏行为

当用户在地址栏输入了一个 url 地址,浏览器会自动解析 URL 地址,并发出一个 GET 请求。

对于 GET 请求中, URL 只允许出现 ASCII 字符,默认对中文字符进行 url 编码。

举例:百度搜索 灌篮高手

image-20231105230924261

decodeURIComponent("%E7%81%8C%E7%AF%AE%E9%AB%98%E6%89%8B");
"灌篮高手";
encodeURIComponent("灌篮高手");
"%E7%81%8C%E7%AF%AE%E9%AB%98%E6%89%8B";
1
2
3
4

# 1.2 原生 HTML 标签行为

  • a 标签 【GET 请求】

    <a href="”www.baidu.com“">跳转百度</a>
    
    1

    会自动解析 href 属性,并发送一个 GET 请求。

    注意 href 其实存在多重省略写法

    • 省略 协议 写法:<a href="//localhost:3000/abc">
    • 省略 协议、域名 、端口 写法。<a href="/abc">
  • <link> <img> <script> <video> <audio>等元素 【GET 请求】

    浏览器会拿到对应的地址,发出 GET 请求。

  • 表单提交行为

    早期让浏览器发送 POST 请求的唯一方案。

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Form表单的示例</title>
      </head>
    
      <body>
        <form action="https://www.baidu.com/" method="post">
          <p>账号:<input type="text" name="loginId" /></p>
          <p>密码: <input type="text" name="password" /></p>
          <button type="submit">登录</button>
        </form>
      </body>
    </html>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

    观察 NetWork 面板

    对应等价的 REST Client 模拟发送 HTTP 请求

    POST / HTTP/1.1
    HOST: www.baidu.com
    Content-Type: application/x-www-form-urlencoded
    
    loginId=admin&loginPwd=12345
    
    1
    2
    3
    4
    5

    虽然现在可以使用 ajax 技术进行 post 请求,原生 form 依旧存在价值,如监听enter 键盘回车事件。

    在 antd 中,通过拦截 form 的 submit 事件对表单能力进行扩展

    <script>
      /* 在 antd 等表单组件中,会对 form 的提交行为拦截 */
      const form = document.querySelector("form");
    
      form.onsubmit = (e) => {
        e.preventDefault();
        console.log("任意逻辑代码");
      };
    </script>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

# 2. 根据 Content-Type 响应结果

  • text/plain: 普通的纯文本,浏览器通常会将响应体原封不动的显示到页面上

    • text/html:html 文档,浏览器通常会将响应体作为页面进行渲染

    • text/javascript或application/javascript:js 代码,浏览器通常会使用 JS 执行引擎将它解析执行

    • text/css:css 代码,浏览器会将它视为样式

    • image/jpeg:浏览器会将它视为 jpg 图片

    • application/octet-stream:二进制数据,会触发浏览器下载功能

    • attachment:附件,会触发下载功能

# 3. 综合案例实践

使用三种方式发送网络请求:

  1. REST client 发送 POST 请求。
POST /d HTTP/1.1
HOST: localhost:7000
Content-Type: application/x-www-form-urlencoded

loginId=admin&loginPwd=12345
1
2
3
4
5
  1. 使用原生表单发送 urlencode 请求,发送 POST 请求。
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Form表单的示例</title>
  </head>

  <body>
    <form action="//localhost:7000/d" method="post">
      <p>账号:<input type="text" name="loginId" /></p>
      <p>密码: <input type="text" name="loginPwd" /></p>
      <button type="submit">登录</button>
    </form>
  </body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  1. 使用 url 发送 GET 请求。 直接地址栏输入url
  • 输入localhost:7000/a ,浏览器以纯文本形式显示。
  • 输入localhost:7000/b ,浏览器以 html 形式显示。
  • 输入localhost:7000/c ,浏览器以附件形式显示。

以 express 框架示例,监听 7000 端口。

const app = require("express")();
const bodyParser = require("body-parser");

const text = `<h1>两只老虎爱跳舞</h1>

小兔子乖乖拔萝卜

我和小鸭子学走路

童年是最美的礼物`;

const PORT = 7000;

// 配置 body-parser 中间件
// Content-Type: application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }));

// 配置 body-parser 中间件
// 解析: Content-Type: application/json
app.use(bodyParser.json());

/* 解析响应体:text/plain */
app.get("/a", (req, res) => {
  res.set("Content-Type", "text/plain; charset=utf-8");
  res.end(text);
});

/* 解析响应体:text/plain */
app.get("/b", (req, res) => {
  res.set("Content-Type", "text/html; charset=utf-8");
  res.end(text);
});

/* 下载文件 */
app.get("/c", (req, res) => {
  res.set("Content-Type", "text/plain; charset=utf-8");
  res.set("Content-Disposition", "attachment; filename=song.txt");
  res.end(text);
});

/* POST请求测试 */
app.post("/d", (req, res) => {
  console.log("sss", req.body);

  /*  console.log("loginId", req.body.loginId); // 输出 "admin"
   console.log("loginPwd", req.body.loginPwd); // 输出 "123123" */
  res.end("success");
});

app.listen(PORT, () => {
  console.log(`server started: ${PORT}`);
});
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

观察响应结果如下:

  1. 访问:localhost:7000/a ,以纯文本形式显示。

  2. 访问:localhost:7000/b ,以 html 形式显示。

  3. 访问: localhost:7000/c ,以附件形式下载,附件名称为 song.txt

  4. 以 REST Client 发送 POST 请求

  5. 使用 html 元素 form 发送 POST请求

# 4.常见错误

对于 http 协议头的简写错误示例:

localhost:7000/d → //localhost:7000/d

<body>
  <!-- ❎ 错误写法 -->
  <form action="localhost:7000/d" method="post">
  <!-- ❎ 正确写法 -->
  <form action="//localhost:7000/d" method="post">
    <p>
      账号:<input type="text" name="loginId">
    </p>
    <p>
      密码: <input type="text" name="loginPwd">
    </p>
    <button type="submit">登录</button>
  </form>
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14

控制台报错:缺少协议头

编辑 (opens new window)
上次更新: 2023/11/06, 0:11:00
浏览器事件循环机制
深入理解Flex布局

← 浏览器事件循环机制 深入理解Flex布局→

最近更新
01
浏览器事件循环机制
10-31
02
浏览器页面渲染机制【2023】
10-15
03
跨域资源请求
10-09
更多文章>
Theme by Vdoing | Copyright © 2020-2023
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式