<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Chaos</title><description>Bloom Into You</description><link>https://blog.chaos-ljc.top/</link><language>zh_CN</language><item><title>Starting from the &quot;Scam Links&quot; on Bilibili</title><link>https://blog.chaos-ljc.top/posts/bilibili-scam-links/</link><guid isPermaLink="true">https://blog.chaos-ljc.top/posts/bilibili-scam-links/</guid><pubDate>Sun, 15 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;:::important[想法来源]
观看 &lt;a href=&quot;https://space.bilibili.com/325903362&quot;&gt;二叉树树&lt;/a&gt; 的视频 &lt;a href=&quot;https://www.bilibili.com/video/BV1tCw8zpE21/&quot;&gt;【B站评论惊现诈骗链接！！！这是怎么做的？？？？】&lt;/a&gt;  后，想要更深入地了解这个现象，从而有了这期博客。
:::&lt;/p&gt;
&lt;h2&gt;引言：关于“诈骗链接”&lt;/h2&gt;
&lt;p&gt;在浏览 &lt;code&gt;BiliBili&lt;/code&gt; 评论区时，常会发现 B 站的视频链接以标题的形式显现，如下图：&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://cdn.jsdelivr.net/gh/lvjianchaos/Images/note/bilibili诈骗链接说开去/videolinkwithtitle.webp&quot; alt=&quot;视频链接显示标题示例&quot; style=&quot;border-radius: 1em;&quot;/&amp;gt;&lt;/p&gt;
&lt;p&gt;但会遇到点击的视频标题与跳转到的视频页标题不一致的现象，也就是我们所说的&lt;strong&gt;诈骗链接&lt;/strong&gt;。具体情形可以从&lt;a href=&quot;https://space.bilibili.com/325903362&quot;&gt;二叉树树&lt;/a&gt;的视频中看到。&lt;/p&gt;
&lt;p&gt;他也给出了制作这样的“诈骗链接”的方法：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;模板：&lt;code&gt;https://www.bilibili.com/video/BV_X/../BV_Y&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;BV_X&lt;/code&gt; 替换为显示标题的 &lt;code&gt;BV&lt;/code&gt; 号，&lt;code&gt;BV_Y&lt;/code&gt; 替换为跳转的 &lt;code&gt;BV&lt;/code&gt; 号&lt;/li&gt;
&lt;li&gt;使用任意B站短链接生成器（例如：&lt;a href=&quot;https://www.bilitools.top/&quot;&gt;B站短链接生成工具&lt;/a&gt;）生成短链接（符合&lt;code&gt;^https://b23\.tv/[A-Za-z0-9]{7}$&lt;/code&gt;），并发布于B站评论区&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;以及漏洞的剖析：前端在渲染链接时，读取第一个合法 &lt;code&gt;BV&lt;/code&gt; 号获取标题，在这里就是 &lt;code&gt;BV_X&lt;/code&gt; 对应的标题，而点击会去跳转到原始的 &lt;code&gt;url&lt;/code&gt;（包含&lt;code&gt;/../&lt;/code&gt;），从而实际跳转到 &lt;code&gt;https://www.bilibili.com/video/BV_Y&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;这样的解释固然合乎道理，但于我而言，我还想要了解更多——为什么会造成这样的结果呢？&lt;/p&gt;
&lt;p&gt;下文就记录了我的一些探索的所得。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;📺关于 B 站评论区链接的渲染&lt;/h2&gt;
&lt;p&gt;据我观察，在B站发布评论时，输入文本是 &lt;strong&gt;B 站相关链接&lt;/strong&gt; 则会被渲染为超链接；其中，&amp;lt;u&amp;gt;若链接指向的是某个 B 站视频，则会被渲染为对应视频标题&amp;lt;/u&amp;gt;。当然，B站评论区还会存在其他类型的超链接，比如：跳转站外的蓝链(广告...)、BV号、av号、@用户昵称 …… 而其中&lt;code&gt;BV号&lt;/code&gt;、&lt;code&gt;av号&lt;/code&gt;等也会替换为其对应的&lt;code&gt;title&lt;/code&gt;进行超链接的渲染。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;总结该现象：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;有的链接直接显示原始 URL；&lt;/li&gt;
&lt;li&gt;有的链接显示成更友好的标题（例如视频标题）；&lt;/li&gt;
&lt;li&gt;同一条评论里，可能两种显示方式同时出现。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;从根本来说，前端 &lt;code&gt;HTML&lt;/code&gt; 中，&amp;lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Structuring_content/Creating_links&quot; title=&quot;了解超链接&quot; target=&quot;blank&quot;&amp;gt;创建超链接&amp;lt;/a&amp;gt; 通过 &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt;标签实现，这里同样如此。问题在于：&amp;lt;u&amp;gt;如何确认评论中某段文本是超链接？超链接的&lt;code&gt;url&lt;/code&gt;如何得来？以及，如何确认渲染内容不是&lt;code&gt;url&lt;/code&gt;而是对应视频标题？该视频标题如何得来？&amp;lt;/u&amp;gt;&lt;/p&gt;
&lt;p&gt;观察网络请求，或查看&amp;lt;a href=&quot;https://sessionhu.github.io/bilibili-API-collect/docs/comment/&quot; target=&quot;blank&quot;&amp;gt;文档&amp;lt;/a&amp;gt;可得：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;GET&lt;/code&gt; 请求 &lt;code&gt;https://api.bilibili.com/x/v2/reply/wbi/main&lt;/code&gt; 懒加载获取评论区明细&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://cdn.jsdelivr.net/gh/lvjianchaos/Images/note/bilibili%E8%AF%88%E9%AA%97%E9%93%BE%E6%8E%A5%E8%AF%B4%E5%BC%80%E5%8E%BB/screenshot-of-the-reply-response.png&quot; style=&quot;zoom: 67%;&quot; /&amp;gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;评论主体通常在 &lt;code&gt;content&lt;/code&gt; 中，&lt;strong&gt;常见字段&lt;/strong&gt;包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;message&lt;/code&gt;：用户输入的原始文本；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;jump_url&lt;/code&gt;：可跳转片段的映射表（键通常是某个可识别 token，如 &lt;code&gt;BV...&lt;/code&gt;）；&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;members&lt;/code&gt;：&lt;code&gt;@用户&lt;/code&gt; 的映射；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;emote&lt;/code&gt;：表情映射；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pictures&lt;/code&gt;: 图片映射；&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;一个典型的 &lt;code&gt;jump_url&lt;/code&gt; 结构是：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;BV12ecbz1EUZ&quot;: {
    &quot;title&quot;: &quot;【Seedance 2.0】星野露比也要跳蕾塞舞ヾ(≧▽≦*)o&quot;,
    &quot;state&quot;: 0,
    ...
  },
  &quot;BV1fscqz9EHs&quot;: {
    &quot;title&quot;: &quot;【Seedance 2.0】伊什塔尔-蕾塞舞★彡&quot;,
    &quot;state&quot;: 0,
    ...
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;注意：&lt;code&gt;jump_url&lt;/code&gt; 的 key 是否与文本切片后的内容“精确一致”，决定了能否显示标题。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;简单地浏览B站前端关于comment的&lt;code&gt;js&lt;/code&gt;代码，对于整体渲染的流程应该可以&lt;strong&gt;概括为 4 步&lt;/strong&gt;：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;参照：&amp;lt;a href=&quot;https://s1.hdslb.com/bfs/seed/jinkela/commentpc/bili-comments.e0090ab8af.js&quot; target=&quot;blank&quot;&amp;gt;bili-comments.e0090ab8af.js&amp;lt;/a&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;拉取评论数据&lt;/strong&gt;&lt;br /&gt;
前端请求评论接口，得到 &lt;code&gt;replies&lt;/code&gt; 列表及每条 &lt;code&gt;content&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;文本解析为富文本节点&lt;/strong&gt;&lt;br /&gt;
核心解析函数会把 &lt;code&gt;message&lt;/code&gt; 切成片段，并生成节点数组（例如 &lt;code&gt;span&lt;/code&gt;、&lt;code&gt;a&lt;/code&gt;、&lt;code&gt;img&lt;/code&gt;）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;节点渲染为 HTML&lt;/strong&gt;&lt;br /&gt;
富文本组件把节点数组转换成真实 DOM，例如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;a href=&quot;...&quot; data-type=&quot;link&quot;&amp;gt;...&amp;lt;/a&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;span&amp;gt;...&amp;lt;/span&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;Hv = function (t, e, n, i, r) {
 return {
     tag: &quot;a&quot;,
     style: i,
     dataset: r || {
         type: &quot;link&quot;
     },
     a: {
         text: uv(e || &quot;网页链接&quot;),
         href: t,
         icon: n,
         target: &quot;_blank&quot;
     }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上述是源码中构造链接节点的一个逻辑，可见：超链接在数据层就是一个 &lt;code&gt;tag: &quot;a&quot;&lt;/code&gt; 节点，&lt;code&gt;dataset.type&lt;/code&gt; 默认是 &lt;code&gt;link&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;点击事件分流&lt;/strong&gt;&lt;br /&gt;
点击链接后，根据 &lt;code&gt;data-type&lt;/code&gt;（&lt;code&gt;link/search/goods/seek&lt;/code&gt; 等）执行不同逻辑和埋点。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;而其中最关键的机制是：&lt;/p&gt;
&lt;h4&gt;先“切片”，再“按片段匹配 jump_url”&lt;/h4&gt;
&lt;h5&gt;1. 先做分片&lt;/h5&gt;
&lt;p&gt;解析器会按照多类规则切片，包括但不限于：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;URL（白名单/正则）；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AV/BV/CV/EP/SS&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@用户名&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;时间点（如 &lt;code&gt;01:23&lt;/code&gt;）；&lt;/li&gt;
&lt;li&gt;普通文本；&lt;/li&gt;
&lt;li&gt;......&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;源码中有这样的一些&lt;strong&gt;正则匹配&lt;/strong&gt;（已添加注释）：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 匹配B站AV号：匹配以AV/av/Av/aV开头，后面跟一串数字的格式（如AV123456、av7890）
p = new RegExp(&quot;(AV|av|Av|aV)[0-9]+&quot;, &quot;ug&quot;), 

// 匹配B站BV号：B站视频的新标识符，规则是BV/bv/Bv/bV开头，
// 接着是1，然后跟9个字符（范围：1-9、A-N、P-Z、a-k、m-z，排除了O和l等易混淆字符）
f = new RegExp(&quot;(BV|bv|Bv|bV)1[1-9A-NP-Za-km-z]{9}&quot;, &quot;ug&quot;), 

// 匹配B站CV号/专栏移动端链接：
// 第一种：CV/cv开头+数字（如CV12345）；第二种：mobile/开头+数字（移动端专栏链接）
m = new RegExp(&quot;((CV|cv)[0-9]+|(mobile/[0-9]+))&quot;, &quot;ug&quot;), 

// 匹配B站EP号：番剧的剧集编号，EP/ep/Ep/eP开头+数字（如EP12、ep34）
y = new RegExp(&quot;(EP|ep|Ep|eP)[0-9]+&quot;, &quot;ug&quot;), 

// 匹配B站SS号：番剧的季度编号，SS/ss/Ss/sS开头+数字（如SS1、ss2）
b = new RegExp(&quot;(SS|ss|Ss|sS)[0-9]+&quot;, &quot;ug&quot;), 

// 匹配中括号包裹的任意Unicode字符：
// 匹配[]内包含的所有合法Unicode字符（涵盖基本多文种平面和扩展平面字符），
// 包括常见字符、emoji、生僻字等，是B站评论区常见的内容包裹格式
w = /\[(?:[\0-&apos;\+-Z\\\^-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+\]/g, 

// 匹配B站及相关域名：
// 包含bilibili主域名（com/tv/cn）、衍生域名（esheep、biligame、b23.tv等）、
// 合作/关联域名（苏宁sugs、人民网、考拉、央视等），$v和Gv是外部变量（可能是协议/参数前缀）
x = new RegExp(&quot;&quot;.concat($v, &quot;?([a-z0-9A-Z-]{1,15}.)?(bilibili\\.(com|tv|cn)|esheep\\.(com|cn|net)|biligame\\.(com|cn|net)|(bilibiliyoo|im9)\\.com|biliapi\\.net|b23\\.tv|bili22\\.cn|bili33\\.cn|bili23\\.cn|bili2233\\.cn|(sugs\\.suning\\.com)|jueze2021\\.peopleapp\\.com|kaola\\.com|bigfun\\.cn|mcbbs\\.net|mp\\.weixin\\.qq\\.com|static\\.cdsb\\.com|bjnews\\.com\\.cn|720yun\\.com|\\.cctv\\.com)($|/|)&quot;).concat(Gv), &quot;ug&quot;),

// 匹配B站视频链接：
// 包含bilibili主站/video路径、b23.tv等短链接，匹配AV号或BV号格式的视频链接
k = new RegExp(&quot;&quot;.concat($v, &quot;?(((uat-)?www\\.bilibili\\.com)|(b23\\.tv|bili22\\.cn|bili33\\.cn|bili23\\.cn|bili2233\\.cn))(/video)?/((av[0-9]+)|((BV)1[1-9A-NP-Za-km-z]{9}))($|/|)&quot;).concat(Gv), &quot;ug&quot;), 

// 匹配B站番剧播放链接：
// 包含bangumi/play路径，匹配EP号或SS号的番剧播放链接
A = new RegExp(&quot;&quot;.concat($v, &quot;?(((uat-)?www\\.bilibili\\.com/bangumi/(play/|media/)|(b23\\.tv|bili22\\.cn|bili33\\.cn|bili23\\.cn|bili2233.cn)/)(ss|ep)[0-9]+)($|/|)&quot;).concat(Gv), &quot;ug&quot;),

// 匹配B站专栏/文章链接：
// 包含read路径，匹配CV号、native?id、app/、mobile/等格式的专栏链接
_ = new RegExp(&quot;&quot;.concat($v, &quot;?(uat-)?www\\.bilibili\\.com/read/((cv[0-9]+)|(native?id=[0-9]+)|(app/[0-9]+)|(native/[0-9]+)|(mobile/[0-9]+))($|/|)&quot;).concat(Gv), &quot;ug&quot;), 

// 匹配时间戳格式：
// 支持 数字#数字:数字:数字（如1#12:34:56）、数字:数字:数字（如12:34:56）、数字：数字：数字（中文冒号）
// 最少匹配 数字:数字:数字（如1:2:3），是视频评论区常见的时间点格式
E = /(\d+#)?(\d+(:|：)){1,2}\d\d/g
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;2. 对每个“候选片段”再检查 &lt;code&gt;jump_url&lt;/code&gt;&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;命中 &lt;code&gt;jump_url[片段]&lt;/code&gt;：用对应信息组装链接节点；&lt;/li&gt;
&lt;li&gt;未命中：走默认链接逻辑（通常保留原文本显示）&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;3. 标题显示规则&lt;/h5&gt;
&lt;p&gt;当命中 &lt;code&gt;jump_url&lt;/code&gt; 时，显示文案通常是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;title || 原片段文本&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;即：&lt;strong&gt;有标题就显示标题，否则退回原文本&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;这个应该是 &lt;code&gt;jump_url&lt;/code&gt; 命中后的标题选择的函数：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;L = function (t, e) {
    var r = e.pc_url
       , o = e.app_url_schema
       , a = e.title
       , s = e.prefix_icon
       , l = e.match_once
       , c = e.icon_position
       , u = e.extra;
    if (l &amp;amp;&amp;amp; N[t])
       return Wv(t);
    N[t] = !0;
    var d = 1 === c
       , h = &quot;&quot;;
    if (r ? h = dv(r) : (C(x),
       x.test(t) ? (h = dv(t, !0),
          i &amp;amp;&amp;amp; (C(k),
             k.test(h) &amp;amp;&amp;amp; (h = Mv(h, i)))) : (C(f),
                C(p),
                C(m),
                f.test(t) ? (h = Ch({
                   bvid: t
                }),
                   i &amp;amp;&amp;amp; (h = Mv(h, i))) : p.test(t) ? (h = Ch({
                      avid: Eu(t)
                   }),
                      i &amp;amp;&amp;amp; (h = Mv(h, i))) : m.test(t) ? (h = Th(Eu(t)),
                         i &amp;amp;&amp;amp; (h = Mv(h, i))) : h = function (t) {
                            if (t.startsWith(&quot;http&quot;)) {
                               var e = t.match(/www.bilibili.com\/h5\/note-app\/view\?cvid=(\d+)/);
                               return null != e &amp;amp;&amp;amp; e[1] ? Th(e[1]) : t
                            }
                            return &quot;&quot;
                         }(t))),
       !h)
       return Wv(t);
    var y = a || t
       , b = &quot;&quot;
       , g = !1
       , w = &quot;&quot;;
    (null != u &amp;amp;&amp;amp; u.goods_item_id || null != u &amp;amp;&amp;amp; u.goods_prefetched_cache) &amp;amp;&amp;amp; (g = !0,
       null != u &amp;amp;&amp;amp; u.goods_pc_click_urls &amp;amp;&amp;amp; Array.isArray(u.goods_pc_click_urls) &amp;amp;&amp;amp; (w = u.goods_pc_click_urls.join(&quot;,&quot;)),
       C(x),
       x.test(t) &amp;amp;&amp;amp; (b = t));
    var A = g ? {
       type: &quot;goods&quot;,
       &quot;goods-url&quot;: w,
       &quot;raw-url&quot;: b
    } : null != u &amp;amp;&amp;amp; u.is_word_search ? {
       type: &quot;search&quot;,
       keyword: uv(y)
    } : {
       type: &quot;link&quot;
    };
    return n === Uf.DESKTOP_APP ? A = v(v({}, A), {}, {
       link: h
    }) : n === Uf.MOBILE_BROWSER &amp;amp;&amp;amp; o &amp;amp;&amp;amp; (A = v(v({}, A), {}, {
       link: o
    })),
       Hv(h, y, s, d ? {
          display: &quot;inline-flex&quot;,
          &quot;flex-direction&quot;: &quot;row-reverse&quot;,
          &quot;--icon-width&quot;: &quot;0.65em&quot;,
          &quot;--icon-height&quot;: &quot;1.2em&quot;
       } : {
          &quot;--icon-width&quot;: &quot;1.2em&quot;,
          &quot;--icon-height&quot;: &quot;1.2em&quot;
       }, A)
 }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;简化并语义化后，关键逻辑如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var L = function (token, config) {
   var title = config.title
   // ...省略 url 解析
   var renderText = title || token
   return Hv(resolvedHref, renderText, prefixIcon, style, dataset)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可见：命中时明确是 &lt;code&gt;title || token&lt;/code&gt;，也就是“有标题显示标题、否则显示原片段文本”。&lt;/p&gt;
&lt;hr /&gt;
&lt;h4&gt;案例&lt;/h4&gt;
&lt;p&gt;上述的论述仍有不足之处，但能够分析出我们这一小节最开始的问题。就拿我们上面的截图作为案例：&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://cdn.jsdelivr.net/gh/lvjianchaos/Images/note/bilibili%E8%AF%88%E9%AA%97%E9%93%BE%E6%8E%A5%E8%AF%B4%E5%BC%80%E5%8E%BB/screenshot-of-the-reply-response.png&quot; style=&quot;zoom: 70%; border-radius: 1em&quot; /&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;message&lt;/code&gt;是：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;https://bilibili.com/video/BV12ecbz1EUZ/../../opus/1035673329881579520/../../video/BV1fscqz9EHs/
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;并且 &lt;code&gt;jump_url&lt;/code&gt; 里有两个键并有对应的标题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;BV12ecbz1EUZ&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;BV1fscqz9EHs&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;最终渲染应该是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;第一段显示原始 URL 链接：&lt;code&gt;https://bilibili.com/video/BV12ecbz1EUZ/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;中间 &lt;code&gt;../../opus/.../../../video/&lt;/code&gt; 显示为普通文本&lt;/li&gt;
&lt;li&gt;第二段 &lt;code&gt;BV1fscqz9EHs&lt;/code&gt; 显示为标题链接&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;与我们的实际渲染相符：&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://cdn.jsdelivr.net/gh/lvjianchaos/Images/note/bilibili%E8%AF%88%E9%AA%97%E9%93%BE%E6%8E%A5%E8%AF%B4%E5%BC%80%E5%8E%BB/example_render.png&quot; alt=&quot;example_render&quot; style=&quot;border-radius: 1em; zoom: 80%&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;在这里为什么第一个 &lt;code&gt;BV号&lt;/code&gt; 没有替换 &lt;code&gt;title&lt;/code&gt;，是 &lt;code&gt;jump_url&lt;/code&gt; 失效了吗？不然，是&lt;strong&gt;切片结果不同&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;前半段先被识别为一个 URL 片段，片段文本是完整 URL，不是 &lt;code&gt;BV12...&lt;/code&gt; 本身；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;jump_url&lt;/code&gt; 的 key 是 &lt;code&gt;BV12...&lt;/code&gt;，与该 URL 片段不精确相等，因此无法直接命中；&lt;/li&gt;
&lt;li&gt;后半段在后续分片中切出了独立 token：&lt;code&gt;BV1f...&lt;/code&gt;，这时与 &lt;code&gt;jump_url&lt;/code&gt; 的 key 精确匹配，所以能替换成标题。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;还有类似这样的：
&amp;lt;img src=&quot;https://cdn.jsdelivr.net/gh/lvjianchaos/Images/note/bilibili%E8%AF%88%E9%AA%97%E9%93%BE%E6%8E%A5%E8%AF%B4%E5%BC%80%E5%8E%BB/example_2.png&quot; alt=&quot;example_2&quot; style=&quot;border-radius: 1em; zoom: 60%&quot; /&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;message&lt;/code&gt;: &quot;&lt;code&gt;https://www.bilibili.com/video/BV1fscqz9EHs/../BV12ecbz1EUZav116057901629925cv1035673329881579520&lt;/code&gt;&quot;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;jump_url&lt;/code&gt; 仅有一个：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;https://www.bilibili.com/video/BV1fscqz9EHs/../BV12ecbz1EUZav116057901629925cv1035673329881579520&lt;/code&gt;: &lt;code&gt;{title: &quot;【Seedance 2.0】伊什塔尔-蕾塞舞★彡&quot;, state: 0,…}&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;为什么没有渲染标题，就是”切片“后的无法精准匹配 &lt;code&gt;jump_url&lt;/code&gt; 的键。当然，&lt;code&gt;jump_url&lt;/code&gt;的计算是后端服务进行的操作，所以问题应该是&lt;strong&gt;后端并不是按前端切片后的数据进行 jump_url 的计算，而自有一套算jump_url的方法&lt;/strong&gt;。而若要提高渲染 &lt;code&gt;jump_url&lt;/code&gt; 为标题的“&amp;lt;s&amp;gt;命中率&amp;lt;/s&amp;gt;”(:不知道这样说是否合理:)，后端的 &lt;code&gt;jump_url&lt;/code&gt; 计算方法就必须更改，比如按照前端同样的逻辑先对消息进行分片后处理是否有要显示 &lt;code&gt;title&lt;/code&gt; 的链接，或者前后端这一块的逻辑代码一起重构以互相匹配。&lt;/p&gt;
&lt;p&gt;那么，回到疑问：如何确认评论中某段文本是超链接？超链接的&lt;code&gt;url&lt;/code&gt;如何得来？以及，如何确认渲染内容不是&lt;code&gt;url&lt;/code&gt;而是对应视频标题？该视频标题如何得来？&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;通过各种正则匹配&lt;/li&gt;
&lt;li&gt;输入的是全的链接自然是全的，而若是 BV 号，av 号等由于其 url 的规律性，自然可以拼接得到&lt;/li&gt;
&lt;li&gt;通过后端传来的数据，核心是 &lt;code&gt;jump_url&lt;/code&gt; 的键值对&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;同样，B站短链是按照上述流程被渲染，如若能够匹配 &lt;code&gt;jump_url&lt;/code&gt; 也就会被渲染为 &lt;code&gt;title&lt;/code&gt; 的形式；而若跳转的视频标题与其不同，就造成“诈骗”；可以见得，&lt;strong&gt;问题就出现在这个 jump_url 计算出的 title 出错了&lt;/strong&gt;，即：对于改模板的 url &lt;code&gt;https://www.bilibili.com/video/BV_X/../BV_Y&lt;/code&gt; 计算出的 &lt;code&gt;title&lt;/code&gt; 是 &lt;code&gt;BV_X&lt;/code&gt; 的标题。&lt;/p&gt;
&lt;hr /&gt;
&lt;h4&gt;补充：流程图与对应代码锚点&lt;/h4&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://cdn.jsdelivr.net/gh/lvjianchaos/Images/note/bilibili%E8%AF%88%E9%AA%97%E9%93%BE%E6%8E%A5%E8%AF%B4%E5%BC%80%E5%8E%BB/flowchart.png&quot; alt=&quot;flowchart&quot; style=&quot;border-radius: 1em&quot; /&amp;gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;解析入口（&lt;code&gt;qv&lt;/code&gt;）&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;function qv(t, e, n, i) {
   var a = t.content
   var s = a.message, l = a.emote, c = a.members, u = a.jump_url, d = a.vote
   // ... 分片与节点构造
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;命中 &lt;code&gt;jump_url&lt;/code&gt; 时优先走 &lt;code&gt;L()&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;var M = Vf(function (token) {
   return null != u &amp;amp;&amp;amp; u[token] ? L(token, u[token]) : Hv(Sh(token), token)
}, ...)
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;渲染层将节点数组变成 HTML&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;setContents(nodes) {
   const html = nodes.map(/* node -&amp;gt; html */).join(&quot;&quot;)
   this.contents.innerHTML = html
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;点击事件基于 &lt;code&gt;dataset&lt;/code&gt; 分流&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;this.disposables.addEventListener(this.contentsElement, &quot;click&quot;, function (e) {
   var n = v({}, e.target.dataset)
   t.dispatchEvent(new CustomEvent(&quot;text-click&quot;, { detail: n }))
})
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;🔗浅谈短链接&lt;/h2&gt;
&lt;p&gt;在说明为什么 &lt;code&gt;https://www.bilibili.com/video/BV_X/../BV_Y&lt;/code&gt; 跳转到的是 BV_Y 之前，先来简单了解一下短链接技术。&lt;/p&gt;
&lt;h3&gt;简述&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;短链接&lt;/strong&gt;是一种 URL 缩短技术，通过&lt;strong&gt;重定向&lt;/strong&gt;将较长的原始链接映射到简短的地址。当用户访问短链接时，服务端返回 &lt;strong&gt;301/302 状态码&lt;/strong&gt;，并在 &lt;code&gt;Location&lt;/code&gt; 响应头中携带真实目标地址，浏览器随即跳转。&lt;/p&gt;
&lt;p&gt;例如，以下这个冗长的 B 站链接：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;https://www.bilibili.com/video/BV12ecbz1EUZ/?spm_id_from=333.999.0.0&amp;amp;vd_source=xxx...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以缩短为：&lt;code&gt;https://b23.tv/X8QkDCP&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://cdn.jsdelivr.net/gh/lvjianchaos/Images/note/bilibili%E8%AF%88%E9%AA%97%E9%93%BE%E6%8E%A5%E8%AF%B4%E5%BC%80%E5%8E%BB/shortlink-query.png&quot; alt=&quot;shortlink-query&quot; style=&quot;zoom:67%; border-radius:1em&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;上图是短链接请求的结果，可见其核心是&amp;lt;u&amp;gt;一次 &lt;strong&gt;302 重定向&lt;/strong&gt;，&lt;code&gt;Location&lt;/code&gt; 字段即为原始的长地址&amp;lt;/u&amp;gt;。&lt;/p&gt;
&lt;p&gt;简单的说，短链接服务就是一个&lt;strong&gt;键值对数据库&lt;/strong&gt;加上一个&lt;strong&gt;重定向服务器&lt;/strong&gt;。核心为两个过程：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;生成时&lt;/strong&gt;：你将一个长链接（如 &lt;code&gt;https://www.example.com/very/long/path?with=many&amp;amp;parameters&lt;/code&gt;）提交给服务，它返回一个短码（如 &lt;code&gt;abc123&lt;/code&gt;），并在数据库中记录 &lt;code&gt;短码 -&amp;gt; 长链接&lt;/code&gt; 的映射 。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;访问时&lt;/strong&gt;：当用户点击 &lt;code&gt;https://short.url/abc123&lt;/code&gt;，短链接服务器接收到请求，根据 &lt;code&gt;abc123&lt;/code&gt; 查到对应的长链接，然后向浏览器返回一个 &lt;strong&gt;HTTP 301（永久重定向）或 302（临时重定向）&lt;/strong&gt; 响应，其中 &lt;code&gt;Location&lt;/code&gt; 头部就是那个原始长链接。浏览器收到后，会自动跳转过去。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;code&gt;b23.tv&lt;/code&gt; 短链生成&lt;/h3&gt;
&lt;p&gt;我们可以通过第三方的 &lt;a href=&quot;https://www.bilitools.top/t/4/&quot;&gt;B站短链接生成工具&lt;/a&gt; 来使用哔哩哔哩的短码服务，当然也可以通过  &lt;a href=&quot;https://sessionhu.github.io/bilibili-API-collect/docs/misc/b23tv.html&quot;&gt;B站的API&lt;/a&gt; 生成 b23.tv 短链，这里给出一个脚本代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot;&quot;&quot;
b23_generator.py - Bilibili b23.tv 短链生成工具

基于B站 API实现：
接口：https://api.bilibili.com/x/share/click
方法：POST
功能：为视频、动态、专栏、文集、用户、课程、站内链接生成短链
&quot;&quot;&quot;

import requests
import json
import re
import sys
from typing import Optional, Dict, Any, Union

class B23Generator:
    &quot;&quot;&quot;B站短链生成器&quot;&quot;&quot;

    # API 端点
    API_URL = &quot;https://api.bilibili.com/x/share/click&quot;

    # 不同类型内容的参数映射表（基于文档）
    TYPE_MAP = {
        &quot;video&quot;: {
            &quot;share_id&quot;: &quot;main.ugc-video-detail.0.0.pv&quot;,
            &quot;share_origin&quot;: &quot;&quot;,
            &quot;description&quot;: &quot;视频&quot;
        },
        &quot;dynamic&quot;: {
            &quot;share_id&quot;: &quot;dt.dt-detail.0.0.pv&quot;,
            &quot;share_origin&quot;: &quot;dynamic&quot;,
            &quot;description&quot;: &quot;动态/图文&quot;
        },
        &quot;article&quot;: {
            &quot;share_id&quot;: &quot;read.column-detail.roof.8.click&quot;,
            &quot;share_origin&quot;: &quot;&quot;,
            &quot;description&quot;: &quot;专栏&quot;
        },
        &quot;collection&quot;: {
            &quot;share_id&quot;: &quot;read.column-readlist.share.0.click&quot;,
            &quot;share_origin&quot;: &quot;&quot;,
            &quot;description&quot;: &quot;文集&quot;
        },
        &quot;user&quot;: {
            &quot;share_id&quot;: &quot;main.space-total.more.0.click&quot;,
            &quot;share_origin&quot;: &quot;&quot;,
            &quot;description&quot;: &quot;用户空间&quot;
        },
        &quot;course&quot;: {
            &quot;share_id&quot;: &quot;pugv.pugv-video-detail.0.0.pv&quot;,
            &quot;share_origin&quot;: &quot;vinfo_player&quot;,
            &quot;description&quot;: &quot;课程&quot;
        },
        &quot;link&quot;: {
            &quot;share_id&quot;: &quot;public.webview.0.0.pv&quot;,
            &quot;share_origin&quot;: &quot;&quot;,
            &quot;description&quot;: &quot;站内链接&quot;
        }
    }

    def __init__(self, buvid: str = &quot;b23_generator&quot;, build: int = 7710300, platform: str = &quot;linux&quot;):
        self.buvid = buvid
        self.build = build
        self.platform = platform

    def _post_request(self, data: Dict[str, Any]) -&amp;gt; Optional[str]:
        headers = {
            &quot;User-Agent&quot;: &quot;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36&quot;,
            &quot;Content-Type&quot;: &quot;application/x-www-form-urlencoded&quot;
        }
        try:
            response = requests.post(self.API_URL, data=data, headers=headers, timeout=10)
            response.raise_for_status()
            result = response.json()
            if result.get(&quot;code&quot;) == 0 and result.get(&quot;data&quot;, {}).get(&quot;content&quot;):
                content = result[&quot;data&quot;][&quot;content&quot;]
                short_url_match = re.search(r&apos;(https?://b23\.tv/[^\s]+)&apos;, content)
                if short_url_match:
                    return short_url_match.group(1)
                return content
            else:
                print(f&quot;API返回错误: {result}&quot;)
                return None
        except Exception as e:
            print(f&quot;请求失败: {e}&quot;)
            return None

    def generate_video(self, oid: Union[int, str]) -&amp;gt; Optional[str]:
        if isinstance(oid, str) and oid.startswith((&apos;BV&apos;, &apos;bv&apos;)):
            print(&quot;注意：请传入AV号数字（aid），而非BV号&quot;)
            return None
        data = {
            &quot;buvid&quot;: self.buvid,
            &quot;build&quot;: self.build,
            &quot;platform&quot;: self.platform,
            &quot;share_channel&quot;: &quot;COPY&quot;,
            &quot;share_mode&quot;: 4,
            &quot;share_id&quot;: self.TYPE_MAP[&quot;video&quot;][&quot;share_id&quot;],
            &quot;oid&quot;: int(oid)
        }
        print(f&quot;正在为视频 (aid={oid}) 生成短链...&quot;)
        return self._post_request(data)

    def generate_dynamic(self, dynamic_id: int) -&amp;gt; Optional[str]:
        data = {
            &quot;buvid&quot;: self.buvid,
            &quot;build&quot;: self.build,
            &quot;platform&quot;: self.platform,
            &quot;share_channel&quot;: &quot;COPY&quot;,
            &quot;share_mode&quot;: 4,
            &quot;share_id&quot;: self.TYPE_MAP[&quot;dynamic&quot;][&quot;share_id&quot;],
            &quot;share_origin&quot;: self.TYPE_MAP[&quot;dynamic&quot;][&quot;share_origin&quot;],
            &quot;oid&quot;: dynamic_id
        }
        print(f&quot;正在为动态 (id={dynamic_id}) 生成短链...&quot;)
        return self._post_request(data)

    def generate_article(self, cvid: int) -&amp;gt; Optional[str]:
        data = {
            &quot;buvid&quot;: self.buvid,
            &quot;build&quot;: self.build,
            &quot;platform&quot;: self.platform,
            &quot;share_channel&quot;: &quot;COPY&quot;,
            &quot;share_mode&quot;: 4,
            &quot;share_id&quot;: self.TYPE_MAP[&quot;article&quot;][&quot;share_id&quot;],
            &quot;oid&quot;: cvid
        }
        print(f&quot;正在为专栏 (cvid={cvid}) 生成短链...&quot;)
        return self._post_request(data)

    def generate_collection(self, rlid: int) -&amp;gt; Optional[str]:
        data = {
            &quot;buvid&quot;: self.buvid,
            &quot;build&quot;: self.build,
            &quot;platform&quot;: self.platform,
            &quot;share_channel&quot;: &quot;COPY&quot;,
            &quot;share_mode&quot;: 4,
            &quot;share_id&quot;: self.TYPE_MAP[&quot;collection&quot;][&quot;share_id&quot;],
            &quot;oid&quot;: rlid
        }
        print(f&quot;正在为文集 (rlid={rlid}) 生成短链...&quot;)
        return self._post_request(data)

    def generate_user(self, mid: int) -&amp;gt; Optional[str]:
        data = {
            &quot;buvid&quot;: self.buvid,
            &quot;build&quot;: self.build,
            &quot;platform&quot;: self.platform,
            &quot;share_channel&quot;: &quot;COPY&quot;,
            &quot;share_mode&quot;: 4,
            &quot;share_id&quot;: self.TYPE_MAP[&quot;user&quot;][&quot;share_id&quot;],
            &quot;oid&quot;: mid
        }
        print(f&quot;正在为用户空间 (mid={mid}) 生成短链...&quot;)
        return self._post_request(data)

    def generate_course(self, course_id: int, origin: str = &quot;vinfo_player&quot;) -&amp;gt; Optional[str]:
        data = {
            &quot;buvid&quot;: self.buvid,
            &quot;build&quot;: self.build,
            &quot;platform&quot;: self.platform,
            &quot;share_channel&quot;: &quot;COPY&quot;,
            &quot;share_mode&quot;: 4,
            &quot;share_id&quot;: self.TYPE_MAP[&quot;course&quot;][&quot;share_id&quot;],
            &quot;share_origin&quot;: origin,
            &quot;oid&quot;: course_id
        }
        print(f&quot;正在为课程 (id={course_id}) 生成短链...&quot;)
        return self._post_request(data)

    def generate_link(self, url: str) -&amp;gt; Optional[str]:
        if not re.match(r&apos;^https?://([\w-]+\.)*bilibili\.com/&apos;, url):
            print(&quot;错误：仅支持 bilibili.com 域名下的站内链接&quot;)
            return None
        data = {
            &quot;buvid&quot;: self.buvid,
            &quot;build&quot;: self.build,
            &quot;platform&quot;: self.platform,
            &quot;share_channel&quot;: &quot;COPY&quot;,
            &quot;share_mode&quot;: 4,
            &quot;share_id&quot;: self.TYPE_MAP[&quot;link&quot;][&quot;share_id&quot;],
            &quot;oid&quot;: url
        }
        print(f&quot;正在为站内链接生成短链: {url}&quot;)
        return self._post_request(data)


def main():
    &quot;&quot;&quot;命令行入口&quot;&quot;&quot;
    import argparse
    parser = argparse.ArgumentParser(
        description=&quot;Bilibili b23.tv 短链生成工具&quot;,
        epilog=&quot;示例:\n  python b23_generator.py video 80433022\n  python b23_generator.py link &apos;https://www.bilibili.com/video/BV1GJ411x7h7&apos;&quot;,
        formatter_class=argparse.RawTextHelpFormatter 

    )
    parser.add_argument(&quot;type&quot;, choices=[&quot;video&quot;, &quot;dynamic&quot;, &quot;article&quot;, &quot;collection&quot;, &quot;user&quot;, &quot;course&quot;, &quot;link&quot;],
                        help=&quot;内容类型&quot;)
    parser.add_argument(&quot;id&quot;, help=&quot;内容ID或链接（视频请传aid数字）&quot;)
    parser.add_argument(&quot;--buvid&quot;, default=&quot;b23_generator&quot;, help=&quot;设备标识（任意非空字符串）&quot;)
    parser.add_argument(&quot;--build&quot;, type=int, default=7710300, help=&quot;客户端版本号（需&amp;gt;5520400）&quot;)

    args = parser.parse_args()

    generator = B23Generator(buvid=args.buvid, build=args.build)

    # 根据类型调用相应方法
    if args.type == &quot;video&quot;:
        result = generator.generate_video(args.id)
    elif args.type == &quot;dynamic&quot;:
        result = generator.generate_dynamic(int(args.id))
    elif args.type == &quot;article&quot;:
        result = generator.generate_article(int(args.id))
    elif args.type == &quot;collection&quot;:
        result = generator.generate_collection(int(args.id))
    elif args.type == &quot;user&quot;:
        result = generator.generate_user(int(args.id))
    elif args.type == &quot;course&quot;:
        result = generator.generate_course(int(args.id))
    elif args.type == &quot;link&quot;:
        result = generator.generate_link(args.id)
    else:
        result = None

    if result:
        print(f&quot;\n✅ 生成成功！短链: {result}&quot;)
    else:
        print(&quot;\n❌ 生成失败，请检查参数和网络&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;需要说明的是，短链接依赖服务端主动返回重定向；而本文将要分析的 &lt;code&gt;../&lt;/code&gt; 路径跳转，属于浏览器对 URL 路径的自动规范化处理，下文将详细展开。&lt;/p&gt;
&lt;h2&gt;🌐URL 的演进与规范&lt;/h2&gt;
&lt;p&gt;回到诈骗链接的现场：短链跳转到的原始长链明明是类似这样的 &lt;code&gt;https://www.bilibili.com/video/BV_X/../BV_Y&lt;/code&gt;，我们可以尝试直接在浏览器输入类似这样的网址，会发现路径中的 &lt;code&gt;/BV_X/..&lt;/code&gt; 消失了，是谁篡改了这个 &lt;code&gt;URL&lt;/code&gt;—— &lt;strong&gt;URL路径规范化&lt;/strong&gt;。&lt;/p&gt;
&lt;h3&gt;回顾 URL 起源与演进&lt;/h3&gt;
&lt;p&gt;1990年，蒂姆·伯纳斯·李在发明万维网时，需要一种方式标识互联网上的资源。他借鉴了当时&lt;code&gt;Unix&lt;/code&gt;文件系统的路径表示法：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用 &lt;code&gt;/&lt;/code&gt; 分隔目录层级&lt;/li&gt;
&lt;li&gt;用 &lt;code&gt;.&lt;/code&gt; 表示当前目录&lt;/li&gt;
&lt;li&gt;用 &lt;code&gt;..&lt;/code&gt; 表示父目录&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这种设计让早期开发者能够直观地理解和链接资源。比如，&lt;code&gt;/docs/../images/logo.png&lt;/code&gt; 显然指向 &lt;code&gt;/images/logo.png&lt;/code&gt;。在当时，这只是一个实用的约定，而非严格的标准。&lt;/p&gt;
&lt;p&gt;随着Web的爆炸式发展，这种“野路子”带来了问题：同一个资源可以有无数个别名（如 &lt;code&gt;/a/b/c&lt;/code&gt; 和 &lt;code&gt;/a/b/../b/c&lt;/code&gt;），这导致缓存、爬虫和安全都面临挑战；并且，URL 的解析方式在不同浏览器、服务器间出现了分歧。例如，对 &lt;code&gt;http://example.com/a/../b&lt;/code&gt; 的处理：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;有的系统会直接发送原始路径&lt;/li&gt;
&lt;li&gt;有的会在服务器端做规范化&lt;/li&gt;
&lt;li&gt;有的甚至会把 &lt;code&gt;..&lt;/code&gt; 当作普通目录名&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这导致了兼容性灾难和安全漏洞（如目录遍历攻击）。于是，互联网工程任务组（IETF）开始推动标准化。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;时间&lt;/th&gt;
&lt;th&gt;规范&lt;/th&gt;
&lt;th&gt;关键贡献&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1994&lt;/td&gt;
&lt;td&gt;RFC 1630&lt;/td&gt;
&lt;td&gt;首次定义“通用资源标识符”，但只是总结现有用法&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1994&lt;/td&gt;
&lt;td&gt;RFC 1738&lt;/td&gt;
&lt;td&gt;正式定义绝对/相对URL，明确相对解析规则&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1998&lt;/td&gt;
&lt;td&gt;RFC 2396&lt;/td&gt;
&lt;td&gt;URI语法独立成规范，“U”从“通用”改为“统一”&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;2005&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;RFC 3986&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;现行标准&lt;/strong&gt;，详细定义解析、规范化、相对引用规则&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;**&lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc3986&quot;&gt;RFC 3986&lt;/a&gt;**是里程碑式的。它明确要求：&amp;lt;u&amp;gt;任何符合标准的URI处理器，在解析路径时必须对&amp;lt;/u&amp;gt; &lt;code&gt;.&lt;/code&gt; &amp;lt;u&amp;gt;和&amp;lt;/u&amp;gt; &lt;code&gt;..&lt;/code&gt; &amp;lt;u&amp;gt;进行规范化处理&amp;lt;/u&amp;gt;。这意味着：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;浏览器在发送请求前，必须规范化路径&lt;/li&gt;
&lt;li&gt;服务器在路由匹配前，必须规范化路径&lt;/li&gt;
&lt;li&gt;任何网络库、框架在处理URL时，都必须遵循这一规则&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;之所以要求路径规范化，主要基于两点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;资源定位的唯一性&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在Web中，一个资源应该只有一个规范URL。规范化确保了无论用户如何构造链接（&lt;code&gt;/a/b/../c&lt;/code&gt; 还是 &lt;code&gt;/a/c&lt;/code&gt;），最终都指向同一资源。这对于缓存、SEO、权限控制至关重要。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;基础安全防线&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;规范化是&lt;strong&gt;防御目录遍历攻击的第一道关卡&lt;/strong&gt;。通过解析 &lt;code&gt;..&lt;/code&gt;，服务器可以判断最终路径是否在Web根目录内。例如，攻击者提交 &lt;code&gt;../../etc/passwd&lt;/code&gt;，规范化后服务器能检测到&lt;a href=&quot;https://en.wikipedia.org/wiki/Directory_traversal_attack&quot;&gt;目录遍历&lt;/a&gt;，直接拒绝请求。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;规范化&lt;/h3&gt;
&lt;p&gt;显而易见的，&lt;strong&gt;规范化的发生&lt;/strong&gt;在底层的基础设施，且往往不止一次：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;浏览器端&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;当你在地址栏输入URL并回车时，浏览器内核在构造HTTP请求之前，就会进行路径规范化。也就是为什么我们开始在浏览器输入：&lt;code&gt;https://www.bilibili.com/video/BV_X/../BV_Y&lt;/code&gt;，回车，就已经是：&lt;code&gt;https://www.bilibili.com/video/BV_Y&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;服务器端&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;即使浏览器因某些原因未规范化（如构造特殊请求），请求到达诸如Nginx的服务器时，在路由匹配前同样会执行路径规范化。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;…………&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;核心：&lt;code&gt;.&lt;/code&gt; 与 &lt;code&gt;..&lt;/code&gt; 的处理&lt;/h4&gt;
&lt;p&gt;URL 标准有明确的规范化算法，简化后的逻辑如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;初始化一个空列表 &lt;code&gt;output&lt;/code&gt; 作为规范化后的路径&lt;/li&gt;
&lt;li&gt;遍历原始路径的每个 segment：
&lt;ul&gt;
&lt;li&gt;若 segment 为 &lt;strong&gt;&lt;code&gt;.&lt;/code&gt;&lt;/strong&gt;（或编码形式 &lt;code&gt;%2e&lt;/code&gt;）→ &lt;strong&gt;跳过&lt;/strong&gt;，不加入 &lt;code&gt;output&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;若 segment 为 &lt;strong&gt;&lt;code&gt;..&lt;/code&gt;&lt;/strong&gt;（或编码形式 &lt;code&gt;%2e%2e&lt;/code&gt;、&lt;code&gt;%2e.&lt;/code&gt; 等）→ 若 &lt;code&gt;output&lt;/code&gt; 非空，&lt;strong&gt;移除最后一个 segment&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;否则 → 将 segment 加入 &lt;code&gt;output&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;以我们的链接为例：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;原始 segment&lt;/th&gt;
&lt;th&gt;操作&lt;/th&gt;
&lt;th&gt;当前 output&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;video&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;加入&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[&quot;video&quot;]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BV_X&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;加入&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[&quot;video&quot;, &quot;BV_X&quot;]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;..&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;移除最后一个&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[&quot;video&quot;]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BV_Y&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;加入&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[&quot;video&quot;, &quot;BV_Y&quot;]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;最终规范化后的路径为 &lt;code&gt;/video/BV_Y&lt;/code&gt;，因此浏览器实际请求的是 &lt;code&gt;https://www.bilibili.com/video/BV_Y&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;示例代码 demo：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 功能：规范化URL路径，移除 &quot;.&quot; 和 &quot;..&quot;
static void normalize_path(char *path) {
    char *p = path;
    char *q = path;
    // 核心逻辑：遍历路径段
    while (*p) {
        if (p[0] == &apos;.&apos; &amp;amp;&amp;amp; (p[1] == &apos;/&apos; || p[1] == &apos;\0&apos;)) {
            p += (p[1] == &apos;/&apos;) ? 2 : 1;  // 跳过 &quot;.&quot;
        } else if (p[0] == &apos;.&apos; &amp;amp;&amp;amp; p[1] == &apos;.&apos; &amp;amp;&amp;amp; (p[2] == &apos;/&apos; || p[2] == &apos;\0&apos;)) {
            // 回退上一级
            while (q &amp;gt; path &amp;amp;&amp;amp; *--q != &apos;/&apos;);
            p += (p[2] == &apos;/&apos;) ? 3 : 2;
        } else {
            // 复制普通段
            while (*p &amp;amp;&amp;amp; *p != &apos;/&apos;) *q++ = *p++;
            if (*p == &apos;/&apos;) *q++ = *p++;
        }
    }
    *q = &apos;\0&apos;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;不止于路径&lt;/h4&gt;
&lt;p&gt;值得注意的是，URL规范化远不止处理 &lt;code&gt;..&lt;/code&gt;。RFC 3986及其实践通常包括以下操作：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;操作&lt;/th&gt;
&lt;th&gt;示例&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Scheme转小写&lt;/td&gt;
&lt;td&gt;&lt;code&gt;HTTP://&lt;/code&gt; → &lt;code&gt;http://&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;协议名不区分大小写&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Host转小写&lt;/td&gt;
&lt;td&gt;&lt;code&gt;EXAMPLE.com&lt;/code&gt; → &lt;code&gt;example.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;域名不区分大小写&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;移除默认端口&lt;/td&gt;
&lt;td&gt;&lt;code&gt;:80&lt;/code&gt;（HTTP）、&lt;code&gt;:443&lt;/code&gt;（HTTPS）&lt;/td&gt;
&lt;td&gt;减少冗余&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;路径解析&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/a/./b/../c&lt;/code&gt; → &lt;code&gt;/a/c&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;移除 &lt;code&gt;.&lt;/code&gt; 和 &lt;code&gt;..&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;百分号解码&lt;/td&gt;
&lt;td&gt;&lt;code&gt;%7E&lt;/code&gt; → &lt;code&gt;~&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;解码安全字符&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;移除片段标识&lt;/td&gt;
&lt;td&gt;&lt;code&gt;#section&lt;/code&gt; 在发送请求时丢弃&lt;/td&gt;
&lt;td&gt;片段不发送到服务器&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;示例：&lt;strong&gt;URI.js（JavaScript 库）的规范化&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// https://www.npmjs.com/package/uri-js
const URI = require(&quot;uri-js&quot;);
let rawUrl = &quot;HTTP://ABC.COM:80/%7Esmith/home.html&quot;;
let normalized = URI.normalize(rawUrl);
console.log(normalized); 
// 输出: &quot;http://abc.com/~smith/home.html&quot;
// 功能：scheme/host转小写，移除默认端口80，解码安全的百分号编码
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;结尾：回到“诈骗链接”&lt;/h2&gt;
&lt;p&gt;现在我们可以完整解释 B 站诈骗链接的机制了：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;使用B站短链服务，将一个未规范化的 URL 生成短链接，而这个 URL 在短链服务里没有被规范化；&lt;/li&gt;
&lt;li&gt;后端计算该短链接 &lt;code&gt;jump_url&lt;/code&gt; 的键值对，在解析这个未规范化的 URL 时，导致提取的是第一个 &lt;code&gt;BV_X&lt;/code&gt; 的 &lt;code&gt;title&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;前端渲染这个 &lt;code&gt;b23.tv&lt;/code&gt; 短链，对应的 &lt;code&gt;title&lt;/code&gt; 是第一个 &lt;code&gt;BV_X&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;点击跳转，浏览器重定向原始 URL，由于自身执行 URL 的标准化，将路径规范化为 &lt;code&gt;/video/BV_Y&lt;/code&gt;，于是“诈骗”发生了。&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;所以，实际上，&lt;code&gt;https://www.bilibili.com/video/BV_X/../../&lt;/code&gt;后可加任意的路径（B站）。例如：&lt;code&gt;https://www.bilibili.com/video/BV_X/../../opus/1035673329881579520&lt;/code&gt;跳转的实际上是：&lt;code&gt;https://www.bilibili.com/opus/1035673329881579520&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;而若要解决这个“诈骗链接”问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;短链接生成服务，注意对原始链接进行规范化&lt;/li&gt;
&lt;li&gt;后端 &lt;code&gt;jump_url&lt;/code&gt; 的计算逻辑，注意对 URL 进行规范化&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;:::tip[😋]
本文有些知识其实不需要大段论述，但实际上我就是为了这个“饺子”，才端出这个“B站诈骗链接”的“醋”喵~
:::&lt;/p&gt;
&lt;h2&gt;相关资料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1tCw8zpE21&quot;&gt;https://www.bilibili.com/video/BV1tCw8zpE21&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://s1.hdslb.com/bfs/seed/jinkela/commentpc/bili-comments.e0090ab8af.js&quot;&gt;https://s1.hdslb.com/bfs/seed/jinkela/commentpc/bili-comments.e0090ab8af.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.bilitools.top/t/4&quot;&gt;https://www.bilitools.top/t/4&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Structuring_content/Creating_links&quot;&gt;https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Structuring_content/Creating_links&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://sessionhu.github.io/bilibili-API-collect/&quot;&gt;https://sessionhu.github.io/bilibili-API-collect/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/URL&quot;&gt;https://en.wikipedia.org/wiki/URL&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/URL_shortening&quot;&gt;https://en.wikipedia.org/wiki/URL_shortening&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://url.spec.whatwg.org/#url&quot;&gt;https://url.spec.whatwg.org/#url&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Directory_traversal_attack&quot;&gt;https://en.wikipedia.org/wiki/Directory_traversal_attack&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Canonicalization#URL&quot;&gt;https://en.wikipedia.org/wiki/Canonicalization#URL&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en/docs/Web/API/URL&quot;&gt;https://developer.mozilla.org/en/docs/Web/API/URL&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;:::note
笔者水平不高，若上述所言有任何错误或不合理之处，恳请见谅、加以斧正；有任何建议也欢迎提出。
:::&lt;/p&gt;
</content:encoded></item><item><title>姥爷</title><link>https://blog.chaos-ljc.top/posts/grandpa/</link><guid isPermaLink="true">https://blog.chaos-ljc.top/posts/grandpa/</guid><description>……像是飞倦了的鸟，落回了巢。那些向外伸展的枝桠，也被时间一根根修剪，最后，只剩下守着根的那份安稳。</description><pubDate>Fri, 31 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;在我心里，姥爷是需要仰头看的人。这份敬意，像院子里那棵老树的根，悄悄扎得很深。他不怎么夸人，话语也不多，但看他眼角的纹路怎样聚散，就能猜到几分他对我们的心思。&lt;/p&gt;
&lt;p&gt;家里人都说，姥爷是水做的骨架，身子弱，性子也软。他的父亲，我叫老太的，听说是个富农。姥爷生在四九年，天地变色的当口。家道是落了，日子却还算安稳，像秋后的水塘，波澜不惊。姥爷就像那水塘边小心护着的秧苗，没经过多少风雨，就这么长大了。&lt;/p&gt;
&lt;p&gt;人一辈子要遇上什么，好像冥冥中都有定数。软和的姥爷，偏就遇上了硬朗的姥娘。姥娘是石头缝里长出的草，不光身板结实，心思也硬，遇事有决断，像个老谋深算的将军。跟人掰扯起来，她从不落下风。姥爷呢，像是怕沾染上那份争斗的尘土，总在一旁默着，像株田埂上割了的麦子。他们俩，一软一硬，竟也磕磕绊绊地走过了五十多年，像河里的两块石头，被水冲刷着，磨圆了棱角，依偎在一起。如今添了重孙丞丞，拌嘴也少了，更多的是笑，那笑纹从眼角一直漾开，快要盛不下了。&lt;/p&gt;
&lt;p&gt;都说姥爷柔弱，确实，他一辈子没怎么碰过泥土，那双手，比起姥娘那双饱经风霜、如同老树皮的手，要细嫩得多。可在我眼里，姥爷那份软和里，藏着不易察觉的韧劲，像水底的青苔，不显眼，却牢固。他不愿在人前示弱，有时会偷偷背过身去抹眼睛，但一转过脸对着我们这些小的，眼里就又有了光，像夜里的星子。他常鼓励我们多认几个字。姥爷是念过书的人，写一手好字，听说年轻时是想考出去，只是后来文革耽搁了。&lt;/p&gt;
&lt;p&gt;这几年，姥爷的身子像老屋的墙，渐渐有些松动了。一点不舒服就让他心慌，往看病的地方也跑得勤了。可只要抱起丞丞，那小小的、暖烘烘的身子一挨着他，他的眼睛就又笑成了一条缝，好像所有忧虑都被这新生的气息给冲散了。他舍不得，舍不得我们这些他看着出生、又看着长大的人。&lt;/p&gt;
&lt;p&gt;还记得很小的时候，上幼儿园。姥爷刚退下来，却闲不住，弄了辆三轮车去拉人。那车在他手里，像匹温顺的老马。在我眼里，那车斗是那样宽大，能装下好多东西。可我一天天长高，那三轮车却在我眼里一天天变小、变旧，铁皮生了锈，轮子也不再光亮。后来有一天，它就彻底不动了，像累极了的老牛，卧在那里。再后来，它就不见了，不知去了哪里。它的颜色，它的模样，在我记忆里也模糊了，像南堰上空飘过的一缕炊烟，散了。&lt;/p&gt;
&lt;p&gt;那辆载着姥爷奔波过的三轮车不见了，姥爷也彻底停了下来，守在了家里。像是飞倦了的鸟，落回了巢。那些向外伸展的枝桠，也被时间一根根修剪，最后，只剩下守着根的那份安稳。&lt;/p&gt;
&lt;p&gt;如今，姥爷坐在院子里，看着儿孙们各自忙碌，看着重孙在地上爬。生命像门前的小河，流过他，又流向更远的地方。他感受着这绵延不绝，脸上是满足的，像秋日晒谷场上的阳光。我站在旁边，看着他，也看着落在他身上的夕阳，那光线，暖暖的，却也带着几丝凉意。&lt;/p&gt;
&lt;p&gt;姥爷是真的老了。&lt;/p&gt;
&lt;p&gt;他心里的那些事，那些年轻时的奔波，那些没能写完的字，怕是也要像那辆三轮车一样，慢慢沉寂下去，只有他自己，在无人的时候，默默回想吧。&lt;/p&gt;
&lt;p&gt;而我，好像还是那个跟在他身后的孩子，却也到了要推开家门，走向自己那片南堰的时候了。&lt;/p&gt;
&lt;p&gt;姥爷还是那个姥爷，只是，再也蹬不动那辆三轮车了。&lt;/p&gt;
</content:encoded></item><item><title>盛夏、死蝉与逝去的友人</title><link>https://blog.chaos-ljc.top/posts/summer-cicada-lost-friend/</link><guid isPermaLink="true">https://blog.chaos-ljc.top/posts/summer-cicada-lost-friend/</guid><description>挡风玻璃上爆开的虫尸，绽放成星云，盛夏正以光年速度向我们袭来……</description><pubDate>Tue, 28 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;一&lt;/h2&gt;
&lt;p&gt;蝉鸣渗过玻璃窗，那声音不似振动，倒像一种介于树脂与电波之间的存在，粘稠地裹挟着破碎的回忆。当搬家工人第八次撞击门框时，某根被空调冷气冻僵的神经突然一颤——一本破旧的笔记本掉落在门旁，封皮上依稀可见2008年梅雨季的霉斑。&lt;/p&gt;
&lt;p&gt;指尖轻掠泛黄的纸页，家乡学校后的新代桥尚未清晰，十六岁那年生物实验室里福尔马林的气味已率先漫溢开来。吕游的白衬衫如往常一样带着水族箱藻类的腥甜味，当他把解剖剪刺入蝉的腹节时，睫毛在夏末的光晕中抖落了细碎的金粉。我看不清他的脸。&lt;/p&gt;
&lt;p&gt;“节间膜，要完整剥离。”他的声音，透着昆虫标本般的透明感。我们并排蹲在实验室前的走廊上，拾起那些被同学踩碎的蝉尸。窗外的合欢树，散发着最后的绒花，粉色的雪片在他肩胛骨上停驻，像是永远飞不起来的残翅。&lt;/p&gt;
&lt;h2&gt;二&lt;/h2&gt;
&lt;p&gt;空调压缩机发出垂死的嗡鸣。此刻，凝结在2023年的尘埃，漂浮在十五年前解剖剪上微弱的光影中，如同两个时节的蝉鸣在记忆的共振腔里共鸣。读到“吕游死了”那行稚嫩的笔迹时，鼻腔内竟涌上一股腥腥的凉意——这不是文字，而是2008年九月十七日黄昏，警用探照灯在河面上划出的那道惨白伤痕。&lt;/p&gt;
&lt;p&gt;记忆的菌丝开始蔓延：他的橡皮擦总被削去一个尖，生物课本第三十七页夹着风干的蜉蝣翅脉，还有葬礼那天停在他母亲发髻上的红蜻蜓（那深如鲜血的红，至今仍在视网膜背面闪烁）。穿黑袈裟的僧侣摇动法铃时，蝉群突然噤声，仿若整个宇宙的振动都坍缩进桐木骨灰盒的缝隙。&lt;/p&gt;
&lt;h2&gt;三&lt;/h2&gt;
&lt;p&gt;搬运工踩过地板的吱呀声，撕开了记忆的茧房。我发现自己正用拇指反复摩挲日记本上某处皱褶——那是被泪水浸泡过的地形图，标记着一个已不复存在的地方。吕游消失前夜，他留给我的玻璃标本瓶，此刻与纸箱深处的颠簸共振。七百二十只蝉的复眼在黑暗中睁开，折射出我们在时光褶皱里遗落的对话：&lt;/p&gt;
&lt;p&gt;“知道吗？蝉若虫要在土里蛰伏十七年。”&lt;/p&gt;
&lt;p&gt;“比我们的年纪还大呢。”&lt;/p&gt;
&lt;p&gt;“可破土后的成虫，只有七天。”&lt;/p&gt;
&lt;p&gt;我们笑得像两个偷饮了时光酒的僭越者。&lt;/p&gt;
&lt;h2&gt;四&lt;/h2&gt;
&lt;p&gt;电梯下降的失重感中，某只垂死的蝉突然在耳膜内振翅。它的鸣叫穿透十五年光阴，与卡车引擎的轰鸣编织成赋格曲。后视镜中，渐远的公寓楼褪色，像浸泡在显影液中的旧相片。当高速公路的风撕开记忆的结痂，我突然听见吕游的声音在时空间隙里低语：&lt;/p&gt;
&lt;p&gt;“不是十七年，也不是七天。”&lt;/p&gt;
&lt;p&gt;“我们都在各自的甬道里，等待破土的那一刻。”&lt;/p&gt;
&lt;p&gt;挡风玻璃上爆开的虫尸，绽放成星云，盛夏正以光年速度向我们袭来。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;始写于22年秋&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded></item><item><title>姥娘，大黄。</title><link>https://blog.chaos-ljc.top/posts/grandma-dahuang/</link><guid isPermaLink="true">https://blog.chaos-ljc.top/posts/grandma-dahuang/</guid><description>这些东西，不说出来，就只能烂在心里，跟着人和狗，一起回到土里去。</description><pubDate>Mon, 27 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;雪停了些日子，去看姥娘。如今多是在丞丞家见她。丞丞是她的重孙子，刚落到这世上四月，像雪后地里钻出的一点新绿。姥娘抱着他，脸上沟壑纵横，笑起来时，眼睛便眯成一道弯弯的缝，露出补过的瓷牙，亮晃晃的，像冬日薄冰反射的日光。丞丞也跟着笑，咯咯咯的声音，干净得像刚洗过的天空。&lt;/p&gt;
&lt;p&gt;姥娘老了，七十多年，风尘仆仆。丞丞小，嫩得能掐出水。看着他们，一个生命的尾声，一个生命的开端，挨得那么近，像河的两岸，中间是奔流不息的时间。这时间的水声，有时听着让人心安，有时，却又觉得它带着寒气，能悄无声息地浇熄一些东西。比如，人心里那点微弱的火苗。&lt;/p&gt;
&lt;p&gt;姥娘年轻时，守着南堰上的猪场。那时的南堰，天高地阔，路是土的，风是硬的。姥娘的身板也硬朗，走起路来，脚下生风。我跟在她身后，她走一步，我得跑两步。家还没拆，南堰还在，猪在圈里哼哼，日子像南堰上的草，不声不响地长着，也黄着。记不清多少个夜晚，妈上夜班，姥娘屋里的灯昏黄，像粘稠的蜜。她的大手拍着我的背，哼着不成调的歌谣，哄我睡去。那时的她，笑起来是暖的，像南堰夏天的阳光。如今，她笑的样子也没变，只是皱纹深了，像被岁月犁过的土地。&lt;/p&gt;
&lt;p&gt;那时，是她立在南堰坡上等我。如今，我早已跑出了她的视线，跑过了那片她将来要躺下的土地。她看着我们这些小的，一代代往前跑，身子越来越沉，脚步越来越慢，却好像还想跟着，再送一程。可我们跑得太快了，快得让她只能留在原地，望着一个个背影，消失在路的尽头，消失在时间的尘埃里。&lt;/p&gt;
&lt;p&gt;南堰坡上，姥娘曾捡回一只小黄狗，叫大黄。她陪了我好些年，算起来，比我大两岁。如今，我的年纪，已是她活过的两倍了。她走了有十年了吧？死在哪一天，记不清了。只记得那天心里空了一下，像被风吹过，第二天，就忘了那阵风。可现在，这风又不知从哪里吹回来，带着点凉意，和一种说不清的慌。她是在哪里咽下最后一口气的？为什么要自己跑出去？我最后一次唤她，她有没有回头看我一眼？像南堰秋天早上的雾，都模糊了。&lt;/p&gt;
&lt;p&gt;她死前几天，就卧在窝里不动了。那天却忽然有了精神，在院子里跑了几圈。我以为她好了，没多想。谁知道，只是回光返照，用尽最后的气力，跑回了她来的地方，南堰。找了个谁也不知道的角落，把自己埋葬了。&lt;/p&gt;
&lt;p&gt;记忆这东西，靠不住，像漏了的筛子。只记得一些零碎的，不相干的。有一次，姥娘扔给大黄一块肉，她老了，牙不行了，叼着，却咬不动，呜咽了一声，把肉吐在地上。她的儿孙，早被送给了左邻右舍，只剩下她一个。姥娘看着她，叹了口气，“都老了……” 把肉切成小薄片，再扔给她。她吃没吃，忘了。只记得我蹲下去，看着她的眼睛。那眼睛黑黢黢的，像两口深不见底的枯井，要把人的魂吸进去。在她眼里，我是什么样子的？那个时候的她，和那个时候的我，心里头都转着些什么念头？怕是只有南堰的风，南堰的土才知道了。这些东西，不说出来，就只能烂在心里，跟着人和狗，一起回到土里去。&lt;/p&gt;
&lt;p&gt;大黄最后还是回到了南堰。那是姥娘捡到她的地方，也是她最后望着的家。或许，那片土地，才是所有生命的根，无论跑得多远，最终都要回去。就像姥娘，守着南堰大半生，如今看着重孙，眼神里还是南堰的影子，有暖阳，也有挥之不去的，落雪的寂静。&lt;/p&gt;
</content:encoded></item><item><title>刷题日志</title><link>https://blog.chaos-ljc.top/posts/problem-solving-log/</link><guid isPermaLink="true">https://blog.chaos-ljc.top/posts/problem-solving-log/</guid><description>刷题日志同步于Github</description><pubDate>Sat, 30 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://github.com/lvjianchaos/Algorithm-Problem&quot;&gt;刷题日志&lt;/a&gt;记录着我平日解决过的算法题与对应的题解（它们有些业已在我博客中发表），通常是有趣的😊，或对我而言有挑战性的😱，有时也记录着我在进行算法竞赛中的思考🤔。&lt;/p&gt;
&lt;p&gt;:::important
你可以在我的GitHub上找到它们！
:::&lt;/p&gt;
&lt;p&gt;::github{repo=&quot;lvjianchaos/Algorithm-Problem&quot;}&lt;/p&gt;
</content:encoded></item><item><title>卡住的文章，“永不抵达”的“我”</title><link>https://blog.chaos-ljc.top/posts/stuck-article/</link><guid isPermaLink="true">https://blog.chaos-ljc.top/posts/stuck-article/</guid><description>在完全承认自己的动机可能不纯粹、可能被审判、可能失败的前提下，依然选择将那个卡住的文章公开发表</description><pubDate>Mon, 25 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;:::note[前面的话]
如果这篇文章没有卡在发表的咽喉里，某种意义上我或许有了些改变……
:::
这个博客其实早在1年前就已搭建并部署了，不过当时是基于Hexo框架。&lt;/p&gt;
&lt;p&gt;显而易见，截至今日我并未发表数量可观的博文，而这些屈指可数的博文也只是我平日里学习得以整理的知识类笔记等等(比如：关于编程、关于开发、关于算法)。&lt;/p&gt;
&lt;p&gt;可最近我总会陷入 想要分享 自己写下的小说、散文以及对书籍、电影、&lt;strong&gt;番剧&lt;/strong&gt;等的体验、感受、评价与思考等 这类偏私人化的写作 的矛盾漩涡。可这仅仅是“想”，我并未付诸行动。&lt;/p&gt;
&lt;p&gt;究其原因，我这个人一个人时总会考虑自己种种行为、思想的正当性或思考我为什么会这样做及这样想（发生原理与意义）。&lt;/p&gt;
&lt;p&gt;所以，对于前者知识类文章的发表/分享我并未“卡住”，因为在我看来：在撰写这些博文时，我总会有意无意地预想一个正在阅读这篇文章的人，从而尽可能写得有逻辑、准确性与结构化，以至于以输出的方式更好地巩固我所学的知识；But，对于后者私人化的写作，我似乎无法接收同样的理由或者找到如前者那样的“正当性”（虚荣心（炫耀自己的小说有多么高深、散文有多么哲思？对书影音的感受评价有多么深刻？从而获得某种对我的好处（物质的利益/心理的快乐）？）？孤独（想要获得认同？希望有人发现我的存在？）？），或许真的是这样或许不是，但就算是，我也会以不成熟（虚荣、寻求认同）的动机而终止这一行为。故称“卡住的文章”。&lt;/p&gt;
</content:encoded></item><item><title>The Art of Naming the Variable Names</title><link>https://blog.chaos-ljc.top/posts/variable-naming-art/</link><guid isPermaLink="true">https://blog.chaos-ljc.top/posts/variable-naming-art/</guid><description>作为入门者所了解的变量命名</description><pubDate>Tue, 29 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;:::important[学习笔记]
原B站up &lt;code&gt;micro_frank&lt;/code&gt; 的视频课程，现已更名为 &lt;a href=&quot;https://space.bilibili.com/19658621&quot;&gt;零界世访&lt;/a&gt;，且该视频已被其删除或隐藏。
:::&lt;/p&gt;
&lt;h2&gt;一、总论&lt;/h2&gt;
&lt;p&gt;身为IT行业的小白，我们不能仅仅沉浸于代码语言的层次上或算法与数据结构的快感中，我们应该思考：但在这些基本都搞定了呢！？我们又该何去何从？&lt;/p&gt;
&lt;p&gt;实际上，最高深的层次往往不是代码的算法与数据结构的构建本身，而是&lt;strong&gt;编程的艺术&lt;/strong&gt;。一些大师级别的人物，他们在谈到编程或软件构建上绝不仅停留在代码、语言、算法和数据结构上，而是提升到一个极高的层次——编程的艺术。也就是说：思维和艺术将会决定你写代码的高度。&lt;/p&gt;
&lt;p&gt;而编程的艺术其中一个重要的点便是——&lt;strong&gt;变量名命名&lt;/strong&gt;。&lt;/p&gt;
&lt;h3&gt;变量名命名的重要性&lt;/h3&gt;
&lt;p&gt;变量名真正的命名往往直接决定软件构建的效果、代码的性能，甚至可能影响到软件的安全和效益，以及所出现的问题一半都可能源于变量名命名。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;我们为何如是说呢？&lt;/strong&gt;&amp;lt;u&amp;gt;“一个好的变量名命名不是一个即时的效果，而是一个长期的效益。”&amp;lt;/u&amp;gt;一个例子：我们设想下列情景：&lt;/p&gt;
&lt;p&gt;（1）我养了一只狗🐕，它叫小黑，小黑死去之后，我又养了一只叫小白的狗🐕；这样并无问题，我们都能分清楚。&lt;/p&gt;
&lt;p&gt;（2）可是，如果我养了10000只&lt;strong&gt;不同&lt;/strong&gt;（这里说不同只是方便假设，因为这里不能用数组，而是无联系的10000个变量）的鸡，那么一万只鸡我该是这样命名吗？：a,b,c,d…aa,ab,………当第二天我们说&lt;code&gt;鸡chicken&lt;/code&gt;下蛋了，那么&lt;code&gt;鸡chicken&lt;/code&gt;是谁呢？我们还能搞清吗？所以说变量名命名的方式直接决定变量名的所带来的后果/效果。&lt;strong&gt;没有好的变量名命名，会导致程序难以理解和维护&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;变量名命名的规范&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;我们这里以C语言来进行代码部分的编写，以进行具体阐释。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;关于常见的&lt;strong&gt;错误命名&lt;/strong&gt;(🔺注意：这是在具有实际开发情景下的代码，不是无实际意义的语法展示示例)：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
// 不明所以，不知其意
int main(void)
{
    int a,b;
    char c;
    
    int a[10];
    
    int ss = s1 + s2;
    
    int i;
    for(i = 0;i &amp;lt; 10;i++)
    {
        //……
    }
    
    return 0;
}

void f() {
    
}
void fff() {
    
}
void havesex() {
    
}
void qian() {
    
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;一些&lt;strong&gt;规范示范&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdbool.h&amp;gt;
int main(void)
{
    double balance = 100.00;
    // double a = 100.00; X错；如果使用这样的代码，过一段时间后，当回看代码时，你还会知道a是余额的意思吗？
    
    return 0;
}

bool is_leap_year(int year) {
    // TODO  ==》 意思是:这里还有代码要写
    return false;
}

// 当然，这是下划线命名，有些命名是使用驼峰命名法，这异曲同工：bool isLeapYear(int year){}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们很容易发现，&lt;strong&gt;不同的变量名命名直接会导致阅读体验的分野&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;而一个标准规范的变量名命名应具备什么样的&lt;strong&gt;特质&lt;/strong&gt;呢？&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;可读性好、便于记忆、见名知意&lt;/strong&gt;；或者更直白的说：一眼就可以看出这是在干什么。我们应心怀这样一句话去命名变量——&amp;lt;u&amp;gt;“优秀的代码命名都是自解释的”&amp;lt;/u&amp;gt;。&lt;/p&gt;
&lt;p&gt;仍是上述的例子：&lt;code&gt;判断是否为闰年&lt;/code&gt; -&lt;code&gt;is_leap_year()&lt;/code&gt;，(关于为何不用中文拼音命名而采用英语命名，这是由语言自身和历史传统影响的，这里不做阐释)，我们能否将其改为&lt;code&gt;is_leap()&lt;/code&gt;呢？不能。&lt;code&gt;leap&lt;/code&gt;还有跳跃之意，若是如此命名，必会造成歧义；而一个&lt;strong&gt;好的变量或函数名应准确表达含义&lt;/strong&gt;，便于阅读与理解。&lt;/p&gt;
&lt;h3&gt;变量名命名的注意&lt;/h3&gt;
&lt;p&gt;但是，准确表达含义并不是要求名字严格按照英文使其变得&lt;strong&gt;特别长&lt;/strong&gt;。举个例子，我们定义一个变量&lt;code&gt;unsigned int the_max_country_number_of_the_olympic_team = 20&lt;/code&gt;；如此定义变量名，我们一定会理解这样的变量是何种意思，但我们不难发现这样的问题：太长了！长到难以使用了！所以说，长度问题也是变量名的一个重要问题；如何&lt;strong&gt;对变量名的长度和完整性进行取舍&lt;/strong&gt;，这是我们亟需掌握的能力。&lt;/p&gt;
&lt;p&gt;上述论断中，我们认识到一个优秀的变量名可以使人“一眼就可以看出这是在干什么”，也就是说&lt;strong&gt;给予人最最直观的体验&lt;/strong&gt;。所以说，一些变量名往往&lt;strong&gt;以动词开头&lt;/strong&gt;，如：&lt;code&gt;double  checkTotal(User user){return 0.00;}&lt;/code&gt;；但是一些诸如&lt;code&gt;double CHECK(User user){return 0.00}&lt;/code&gt;、&lt;code&gt;double checks(User user){return 0.00}&lt;/code&gt;、&lt;code&gt;double havemoney(){}&lt;/code&gt;是不合常识与规范的。&lt;/p&gt;
&lt;p&gt;这里，我们写一些好的&lt;strong&gt;变量名命名&lt;/strong&gt;(&amp;lt;u&amp;gt;驼峰命名法&amp;lt;/u&amp;gt;)。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 关于速度
double trainVelocity = 310.25;  // 高铁运行时速(km/h)
double velocityInMph = ;  //
// 关于日期
Date currentDate = ;
// 一些行号
int lines;
int linesPerPage;  // 每一页的行数
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;一个小结：我们的首要认识——变量名命名十分重要，乃至直接决定着我们代码的质量、性能；甚至如果我们的变量命名是有问题的，那么我们的项目到最后总是难以重构与维护。可谓说问题之大了。&lt;/p&gt;
&lt;p&gt;我们不妨看看一些著名的项目如Linux的源码，向大师级别的人物学习。&lt;/p&gt;
&lt;h2&gt;二、更为恰当地命名&lt;/h2&gt;
&lt;p&gt;我们肯定会有这样的&lt;strong&gt;疑问&lt;/strong&gt;：使用像英语这样的语言，我们所要表达一个相同的意思常常会有不同的表述（比如说：表示钱💴，可能有人表示为&lt;code&gt;double wallet = 100.00&lt;/code&gt;，而有些人表示为诸如 &lt;code&gt;money&lt;/code&gt; &lt;code&gt;balance&lt;/code&gt;等其他名称）。&lt;em&gt;所以说，这种命名方式是否为规范或恰当呢？或者说我们是否有更好的命名方式呢？而当我们面对上述这样不同单词表达相同意思的情况，又该如何选择呢？&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;这里引入这样的”暴论“——一个好的名字应有&lt;strong&gt;2个意思&lt;/strong&gt;：&amp;lt;u&amp;gt;1. 表达了什么&amp;lt;/u&amp;gt;(如：&lt;code&gt;double balance; //表达的就是(用户/账户的)余额&lt;/code&gt;)；&amp;lt;u&amp;gt;2. 单词的立意&amp;lt;/u&amp;gt;(上述&lt;code&gt;balance&lt;/code&gt;具有多个意思:平衡、保持平横、抵消……)，单词的立意本质上说，它就是与前面的类型等因素相称，不造成歧义即仅表现出一种明确的意思。当一个变量名具备这两种性质，那么就会知道它所要表达的本意。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：关于变量，我们所要表达的东西一定不能是怎样的；如果是，那么问题出现&amp;lt;u&amp;gt;，因为变量根本不是“怎样的”，而是表达一个”什么“&amp;lt;/u&amp;gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 日常生活用语
studentData;
// input计算机界的学术语==》是更凸显IT界特指的变量
input_stu_data;

inputRec;

double sum;  
// sum的意思？虽然名词词性上是金额总数；但实际上它是属于一种计算机术语还是其他的呢？我们的立意并不清晰
// 假设这个sum变量定义在财务报告系统，那么这个sum是什么？是求和函数还是要干什么？所以会产生歧义，会引起混淆
// 因此为了准确表达总额的含义，我们不妨可以这样定义：double calcValue;这样就会很清晰的知道这是一个值、一个计算总值
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;所以，我们还是回到上述的2个意思：&amp;lt;u&amp;gt;1. 表达了什么&amp;lt;/u&amp;gt;；&amp;lt;u&amp;gt;2. 单词的立意&amp;lt;/u&amp;gt;。单词有各种各样的意思，在学术界它是怎样？它是否会有歧义？这是我们所要在乎与考虑的。&lt;/p&gt;
&lt;h2&gt;三、讲究长度的变量&lt;/h2&gt;
&lt;p&gt;大家都知道像&lt;code&gt;unsigned int the_max_country_number_of_the_olympic_team = 20&lt;/code&gt;；这样的变量名长度一定是有问题的。我们&lt;strong&gt;参考以下论文：&lt;a href=&quot;https://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&amp;amp;arnumber=44385&quot;&gt;Debugging effort estimation using software metrics&lt;/a&gt;和这个网站&lt;a href=&quot;https://ifsq.org/indicator-SP-5.html&quot;&gt;IFSQ:SP-5—Poor Choice of Name&lt;/a&gt;的概述&lt;/strong&gt;。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Research Findings:&lt;/strong&gt; The effort required to &amp;lt;u&amp;gt;debug a program is minimized&amp;lt;/u&amp;gt; when variables had names &amp;lt;u&amp;gt;averaging 10 to 16 characters long&amp;lt;/u&amp;gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;实际情况，在国内的企业命名长度要求大多为：9-15、10-16或&lt;strong&gt;8-20&lt;/strong&gt;，还有些是8-16个。我们平常选取最大范围8-20的长度，可以说是问题不大。&lt;/p&gt;
&lt;p&gt;这里，我们还可&lt;strong&gt;以参阅这篇文章博客&lt;a href=&quot;https://willkoehrsen.github.io/data%20science/software%20engineering/data-scientists-your-variable-names-are-awful-heres-how-to-fix-them/&quot;&gt;Data Scientists Your Variable Names Are Awful Heres How To Fix Them&lt;/a&gt;的论述&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;于是，我们对上述极长的代码修改为&lt;code&gt;numTeamMembers&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;注意，变量名往往和其所处的作用域来进一步补充完备意思，所以这里虽不呈现奥林匹克之意，但它应该仅作用于奥林匹克的命名空间之下。这是我们之后要谈的。&lt;/p&gt;
&lt;h2&gt;四、变量作用域与长度及规范&lt;/h2&gt;
&lt;p&gt;以&lt;code&gt;for&lt;/code&gt;语句为例&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  // 变量作用域

  // 函数（OOP 方法） 类
 for(int i = length - 1;i &amp;gt;= 0;i --)
 {
     for(int j = 0;j &amp;lt; i;j ++)
     {
        
     }
 }
  // 我们会发现i和k的作用域或者说适用范围只在for语句中，这时候准确说来并无问题。
  // 这时变量范围被局限住了
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;  // 但是
 int i;
 for(i = length - 1;i &amp;gt;= 0;i --)
 {
     for(int j = 0;j &amp;lt; i;j ++)
     {
        
     }
 }

 i ++;
 i --;
 // 若i超出for的作用范围，这时我们就不能定义为i，因为没人知道这个i是啥；
 // 当我们定义在for循环内我们都知道这是计数器，但脱离for之后，我们的i++,i--又表示什么含义呢？
 // 不得而知，所以不能这样命名。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;因此，我们会发现，&amp;lt;u&amp;gt;变量名命名在什么地方会决定变量名该如何命名，函数（OOP 方法）、类、作用域都会决定你如何去命名变量&amp;lt;/u&amp;gt;。&lt;/p&gt;
&lt;p&gt;这里，我们或可以参阅这本书&lt;a href=&quot;https://www.amazon.com/exec/obidos/ASIN/0876268165/acmorg-20&quot;&gt;Software psychology: Human factors in computer and information systems&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;结果简要概述&lt;/strong&gt;：特别短的名字(&amp;lt;8)我们常定义为局部变量，在for循环内可能会更短；它的作用域可能会决定它的长和短：&lt;strong&gt;当一个变量名越短，可能它的作用域就越小&lt;/strong&gt;，甚至小到i,k这样的单字符。&lt;/p&gt;
&lt;p&gt;但一些&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E9%98%B2%E5%BE%A1%E6%80%A7%E7%BC%96%E7%A8%8B&quot;&gt;防御性编程&lt;/a&gt;会可能刻意绕开这些短的变量名。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;变量名可能一样&lt;/strong&gt;，我们会使用命名空间等所处的域不同来进行划分以&lt;strong&gt;区分变量&lt;/strong&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 1. wallet
package com.goolgle.user;
// mysql sql引入要正确
public class Wallet {
    
}
// 2. wallet  
package com.goolgle.dog;
public class Wallet {
    
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;更为学术的说，我们称作这为：&lt;strong&gt;划分全局&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4#&quot;&gt;命名空间&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;namespace User {
    // Wallet
}

namespace Dog {
    // Wallet
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;对于一些不支持上述&lt;strong&gt;划分全局命名空间&lt;/strong&gt;的操作的语言如：&lt;code&gt;C&lt;/code&gt;语言。我们可以:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;design_Emp
dev_Emp
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;而对于可以使用&lt;strong&gt;package&lt;/strong&gt;的&lt;code&gt;Java&lt;/code&gt;语言，则更为简便:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;package com.goolgle.design;
// mysql sql引入要正确
public class Employee {
    
}

package com.goolgle.dev;
public class Employee {
    
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;五、细化:关于变量计数词&lt;/h2&gt;
&lt;p&gt;我们首先要明确：所要讨论的计数词&lt;strong&gt;是什么&lt;/strong&gt;——这个计数不是循环中的&lt;code&gt;i&lt;/code&gt;、&lt;code&gt;j&lt;/code&gt; 、&lt;code&gt;k&lt;/code&gt;，而是我们经常所用的计数，如：总共的人数、总分数和平均数、最大及最小值等各种各样的东西。我们在设计这些&lt;strong&gt;涉及到计算的意思的变量名&lt;/strong&gt;，我们该怎样比较确恰地命名，这是此段所要探讨的。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
int main(void)
{
    // sum，avg/average，max，min，total……
    // num
    // 这些单词的表述都很好，但我们要注意一些点 
    
    // 举例：员工总数
    // 不建议使用sum，更建议total(总计)
    // int total_emp; // 或直接写全:total_employees

    // 为什么不用num呢？歧义的风险
    // int num_employees; // =》员工数量
    // int employee_num; // =》员工号？
    // 我们将其倒错会产生截然不同的意思
    // 所以，以上命名都不合理
    // 因此，我们可以对其进行规定约束：基本上，将计数词放在后面，除非特殊要求
 // 我们这样写：名词提前-计数词放后
    // 注意：这是为了好看的一种约束，这不是死板的。
    // 对错并无分别，取决于企业的规范
    int employee_total;
   // 若要表达员工号，也是尽可能不用num
    int employee_index;
    // 我们把计数单词放在前面，就意味者它的中心义或内涵
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;结论：常进行一种约束：&lt;strong&gt;&amp;lt;u&amp;gt;名词提前-计数词放后&amp;lt;/u&amp;gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;六、细化:对立词选择建议&lt;/h2&gt;
&lt;p&gt;在计算机术语中，常有一些有意思的词，他们总是对立的，然后这些对立的词常相匹配组成一些完整的变量。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
int main(void)
{
    up/down
    old/new
 first/last
    min/max
    opened/closed
    locked/unlocked
    ...
    begin/end
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;七、细化:常量、宏与指针&lt;/h2&gt;
&lt;p&gt;建议直接参考伟大的项目&lt;a href=&quot;https://github.com/torvalds/linux&quot;&gt;linux&lt;/a&gt;的源码。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#define NUM_FIVE 5  // × 表示具体数字错误也就是说不能出现FIVE 可以改为数组的容量

// C++的常量：Google常声明常量以k开头
// constant
const int kDaysInYear = 100;
// 题外话：匈牙利命名法
int *pSize;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这只是一个&lt;code&gt;约定&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;八、细化:循环计数器i,j,k&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
int main(void)
{
    for(int i = 0;i &amp;lt; totalEmp.count;i++)
    {
        data[i] = j;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;从编程艺术来考虑，循环当中使用&lt;code&gt;i,j,k&lt;/code&gt;是否规范？实际情况是&lt;strong&gt;允许&lt;/strong&gt;的（&lt;strong&gt;前提是&lt;/strong&gt;必须定义&lt;strong&gt;在循环里面&lt;/strong&gt;，即&lt;strong&gt;作用域的界定范围在循环中&lt;/strong&gt;，不会污染到其他东西）。例：&lt;code&gt;JavaScript&lt;/code&gt;这门语言若定义为全局，它就会造成&lt;strong&gt;变量的污染&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;但是，如果&lt;code&gt;i&lt;/code&gt;定义在&lt;strong&gt;循环之外&lt;/strong&gt;，这么写是&lt;strong&gt;存在问题&lt;/strong&gt;的，因为这样的&lt;code&gt;i&lt;/code&gt;很可能被循环之后的代码运用到，那此时这个&lt;code&gt;i&lt;/code&gt;是什么数据？它是&lt;strong&gt;垃圾数据&lt;/strong&gt;，它并没有得到有效的管理。&lt;/p&gt;
&lt;p&gt;如果我们的&lt;code&gt;i&lt;/code&gt;不仅在循环内用到，还在其外用到，那么它就不能定义为&lt;code&gt;i&lt;/code&gt;，而应定义为一个&lt;strong&gt;更具体的名称&lt;/strong&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
int main(void)
{
    int record_count = 0;
    
    while ( condition ) {
        record_count ++;
    }
    
    record_count ++;
    cout &amp;lt;&amp;lt; record_count &amp;lt;&amp;lt; endl;
    
    return 0；
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;还有一种情况&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
int main(void)
{
    for(int i = 0;i &amp;lt; 9;i ++) {
        for(int j = 0;j &amp;lt; i;j ++) {
            // TODO
        }
    }
    // 这样写，并不是很好
    // 如果for循环多且嵌套或数组二维或更高维，那就不要再i,j,k了
    // 因为：我们很可能会不知道谁是谁了
    // 而要规避掉这样的风险，或者说更好一些，只需要对变量名规范命名就可以了；这样在逻辑上，我们可以直接清晰的明白所循环的是什么

    return 0；
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;需要&lt;strong&gt;指明&lt;/strong&gt;：项目工程的开发与维护不可能仅一个人搞，团队协作是常态，因此好的变量命名会带给其他人对你代码直观的理解，别人才更易接手这样的项目。&lt;/p&gt;
&lt;h2&gt;九、细化:枚举命名的讲究&lt;/h2&gt;
&lt;p&gt;给人的感觉与常量相似，因为也是大写。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
int main(void)
{
    enum AlternateurlTableErrors {
        OK = 0,
        OUT_OF_MEMORY = 1,
        MALFORMED_INPUT = 2
    };
    return 0；
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;也有些语言、企业会规定加前缀等东西。&lt;/p&gt;
&lt;h2&gt;十、细化:临时变量的讲究&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
int main(void)
{
    // 如果说后面还会用到temp这个变量，那么最好不要定义为temp
    // 它不像在for循环中的，定义作用域在内
    for(int i = 0;i &amp;lt; 19;i ++) {
        int temp = array[i];
        ..
        ...
    }
    // 但是如果后面还要被使用，那就不要定义为temp
    // 比如求三角形周长
    // int temp = a + b + c; ×错 有些随意 缺乏描述
    int triangle_perimeter = a + b + c;
    
    return 0；
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>HTTP协议初探</title><link>https://blog.chaos-ljc.top/posts/http-protocol-intro/</link><guid isPermaLink="true">https://blog.chaos-ljc.top/posts/http-protocol-intro/</guid><description>HTTP概念、发展与应用</description><pubDate>Sun, 26 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;hr /&gt;
&lt;h2&gt;初识HTTP&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Before Reading&lt;/strong&gt;  :  &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Overview&quot;&gt;An overview of HTTP&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;背景知识&lt;/h3&gt;
&lt;p&gt;Q: 从浏览器的地址栏输入URL()按下回车到页面展示出来,发生了什么?
A: 简而言之,包括 ==URL 解析、DNS 解析、TCP 连接建立、HTTP 请求发送、服务器处理请求、&lt;strong&gt;HTTP 响应发送&lt;/strong&gt;、浏览器接收响应、浏览器渲染页面、加载资源和 JavaScript 执行==。其中每个步骤又包含多个子步骤和复杂的处理逻辑.&lt;/p&gt;
&lt;p&gt;先回想一下&lt;strong&gt;TCP/IP 协议栈&lt;/strong&gt;(&lt;strong&gt;应用层&lt;/strong&gt;;&lt;strong&gt;传输层&lt;/strong&gt;;&lt;strong&gt;网络层&lt;/strong&gt;;&lt;strong&gt;网络接口层&lt;/strong&gt;),在这里,我们呢主要关注最上层的应用层中HTTP相关内容.&lt;/p&gt;
&lt;h3&gt;什么是HTTP?&lt;/h3&gt;
&lt;p&gt;HTTP（HyperText Transfer Protocol，超文本传输协议）是互联网上应用最广泛的一种网络协议。它是客户端和服务器之间进行通信的基础协议，用于传输超文本（如 HTML 文档）和其他资源（如图像、视频、JSON 数据等）。&lt;/p&gt;
&lt;h4&gt;概念&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;超文本传输协议&lt;/strong&gt;：HTTP 是一种应用层协议，用于在客户端和服务器之间传输超文本（如 HTML 文档）和其他资源。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;基于 TCP 协议&lt;/strong&gt;：HTTP 建立在 TCP/IP 协议栈之上，使用 TCP 协议进行数据传输。默认情况下，HTTP 使用 80 端口，HTTPS 使用 443 端口。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;请求-响应模型&lt;/strong&gt;：HTTP 采用请求-响应模型，客户端发送 HTTP 请求，服务器返回 HTTP 响应。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;简单可扩展&lt;/strong&gt;：HTTP 协议设计简单，易于实现和扩展。它支持多种请求方法（如 GET、POST、PUT、DELETE 等）和头部字段，可以根据需要进行扩展。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;无状态&lt;/strong&gt;：HTTP 协议是无状态的，即服务器不会保存客户端的请求状态。每个请求都是独立的，服务器不会记住之前的请求。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;特点&lt;/h4&gt;
&lt;h5&gt;1. 请求-响应模型&lt;/h5&gt;
&lt;p&gt;HTTP 采用请求-响应模型，客户端发送 HTTP 请求，服务器返回 HTTP 响应。请求和响应都包含以下几个部分：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;请求行/状态行&lt;/strong&gt;：包含请求方法（如 GET、POST）、请求路径（如 &lt;code&gt;/index.html&lt;/code&gt;）和 HTTP 协议版本（如 HTTP/1.1）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;请求头/响应头&lt;/strong&gt;：包含各种头部字段，如 &lt;code&gt;User-Agent&lt;/code&gt;、&lt;code&gt;Accept&lt;/code&gt;、&lt;code&gt;Content-Type&lt;/code&gt;、&lt;code&gt;Content-Length&lt;/code&gt; 等。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;请求体/响应体&lt;/strong&gt;：包含请求或响应的数据，通常是 HTML、JSON、XML 等格式的数据。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;2. 简单可扩展&lt;/h5&gt;
&lt;p&gt;HTTP 协议设计简单，易于实现和扩展。它支持多种请求方法（如 GET、POST、PUT、DELETE 等）和头部字段，可以根据需要进行扩展。例如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;请求方法&lt;/strong&gt;：HTTP 定义了多种请求方法，如 GET（获取资源）、POST（提交数据）、PUT（更新资源）、DELETE（删除资源）等。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;头部字段&lt;/strong&gt;：HTTP 支持多种头部字段，如 &lt;code&gt;User-Agent&lt;/code&gt;（客户端标识）、&lt;code&gt;Accept&lt;/code&gt;（客户端接受的媒体类型）、&lt;code&gt;Content-Type&lt;/code&gt;（请求或响应的数据类型）等。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;3. 无状态&lt;/h5&gt;
&lt;p&gt;HTTP 协议是无状态的，即服务器不会保存客户端的请求状态。&lt;strong&gt;每个请求都是独立的，服务器不会记住之前的请求&lt;/strong&gt;。这种设计简化了服务器的设计和实现，但也带来了一些问题，如需要通过其他方式（如 Cookie、Session）来维护客户端状态。&lt;/p&gt;
&lt;h5&gt;4. 基于 TCP 协议&lt;/h5&gt;
&lt;p&gt;HTTP 建立在 TCP/IP 协议栈之上，使用 TCP 协议进行数据传输。TCP 协议提供了可靠的、面向连接的通信服务，确保数据在网络中的可靠传输。&lt;/p&gt;
&lt;h5&gt;5. 支持多种资源类型&lt;/h5&gt;
&lt;p&gt;HTTP 不仅用于传输超文本（如 HTML 文档），还支持传输多种资源类型，如图像、视频、音频、JSON 数据、XML 数据等。通过 &lt;code&gt;Content-Type&lt;/code&gt; 头部字段，服务器可以指定响应数据的类型，客户端可以根据类型进行处理。&lt;/p&gt;
&lt;h3&gt;协议分析&lt;/h3&gt;
&lt;h4&gt;发展&lt;/h4&gt;
&lt;h5&gt;HTTP/0.9 (1991) - The One Liner&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;特点&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;最早的 HTTP 版本，非常简单。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;只支持 GET 请求方法。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;没有请求头和响应头，只有请求行和响应体。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;响应体只能是 HTML 文档。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;*&lt;strong&gt;示例&lt;/strong&gt;:&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;请求&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;Get /index.html
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;响应&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;(response body)
(connection closed)
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;HTTP/1.0（1996）&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;特点&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;引入了请求头和响应头，支持多种请求方法（如 GET、&lt;strong&gt;POST、HEAD&lt;/strong&gt;）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;添加了&lt;strong&gt;状态码(Status code)&lt;/strong&gt; 以标识响应&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;支持多种资源类型（如 HTML、&lt;strong&gt;图像、视频、纯文本等&lt;/strong&gt;）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;每个请求都需要建立一个新的 TCP 连接，请求完成后立即关闭连接。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;引入了字符集支持, 包括多部分类型、授权、缓存、内容编码等&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;:&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;请求
如下所示:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;除了请求,&lt;/li&gt;
&lt;li&gt;客户端还发送了它的个人信息,&lt;/li&gt;
&lt;li&gt;要求的响应类型&lt;/li&gt;
&lt;li&gt;等等等等&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;GET / HTTP/1.0
Host: cs.fyi
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5)
Accept: */*
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;响应
如下所示:
&lt;ul&gt;
&lt;li&gt;响应的开头是吧友版本号的HTTP,接着一个状态码和一个描述它的词或原因&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;HTTP/1.0 200 OK 
Content-Type: text/html
Content-Length: 137582
Expires: Thu, 05 Dec 1997 16:00:00 GMT
Last-Modified: Wed, 5 August 1996 15:55:28 GMT
Server: Apache 0.84

(response body)
(connection closed)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在这一版本,请求和响应仍然保持着ASCII编码, 但是响应体课包含图片,视频,纯文本和其他内容类型, 比起&quot;Hyper Text&quot;, HMTP(Hypermedia transfer protocol)或许是更恰切的描述,不过...&lt;/p&gt;
&lt;p&gt;HTTP/1.0的一个&lt;strong&gt;主要缺点&lt;/strong&gt;是不能为每此连接提供多个请求。也就是说，无论何时客户机需要从服务器获得什么东西，它都必须打开一个新的 TCP 连接，在这个单个请求完成之后，连接将被关闭。对于任何下一个需求，它必须在一个新的连接上。
So,Why is it &lt;strong&gt;BAD&lt;/strong&gt;?
让我们假设您访问了一个包含10个图像、5个样式表和5个 javascript 文件的网页，当请求该网页时，总共需要获取20个项目。由于服务器在请求完成后立即关闭连接，因此将有一系列20个独立的连接，其中每个项目将在其各自的连接上逐个提供服务。这种大量的连接会导致严重的性能损失，因为需要一个新的 TCP 连接会造成严重的性能损失，这是由于&lt;strong&gt;三路握手&lt;/strong&gt;后缓慢启动造成的&lt;/p&gt;
&lt;h6&gt;Three-way Handshake&lt;/h6&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;客户端发送 SYN 包&lt;/strong&gt;：客户端向服务器发送一个 SYN（同步）包(由 客户机拾取一个随机数假设为&lt;code&gt;x&lt;/code&gt; 组成)，请求建立连接。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;服务器发送 SYN-ACK 包&lt;/strong&gt;：服务器收到 SYN 包后，向客户端发送一个 SYN-ACK（同步-确认）包(由 服务器拾取的 &lt;code&gt;y&lt;/code&gt; 和数字 &lt;code&gt;x+1&lt;/code&gt;，其中 &lt;code&gt;x&lt;/code&gt; 是客户端发送的数字 组成)，表示同意建立连接。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;客户端发送 ACK 包&lt;/strong&gt;：客户端收到 SYN-ACK 包后，向服务器发送一个 ACK（确认）包(由 &lt;code&gt;y+1&lt;/code&gt; 组成)，表示连接已建立。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;三次握手完成后，客户端和服务器之间的数据共享就可以开始了。值得注意的是，客户端可以在调度最后一个 ACK 数据包后立即开始发送应用程序数据，但服务器仍然必须等待收到 ACK 数据包才能完成请求。&lt;/p&gt;
&lt;p&gt;当然, 严重的性能损耗, 不仅仅是由于 TCP 连接的建立过程（三次握手）造成的，还包括 TCP 连接的关闭、连接的重复建立和关闭、并发请求的限制以及缺乏缓存机制等因素。&lt;/p&gt;
&lt;h5&gt;HTTP/1.1（1997）&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;特点&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;引入了持久连接（Keep-Alive）&lt;/strong&gt;，允许多个请求和响应复用同一个 TCP 连接，减少了连接建立和关闭的开销。要关闭连接，请求上必须有标题 Connection: close。客户端通常在最后一个请求中发送此标头以安全地关闭连接。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;支持管道化（Pipelining）&lt;/strong&gt;，允许客户端在一个 TCP 连接上发送多个请求，服务器按顺序返回响应。那么,客户机如何知道这是第一个响应下载完成和下一个响应的内容开始的地方? 要解决这个问题，必须有 Content-Length 头，客户端可以使用它来确定响应的结束位置，并且可以开始等待下一个响应。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It should be noted that in order to benefit from persistent connections or pipelining, Content-Length header must be available on the response, because this would let the client know when the transmission completes and it can send the next request (in normal sequential way of sending requests) or start waiting for the the next response (when pipelining is enabled).&lt;/p&gt;
&lt;p&gt;But there was still an issue with this approach. And that is, what if the data is dynamic and server cannot find the content length before hand? Well in that case, you really can’t benefit from persistent connections, could you?! In order to solve this HTTP/1.1 introduced chunked encoding. In such cases server may omit content-Length in favor of chunked encoding (more to it in a moment). However, if none of them are available, then the connection must be closed at the end of request.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;分块传输(Chunked Transfers)&lt;/strong&gt; ,  在动态内容的情况下，当服务器在传输开始时无法真正找到 Content-Length 时，它可能会开始分块发送内容（逐块），并在发送时为每个块添加 Content-Length。当所有 chunk 都发送完毕时，即整个传输已完成，它会发送一个空 chunk，即 Content-Length 设置为零的 chunk，以识别传输已完成的客户端。为了通知客户端有关分块传输的信息，服务器包含标头 Transfer-Encoding： chunked&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;引入了缓存机制（如 &lt;code&gt;Cache-Control&lt;/code&gt;、&lt;code&gt;ETag&lt;/code&gt;），提高了性能。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;支持虚拟主机（Virtual Hosts），允许多个域名共享同一个 IP 地址。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;引入了更多的请求方法（如 PUT、DELETE、OPTIONS、PATCH）和状态码。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;客户端 Cookie&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;增强的压缩支持&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;......&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;HTTP/2（2015）&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;特点&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;基于 Google 的 SPDY 协议，改进了性能和安全性。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;引入了&lt;strong&gt;多路复用（Multiplexing）&lt;/strong&gt;，允许多个请求和响应在同一个 TCP 连接上并行传输，解决了 HTTP/1.1 的队头阻塞问题, 即客户端不必等待需要时间的请求，而其他请求仍将得到处理。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;引入了&lt;strong&gt;头部压缩（Header Compression）&lt;/strong&gt;，减少了传输的数据量。
它的本质是，当我们不断从同一个客户端访问服务器时，我们会一遍又一遍地在标头中发送大量冗余数据，有时可能会有 cookie 增加标头大小，从而导致带宽使用和延迟增加&lt;/p&gt;
&lt;p&gt;与请求和响应不同，标头不是以 gzip 或 compress 等格式压缩的，但有一种不同的标头压缩机制，即使用霍夫曼代码对文本值进行编码，并且标头表由客户端和服务器维护，客户端和服务器在后续请求中省略任何重复的标头（例如用户代理等），并使用由两者维护的标头表引用它们。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;补充: 当我们谈论 Headers 时，Headers 仍然与 HTTP/1.1 中相同，除了添加了一些伪 Headers，即 &lt;code&gt;：method&lt;/code&gt;、&lt;code&gt;：scheme&lt;/code&gt;、&lt;code&gt;：host&lt;/code&gt; 和 &lt;code&gt;:p ath&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;支持&lt;strong&gt;服务器推送（Server Push）&lt;/strong&gt;，服务器可以在客户端请求之前主动推送资源，减少了客户端的等待时间。
例如，假设浏览器加载了一个网页，它会解析整个页面以找出它必须从服务器加载的远程内容，然后向服务器发送后续请求以获取该内容&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;服务器推送允许服务器通过推送它知道客户端将需要的数据来减少往返。它是如何完成的呢? 服务器发送一个名为 PUSH_PROMISE 的特殊帧通知客户端，“嘿，我即将将此资源发送给您！不要向我要。” PUSH_PROMISE 帧与导致推送发生的流相关联，并且它包含承诺的流 ID，即服务器将发送要推送的资源的流。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;使用&lt;strong&gt;二进制帧（Binary Framing）&lt;/strong&gt; 代替了文本格式，提高了解析效率。
作为二进制协议，它更容易解析，但与 HTTP/1.x 不同的是，它不再能被人眼读取。HTTP/2 的主要构建块是帧和流&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;HTTP 消息现在由一个或多个帧组成。元数据有一个 HEADERS 帧，有效负载有一个  DATA 帧，并且存在几种其他类型的帧（HEADERS、DATA、RST_STREAM、SETTINGS、PRIORITY 等），您可以通过 [HTTP/2 规范](https://http2.github.io/http2-spec/#FrameTypes)进行检查。
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;每个 HTTP/2 请求和响应都有一个唯一的流 ID，并被划分为帧。帧只不过是二进制数据片段。帧的集合称为 Stream。每个帧都有一个 stream id，用于标识它所属的流，并且每个帧都有一个通用的标头。此外，除了 stream ID 唯一之外，值得一提的是，客户端发起的任何请求都使用奇数，而来自服务器的响应具有偶数 stream ID。&lt;/p&gt;
&lt;p&gt;除了 HEADERS 和 DATA 之外，这里值得一提的另一个帧类型是 RST_STREAM。这是一种特殊的帧类型，用于中止某些流，即客户端可以发送此帧以让服务器知道我不再需要此流。在 HTTP/1.1 中，让服务器停止向客户端发送响应的唯一方法是关闭连接，这会导致延迟增加，因为必须为任何连续请求打开新连接。在 HTTP/2 中，客户端可以使用 RST_STREAM 并停止接收特定流，而连接仍将打开，而其他流仍将处于活动状态。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;请求优先级(Request Prioritization)&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;客户端可以通过在打开流的 HEADERS 帧中包含优先级信息来为流分配优先级。在任何其他时间，客户端都可以发送 PRIORITY 帧来更改流的优先级。&lt;/p&gt;
&lt;p&gt;在没有任何优先级信息的情况下，server 异步处理请求，即没有任何顺序。如果为流分配了优先级，则根据此优先级信息，server 决定需要提供多少资源来处理哪个请求。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;HTTP/2 使用二进制帧传输数据，无法直接查看文本格式。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;HTTP/3（2020）&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;特点&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;基于 Google 的 QUIC 协议，改进了性能和安全性。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;使用 UDP 协议代替 TCP 协议，减少了连接建立和拥塞控制的开销。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;引入了多路复用（Multiplexing），允许多个请求和响应在同一个 UDP 连接上并行传输，解决了 TCP 的队头阻塞问题。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;引入了头部压缩（QPACK），减少了传输的数据量。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;支持 0-RTT（Zero Round Trip Time）连接建立，减少了连接建立的时间。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;使用 TLS 1.3 进行加密，提高了安全性。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;HTTP/3 使用 QUIC 协议传输数据，无法直接查看文本格式。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h6&gt;总结&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;HTTP/0.9&lt;/strong&gt;：最早的版本，非常简单，只支持 GET 请求。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;HTTP/1.0&lt;/strong&gt;：引入了请求头和响应头，支持多种请求方法和资源类型。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;HTTP/1.1&lt;/strong&gt;：引入了持久连接、管道化、缓存机制、虚拟主机等，提高了性能和功能。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;HTTP/2&lt;/strong&gt;：基于 SPDY 协议，引入了多路复用、头部压缩、服务器推送等，进一步提高了性能。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;HTTP/3&lt;/strong&gt;：基于 QUIC 协议，使用 UDP 代替 TCP，引入了多路复用、头部压缩、0-RTT 连接建立等，进一步提高了性能和安全性。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;报文&lt;/h4&gt;
&lt;p&gt;以 ==HTTP/1.1== 为例
&lt;img src=&quot;https://cdn.jsdelivr.net/gh/lvjianchaos/Images/note/HTTP/HTTP_1.1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h5&gt;请求方法 Method&lt;/h5&gt;
&lt;h6&gt;1. GET&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;用途&lt;/strong&gt;：获取资源。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;特点&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;请求参数通过 URL 传递，通常用于获取数据。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;请求是幂等的，即多次请求不会改变服务器状态。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;请求体为空。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;GET /index.html HTTP/1.1
Host: www.example.com
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;2. POST&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;用途&lt;/strong&gt;：提交数据，通常用于创建资源。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;特点&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;请求参数通过请求体传递，通常用于提交表单数据或上传文件。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;请求不是幂等的，即多次请求可能会改变服务器状态。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;请求体可以包含数据。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;POST /submit HTTP/1.1
Host: www.example.com
Content-Type: application/x-www-form-urlencoded

name=John&amp;amp;age=30
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;3. PUT&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;用途&lt;/strong&gt;：更新资源，通常用于替换现有资源。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;特点&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;请求参数通过请求体传递，通常用于更新资源。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;请求是幂等的，即多次请求不会改变服务器状态。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;请求体可以包含数据。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;PUT /user/123 HTTP/1.1
Host: www.example.com
Content-Type: application/json
{
  &quot;name&quot;: &quot;John&quot;,
  &quot;age&quot;: 30
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;4. DELETE&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;用途&lt;/strong&gt;：删除资源。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;特点&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;请求参数通过 URL 传递，通常用于删除资源。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;请求是幂等的，即多次请求不会改变服务器状态。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;请求体为空。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;DELETE /user/123 HTTP/1.1
Host: www.example.com
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;5. HEAD&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;用途&lt;/strong&gt;：获取资源的元数据，不返回响应体。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;特点&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;请求参数通过 URL 传递，通常用于获取资源的元数据（如响应头）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;请求是幂等的，即多次请求不会改变服务器状态。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;响应体为空。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;HEAD /index.html HTTP/1.1
Host: www.example.com
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;6. OPTIONS&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;用途&lt;/strong&gt;：获取服务器支持的请求方法和功能。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;特点&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;请求参数通过 URL 传递，通常用于获取服务器支持的请求方法和功能。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;请求是幂等的，即多次请求不会改变服务器状态。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;响应体通常包含服务器支持的请求方法和功能。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;OPTIONS /user HTTP/1.1
Host: www.example.com
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;7. PATCH&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;用途&lt;/strong&gt;：部分更新资源。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;特点&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;请求参数通过请求体传递，通常用于部分更新资源。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;请求不是幂等的，即多次请求可能会改变服务器状态。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;请求体可以包含数据。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;PATCH /user/123 HTTP/1.1
Host: www.example.com
Content-Type: application/json
{
  &quot;age&quot;: 31
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;8. CONNECT&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;用途&lt;/strong&gt;：建立隧道，通常用于代理服务器。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;特点&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;请求参数通过 URL 传递，通常用于建立隧道。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;请求不是幂等的，即多次请求可能会改变服务器状态。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;请求体为空。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;CONNECT www.example.com:443 HTTP/1.1
Host: www.example.com
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;9. TRACE&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;用途&lt;/strong&gt;：回显请求，用于调试。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;特点&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;请求参数通过 URL 传递，通常用于调试。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;请求是幂等的，即多次请求不会改变服务器状态。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;响应体包含请求的原始数据。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;TRACE /index.html HTTP/1.1
Host: www.example.com
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;==总结==&lt;/h6&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;方法&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;用途&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;GET&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;获取资源&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;POST&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;提交数据，通常用于创建资源&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;PUT&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;更新资源，通常用于替换现有资源&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DELETE&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;删除资源&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;HEAD&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;获取资源的元数据，不返回响应体&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;OPTIONS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;获取服务器支持的请求方法和功能&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;PATCH&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;部分更新资源&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CONNECT&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;建立隧道，通常用于代理服务器&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;TRACE&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;回显请求，用于调试&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h6&gt;&lt;strong&gt;补充:&lt;/strong&gt;&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;Safe(安全的) : 不会修改服务器的数据的方法
GET HEAD OPTIONS&lt;/li&gt;
&lt;li&gt;Idempotent(幂等的) : 同样的请求被执行一次与连续执行多次的效果是==一样==的, 服务器的状态也是==一样==的, 所有的safe方法都是Idempotent的
GET HEAD OPTIONS DELETE&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;状态码 Status Code&lt;/h5&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Status Code&lt;/th&gt;
&lt;th&gt;解释&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;1xx&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;信息性状态码，表示请求已被接收，继续处理&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;2xx&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;成功状态码，表示请求已成功处理&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;3xx&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;重定向状态码，表示需要进一步操作才能完成请求&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;4xx&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;客户端错误状态码，表示客户端请求有误&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;5xx&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;服务器错误状态码，表示服务器处理请求时出错&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h6&gt;常见的例子&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;100 Continue&lt;/strong&gt;：服务器已接收到请求头，客户端应继续发送请求体&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;200 OK&lt;/strong&gt;：请求成功，服务器返回请求的资源&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;301 Moved Permanently&lt;/strong&gt;：请求的资源已永久移动到新位置，客户端应使用新的 URL&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;302 Found&lt;/strong&gt;：请求的资源临时移动到新位置，客户端应继续使用原 URL&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;401 Unauthorized&lt;/strong&gt;：请求需要身份验证，客户端未提供有效的身份验证信息&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;404 Not Found&lt;/strong&gt;：请求的资源不存在&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;500 Internal Server Error&lt;/strong&gt;：服务器内部错误，无法完成请求&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;502 Bad Gateway&lt;/strong&gt;：服务器作为网关或代理时，从上游服务器收到无效响应&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;504 Gateway Timeout&lt;/strong&gt;：服务器作为网关或代理时，等待上游服务器的响应超时&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;RESTful API&lt;/h5&gt;
&lt;p&gt;RESTful API（Representational State Transfer,表现层状态转化 API）是一种基于 HTTP 协议的 Web 服务设计风格它通过标准的 HTTP 方法（如 GET、POST、PUT、DELETE）来操作资源，并使用统一的接口来访问和操作这些资源。RESTful API 的设计理念是简单、轻量、易于扩展和维护。&lt;/p&gt;
&lt;h6&gt;核心概念&lt;/h6&gt;
&lt;ol&gt;
&lt;li&gt;资源（Resource）&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;资源&lt;/strong&gt;是 RESTful API 的核心概念，表示系统中的任何对象或数据。资源可以是用户、订单、产品、文章等。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;每个资源都有一个&lt;strong&gt;唯一的标识符（URI）&lt;/strong&gt;，用于在系统中唯一标识该资源。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;统一接口（Uniform Interface）&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;统一接口&lt;/strong&gt;是指 RESTful API 使用标准的 HTTP 方法来操作资源。常见的 HTTP 方法包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;GET&lt;/strong&gt;：获取资源。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;POST&lt;/strong&gt;：创建资源。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;PUT&lt;/strong&gt;：更新资源。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;DELETE&lt;/strong&gt;：删除资源。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;通过统一接口，客户端可以使用相同的接口来访问和操作不同的资源，简化了 API 的设计和使用。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;状态转移（State Transfer）&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;状态转移&lt;/strong&gt;是指客户端和服务器之间的交互过程中，资源的状态会发生变化。RESTful API &amp;lt;u&amp;gt;通过 HTTP 方法来实现状态转移&amp;lt;/u&amp;gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;例如，客户端发送一个 POST 请求来创建一个新资源，服务器接收到请求后，资源的状态从“不存在”变为“存在”。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;无状态（Stateless）&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;无状态&lt;/strong&gt;是指服务器不会保存客户端的请求状态。每个请求都是独立的，服务器不会记住之前的请求。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;这种设计简化了服务器的设计和实现，但也带来了一些问题，如需要通过其他方式（如 Cookie、Session）来维护客户端状态。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h6&gt;RESTful API 的设计原则&lt;/h6&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;使用名词表示资源&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;资源应该使用名词来表示，而不是动词。例如，使用 &lt;code&gt;/users&lt;/code&gt; 表示用户资源，而不是 &lt;code&gt;/getUsers&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;使用 HTTP 方法操作资源&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;使用标准的 HTTP 方法来操作资源：
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;GET&lt;/strong&gt;：获取资源。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;POST&lt;/strong&gt;：创建资源。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;PUT&lt;/strong&gt;：更新资源。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;DELETE&lt;/strong&gt;：删除资源。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;使用 URI 标识资源&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;每个资源都有一个唯一的 URI，用于在系统中唯一标识该资源。例如，&lt;code&gt;/users/123&lt;/code&gt; 表示 ID 为 123 的用户资源。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;使用 JSON 或 XML 表示资源&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;资源通常使用 JSON 或 XML 格式表示，便于客户端和服务器之间的数据交换。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;使用状态码表示请求结果&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;使用标准的 HTTP 状态码表示请求的结果，如 200 OK、404 Not Found、500 Internal Server Error 等。&lt;/li&gt;
&lt;/ul&gt;
&lt;h6&gt;示例&lt;/h6&gt;
&lt;p&gt;==用户管理==&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;获取所有用户&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;请求方法&lt;/strong&gt;：GET&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;URI&lt;/strong&gt;：&lt;code&gt;/users&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;GET /users HTTP/1.1
Host: api.example.com
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;响应&lt;/strong&gt;：&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;[
  {
 &quot;id&quot;: 1,
 &quot;name&quot;: &quot;Alice&quot;,
 &quot;email&quot;: &quot;alice@example.com&quot;
  },
  {
 &quot;id&quot;: 2,
 &quot;name&quot;: &quot;Bob&quot;,
 &quot;email&quot;: &quot;bob@example.com&quot;
  }
]
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;获取单个用户&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;请求方法&lt;/strong&gt;：GET&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;URI&lt;/strong&gt;：&lt;code&gt;/users/{id}&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;GET /users/1 HTTP/1.1
Host: api.example.com
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;响应&lt;/strong&gt;：&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;id&quot;: 1,
  &quot;name&quot;: &quot;Alice&quot;,
  &quot;email&quot;: &quot;alice@example.com&quot;
}    
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;创建用户&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;请求方法&lt;/strong&gt;：POST&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;URI&lt;/strong&gt;：&lt;code&gt;/users&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;POST /users HTTP/1.1
Host: api.example.com
Content-Type: application/json
{
  &quot;name&quot;: &quot;Charlie&quot;,
  &quot;email&quot;: &quot;charlie@example.com&quot;
}   
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;响应&lt;/strong&gt;：&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;id&quot;: 3,
  &quot;name&quot;: &quot;Charlie&quot;,
  &quot;email&quot;: &quot;charlie@example.com&quot;
}    
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;更新用户&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;请求方法&lt;/strong&gt;：PUT&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;URI&lt;/strong&gt;：&lt;code&gt;/users/{id}&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;PUT /users/1 HTTP/1.1
Host: api.example.com
Content-Type: application/json
{
  &quot;name&quot;: &quot;Alice Smith&quot;,
  &quot;email&quot;: &quot;alice.smith@example.com&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;响应&lt;/strong&gt;：&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;id&quot;: 1,
  &quot;name&quot;: &quot;Alice Smith&quot;,
  &quot;email&quot;: &quot;alice.smith@example.com&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;删除用户&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;请求方法&lt;/strong&gt;：DELETE&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;URI&lt;/strong&gt;：&lt;code&gt;/users/{id}&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;DELETE /users/1 HTTP/1.1
Host: api.example.com
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;响应&lt;/strong&gt;：&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;message&quot;: &quot;User deleted successfully&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;常用请求头&lt;/h5&gt;
&lt;h6&gt;1. Host&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;用途&lt;/strong&gt;：指定请求的目标主机和端口。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;Host: www.example.com
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;2. User-Agent&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;用途&lt;/strong&gt;：标识客户端的类型和版本。如:UA头部等&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;3. Accept&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;用途&lt;/strong&gt;：指定客户端可以接受的响应内容类型。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;4. Accept-Language&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;用途&lt;/strong&gt;：指定客户端可以接受的自然语言。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;Accept-Language: en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;5. Accept-Encoding&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;用途&lt;/strong&gt;：指定客户端可以接受的编码方式（如 gzip、deflate）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;Accept-Encoding: gzip, deflate, br
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;6. Content-Type&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;用途&lt;/strong&gt;：指定请求体的 MIME 类型。
MIME 类型（Multipurpose Internet Mail Extensions Type）是一种标准，用于表示文档、文件或字节流的性质和格式。MIME 类型由两部分组成：&lt;strong&gt;类型&lt;/strong&gt;（Type）和&lt;strong&gt;子类型&lt;/strong&gt;（Subtype），中间用斜杠（&lt;code&gt;/&lt;/code&gt;）分隔。例如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;text/plain&lt;/code&gt;：表示纯文本文件。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;text/html&lt;/code&gt;：表示 HTML 文件。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;application/json&lt;/code&gt;：表示 JSON 数据。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;image/jpeg&lt;/code&gt;：表示 JPEG 图像文件。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;audio/mpeg&lt;/code&gt;：表示 MP3 音频文件。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;video/mp4&lt;/code&gt;：表示 MP4 视频文件。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;Content-Type: application/json
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;7. Content-Length&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;用途&lt;/strong&gt;：指定请求体的长度（以字节为单位）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;Content-Length: 1234
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;8. Authorization&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;用途&lt;/strong&gt;：指定客户端的身份验证信息。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;9. Cookie&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;用途&lt;/strong&gt;：指定客户端发送的 Cookie 信息。有Cookie并且同域访问时会自动带上&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;Cookie: session_id=abc123; user_id=456
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;10. Referer&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;用途&lt;/strong&gt;：指定请求的来源 URL(适用于所有类型的请求, 会精确到详细页面地址, csrf拦截长常到这个字段)。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;Referer: https://www.example.com/previous-page
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;11. Origin&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;用途&lt;/strong&gt;：指定请求的来源域名，通常用于跨域请求。Origin比Refer更尊重隐私&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;Origin: https://www.example.com
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;12. Cache-Control&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;用途&lt;/strong&gt;：指定缓存策略。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;Cache-Control: no-cache
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;13. If-Modified-Since&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;用途&lt;/strong&gt;：指定请求资源的最后修改时间，用于条件请求。对应服务器的Last-Modified, 用来匹配看文件是否变动,只能精确到1s之内&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;If-Modified-Since: Sat, 29 Oct 2022 19:43:31 GMT
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;14. If-None-Match&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;用途&lt;/strong&gt;：指定请求资源的 ETag，用于条件请求。对应服务器的ETag, 用来匹配文件内容是否改变(非常精确)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;If-None-Match: &quot;abc123456789&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;15. Connection&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;用途&lt;/strong&gt;：指定连接的管理方式，如保持连接（Keep-Alive）或关闭连接（Close）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;Connection: keep-alive
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;16. Upgrade&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;用途&lt;/strong&gt;：指定客户端希望升级的协议，如 WebSocket。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;Upgrade: websocket
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;17. X-Requested-With&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;用途&lt;/strong&gt;：标识请求是否为 AJAX 请求。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;X-Requested-With: XMLHttpRequest
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;18. X-Forwarded-For&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;用途&lt;/strong&gt;：指定客户端的真实 IP 地址，通常用于代理服务器。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;X-Forwarded-For: 192.0.2.1
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;19. X-Forwarded-Proto&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;用途&lt;/strong&gt;：指定客户端请求的协议（如 HTTP 或 HTTPS），通常用于代理服务器。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;X-Forwarded-Proto: https
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;20. X-CSRF-Token&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;用途&lt;/strong&gt;：指定 CSRF（跨站请求伪造）令牌，用于防止 CSRF 攻击。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;X-CSRF-Token: abc123456789
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;常见响应头&lt;/h5&gt;
&lt;h6&gt;1. Content-Type&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;用途&lt;/strong&gt;：指定响应体的 MIME 类型。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;Content-Type: text/html; charset=UTF-8
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;解释&lt;/strong&gt;：&lt;code&gt;Content-Type&lt;/code&gt; 响应头用于指定响应体的 MIME 类型，如 &lt;code&gt;text/html&lt;/code&gt; 表示 HTML 文档，&lt;code&gt;application/json&lt;/code&gt; 表示 JSON 数据。&lt;code&gt;charset&lt;/code&gt; 参数用于指定字符编码，如 &lt;code&gt;UTF-8&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h6&gt;2. Cache-Control&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;用途&lt;/strong&gt;：指定缓存策略。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;Cache-Control: max-age=3600, public
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;解释&lt;/strong&gt;：&lt;code&gt;Cache-Control&lt;/code&gt; 响应头用于指定缓存策略，如 &lt;code&gt;max-age=3600&lt;/code&gt; 表示资源可以在 3600 秒（1 小时）内使用缓存，&lt;code&gt;public&lt;/code&gt; 表示资源可以被任何缓存存储。&lt;/li&gt;
&lt;/ul&gt;
&lt;h6&gt;3. Last-Modofied&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;用途&lt;/strong&gt;：指定资源的最后修改时间。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;Last-Modified: Wed, 21 Oct 2025 07:28:00 GMT
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;解释&lt;/strong&gt;：&lt;code&gt;Last-Modified&lt;/code&gt; 响应头用于指定资源的最后修改时间，客户端在后续请求中可以通过 &lt;code&gt;If-Modified-Since&lt;/code&gt; 请求头来判断资源是否已修改。&lt;/li&gt;
&lt;/ul&gt;
&lt;h6&gt;4. Expires&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;用途&lt;/strong&gt;：指定资源的过期时间。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;Expires: Wed, 21 Oct 2025 07:28:00 GMT
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;解释&lt;/strong&gt;：&lt;code&gt;Expires&lt;/code&gt; 响应头用于指定资源的过期时间，表示资源在指定时间之前可以使用缓存，而不需要重新请求服务器。&lt;/li&gt;
&lt;/ul&gt;
&lt;h6&gt;5. Max-age&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;用途&lt;/strong&gt;：指定资源的缓存时间（以秒为单位）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;http
Cache-Control: max-age=3600
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;解释&lt;/strong&gt;：&lt;code&gt;Max-Age&lt;/code&gt; 是 &lt;code&gt;Cache-Control&lt;/code&gt; 响应头的一个指令，用于指定资源的缓存时间（以秒为单位）。它告诉客户端在指定时间内可以使用缓存的资源，而不需要重新请求服务器。&lt;/li&gt;
&lt;/ul&gt;
&lt;h6&gt;6. Set-Cookie&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;用途&lt;/strong&gt;：设置 Cookie。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;http
Set-Cookie: session_id=abc123; Expires=Wed, 21 Oct 2025 07:28:00 GMT; Path=/; Secure; HttpOnly

&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;解释&lt;/strong&gt;：&lt;code&gt;Set-Cookie&lt;/code&gt; 响应头用于设置 Cookie，客户端在后续的请求中会将该 Cookie 发送回服务器。&lt;code&gt;Expires&lt;/code&gt; 参数用于指定 Cookie 的过期时间，&lt;code&gt;Path&lt;/code&gt; 参数用于指定 Cookie 的路径，&lt;code&gt;Secure&lt;/code&gt; 参数表示 Cookie 只能通过 HTTPS 协议发送，&lt;code&gt;HttpOnly&lt;/code&gt; 参数表示 Cookie 不能通过 JavaScript 访问。&lt;/li&gt;
&lt;/ul&gt;
&lt;h6&gt;7. Server&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;用途&lt;/strong&gt;：指定服务器软件的名称和版本。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;http
Server: Apache/2.4.46 (Unix)
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;解释&lt;/strong&gt;：&lt;code&gt;Server&lt;/code&gt; 响应头用于指定服务器软件的名称和版本，如 &lt;code&gt;Apache/2.4.46&lt;/code&gt; 表示服务器使用的是 Apache 2.4.46 版本。&lt;/li&gt;
&lt;/ul&gt;
&lt;h6&gt;8.Access-Control-Allow-Origin&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;用途&lt;/strong&gt;：指定允许访问资源的源（域名）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;Access-Control-Allow-Origin: *
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;解释&lt;/strong&gt;：&lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt; 响应头用于指定允许访问资源的源（域名）。&lt;code&gt;*&lt;/code&gt; 表示允许所有域名访问资源，也可以指定具体的域名，如 &lt;code&gt;https://www.example.com&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;缓存&lt;/h5&gt;
&lt;p&gt;HTTP 缓存（HTTP Caching）是一种用于减少网络请求和提高网站性能的技术。通过缓存，客户端（通常是浏览器）可以在本地存储资源的副本，并在后续请求中使用这些缓存的资源，而不需要重新请求服务器。HTTP 缓存可以显著减少网络延迟和带宽消耗，提高网站的响应速度和用户体验。&lt;/p&gt;
&lt;h6&gt;HTTP 缓存的类型&lt;/h6&gt;
&lt;ol&gt;
&lt;li&gt;强缓存&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;强缓存&lt;/strong&gt;是指客户端在缓存有效期内直接使用本地缓存的资源，而不需要向服务器发送请求。强缓存通过以下响应头来控制：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;Cache-Control&lt;/code&gt;&lt;/strong&gt;：指定缓存策略，如 &lt;code&gt;max-age&lt;/code&gt;、&lt;code&gt;public&lt;/code&gt;、&lt;code&gt;private&lt;/code&gt;、&lt;code&gt;no-cache&lt;/code&gt;、&lt;code&gt;no-store&lt;/code&gt; 等。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;Expires&lt;/code&gt;&lt;/strong&gt;：指定资源的过期时间，表示资源在指定时间之前可以使用缓存。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Cache-Control&lt;/code&gt; 指令
到期&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;max-age&lt;/code&gt;&lt;/strong&gt;：指定资源的缓存时间（以秒为单位）。例如，&lt;code&gt;Cache-Control: max-age=3600&lt;/code&gt; 表示资源可以在 3600 秒（1 小时）内使用缓存。
可缓存性&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;public&lt;/code&gt;&lt;/strong&gt;：表示资源可以被任何缓存存储，包括代理服务器。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;private&lt;/code&gt;&lt;/strong&gt;：表示资源只能被客户端缓存，不能被代理服务器缓存。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;no-cache&lt;/code&gt;&lt;/strong&gt;：表示资源在使用缓存之前必须向服务器验证，即使缓存未过期。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;no-store&lt;/code&gt;&lt;/strong&gt;：表示资源不能被缓存，每次请求都必须从服务器获取最新的资源。
重新验证/重新加载&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;must-revaliable&lt;/code&gt;&lt;/strong&gt;:  一旦资源过期，在成功向原始服务器验证之前，不能使用&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Expires&lt;/code&gt; 响应头&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Expires&lt;/code&gt; 响应头的值是一个具体的日期和时间，表示资源的过期时间。例如，&lt;code&gt;Expires: Wed, 21 Oct 2025 07:28:00 GMT&lt;/code&gt; 表示资源在 2025 年 10 月 21 日 07:28:00 之前可以使用缓存。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;协商缓存&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;协商缓存&lt;/strong&gt;是指客户端在缓存过期后，向服务器发送请求，询问资源是否有更新。服务器通过比较资源的最后修改时间和客户端的缓存时间，决定是否返回新的资源。协商缓存通过以下响应头和请求头来控制：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;Last-Modified&lt;/code&gt;&lt;/strong&gt; 和 &lt;strong&gt;&lt;code&gt;If-Modified-Since&lt;/code&gt;&lt;/strong&gt;：通过资源的最后修改时间来判断资源是否有更新。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;ETag&lt;/code&gt;&lt;/strong&gt; 和 &lt;strong&gt;&lt;code&gt;If-None-Match&lt;/code&gt;&lt;/strong&gt;：通过资源的唯一标识符（ETag）来判断资源是否有更新。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;Last-Modified&lt;/code&gt; 和 &lt;code&gt;If-Modified-Since&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;Last-Modified&lt;/code&gt;&lt;/strong&gt;：服务器在响应头中返回资源的最后修改时间。例如，&lt;code&gt;Last-Modified: Wed, 21 Oct 2025 07:28:00 GMT&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;If-Modified-Since&lt;/code&gt;&lt;/strong&gt;：客户端在请求头中发送资源的最后修改时间，服务器通过比较这个时间和资源的实际修改时间，决定是否返回新的资源。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;ETag&lt;/code&gt; 和 &lt;code&gt;If-None-Match&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;ETag&lt;/code&gt;&lt;/strong&gt;：服务器在响应头中返回资源的唯一标识符（ETag）。例如，&lt;code&gt;ETag: &quot;abc123456789&quot;&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;If-None-Match&lt;/code&gt;&lt;/strong&gt;：客户端在请求头中发送资源的 ETag，服务器通过比较这个 ETag 和资源的实际 ETag，决定是否返回新的资源。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h6&gt;HTTP 缓存的工作流程&lt;/h6&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/lvjianchaos/Images/note/HTTP/%E7%BC%93%E5%AD%98_%E5%B7%A5%E4%BD%9C%E6%B5%81%E7%A8%8B.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;强缓存的工作流程&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;客户端请求资源&lt;/strong&gt;：客户端向服务器发送请求，请求某个资源。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;服务器返回资源和缓存策略&lt;/strong&gt;：服务器返回资源，并在响应头中设置 &lt;code&gt;Cache-Control&lt;/code&gt; 和 &lt;code&gt;Expires&lt;/code&gt; 等缓存策略。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;客户端缓存资源&lt;/strong&gt;：客户端根据缓存策略缓存资源。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;客户端使用缓存资源&lt;/strong&gt;：在缓存有效期内，客户端直接使用本地缓存的资源，而不需要重新请求服务器。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;协商缓存的工作流程&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;客户端请求资源&lt;/strong&gt;：客户端向服务器发送请求，请求某个资源。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;服务器返回资源和缓存策略&lt;/strong&gt;：服务器返回资源，并在响应头中设置 &lt;code&gt;Last-Modified&lt;/code&gt; 和 &lt;code&gt;ETag&lt;/code&gt; 等缓存策略。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;客户端缓存资源&lt;/strong&gt;：客户端根据缓存策略缓存资源。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;客户端缓存过期&lt;/strong&gt;：在缓存过期后，客户端向服务器发送请求，并在请求头中设置 &lt;code&gt;If-Modified-Since&lt;/code&gt; 和 &lt;code&gt;If-None-Match&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;服务器验证资源&lt;/strong&gt;：服务器通过比较 &lt;code&gt;If-Modified-Since&lt;/code&gt; 和 &lt;code&gt;If-None-Match&lt;/code&gt; 与资源的实际修改时间和 ETag，决定是否返回新的资源。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;服务器返回响应&lt;/strong&gt;：如果资源未修改，服务器返回 &lt;code&gt;304 Not Modified&lt;/code&gt; 响应，客户端继续使用缓存的资源；如果资源已修改，服务器返回新的资源。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h6&gt;缓存的最佳实践&lt;/h6&gt;
&lt;ol&gt;
&lt;li&gt;使用 &lt;code&gt;Cache-Control&lt;/code&gt; 和 &lt;code&gt;Expires&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;对于静态资源（如图片、CSS 文件、JavaScript 文件），可以设置较长的 &lt;code&gt;max-age&lt;/code&gt; 和 &lt;code&gt;Expires&lt;/code&gt;，以减少客户端对服务器的请求，提高网站的性能。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;对于动态内容（如 API 响应），可以设置较短的 &lt;code&gt;max-age&lt;/code&gt; 和 &lt;code&gt;Expires&lt;/code&gt;，以确保客户端能够及时获取最新的内容。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;使用 &lt;code&gt;Last-Modified&lt;/code&gt; 和 &lt;code&gt;ETag&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;对于经常更新的资源，可以使用 &lt;code&gt;Last-Modified&lt;/code&gt; 和 &lt;code&gt;ETag&lt;/code&gt; 进行协商缓存，减少带宽消耗。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;对于不经常更新的资源，可以使用强缓存策略，减少客户端对服务器的请求。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;避免缓存敏感数据&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;对于包含敏感数据的响应（如用户个人信息、支付信息等），应设置 &lt;code&gt;Cache-Control: no-store&lt;/code&gt;，确保数据不会被缓存。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;使用 CDN&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;使用内容分发网络（CDN）可以进一步提高缓存的效果，减少服务器的负载和网络延迟。&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;Cookie&lt;/h5&gt;
&lt;p&gt;Cookie 是一种在客户端（通常是浏览器）和服务器之间传递的小型文本数据。服务器可以通过 HTTP 响应头（&lt;code&gt;Set-Cookie&lt;/code&gt;）将 Cookie 发送给客户端，客户端在后续的请求中通过 HTTP 请求头（&lt;code&gt;Cookie&lt;/code&gt;）将 Cookie 发送回服务器。Cookie 通常用于存储用户会话信息、用户偏好设置、购物车内容等。&lt;/p&gt;
&lt;h6&gt;Cookie 的用途&lt;/h6&gt;
&lt;ol&gt;
&lt;li&gt;会话管理&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;会话管理&lt;/strong&gt;是 Cookie 最常见的用途之一。服务器可以通过 Cookie 来识别用户的会话，从而实现用户登录状态的保持。&lt;/li&gt;
&lt;li&gt;例如，当用户登录网站时，服务器会生成一个唯一的会话 ID，并将其存储在 Cookie 中。客户端在后续的请求中会将该 Cookie 发送回服务器，服务器通过会话 ID 识别用户。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;个性化设置&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;个性化设置&lt;/strong&gt;是指根据用户的偏好设置，提供个性化的内容和服务。服务器可以通过 Cookie 存储用户的偏好设置，并在后续的请求中根据这些设置提供个性化的内容。&lt;/li&gt;
&lt;li&gt;例如，网站可以根据用户的语言偏好设置，提供不同语言的页面内容。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;购物车&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;购物车&lt;/strong&gt;是电子商务网站中常见的功能。服务器可以通过 Cookie 存储用户的购物车内容，并在用户浏览网站时保持购物车的状态。&lt;/li&gt;
&lt;li&gt;例如，当用户将商品添加到购物车时，服务器会将商品信息存储在 Cookie 中。用户在浏览其他页面时，购物车内容会保持不变。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;跟踪用户行为&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;跟踪用户行为&lt;/strong&gt;是指通过 Cookie 记录用户的浏览行为，从而进行用户行为分析和广告投放。&lt;/li&gt;
&lt;li&gt;例如，广告平台可以通过 Cookie 记录用户的浏览历史，从而向用户展示相关的广告。&lt;/li&gt;
&lt;/ul&gt;
&lt;h6&gt;Cookie 的属性&lt;/h6&gt;
&lt;p&gt;Cookie 可以通过多个属性来控制其行为和生命周期。以下是一些常见的 Cookie 属性：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;Name&lt;/code&gt; 和 &lt;code&gt;Value&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Name&lt;/code&gt;&lt;/strong&gt;：Cookie 的名称，用于在客户端和服务器之间唯一标识该 Cookie。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Value&lt;/code&gt;&lt;/strong&gt;：Cookie 的值，存储在 Cookie 中的数据。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;Domain&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Domain&lt;/code&gt;&lt;/strong&gt;：指定 Cookie 可以发送给哪些域名。默认情况下，Cookie 只能发送给设置它的域名。&lt;/li&gt;
&lt;li&gt;例如，如果 &lt;code&gt;Domain&lt;/code&gt; 设置为 &lt;code&gt;example.com&lt;/code&gt;，则 Cookie 可以发送给 &lt;code&gt;www.example.com&lt;/code&gt; 和 &lt;code&gt;blog.example.com&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;Path&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Path&lt;/code&gt;&lt;/strong&gt;：指定 Cookie 可以发送给哪些路径。默认情况下，Cookie 只能发送给设置它的路径及其子路径。&lt;/li&gt;
&lt;li&gt;例如，如果 &lt;code&gt;Path&lt;/code&gt; 设置为 &lt;code&gt;/blog&lt;/code&gt;，则 Cookie 可以发送给 &lt;code&gt;/blog&lt;/code&gt; 和 &lt;code&gt;/blog/post&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;Expires&lt;/code&gt; 和 &lt;code&gt;Max-Age&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Expires&lt;/code&gt;&lt;/strong&gt;：指定 Cookie 的过期时间，表示 Cookie 在客户端的存储时间。过期后，Cookie 会被删除。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Max-Age&lt;/code&gt;&lt;/strong&gt;：指定 Cookie 的最大存活时间（以秒为单位）。过期后，Cookie 会被删除。&lt;/li&gt;
&lt;li&gt;例如，&lt;code&gt;Expires=Wed, 21 Oct 2025 07:28:00 GMT&lt;/code&gt; 表示 Cookie 在 2025 年 10 月 21 日 07:28:00 过期。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;Secure&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Secure&lt;/code&gt;&lt;/strong&gt;：指定 Cookie 只能通过 HTTPS 协议发送。如果设置为 &lt;code&gt;Secure&lt;/code&gt;，则 Cookie 不会通过 HTTP 协议发送。&lt;/li&gt;
&lt;li&gt;例如，&lt;code&gt;Secure;&lt;/code&gt; 表示 Cookie 只能通过 HTTPS 协议发送。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;HttpOnly&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;HttpOnly&lt;/code&gt;&lt;/strong&gt;：指定 Cookie 不能通过 JavaScript 访问。如果设置为 &lt;code&gt;HttpOnly&lt;/code&gt;，则 Cookie 只能通过 HTTP 请求发送，不能通过 JavaScript 访问。&lt;/li&gt;
&lt;li&gt;例如，&lt;code&gt;HttpOnly;&lt;/code&gt; 表示 Cookie 不能通过 JavaScript 访问。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;SameSite&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;SameSite&lt;/code&gt;&lt;/strong&gt;：指定 Cookie 在跨站请求时的行为。有三个可选值：
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Strict&lt;/code&gt;&lt;/strong&gt;：Cookie 只能发送给同站请求，不能发送给跨站请求。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Lax&lt;/code&gt;&lt;/strong&gt;：Cookie 可以发送给同站请求和部分跨站请求（如 GET 请求）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;None&lt;/code&gt;&lt;/strong&gt;：Cookie 可以发送给同站请求和跨站请求，但必须设置 &lt;code&gt;Secure&lt;/code&gt; 属性。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;例如，&lt;code&gt;SameSite=Lax;&lt;/code&gt; 表示 Cookie 可以发送给同站请求和部分跨站请求。&lt;/li&gt;
&lt;/ul&gt;
&lt;h6&gt;设置 Cookie&lt;/h6&gt;
&lt;p&gt;服务器可以通过 HTTP 响应头（&lt;code&gt;Set-Cookie&lt;/code&gt;）设置 Cookie。以下是一个设置 Cookie 的示例：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;HTTP/1.1 200 OK
Set-Cookie: session_id=abc123; Expires=Wed, 21 Oct 2025 07:28:00 GMT; Domain=example.com; Path=/; Secure; HttpOnly; SameSite=Lax
Content-Type: text/html

&amp;lt;html&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;Example&amp;lt;/title&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;h1&amp;gt;Hello, World!&amp;lt;/h1&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;发送 Cookie&lt;/h6&gt;
&lt;p&gt;客户端在后续的请求中会通过 HTTP 请求头（&lt;code&gt;Cookie&lt;/code&gt;）将 Cookie 发送回服务器。以下是一个发送 Cookie 的示例：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;GET /index.html HTTP/1.1
Host: www.example.com
Cookie: session_id=abc123
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;Cookie 的安全性&lt;/h6&gt;
&lt;ol&gt;
&lt;li&gt;防止 CSRF 攻击&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;**CSRF（跨站请求伪造）**攻击是指攻击者通过诱导用户访问恶意网站，利用用户的身份发送恶意请求。&lt;/li&gt;
&lt;li&gt;通过设置 &lt;code&gt;SameSite=Strict&lt;/code&gt; 或 &lt;code&gt;SameSite=Lax&lt;/code&gt;，可以防止 CSRF 攻击。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;防止 XSS 攻击&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;**XSS（跨站脚本）**攻击是指攻击者通过注入恶意脚本，窃取用户的 Cookie 信息。&lt;/li&gt;
&lt;li&gt;通过设置 &lt;code&gt;HttpOnly&lt;/code&gt;，可以防止 JavaScript 访问 Cookie，从而防止 XSS 攻击。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;防止中间人攻击&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;中间人攻击&lt;/strong&gt;是指攻击者通过拦截网络通信，窃取用户的 Cookie 信息。&lt;/li&gt;
&lt;li&gt;通过设置 &lt;code&gt;Secure&lt;/code&gt;，可以确保 Cookie 只能通过 HTTPS 协议发送，从而防止中间人攻击。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Cookie 是一种在客户端和服务器之间传递的小型文本数据，通常用于存储用户会话信息、用户偏好设置、购物车内容等。Cookie 可以通过多个属性来控制其行为和生命周期，如 &lt;code&gt;Domain&lt;/code&gt;、&lt;code&gt;Path&lt;/code&gt;、&lt;code&gt;Expires&lt;/code&gt;、&lt;code&gt;Max-Age&lt;/code&gt;、&lt;code&gt;Secure&lt;/code&gt;、&lt;code&gt;HttpOnly&lt;/code&gt; 和 &lt;code&gt;SameSite&lt;/code&gt;。服务器可以通过 HTTP 响应头（&lt;code&gt;Set-Cookie&lt;/code&gt;）设置 Cookie，客户端在后续的请求中通过 HTTP 请求头（&lt;code&gt;Cookie&lt;/code&gt;）将 Cookie 发送回服务器。通过合理使用 Cookie，可以实现各种网络应用的功能需求，同时确保安全性。&lt;/p&gt;
&lt;h4&gt;HTTPS概述&lt;/h4&gt;
&lt;p&gt;HTTPS（HyperText Transfer Protocol Secure）是 HTTP 的安全版本，使用 SSL/TLS 协议对数据进行加密和认证，确保数据在传输过程中的安全性和完整性。HTTPS 通过加密通信内容，防止数据被窃听和篡改，同时通过数字证书对服务器进行认证，确保客户端连接到的是合法的服务器。&lt;/p&gt;
&lt;h5&gt;HTTPS 的工作原理&lt;/h5&gt;
&lt;h6&gt;1. 加密通信&lt;/h6&gt;
&lt;p&gt;HTTPS 使用 SSL/TLS 协议对数据进行加密，确保数据在传输过程中不会被窃听和篡改。SSL/TLS 协议使用对称加密和非对称加密相结合的方式，对数据进行加密和解密。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;对称加密&lt;/strong&gt;：使用相同的密钥对数据进行加密和解密。对称加密速度快，但密钥传输存在安全风险。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;非对称加密&lt;/strong&gt;：使用公钥和私钥对数据进行加密和解密。公钥可以公开，私钥必须保密。非对称加密安全性高，但速度较慢。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h6&gt;2. 数字证书&lt;/h6&gt;
&lt;p&gt;HTTPS 使用数字证书对服务器进行认证，确保客户端连接到的是合法的服务器。数字证书由受信任的第三方机构（CA，Certificate Authority）签发，包含服务器的公钥、服务器信息和 CA 的签名。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;公钥&lt;/strong&gt;：用于加密数据，客户端使用服务器的公钥对数据进行加密。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;私钥&lt;/strong&gt;：用于解密数据，服务器使用自己的私钥对数据进行解密。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;CA 签名&lt;/strong&gt;：用于验证数字证书的真实性，客户端使用 CA 的公钥验证证书的签名。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h6&gt;3. 握手过程&lt;/h6&gt;
&lt;p&gt;HTTPS 的握手过程包括以下几个步骤：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;客户端发送 ClientHello&lt;/strong&gt;：客户端向服务器发送 ClientHello 消息，包含支持的 SSL/TLS 版本、加密算法列表和随机数。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;服务器发送 ServerHello&lt;/strong&gt;：服务器选择 SSL/TLS 版本和加密算法，并生成随机数，发送 ServerHello 消息。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;服务器发送证书&lt;/strong&gt;：服务器将自己的数字证书发送给客户端，包含服务器的公钥和 CA 的签名。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;客户端验证证书&lt;/strong&gt;：客户端使用 CA 的公钥验证证书的签名，确保证书的真实性。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;客户端生成 Pre-Master Secret&lt;/strong&gt;：客户端生成 Pre-Master Secret，并使用服务器的公钥进行加密，发送给服务器。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;服务器解密 Pre-Master Secret&lt;/strong&gt;：服务器使用自己的私钥解密 Pre-Master Secret。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;客户端和服务器生成 Master Secret&lt;/strong&gt;：客户端和服务器使用 Pre-Master Secret 和之前的随机数，生成 Master Secret。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;客户端和服务器生成会话密钥&lt;/strong&gt;：客户端和服务器使用 Master Secret 生成会话密钥，用于对称加密通信内容。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;客户端发送 Finished&lt;/strong&gt;：客户端发送 Finished 消息，使用会话密钥加密，表示握手过程完成。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;服务器发送 Finished&lt;/strong&gt;：服务器发送 Finished 消息，使用会话密钥加密，表示握手过程完成。
&lt;img src=&quot;https://cdn.jsdelivr.net/gh/lvjianchaos/Images/note/HTTP/HTTPS.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h5&gt;HTTPS 的优点&lt;/h5&gt;
&lt;h6&gt;1. 数据加密&lt;/h6&gt;
&lt;p&gt;HTTPS 使用 SSL/TLS 协议对数据进行加密，确保数据在传输过程中不会被窃听和篡改。&lt;/p&gt;
&lt;h6&gt;2. 服务器认证&lt;/h6&gt;
&lt;p&gt;HTTPS 使用数字证书对服务器进行认证，确保客户端连接到的是合法的服务器。&lt;/p&gt;
&lt;h6&gt;3. 数据完整性&lt;/h6&gt;
&lt;p&gt;HTTPS 使用消息认证码（MAC）确保数据在传输过程中不会被篡改。&lt;/p&gt;
&lt;h5&gt;HTTPS 的缺点&lt;/h5&gt;
&lt;h6&gt;1. 性能开销&lt;/h6&gt;
&lt;p&gt;HTTPS 的握手过程和加密解密操作会引入一定的性能开销，尤其是在低带宽和高延迟的网络环境中。&lt;/p&gt;
&lt;h6&gt;2. 部署成本&lt;/h6&gt;
&lt;p&gt;HTTPS 需要购买和配置数字证书，部署和维护 HTTPS 服务器的成本较高。&lt;/p&gt;
&lt;h5&gt;例子&lt;/h5&gt;
&lt;h6&gt;1. 使用 HTTPS 访问网站&lt;/h6&gt;
&lt;p&gt;&lt;strong&gt;客户端请求&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;GET /index.html HTTP/1.1
Host: www.example.com
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;服务器响应&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1234
&amp;lt;html&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;Example&amp;lt;/title&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;h1&amp;gt;Hello, World!&amp;lt;/h1&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在这个例子中，客户端使用 HTTPS 协议访问 &lt;code&gt;www.example.com&lt;/code&gt; 网站，服务器返回一个 HTML 文件。HTTPS 协议确保数据在传输过程中被加密，防止数据被窃听和篡改。&lt;/p&gt;
&lt;h6&gt;2. 使用数字证书认证服务器&lt;/h6&gt;
&lt;p&gt;&lt;strong&gt;服务器证书&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;subject&quot;: {
    &quot;commonName&quot;: &quot;www.example.com&quot;,
    &quot;organization&quot;: &quot;Example Inc.&quot;,
    &quot;country&quot;: &quot;US&quot;
  },
  &quot;issuer&quot;: {
    &quot;commonName&quot;: &quot;DigiCert SHA2 Secure Server CA&quot;,
    &quot;organization&quot;: &quot;DigiCert Inc.&quot;,
    &quot;country&quot;: &quot;US&quot;
  },
  &quot;publicKey&quot;: &quot;-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----&quot;,
  &quot;signature&quot;: &quot;...&quot;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在这个例子中，服务器使用数字证书对自身进行认证。数字证书包含服务器的公钥、服务器信息和 CA 的签名。客户端使用 CA 的公钥验证证书的签名，确保证书的真实性。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;HTTP协议应用场景分析 —— 以&lt;a href=&quot;https://www.toutiao.com/&quot;&gt;今日头条&lt;/a&gt;为例&lt;/h2&gt;
&lt;h3&gt;静态资源&lt;/h3&gt;
&lt;p&gt;第一次进入页面：
&lt;img src=&quot;https://cdn.jsdelivr.net/gh/lvjianchaos/Images/note/HTTP/%E4%BB%8A%E6%97%A5%E5%A4%B4%E6%9D%A1_%E9%9D%99%E6%80%81%E8%B5%84%E6%BA%90_index.css.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;刷新一次后：
&lt;img src=&quot;https://cdn.jsdelivr.net/gh/lvjianchaos/Images/note/HTTP/%E4%BB%8A%E6%97%A5%E5%A4%B4%E6%9D%A1_%E9%9D%99%E6%80%81%E8%B5%84%E6%BA%90_index.css2.png&quot; alt=&quot;&quot; /&gt;
此时，状态码200一定发起了请求（指 client请求—server响应 的完整过程）吗？
（来自磁盘缓存）=》这次的请求是从&lt;strong&gt;本地缓存&lt;/strong&gt;拿到的
观察&lt;strong&gt;缓存策略&lt;/strong&gt;：
&lt;img src=&quot;https://cdn.jsdelivr.net/gh/lvjianchaos/Images/note/HTTP/%E4%BB%8A%E6%97%A5%E5%A4%B4%E6%9D%A1_%E9%9D%99%E6%80%81%E8%B5%84%E6%BA%90_index.css_%E7%BC%93%E5%AD%98%E7%AD%96%E7%95%A5.png&quot; alt=&quot;&quot; /&gt;
Cache-Control: max-age=2592000&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;强缓存&lt;/li&gt;
&lt;li&gt;Cache-Control: 一月
别的信息：&lt;/li&gt;
&lt;li&gt;Access-Control-Allow-Origin: * =&amp;gt; &lt;em&gt;允许所有域名访问&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Content-Type: text/css =&amp;gt; &lt;em&gt;资源类型： css&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;静态资源在部署上有没有什么方案呢？
为了性能优化/用户体验优化，即：让用户更快地看到页面和做一些可交互的行为。而影响到这些的可能是，整个页面里的静态资源的访问速度。那我们提高静态资源的访问速度也一定程度上加强了用户体验。&lt;/p&gt;
&lt;p&gt;静态资源方案： 缓存 + CDN + 文件名hash&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CDN：Content Delivery NetWork，内容分发网络&lt;/li&gt;
&lt;li&gt;通过用户就近性和服务器负载的判断，CDN确保内容以一种极为高效的方式为用户的请求提供服务。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;除了保证用户拿到的资源更快，我们还要保证用户拿到文件更新！
所以一般我们在文件名上做一些手脚，在我们这里就是&lt;strong&gt;文件名哈希&lt;/strong&gt;。可以看到上述图片中的文件名的中间：&lt;em&gt;index.7f8dc804.css&lt;/em&gt; 。&lt;/p&gt;
&lt;h3&gt;登录&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;业务场景&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;表单登录&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;扫码登录&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;技术方式&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SSO&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;账号密码登录&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;打开控制台 - network - 勾选 preserve log - 过滤quick_login
&lt;img src=&quot;https://cdn.jsdelivr.net/gh/lvjianchaos/Images/note/HTTP/%E4%BB%8A%E6%97%A5%E5%A4%B4%E6%9D%A1_%E9%9D%99%E6%80%81%E8%B5%84%E6%BA%90_%E7%99%BB%E5%BD%95.png&quot; alt=&quot;&quot; /&gt;
观察请求，这两个有什么区别呢？
最大的区别：Method不同
第一个请求：
&lt;img src=&quot;https://cdn.jsdelivr.net/gh/lvjianchaos/Images/note/HTTP/%E7%99%BB%E5%BD%95%E8%AF%B7%E6%B1%821.png&quot; alt=&quot;&quot; /&gt;
&lt;strong&gt;发起的是：OPTIONS请求，为什么呢？&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;跨域， cross-origin&lt;/strong&gt;
&lt;strong&gt;什么是跨域？&lt;/strong&gt;
一个域名由：scheme+host name+port 组成，只要是不同的话，我们都认为是跨域。
&lt;img src=&quot;https://cdn.jsdelivr.net/gh/lvjianchaos/Images/note/HTTP/%E8%B7%A8%E5%9F%9F%E8%BE%A8%E6%9E%90.png&quot; alt=&quot;&quot; /&gt;
一般来说，
*- https 默认使用端口号 443&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;http 默认使用端口号 80*
跨域&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;CORS(Cross-Origin Resource Sharing)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;预请求：获知服务端是否允许该跨域资源请求（&lt;em&gt;复杂请求&lt;/em&gt;）&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;简单请求&lt;/li&gt;
&lt;li&gt;复杂请求：只有在复杂请求才会发起跨域请求，生产生活场景中大部分都是复杂请求&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;相关协议头&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Access-Control-Allow-Origin&lt;/li&gt;
&lt;li&gt;Access-Control-Allow-Origin&lt;/li&gt;
&lt;li&gt;Access-Control-Expose-Headers&lt;/li&gt;
&lt;li&gt;Access-Control-Max-Age&lt;/li&gt;
&lt;li&gt;Access-Control-Allow-Credentials&lt;/li&gt;
&lt;li&gt;Access-Control-Allow-Methods&lt;/li&gt;
&lt;li&gt;Access-Control-Allow-Headers&lt;/li&gt;
&lt;li&gt;Access-Control-Request-Method&lt;/li&gt;
&lt;li&gt;Access-Control-Request-Headers&lt;/li&gt;
&lt;li&gt;Origin
&lt;img src=&quot;https://cdn.jsdelivr.net/gh/lvjianchaos/Images/note/HTTP/%E8%B7%A8%E5%9F%9F%E7%A4%BA%E6%84%8F%E5%9B%BE.png&quot; alt=&quot;&quot; /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;跨域解决方案&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CORS&lt;/li&gt;
&lt;li&gt;代理服务器
&lt;ul&gt;
&lt;li&gt;同源策略是浏览器的安全策略，不是HTTP&lt;/li&gt;
&lt;li&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/lvjianchaos/Images/note/HTTP/%E4%BB%A3%E7%90%86%E6%9C%8D%E5%8A%A1%E5%99%A8.png&quot; alt=&quot;&quot; /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Iframe通信
&lt;ul&gt;
&lt;li&gt;限制较多，诸多不便&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/lvjianchaos/Images/note/HTTP/%E7%99%BB%E5%BD%95_%E5%85%B7%E4%BD%93%E9%97%AE%E9%A2%98.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;想什么地址做了什么动作？&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;使用POST方法&lt;/li&gt;
&lt;li&gt;目标域名：&lt;a href=&quot;https://sso.toutiao.com&quot;&gt;https://sso.toutiao.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;目标：quick_login/v2/&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;携带了那些信息？返回了哪些信息？&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;携带：&lt;/li&gt;
&lt;li&gt;Post body，数据格式为form&lt;/li&gt;
&lt;li&gt;希望获取的数据格式为json&lt;/li&gt;
&lt;li&gt;已有的cookie&lt;/li&gt;
&lt;li&gt;返回信息&lt;/li&gt;
&lt;li&gt;数据格式json&lt;/li&gt;
&lt;li&gt;set-cookie信息&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;刷新一下页面或下次进入页面问什么能够记住登陆状态呢？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;鉴权&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Session + cookie
&lt;img src=&quot;https://cdn.jsdelivr.net/gh/lvjianchaos/Images/note/HTTP/Session%2BCookie.png&quot; alt=&quot;&quot; /&gt;&lt;/li&gt;
&lt;li&gt;JWT(JSON web token)
&lt;img src=&quot;https://cdn.jsdelivr.net/gh/lvjianchaos/Images/note/HTTP/JWT.png&quot; alt=&quot;&quot; /&gt;
各自的优劣，和应用场景&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;点击右上角发文章，跳转后的网站为什么自动登录？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;SSO：单点登录（Single Sign On）
&lt;img src=&quot;https://cdn.jsdelivr.net/gh/lvjianchaos/Images/note/HTTP/SSO%E7%A4%BA%E6%84%8F%E5%9B%BE.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;HTTP协议实战分析&lt;/h2&gt;
&lt;p&gt;在实际开发中，HTTP协议的应用非常广泛，涵盖了从浏览器到服务器的各个层面。以下是详细的实战分析，包括浏览器中的AJAX请求（XHR和Fetch）、Node.js中的HTTP/HTTPS标准库和常用请求库（axios），以及如何通过网络优化和稳定性提升用户体验。&lt;/p&gt;
&lt;h3&gt;浏览器&lt;/h3&gt;
&lt;h4&gt;AJAX之XHR&lt;/h4&gt;
&lt;p&gt;XMLHttpRequest（XHR）是浏览器中最早用于进行AJAX请求的技术。虽然现在有更现代的Fetch API，但XHR仍然在一些旧项目中使用。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;readyState&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;编号&lt;/th&gt;
&lt;th&gt;状态&lt;/th&gt;
&lt;th&gt;含义&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;UNSENT&lt;/td&gt;
&lt;td&gt;代理被创建，但尚未调用open()方法&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;OPENED&lt;/td&gt;
&lt;td&gt;open()方法已经被调用&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;HEADERS_RECEIVED&lt;/td&gt;
&lt;td&gt;send()方法已经被调用，并且头部和状态已经可获得&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;LOADNING&lt;/td&gt;
&lt;td&gt;下载中；responseText属性已经包含部分数据&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;DONE&lt;/td&gt;
&lt;td&gt;下载操作已完成&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h5&gt;封装XHR请求方法&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;function ajax(options) {
    // 默认配置
    const defaults = {
        method: &apos;GET&apos;, // 默认请求方法为GET
        url: &apos;&apos;, // 请求的URL
        async: true, // 默认异步请求
        data: null, // 请求数据
        headers: {}, // 请求头
        success: function() {}, // 请求成功回调
        error: function() {} // 请求失败回调
    };

    // 合并用户配置和默认配置
    const settings = Object.assign({}, defaults, options);

    // 创建XMLHttpRequest对象
    const xhr = new XMLHttpRequest();

    // 配置请求
    xhr.open(settings.method, settings.url, settings.async);

    // 设置请求头
    for (let header in settings.headers) {
        xhr.setRequestHeader(header, settings.headers[header]);
    }

    // 设置回调函数
    xhr.onreadystatechange = function() {
        if (xhr.readyState === 4) {
            if (xhr.status === 200) {
                // 请求成功，调用success回调
                settings.success(xhr.responseText);
            } else {
                // 请求失败，调用error回调
                settings.error(xhr.statusText);
            }
        }
    };

    // 发送请求
    xhr.send(settings.data);
}

// 使用示例
ajax({
    url: &apos;https://api.example.com/data&apos;,
    success: function(response) {
        console.log(&apos;GET请求成功:&apos;, response);
    },
    error: function(error) {
        console.error(&apos;GET请求失败:&apos;, error);
    }
});
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;AJAX之Fetch&lt;/h4&gt;
&lt;p&gt;Fetch API是现代浏览器中用于进行网络请求的一种更简洁、更强大的方式。它基于Promise，提供了更直观和灵活的API。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;XML的升级版&lt;/li&gt;
&lt;li&gt;使用Promise&lt;/li&gt;
&lt;li&gt;模块化设计，Response，Request，Header对象&lt;/li&gt;
&lt;li&gt;通过数据流处理对象，支持分块读取&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;封装Fetch请求方法&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;function fetchRequest(url, options = {}) {
    // 默认配置
    const defaults = {
        method: &apos;GET&apos;, // 默认请求方法为GET
        headers: {
            &apos;Content-Type&apos;: &apos;application/json&apos; // 默认请求头
        },
        credentials: &apos;same-origin&apos; // 默认凭据模式
    };

    // 合并用户配置和默认配置
    const settings = Object.assign({}, defaults, options);

    // 发送请求
    return fetch(url, settings)
        .then(response =&amp;gt; {
            if (!response.ok) {
                throw new Error(`HTTP error! status: ${response.status}`);
            }
            return response.json(); // 将响应解析为JSON
        })
        .catch(error =&amp;gt; {
            console.error(&apos;Fetch error:&apos;, error);
            throw error;
        });
}

// 使用示例
fetchRequest(&apos;https://api.example.com/data&apos;)
    .then(data =&amp;gt; console.log(&apos;GET请求成功:&apos;, data))
    .catch(error =&amp;gt; console.error(&apos;GET请求失败:&apos;, error));
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Node.js&lt;/h3&gt;
&lt;h4&gt;标准库：HTTP/HTTPS&lt;/h4&gt;
&lt;p&gt;Node.js提供了内置的&lt;code&gt;http&lt;/code&gt;和&lt;code&gt;https&lt;/code&gt;模块，用于创建HTTP和HTTPS服务器以及发送HTTP请求。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;默认模块，无需安装其他依赖&lt;/li&gt;
&lt;li&gt;功能有限/不是十分友好&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;使用HTTP模块发送请求&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;const http = require(&apos;http&apos;);

function httpRequest(options, data) {
    return new Promise((resolve, reject) =&amp;gt; {
        const req = http.request(options, (res) =&amp;gt; {
            let responseData = &apos;&apos;;

            res.on(&apos;data&apos;, (chunk) =&amp;gt; {
                responseData += chunk;
            });

            res.on(&apos;end&apos;, () =&amp;gt; {
                resolve(responseData);
            });
        });

        req.on(&apos;error&apos;, (error) =&amp;gt; {
            reject(error);
        });

        if (data) {
            req.write(data);
        }

        req.end();
    });
}

// 使用示例
const options = {
    hostname: &apos;api.example.com&apos;,
    port: 80,
    path: &apos;/data&apos;,
    method: &apos;GET&apos;
};

httpRequest(options)
    .then(data =&amp;gt; console.log(&apos;GET请求成功:&apos;, data))
    .catch(error =&amp;gt; console.error(&apos;GET请求失败:&apos;, error));
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;常用请求库：axios&lt;/h4&gt;
&lt;p&gt;axios是一个基于Promise的HTTP客户端，适用于浏览器和Node.js。它提供了更简洁的API，并且支持拦截器、取消请求等功能。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;支持浏览器，nodejs环境&lt;/li&gt;
&lt;li&gt;丰富的拦截器&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;安装axios&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;npm install axios
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;使用axios发送请求&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;const axios = require(&apos;axios&apos;);

function axiosRequest(url, options = {}) {
    return axios({
        url,
        ...options
    })
        .then(response =&amp;gt; response.data)
        .catch(error =&amp;gt; {
            console.error(&apos;Axios error:&apos;, error);
            throw error;
        });
}

// 使用示例
axiosRequest(&apos;https://api.example.com/data&apos;)
    .then(data =&amp;gt; console.log(&apos;GET请求成功:&apos;, data))
    .catch(error =&amp;gt; console.error(&apos;GET请求失败:&apos;, error));
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;用户体验&lt;/h3&gt;
&lt;h4&gt;网络优化&lt;/h4&gt;
&lt;p&gt;网络优化是提升用户体验的关键。以下是一些常见的网络优化策略：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;压缩资源&lt;/strong&gt;：使用Gzip或Brotli压缩静态资源（如HTML、CSS、JavaScript），减少传输数据量。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;缓存策略&lt;/strong&gt;：通过设置HTTP响应头（如&lt;code&gt;Cache-Control&lt;/code&gt;、&lt;code&gt;Expires&lt;/code&gt;），利用浏览器缓存减少重复请求。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CDN加速&lt;/strong&gt;：使用内容分发网络（CDN）将静态资源分发到全球多个节点，减少延迟和提高加载速度。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;减少HTTP请求&lt;/strong&gt;：合并CSS和JavaScript文件，减少页面加载时的HTTP请求数量。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;使用HTTP/2&lt;/strong&gt;：HTTP/2支持多路复用、头部压缩等特性，可以显著提升页面加载速度。&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;稳定性&lt;/h4&gt;
&lt;p&gt;稳定性是确保应用可靠运行的关键。以下是一些提升稳定性的策略：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;错误处理&lt;/strong&gt;：在请求失败时，提供友好的错误提示，并记录错误日志以便后续分析。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;超时设置&lt;/strong&gt;：为请求设置合理的超时时间，避免长时间等待导致用户体验下降。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;重试机制&lt;/strong&gt;：在请求失败时，自动重试请求，提高请求成功率。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;监控和报警&lt;/strong&gt;：使用监控工具（如Prometheus、Grafana）实时监控应用性能，并在异常时发送报警通知。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;总结&lt;/h3&gt;
&lt;p&gt;通过封装XHR和Fetch请求方法，我们可以在浏览器中实现简洁、可复用的AJAX请求。在Node.js中，可以使用内置的HTTP/HTTPS模块或第三方库（如axios）进行网络请求。通过网络优化和稳定性提升，我们可以显著改善用户体验，确保应用的可靠运行。在实际开发中，可以根据需求进一步优化和扩展这些方法，以满足复杂的业务需求。&lt;/p&gt;
</content:encoded></item><item><title>Multipliers</title><link>https://blog.chaos-ljc.top/posts/multipliers/</link><guid isPermaLink="true">https://blog.chaos-ljc.top/posts/multipliers/</guid><pubDate>Tue, 27 Aug 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://codeforces.com/contest/615&quot;&gt;Cf Round 338 (Div. 2)&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://codeforces.com/problemset/problem/615/D&quot;&gt;D. Multipliers&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!--more--&amp;gt;&lt;/p&gt;
&lt;p&gt;2 seconds / 256 megabytes&lt;/p&gt;
&lt;p&gt;standard input / standard output&lt;/p&gt;
&lt;h2&gt;题目&lt;/h2&gt;
&lt;p&gt;Ayrat has number &lt;em&gt;n&lt;/em&gt;, represented as it&apos;s prime factorization &lt;em&gt;pi&lt;/em&gt; of size &lt;em&gt;m&lt;/em&gt;, i.e. &lt;em&gt;n&lt;/em&gt; = &lt;em&gt;p&lt;/em&gt;1·&lt;em&gt;p&lt;/em&gt;2·...·&lt;em&gt;pm&lt;/em&gt;. Ayrat got secret information that that the product of all divisors of &lt;em&gt;n&lt;/em&gt; taken modulo 1e9 + 7 is the password to the secret data base. Now he wants to calculate this value.&lt;/p&gt;
&lt;h2&gt;输入&lt;/h2&gt;
&lt;p&gt;The first line of the input contains a single integer &lt;em&gt;m&lt;/em&gt; (1 ≤ &lt;em&gt;m&lt;/em&gt; ≤ 200 000) — the number of primes in factorization of &lt;em&gt;n&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The second line contains &lt;em&gt;m&lt;/em&gt; primes numbers &lt;em&gt;pi&lt;/em&gt; (2 ≤ &lt;em&gt;pi&lt;/em&gt; ≤ 200 000).&lt;/p&gt;
&lt;h2&gt;输出&lt;/h2&gt;
&lt;p&gt;Print one integer — the product of all divisors of &lt;em&gt;n&lt;/em&gt; modulo 1e9 + 7.&lt;/p&gt;
&lt;h2&gt;样例&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;输入&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;2
2 3&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;输出&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;36&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;输入&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;3
2 3 2&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;输出&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;1728&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;解释&lt;/h2&gt;
&lt;p&gt;In the first sample &lt;em&gt;n&lt;/em&gt; = 2·3 = 6. The divisors of 6 are 1, 2, 3 and 6, their product is equal to 1·2·3·6 = 36.&lt;/p&gt;
&lt;p&gt;In the second sample 2·3·2 = 12. The divisors of 12 are 1, 2, 3, 4, 6 and 12. 1·2·3·4·6·12 = 1728.&lt;/p&gt;
&lt;h2&gt;题解&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;题意&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;将一个数n用素数因式p[i]表示（p可能相等），大小为m.要你求出n的所有因子的乘积取模&lt;code&gt;1e9+7&lt;/code&gt;。即：所有质数p相乘等于的n的所有因子的乘积取模&lt;code&gt;1e9+7&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;思路&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;由于限时&lt;code&gt;2s&lt;/code&gt;、数据范围较大，所以暴力破解一定会&lt;code&gt;TLE&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;那么我们如何去解这道题呢？&lt;/p&gt;
&lt;p&gt;题目为什么将所有的质因子交予我们呢？没错，我们先在知道这个数n的质因子及其个数。&lt;/p&gt;
&lt;p&gt;这时，就需要具备&lt;strong&gt;质因数分解定理&lt;/strong&gt;的知识了。&lt;/p&gt;
&lt;h4&gt;何为&lt;strong&gt;质因数分解定理&lt;/strong&gt;？&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;质因数分解定理&lt;/strong&gt;，也称为&lt;strong&gt;唯一分解定理&lt;/strong&gt;，是数论中的一个基本定理，它说明了每一个大于1的自然数都可以&lt;strong&gt;唯一地&lt;/strong&gt;分解成几个质数的乘积。这个定理的正式表述如下：&lt;/p&gt;
&lt;p&gt;任何一个大于1的自然数N都可以&lt;strong&gt;唯一地&lt;/strong&gt;表示为几个质数乘积的形式，即：
$$
n = p_1^{e_1} * p_2^{e_2}*···p_k^{e_k}
$$
其中，&lt;code&gt;p1,p2,···,pk&lt;/code&gt;是质数，而&lt;code&gt;e1,e2,···,ek&lt;/code&gt;是正整数，这个表示称为N的质因数分解。
$$
Number\ of\ factors=(e_1+1)×(e_2+1)×(e_3+1)×...×(e_k+1)
$$
在这个基础上，我们或可知道这个数的&lt;strong&gt;因数个数&lt;/strong&gt;如何求；即：通过分解这个数为质因数的乘积来计算。公式如上：这是&lt;strong&gt;因为&lt;/strong&gt;每个质因数可以选择&lt;strong&gt;从0到其指数的任意次数&lt;/strong&gt;来相乘，形成一个因数。因此，对于每个质因数的每个指数，都有指数加1种选择。&lt;/p&gt;
&lt;p&gt;例如，如果一个数n的质因数分解为 &lt;code&gt;n=2^2×3^1×5^1&lt;/code&gt;，那么它的因数个数就是：&lt;code&gt;(2+1)×(1+1)×(1+1)=3×2×2=12(2+1)×(1+1)×(1+1)=3×2×2=12&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;所以，这个数有12个因数。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;因数个数定理&lt;/strong&gt;可以利用&lt;strong&gt;数学归纳法&lt;/strong&gt;证明，本蒟蒻并不太会严格的数学证明，如有需要，还请自行查阅相关资料。&lt;/p&gt;
&lt;p&gt;那么我们可以分析&lt;strong&gt;每个素因子p对于答案的贡献&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;只含有p的因子有p,p^2,^p^3,……,p^e，而不含p的因子个数为&lt;code&gt;sum/(e + 1)&lt;/code&gt;(我们不妨设总因数个数为sum)。每一个含有p的因子都会和不含p的因子组合，则每一个组合出现的次数为&lt;code&gt;sum/(e + 1)&lt;/code&gt;，那么这个p对答案的贡献就是:
$$
p^{sum/(e + 1)}&lt;em&gt;p^{2&lt;/em&gt;[sum/(e + 1)]}&lt;em&gt;·····&lt;em&gt;p^{k&lt;/em&gt;[sum/(e + 1)]}=p^{[sum/(e + 1)]&lt;/em&gt;(1+2+···+e)}=p^{[e*(1+e)/2]&lt;em&gt;[sum/(e + 1)]}=p^{[(e&lt;/em&gt;sum)/2]}
$$
(另外，我们可能会想到&lt;code&gt;/2&lt;/code&gt;会出现问题，但我们发现，如果e[1-k]中全为偶数，那么e可被2整除；若e[1~k]中出现一个奇数，那么那个e+1可被2整除，它组成了sum。所以如果是此时关于&lt;code&gt;/2&lt;/code&gt;就不会产生问题。)&lt;/p&gt;
&lt;p&gt;这样，loop求出每个质数的贡献相乘并记得过程中取模即可？真的是这样吗？&lt;/p&gt;
&lt;p&gt;记得我们的sum是啥吗？
$$
sum=(e_1+1)×(e_2+1)×(e_3+1)×...×(e_k+1)
$$
&lt;strong&gt;它太大了！&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;所以问题来了，我们如何处理“&lt;strong&gt;指数爆炸&lt;/strong&gt;”呢，或者说怎么降低指数呢？&lt;/p&gt;
&lt;h4&gt;费马小定理&lt;/h4&gt;
&lt;p&gt;定理的内容是：&lt;/p&gt;
&lt;p&gt;如果&lt;em&gt;p&lt;/em&gt;是一个素数，那么对于任何整数&lt;em&gt;a&lt;/em&gt;，都有
$$
a^{p-1}≡1(modp)
$$
换句话说，当&lt;em&gt;p&lt;/em&gt;是一个素数时，&lt;em&gt;a&lt;/em&gt;的&lt;em&gt;p−1&lt;/em&gt;次幂除以&lt;em&gt;p&lt;/em&gt;的余数总是1。&lt;/p&gt;
&lt;p&gt;证明本蒟蒻就不自讨苦吃了，还请各位佬另寻它路orz。&lt;/p&gt;
&lt;p&gt;这个定理可以被用来简化指数运算。例如，假设我们想要计算a^n模p的值，其中n是一个非常大的数，而p是一个素数。如果n不是p−1的倍数，直接计算a^n可能会非常困难。但是，如果我们能够将n表示为k*(p−1)+m，其中m&amp;lt;p−1，那么我们可以利用费马小定理来简化计算：
$$
a^{p-1}≡a^{k*(p-1)+m}≡(a^{p-1})^k*a^m(mod p)≡a^m(mod p)
$$
所以在这里，我们可以将p的指数&lt;code&gt;[e*sum)/2] MOD (p-1)&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;那我们就进而考虑&lt;code&gt;[e*sum)/2] MOD (p-1)&lt;/code&gt;该如何计算。&lt;/p&gt;
&lt;p&gt;这个/2很&lt;strong&gt;影响我们边取余边除&lt;/strong&gt;，分类讨论：&lt;/p&gt;
&lt;p&gt;只会有两种情况：奇/偶&lt;/p&gt;
&lt;p&gt;1）e[i~k]只要存在一个&lt;strong&gt;奇&lt;/strong&gt;数，那么在求sum的过程中将第一个遇到的奇数/2再取余，其他照样即可；&lt;/p&gt;
&lt;p&gt;2）若全为&lt;strong&gt;偶数&lt;/strong&gt;，那么我们sum照算，只要将每个e/2即可。&lt;/p&gt;
&lt;p&gt;剩下的就交给&lt;strong&gt;快速幂&lt;/strong&gt;(若不知道&lt;code&gt;ksm&lt;/code&gt;可看代码注释&lt;s&gt;板子如下&lt;/s&gt;)了。&lt;/p&gt;
&lt;h2&gt;代码&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;bits/stdc++.h&amp;gt;
using namespace std;
using ll = long long;
const int M = 2e5 + 9;
const int MOD = 1e9 + 7;
ll p[M],c_p[M]; // 分别表示质因子，质因子个数
vector&amp;lt;ll&amp;gt; v; // 存储质因子，无重复，方便后续操作
// 快读
inline int read(){
    int x = 0, f = 1; char ch = getchar();
    while(ch &amp;lt; &apos;0&apos; || ch &amp;gt; &apos;9&apos;){ if(ch == &apos;-&apos;) f = -1; ch = getchar(); }
    while(ch &amp;gt;= &apos;0&apos; &amp;amp;&amp;amp; ch &amp;lt;= &apos;9&apos;){ x = x * 10 + (ch ^ 48); ch = getchar(); }
    return x * f;
}
// 快速幂=&amp;gt;倍增思想
ll ksm(ll base,ll power)
{
    // base =&amp;gt; 底
    // power =&amp;gt; 幂
    ll res = 1;
    while(power)
    {
        if(power &amp;amp; 1) res = res * base % MOD; // power为奇数，
        base = base * base % MOD;
        power &amp;gt;&amp;gt;= 1; // 必然会有power变为1的时刻，此时base即为所求
        // power有两种情况：偶数时，底数平方，power整除2；
        //       奇数时，底数平方，幂整除2向下取整，还有1项在此过程中被搞没了，所以在此之前还要在原基础上乘以底数
    }
    return res;
}
int main(void)
{
 ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); // 关闭同步流
    // 初始化与输入
 int m = read();
 ll sum = 1; // 因子总数
    ll ans = 1; // 答案
    bool fg = 1; // 用于判断质因子数中是否存在奇数
 for(int i = 1;i &amp;lt;= m;++ i)
 {
  p[i] = read();
  if(!c_p[p[i]]) v.push_back(p[i]); // 如果未出现过，push入vector中
  c_p[p[i]] ++; // 个数++
 } 
 
 for(auto &amp;amp;i :v) if(c_p[i] &amp;amp; 1) fg = 0; // 判断出现过奇数
    
 if(fg) // 如果不存在奇数，即c_p[i]都是偶数
 {
  for(auto &amp;amp;i :v)
        {
            sum = sum * (c_p[i] + 1) % (MOD - 1); // 求因子总数，注意一定要在修改c_p[i]前
            c_p[i] &amp;gt;&amp;gt;= 1;// 相当于整除2
        }
  for(auto &amp;amp;i :v) ans = ans * ksm(i,sum * c_p[i] % (MOD - 1)) % MOD;
 }
 else // 存在奇数
 {
  sum = 1;
  bool wc = 1; // 只是随便找一个奇数在算sum时整除2
  for(auto &amp;amp;i :v)
  {
   if(c_p[i]&amp;amp;1 &amp;amp;&amp;amp; wc)
   {
    wc = 0;
    sum = sum * (c_p[i] + 1) / 2 % (MOD - 1);
   }
   else sum = sum * (c_p[i] + 1) % (MOD - 1);
  }
  for(auto &amp;amp;i :v) ans = ans * ksm(i,sum * c_p[i] % (MOD - 1)) % MOD;
 }
 cout &amp;lt;&amp;lt; ans &amp;lt;&amp;lt; &apos;\n&apos;;
 return 0;
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>快速分解素因数</title><link>https://blog.chaos-ljc.top/posts/fast-prime-factorization/</link><guid isPermaLink="true">https://blog.chaos-ljc.top/posts/fast-prime-factorization/</guid><description>分解质因数入门</description><pubDate>Fri, 21 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;参考：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/267884783&quot;&gt;https://zhuanlan.zhihu.com/p/267884783&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.cnblogs.com/idreamo/p/9411265.html&quot;&gt;https://www.cnblogs.com/idreamo/p/9411265.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.csdn.net/qq_39972971/article/details/82346390&quot;&gt;https://blog.csdn.net/qq_39972971/article/details/82346390&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.cnblogs.com/RioTian/p/13928916.html&quot;&gt;https://www.cnblogs.com/RioTian/p/13928916.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.cnblogs.com/book-book/p/6349362.html&quot;&gt;https://www.cnblogs.com/book-book/p/6349362.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://oi-wiki.org/math/number-theory/pollard-rho/&quot;&gt;https://oi-wiki.org/math/number-theory/pollard-rho/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/220203643&quot;&gt;https://zhuanlan.zhihu.com/p/220203643&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;分解质因数&lt;/h1&gt;
&lt;h3&gt;引入&lt;/h3&gt;
&lt;p&gt;给定一个正整数 $N∈N^+$，试&lt;strong&gt;快速&lt;/strong&gt;找到它的一个 非平凡因数 ( 除了1和数本身以外的因数 )。&lt;/p&gt;
&lt;h4&gt;朴素算法&lt;/h4&gt;
&lt;p&gt;考虑&lt;strong&gt;朴素&lt;/strong&gt;算法——试除法，因数是成对分布的，$N$ 的所有因数可以被分成两块，即 $[2,\sqrt{N}]$ 和 $[\sqrt{N}+1,N]$ 。只需要把 $[2, \sqrt {N}]$) 里的数遍历一遍，再根据除法就可以找出至少两个因数了。这个方法的时间复杂度为 $O(\sqrt {N})$。&lt;/p&gt;
&lt;p&gt;最简单的算法即从 $[2,\sqrt{N}]$ 进行遍历。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;vector&amp;lt;int&amp;gt; breakdown(int N) { 
 vector&amp;lt;int&amp;gt; result; 
 for (int i = 2; i * i &amp;lt;= N; i++) { 
  if (N % i == 0) { 
  // 如果 i 能够整除 N，说明 i 为 N 的一个质因子。 
   while (N % i == 0) N /= i; 
   result.push_back(i); 
  } 
 } 
 if (N != 1) { 
  // 说明再经过操作之后 N 留下了一个素数 
  result.push_back(N); 
 } 
 return result; 
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们能够证明 &lt;code&gt;result&lt;/code&gt; 中的所有元素均为 &lt;code&gt;N&lt;/code&gt; 的素因数。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;首先证明元素均为 $N$ 的素因数：因为当且仅当 N % i == 0 满足时，result 发生变化：$i$ 储存 ，说明此时 $i$ 能整除 $\frac{N}{A}$ ，说明了存在一个数 $p$ 使得 $pi = \frac{N}{A}$ ，即 （其中，$A$ 为 $N$ 自身发生变化后遇到 $i$ 时所除的数。我们注意到 result 若在 $push\ i$ 之前就已经有数了，为 $R_1,R_2,...,R_n$ ，那么有 $N = \frac{N}{R_1^{q_1}·R_2^{q_2}...R_n^{q_n}}$，被除的乘积即为 $A$ ）。所以 i 为 N 的因子。&lt;/p&gt;
&lt;p&gt;其次证明 result 中均为素数。我们假设存在一个在 result 中的合数 $K$ ，并根据&lt;strong&gt;算术基本定理&lt;/strong&gt;，分解为一个素数序列 $K = K_1^{e_1}·K_2^{e_2}·...·K_n^{e_n}$ ，而因为 $K_1 &amp;lt; K$，所以它一定会在 $K$ 之前被遍历到，并令 &lt;code&gt;while(N % k1 == 0) N /= k1&lt;/code&gt;，即让 &lt;code&gt;N&lt;/code&gt; 没有了素因子 $K_1$ ，故遍历到 $K$ 时，N 和 K 已经没有了整除关系了。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;值得指出的是，如果开始已经打了一个&lt;strong&gt;素数表&lt;/strong&gt;的话，时间复杂度将从 $O(\sqrt{N})$ 下降到 $O(\sqrt \frac{N}{ln N})$。具体可以去了解 &lt;strong&gt;筛法&lt;/strong&gt; 的相关知识。&lt;a href=&quot;https://zhuanlan.zhihu.com/p/100051075&quot;&gt;【埃筛 欧筛】&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;但是，当 $N$ 是个大整数时( $\ge 10^{18}$ )，这个算法的运行时间是不优秀的。我们期望一个更优秀的算法。&lt;/p&gt;
&lt;p&gt;这里给出一种想法，即：通过&lt;strong&gt;随机的方法&lt;/strong&gt;，猜测一个数是不是 $N$ 的因数，如果运气好可以在 $O(1)$ 的时间复杂度下求解答案，但是对于的数据 $N$ 是个大整数时( $\ge 10^{18}$ )，成功猜测的概率是 $\frac{1}{10^{18}}$ ,
, 期望猜测的次数是 $10^{18}$ 。如果是在$[2, \sqrt {N}]$) 里进行猜测，成功率会大一些。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;template &amp;lt;class T&amp;gt;
T randint(T l, T r = 0) // 生成随机数建议用&amp;lt;random&amp;gt;里的引擎和分布，而不是rand()模数，那样保证是均匀分布
{
   static mt19937 eng(time(0));
   if (l &amp;gt; r)
       swap(l, r);
   uniform_int_distribution&amp;lt;T&amp;gt; dis(l, r);
   return dis(eng);
}
int find_factor(int n)
{
   if (is_prime(n)) // 特判素数
       return n;
   int x, d;
   do
   {
       x = randint(2, n - 1); // 生成2和n-1之间的随机数
       d = gcd(n, x);       // 求gcd
   } while (d == 1);
   return d;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在最差的情况下，$n = p^2$ (p是质数)，这时 $[1,p^2-1]$ 里只有 1 个 p 是 n 的因数；然而，当 $x$ 取 $p,2p,3p,...,(p-1)p$ 时，都有 $gcd(x,n) = p &amp;gt; 1$ ，而公因数自然是 n 的因数 。所以此时最差期望时间复杂度可以降到 $O(\sqrt{n}logn)$ (gcd的复杂度log，根号来自 $x$ 有约 $p - 1 ≈ \sqrt{n}$ 种满足条件的选择) ，当然，它连朴素的试除法都比不过。&lt;/p&gt;
&lt;p&gt;因此，我们仍希望有方法来优化猜测。&lt;/p&gt;
&lt;h2&gt;Pollard Rho 算法&lt;/h2&gt;
&lt;h4&gt;生日悖论&lt;/h4&gt;
&lt;p&gt;为了尝试优化，我们或许需要了解&lt;strong&gt;生日悖论&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;生日悖论不算严格意义上的悖论，只是反直觉罢了。&lt;/p&gt;
&lt;p&gt;这里给出一个简易的表述：一个房间里有23个人，则他们中有两人生日相同的概率超过一半（不考虑闰年）。关于证明：即 $\frac{365}{365} × \frac{364}{365} × \frac{363}{365} × ... \frac{365 - 22}{365} &amp;lt; \frac{1}{2}$。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;为了破除旧的生日悖论的直觉，建立新的认识，我们尝试去解决生日悖论的提问：一个房间中至少多少人，才能使其中两个人生日相同的概率达到 $50%$ ?&lt;/p&gt;
&lt;p&gt;解：假设一年有 n 天，房间中有 k 人，用整数 1，2，...，k 对这些人进行编码。假定每个人的生日均匀分布于 n 天之中，且两个人的生日相互独立。
设 k 个人生日互不相同为事件 A，则事件 A 的概率为：$P(A) = \prod \limits_{i=0}^{k-1} \frac{n-i}{n}$ ，
至少有两个人生日相同的概率为 $P(\bar{A})=1-P(A)$。
根据题意得$P(\bar{A})\ge \frac{1}{2}$，那么有 $P(A)= \prod \limits_{i=0}^{k-1} \frac{n-i}{n}\le \frac{1}{2}$
由不等式 $1+x\le e^x$ 可得：$P(A)=\prod \limits_{i=0}^{k-1}exp(-\frac{i}{n})=exp(-\frac{k(k-1)}{2n})$&lt;br /&gt;
因此，$exp(-\frac{k(k-1)}{2n})\le \frac{1}{2} =&amp;gt; P(A)\le\frac{1}{2}$
将 $n=365$ 带入得：$k\ge23$。所以一个房间中至少23人，使其中两个人生日相同的概率达到 $50%$。&lt;/p&gt;
&lt;p&gt;而当 $k&amp;gt;56,n=365$ 时，出现两个人同一天生日的概率将大于一 $99%$ 。
那么在一年有 $n$ 天的情况下，当房间中有 $\frac{1}{2}(\sqrt{8nln2 + 1}+1)≈\sqrt{2nln2}$个人时，至少有两个人的生日相同的概率约为 $50%$。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这时，我们可以建立这样一个直觉：如果我们不断在某个范围内生成随机整数，很快便会生成到重复的数，期望大约在根号级别。精确地说，对于一个 $[1,𝑁]$ 内整数的理想随机数生成器，生成序列中第一个重复数字前期望有 $\sqrt{\frac{π𝑁}{2}}$ 个数。
&lt;strong&gt;证明&lt;/strong&gt;参见&lt;a href=&quot;https://www.zhihu.com/question/367513670/answer/991562741&quot;&gt;知乎问答&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;这里我们回归到原题，对于最坏的情形 $n=p^2$，如果我们不断在 $[1,n-1]$ 间生成随机数，那么期望在生成大约 $\sqrt{p}=n^{\frac{1}{4}}$ 个数后，可以出现 $2$ 个在模 $p$ 下相同的数（注意 $[1,n-1]$间的随机数模 $p$ 大致是 $[0,p-1]$ 间的随机数）。那么这 $2$ 个数的差的绝对值 $d$，就一定满足 $d \equiv 0\ (mod\ p)$ ，也就满足 $gcd(d,n)&amp;gt;1$ 。&lt;/p&gt;
&lt;p&gt;但是知晓这件事的意义并不是那么大。正如生日悖论虽然正确，但你不一定等在班上遇到和自己生日相同的人，因为这个高概率是在&lt;strong&gt;两两比较&lt;/strong&gt;下才成立的。对于这 $n^{\frac{1}{4}}$ 个数两两验证，复杂度还是立刻回到 $O(\sqrt{n}logn)$，这并没有什么进步，所以我们需要一些技巧。&lt;/p&gt;
&lt;h3&gt;利用最大公约数求出一个因数 || 伪随机数序列&lt;/h3&gt;
&lt;p&gt;我们再次回顾一下这个性质：$n$ 和 某个数 的最大公约数一定是 $n$ 的因数，即 $\forall k ∈ N_+,gcd(k,n)|n$，只要选择恰当的 $k$ 使得 $1 &amp;lt;gcd(k,n)&amp;lt;n$，就可以求得 $n$ 的一个约数 $gcd(k,n)$。满足这样条件的 $k$ 不少， $n$ 有若干个质因子，每个质因子及其大部分倍数都是可行的。&lt;/p&gt;
&lt;p&gt;我们通过 $f(x)=(x^2+c)\ mod\ n$ 来生成一个序列 ${x_i}$：随机取一个 $x_1$ ，令 $x_2 = f(x_1),x_3 = f(x_2),...,x_i=f(x_{i-1})$ 。其中 $c$ 是一个随机选取的常数。&lt;/p&gt;
&lt;p&gt;举个例子，取 $n=9400,c=24,x_1=0$，$f(x)$ 生成数据为：
$0,24,600,2824,3800,1624,5400,1224,3600,6824,8800,2824,3800,1624,...$&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/lvjianchaos/Images/note/%E5%BF%AB%E9%80%9F%E5%88%86%E8%A7%A3%E7%B4%A0%E5%9B%A0%E6%95%B0.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;可以发现数据在 $x_4$ 以后都在 $2824,3800,1624,5400,1224,3600,6824,8800$ 之间循环。如果将这些数如上图一样排列起来，会发现这个图像酷似一个𝜌，算法也因此得名 $rho$。
不难发现这是显然的，因为每个数都是由前一个数决定的，可生成的数又是有限的，那么迟早会进入&lt;strong&gt;循环&lt;/strong&gt;。当然，这个循环极大可能是&lt;strong&gt;混循环&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;而之所以选择 $f(x)=(x^2+c)\ mod\ n$ 这个函数生成序列，是因为它有一个性质：
$\forall x \equiv y\ (mod\ p),\ f(y)$&lt;/p&gt;
</content:encoded></item></channel></rss>