{"id":79135,"date":"2026-03-01T07:36:43","date_gmt":"2026-02-28T23:36:43","guid":{"rendered":"https:\/\/www.wsisp.com\/helps\/79135.html"},"modified":"2026-03-01T07:36:43","modified_gmt":"2026-02-28T23:36:43","slug":"http-%e7%8a%b6%e6%80%81%e7%a0%81%ef%bc%9a%e5%ae%a2%e6%88%b7%e7%ab%af%e4%b8%8e%e6%9c%8d%e5%8a%a1%e5%99%a8%e7%9a%84%e9%80%9a%e4%bf%a1%e8%af%ad%e8%a8%80-%e7%ac%ac%e4%b8%89%e9%83%a8","status":"publish","type":"post","link":"https:\/\/www.wsisp.com\/helps\/79135.html","title":{"rendered":"HTTP \u72b6\u6001\u7801\uff1a\u5ba2\u6237\u7aef\u4e0e\u670d\u52a1\u5668\u7684\u901a\u4fe1\u8bed\u8a00\u2014\u2014\u7b2c\u4e09\u90e8\u5206\uff1a\u91cd\u5b9a\u5411\u7c7b\u72b6\u6001\u7801\uff083xx\uff09\u5168\u9762\u5256\u6790"},"content":{"rendered":"<h4>\u7b2c10\u7ae0&#xff1a;\u91cd\u5b9a\u5411\u673a\u5236\u539f\u7406<\/h4>\n<h5>10.1 \u91cd\u5b9a\u5411\u7684\u57fa\u672c\u6982\u5ff5<\/h5>\n<h6>10.1.1 \u91cd\u5b9a\u5411\u7684\u5b9a\u4e49\u4e0e\u672c\u8d28<\/h6>\n<p>HTTP\u91cd\u5b9a\u5411\u662f\u4e00\u79cd\u5ba2\u6237\u7aef-\u670d\u52a1\u5668\u901a\u4fe1\u673a\u5236&#xff0c;\u5f53\u8bf7\u6c42\u7684\u8d44\u6e90\u4f4d\u7f6e\u53d1\u751f\u53d8\u5316\u6216\u9700\u8981\u4ece\u4e0d\u540c\u4f4d\u7f6e\u83b7\u53d6\u65f6&#xff0c;\u670d\u52a1\u5668\u6307\u793a\u5ba2\u6237\u7aef\u5411\u65b0\u5730\u5740\u91cd\u65b0\u53d1\u8d77\u8bf7\u6c42\u3002\u4ece\u672c\u8d28\u4e0a\u8bb2&#xff0c;\u91cd\u5b9a\u5411\u4e0d\u662f\u8d44\u6e90\u672c\u8eab&#xff0c;\u800c\u662f\u5173\u4e8e\u8d44\u6e90\u4f4d\u7f6e\u7684&#034;\u5143\u4fe1\u606f&#034;\u3002<\/p>\n<p>\u6280\u672f\u5b9a\u4e49&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u534f\u8bae\u5c42\u9762&#xff1a;HTTP\/1.1\u89c4\u8303RFC 7231\u5b9a\u4e493xx\u72b6\u6001\u7801\u4e3a&#034;\u91cd\u5b9a\u5411\u72b6\u6001\u7801&#034;<\/p>\n<\/li>\n<li>\n<p>\u529f\u80fd\u5c42\u9762&#xff1a;\u4f4d\u7f6e\u8f6c\u79fb\u6307\u4ee4&#xff0c;\u901a\u5e38\u4f34\u968fLocation\u54cd\u5e94\u5934<\/p>\n<\/li>\n<li>\n<p>\u8bed\u4e49\u5c42\u9762&#xff1a;\u8d44\u6e90\u8868\u793a\u5728\u4e0d\u540cURI\u4e0b\u53ef\u7528<\/p>\n<\/li>\n<\/ul>\n<h6>10.1.2 \u91cd\u5b9a\u5411\u7684\u5386\u53f2\u6f14\u8fdb<\/h6>\n<p>HTTP\u91cd\u5b9a\u5411\u673a\u5236\u968f\u7740\u534f\u8bae\u7248\u672c\u6f14\u8fdb\u4e0d\u65ad\u6210\u719f&#xff1a;<\/p>\n<p>HTTP\/0.9\u548c\u65e9\u671fHTTP\/1.0&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u4ec5\u6709\u57fa\u7840\u91cd\u5b9a\u5411\u6982\u5ff5<\/p>\n<\/li>\n<li>\n<p>\u5b9e\u73b0\u4e0d\u4e00\u81f4&#xff0c;\u7f3a\u4e4f\u6807\u51c6\u5316<\/p>\n<\/li>\n<\/ul>\n<p>HTTP\/1.0 (RFC 1945, 1996)&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u6b63\u5f0f\u5b9a\u4e49\u4e86301\u548c302\u72b6\u6001\u7801<\/p>\n<\/li>\n<li>\n<p>\u4f46\u89c4\u8303\u76f8\u5bf9\u7b80\u5355&#xff0c;\u5bfc\u81f4\u6d4f\u89c8\u5668\u5b9e\u73b0\u5dee\u5f02<\/p>\n<\/li>\n<\/ul>\n<p>HTTP\/1.1 (RFC 2616, 1999 \u2192 RFC 7231, 2014)&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u5f15\u5165303\u3001307\u72b6\u6001\u7801<\/p>\n<\/li>\n<li>\n<p>\u660e\u786e\u91cd\u5b9a\u5411\u7f13\u5b58\u8bed\u4e49<\/p>\n<\/li>\n<li>\n<p>\u89c4\u8303\u65b9\u6cd5\u4fdd\u6301\u884c\u4e3a<\/p>\n<\/li>\n<\/ul>\n<p>\u73b0\u4ee3HTTP\/2\u548cHTTP\/3&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u534f\u8bae\u6539\u53d8\u4e0d\u5f71\u54cd\u91cd\u5b9a\u5411\u8bed\u4e49<\/p>\n<\/li>\n<li>\n<p>\u4f46\u4f20\u8f93\u6548\u7387\u4f18\u5316\u5f71\u54cd\u91cd\u5b9a\u5411\u6027\u80fd\u8003\u8651<\/p>\n<\/li>\n<\/ul>\n<h6>10.1.3 \u91cd\u5b9a\u5411\u7684\u54f2\u5b66\u601d\u8003&#xff1a;REST\u67b6\u6784\u89c6\u89d2<\/h6>\n<p>\u4eceREST&#xff08;\u8868\u8ff0\u6027\u72b6\u6001\u8f6c\u79fb&#xff09;\u67b6\u6784\u98ce\u683c\u770b&#xff0c;\u91cd\u5b9a\u5411\u4f53\u73b0\u4e86\u51e0\u4e2a\u91cd\u8981\u539f\u5219&#xff1a;<\/p>\n<li>\n<p>\u7edf\u4e00\u63a5\u53e3&#xff1a;\u91cd\u5b9a\u5411\u662f\u6807\u51c6\u5316\u7684\u5ba2\u6237\u7aef-\u670d\u52a1\u5668\u4ea4\u4e92\u6a21\u5f0f<\/p>\n<\/li>\n<li>\n<p>\u5206\u5c42\u7cfb\u7edf&#xff1a;\u91cd\u5b9a\u5411\u5141\u8bb8\u5728\u67b6\u6784\u4e0d\u540c\u5c42\u7ea7\u5904\u7406\u4f4d\u7f6e\u53d8\u66f4<\/p>\n<\/li>\n<li>\n<p>\u6309\u9700\u4ee3\u7801&#xff1a;\u91cd\u5b9a\u5411\u53ef\u89c6\u4e3a\u670d\u52a1\u5668\u5411\u5ba2\u6237\u7aef\u63d0\u4f9b\u7684&#034;\u6267\u884c\u6307\u4ee4&#034;<\/p>\n<\/li>\n<p>Roy Fielding\u535a\u58eb\u5728\u8bba\u6587\u4e2d\u5f3a\u8c03&#xff0c;\u91cd\u5b9a\u5411\u662fREST\u67b6\u6784\u4e2d&#034;\u72b6\u6001\u8f6c\u79fb&#034;\u7684\u91cd\u8981\u673a\u5236&#xff0c;\u4f7f\u5ba2\u6237\u7aef\u72b6\u6001\u80fd\u591f\u968f\u7740\u8d44\u6e90\u4f4d\u7f6e\u53d8\u5316\u800c\u6b63\u786e\u8fc1\u79fb\u3002<\/p>\n<h5>10.2 \u91cd\u5b9a\u5411\u7684\u6267\u884c\u6d41\u7a0b<\/h5>\n<h6>10.2.1 \u6807\u51c6\u91cd\u5b9a\u5411\u6d41\u7a0b\u8be6\u6790<\/h6>\n<p>\u5355\u6b21\u91cd\u5b9a\u5411\u7684\u8be6\u7ec6\u6b65\u9aa4&#xff1a;<\/p>\n<li>\n<p>\u521d\u59cb\u8bf7\u6c42\u53d1\u8d77<\/p>\n<p>http<\/p>\n<p> GET \/old-path HTTP\/1.1<br \/>\nHost: example.com<br \/>\nUser-Agent: Mozilla\/5.0<br \/>\nAccept: text\/html,application\/xhtml&#043;xml <\/li>\n<li>\n<p>\u670d\u52a1\u5668\u54cd\u5e94\u91cd\u5b9a\u5411<\/p>\n<p>http<\/p>\n<p> HTTP\/1.1 301 Moved Permanently<br \/>\nLocation: https:\/\/example.com\/new-path<br \/>\nContent-Type: text\/html; charset&#061;utf-8<br \/>\nContent-Length: 178<br \/>\nDate: Mon, 21 Oct 2024 09:00:00 GMT<br \/>\nCache-Control: public, max-age&#061;3600<\/p>\n<p>&lt;!DOCTYPE html&gt;<br \/>\n&lt;html&gt;&lt;head&gt;&lt;title&gt;301 Moved Permanently&lt;\/title&gt;&lt;\/head&gt;<br \/>\n&lt;body&gt;&lt;h1&gt;Moved Permanently&lt;\/h1&gt;&lt;\/body&gt;&lt;\/html&gt; <\/li>\n<li>\n<p>\u5ba2\u6237\u7aef\u5904\u7406\u903b\u8f91&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u89e3\u6790\u72b6\u6001\u7801&#xff0c;\u786e\u8ba4\u662f\u91cd\u5b9a\u5411<\/p>\n<\/li>\n<li>\n<p>\u68c0\u67e5Location\u5934\u90e8\u83b7\u53d6\u65b0URL<\/p>\n<\/li>\n<li>\n<p>\u9a8c\u8bc1\u91cd\u5b9a\u5411\u662f\u5426\u5b89\u5168&#xff08;\u9632\u6b62\u91cd\u5b9a\u5411\u5faa\u73af&#xff09;<\/p>\n<\/li>\n<li>\n<p>\u51b3\u5b9a\u662f\u5426\u4fdd\u6301\u539f\u59cb\u8bf7\u6c42\u65b9\u6cd5&#xff08;\u6839\u636e\u72b6\u6001\u7801\u7c7b\u578b&#xff09;<\/p>\n<\/li>\n<li>\n<p>\u5411\u65b0URL\u53d1\u8d77\u8bf7\u6c42<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p>\u91cd\u5b9a\u5411\u8bf7\u6c42\u53d1\u9001&#xff1a;<\/p>\n<p>http<\/p>\n<p> GET \/new-path HTTP\/1.1<br \/>\nHost: example.com<br \/>\nUser-Agent: Mozilla\/5.0<br \/>\nAccept: text\/html,application\/xhtml&#043;xml<br \/>\nReferer: https:\/\/example.com\/old-path <\/li>\n<li>\n<p>\u6700\u7ec8\u54cd\u5e94\u63a5\u6536&#xff1a;<\/p>\n<p>http<\/p>\n<p> HTTP\/1.1 200 OK<br \/>\nContent-Type: text\/html; charset&#061;utf-8<br \/>\nContent-Length: 2456<\/p>\n<p>&lt;!DOCTYPE html&gt;<br \/>\n&lt;html&gt;&#8230;\u5b9e\u9645\u5185\u5bb9&#8230;&lt;\/html&gt; <\/li>\n<h6>10.2.2 \u6d4f\u89c8\u5668\u5904\u7406\u91cd\u5b9a\u5411\u7684\u5185\u90e8\u673a\u5236<\/h6>\n<p>\u73b0\u4ee3\u6d4f\u89c8\u5668\u5b9e\u73b0\u91cd\u5b9a\u5411\u5904\u7406\u65f6\u5305\u542b\u590d\u6742\u903b\u8f91&#xff1a;<\/p>\n<p>\u8bf7\u6c42-\u54cd\u5e94\u7ba1\u9053\u4f18\u5316&#xff1a;<\/p>\n<p>text<\/p>\n<p>\u539f\u59cb\u8bf7\u6c42 \u2192 \u89e3\u6790\u54cd\u5e94 \u2192 \u68c0\u67e5\u72b6\u6001\u7801 \u2192<br \/>\n\u7f13\u5b58\u68c0\u67e5 \u2192 \u5b89\u5168\u9a8c\u8bc1 \u2192 \u6784\u5efa\u65b0\u8bf7\u6c42 \u2192<br \/>\n\u8fde\u63a5\u590d\u7528\u68c0\u67e5 \u2192 \u53d1\u9001\u65b0\u8bf7\u6c42 <\/p>\n<p>\u8fde\u63a5\u7ba1\u7406\u7b56\u7565&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u5982\u679c\u65b0\u65e7URL\u5728\u540c\u4e00\u57df\u540d\u4e0b&#xff0c;\u5c3d\u53ef\u80fd\u590d\u7528TCP\u8fde\u63a5<\/p>\n<\/li>\n<li>\n<p>HTTP\/2\u548cHTTP\/3\u4e0b\u5229\u7528\u591a\u8def\u590d\u7528\u4f18\u5316<\/p>\n<\/li>\n<li>\n<p>\u8fde\u63a5\u6c60\u7ba1\u7406\u907f\u514d\u91cd\u590d\u63e1\u624b<\/p>\n<\/li>\n<\/ul>\n<p>\u5b89\u5168\u9650\u5236\u4e0e\u9632\u62a4&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u6700\u5927\u91cd\u5b9a\u5411\u9650\u5236&#xff08;\u901a\u5e3820\u6b21&#xff0c;\u9632\u5faa\u73af&#xff09;<\/p>\n<\/li>\n<li>\n<p>\u8de8\u57df\u91cd\u5b9a\u5411\u5b89\u5168\u7b56\u7565<\/p>\n<\/li>\n<li>\n<p>\u654f\u611f\u4fe1\u606f\u4fdd\u62a4&#xff08;\u5982Authorization\u5934\u4f20\u9012&#xff09;<\/p>\n<\/li>\n<\/ul>\n<h6>10.2.3 \u91cd\u5b9a\u5411\u94fe\u4e0e\u5faa\u73af\u68c0\u6d4b<\/h6>\n<p>\u91cd\u5b9a\u5411\u94fe\u793a\u4f8b&#xff1a;<\/p>\n<p>text<\/p>\n<p>\u5ba2\u6237\u7aef \u2192 A (302 \u2192 B) \u2192 B (301 \u2192 C) \u2192 C (307 \u2192 D) \u2192 D (200 OK) <\/p>\n<p>\u5faa\u73af\u68c0\u6d4b\u7b97\u6cd5&#xff1a;<\/p>\n<p>javascript<\/p>\n<p>\/\/ \u7b80\u5316\u7684\u91cd\u5b9a\u5411\u5faa\u73af\u68c0\u6d4b\u903b\u8f91<br \/>\nclass RedirectHandler {<br \/>\n  constructor(maxRedirects &#061; 20) {<br \/>\n    this.maxRedirects &#061; maxRedirects;<br \/>\n    this.visitedUrls &#061; new Set();<br \/>\n    this.redirectCount &#061; 0;<br \/>\n  }<\/p>\n<p>  handleRedirect(response, currentUrl) {<br \/>\n    \/\/ \u68c0\u67e5\u91cd\u5b9a\u5411\u6b21\u6570\u9650\u5236<br \/>\n    if (this.redirectCount &gt;&#061; this.maxRedirects) {<br \/>\n      throw new Error(&#039;Too many redirects&#039;);<br \/>\n    }<\/p>\n<p>    \/\/ \u83b7\u53d6\u91cd\u5b9a\u5411\u76ee\u6807<br \/>\n    const location &#061; response.headers.get(&#039;Location&#039;);<br \/>\n    const redirectUrl &#061; new URL(location, currentUrl).href;<\/p>\n<p>    \/\/ \u68c0\u67e5\u5faa\u73af<br \/>\n    if (this.visitedUrls.has(redirectUrl)) {<br \/>\n      throw new Error(&#039;Redirect loop detected&#039;);<br \/>\n    }<\/p>\n<p>    \/\/ \u8bb0\u5f55\u5e76\u7ee7\u7eed<br \/>\n    this.visitedUrls.add(currentUrl);<br \/>\n    this.redirectCount&#043;&#043;;<br \/>\n    return redirectUrl;<br \/>\n  }<br \/>\n} <\/p>\n<p>\u5b9e\u9645\u6848\u4f8b\u5206\u6790&#xff1a;\u77e5\u540d\u7f51\u7ad9\u91cd\u5b9a\u5411\u94fe<\/p>\n<ul>\n<li>\n<p>Twitter&#xff1a;t.co\u77ed\u94fe\u63a5\u670d\u52a1\u591a\u5c42\u91cd\u5b9a\u5411<\/p>\n<\/li>\n<li>\n<p>Google\u641c\u7d22&#xff1a;\u5730\u57df\u5316\u91cd\u5b9a\u5411\u94fe<\/p>\n<\/li>\n<li>\n<p>CDN\u670d\u52a1&#xff1a;\u8fb9\u7f18\u8282\u70b9\u5230\u6e90\u7ad9\u7684\u91cd\u5b9a\u5411<\/p>\n<\/li>\n<\/ul>\n<h5>10.3 \u91cd\u5b9a\u5411\u7684\u5206\u7c7b\u7ef4\u5ea6<\/h5>\n<h6>10.3.1 \u6309\u6301\u4e45\u6027\u5206\u7c7b<\/h6>\n<p>\u6c38\u4e45\u91cd\u5b9a\u5411&#xff08;301\u3001308&#xff09;&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u8bed\u4e49&#xff1a;\u8d44\u6e90\u5df2\u6c38\u4e45\u79fb\u52a8\u5230\u65b0\u4f4d\u7f6e<\/p>\n<\/li>\n<li>\n<p>\u7f13\u5b58\u884c\u4e3a&#xff1a;<\/p>\n<p>http<\/p>\n<p> HTTP\/1.1 301 Moved Permanently<br \/>\nLocation: \/new-url<br \/>\nCache-Control: public, max-age&#061;31536000  # \u901a\u5e381\u5e74\u7f13\u5b58 <\/li>\n<li>\n<p>\u5ba2\u6237\u7aef\u5904\u7406&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u66f4\u65b0\u4e66\u7b7e\u548c\u6536\u85cf<\/p>\n<\/li>\n<li>\n<p>\u641c\u7d22\u5f15\u64ce\u66f4\u65b0\u7d22\u5f15<\/p>\n<\/li>\n<li>\n<p>\u540e\u7eed\u8bf7\u6c42\u76f4\u63a5\u8bbf\u95ee\u65b0URL<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>\u4e34\u65f6\u91cd\u5b9a\u5411&#xff08;302\u3001303\u3001307&#xff09;&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u8bed\u4e49&#xff1a;\u8d44\u6e90\u4e34\u65f6\u4f4d\u4e8e\u4e0d\u540c\u4f4d\u7f6e<\/p>\n<\/li>\n<li>\n<p>\u7f13\u5b58\u884c\u4e3a&#xff1a;<\/p>\n<p>http<\/p>\n<p> HTTP\/1.1 302 Found<br \/>\nLocation: \/temporary-location<br \/>\nCache-Control: no-store, no-cache  # \u901a\u5e38\u4e0d\u7f13\u5b58<br \/>\nExpires: 0 <\/li>\n<li>\n<p>\u5ba2\u6237\u7aef\u5904\u7406&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u4e0d\u66f4\u65b0\u4e66\u7b7e<\/p>\n<\/li>\n<li>\n<p>\u641c\u7d22\u5f15\u64ce\u7ee7\u7eed\u6293\u53d6\u539f\u59cbURL<\/p>\n<\/li>\n<li>\n<p>\u6bcf\u6b21\u8bbf\u95ee\u90fd\u53ef\u80fd\u91cd\u5b9a\u5411<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h6>10.3.2 \u6309\u65b9\u6cd5\u4fdd\u6301\u5206\u7c7b<\/h6>\n<p>\u65b9\u6cd5\u4fdd\u6301\u91cd\u5b9a\u5411&#xff08;301\u3001302\u7684\u539f\u59cb\u610f\u56fe\u3001307\u3001308&#xff09;&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u4fdd\u6301\u539f\u59cbHTTP\u65b9\u6cd5&#xff08;GET\u4fdd\u6301GET&#xff0c;POST\u4fdd\u6301POST&#xff09;<\/p>\n<\/li>\n<li>\n<p>\u9002\u7528\u4e8eAPI\u548c\u8868\u5355\u63d0\u4ea4\u573a\u666f<\/p>\n<\/li>\n<\/ul>\n<p>\u65b9\u6cd5\u53d8\u66f4\u91cd\u5b9a\u5411&#xff08;303&#xff09;&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u603b\u662f\u5c06\u8bf7\u6c42\u65b9\u6cd5\u8f6c\u4e3aGET<\/p>\n<\/li>\n<li>\n<p>\u9002\u7528\u4e8ePOST\u540e\u5c55\u793a\u7ed3\u679c\u7684\u573a\u666f<\/p>\n<\/li>\n<\/ul>\n<p>\u6d4f\u89c8\u5668\u5b9e\u9645\u884c\u4e3a\u7684\u5386\u53f2\u6f14\u53d8&#xff1a;<\/p>\n<table>\n<tr>\u65f6\u671f301\u884c\u4e3a302\u884c\u4e3a307\/308\u884c\u4e3a<\/tr>\n<tbody>\n<tr>\n<td>\u65e9\u671f\u6d4f\u89c8\u5668<\/td>\n<td>POST\u2192GET&#xff08;\u9519\u8bef&#xff09;<\/td>\n<td>POST\u2192GET&#xff08;\u9519\u8bef&#xff09;<\/td>\n<td>\u672a\u5f15\u5165<\/td>\n<\/tr>\n<tr>\n<td>\u4e2d\u671f\u5b9e\u73b0<\/td>\n<td>\u90e8\u5206\u4fdd\u6301<\/td>\n<td>POST\u2192GET<\/td>\n<td>\u4e25\u683c\u4fdd\u6301<\/td>\n<\/tr>\n<tr>\n<td>\u73b0\u4ee3\u6d4f\u89c8\u5668<\/td>\n<td>\u7406\u8bba\u4e0a\u5e94\u4fdd\u6301<\/td>\n<td>\u5b9e\u9645\u5e38\u8f6cGET<\/td>\n<td>\u4e25\u683c\u4fdd\u6301<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h6>10.3.3 \u6309\u7f13\u5b58\u7b56\u7565\u5206\u7c7b<\/h6>\n<p>\u53ef\u7f13\u5b58\u91cd\u5b9a\u5411&#xff1a;<\/p>\n<ul>\n<li>\n<p>301\u3001308\u53ef\u88ab\u5ba2\u6237\u7aef\u548c\u4ee3\u7406\u7f13\u5b58<\/p>\n<\/li>\n<li>\n<p>\u7f13\u5b58\u65f6\u95f4\u7531Cache-Control\u548cExpires\u63a7\u5236<\/p>\n<\/li>\n<li>\n<p>\u4ee3\u7406\u670d\u52a1\u5668\u53ef\u80fd\u5b58\u50a8\u91cd\u5b9a\u5411\u6620\u5c04<\/p>\n<\/li>\n<\/ul>\n<p>\u4e0d\u53ef\u7f13\u5b58\u91cd\u5b9a\u5411&#xff1a;<\/p>\n<ul>\n<li>\n<p>302\u3001303\u3001307\u901a\u5e38\u4e0d\u7f13\u5b58<\/p>\n<\/li>\n<li>\n<p>\u6bcf\u6b21\u90fd\u9700\u8981\u5411\u539f\u59cb\u670d\u52a1\u5668\u9a8c\u8bc1<\/p>\n<\/li>\n<li>\n<p>\u4ee3\u7406\u670d\u52a1\u5668\u5e94\u4f20\u9012\u6bcf\u6b21\u8bf7\u6c42<\/p>\n<\/li>\n<\/ul>\n<h6>10.3.4 \u6309\u89e6\u53d1\u573a\u666f\u5206\u7c7b<\/h6>\n<p>\u670d\u52a1\u5668\u9a71\u52a8\u91cd\u5b9a\u5411&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u57fa\u4e8e\u670d\u52a1\u5668\u914d\u7f6e&#xff08;.htaccess\u3001Nginx\u914d\u7f6e&#xff09;<\/p>\n<\/li>\n<li>\n<p>\u57fa\u4e8e\u5e94\u7528\u903b\u8f91&#xff08;PHP header()\u3001Express redirect()&#xff09;<\/p>\n<\/li>\n<li>\n<p>\u57fa\u4e8e\u5185\u5bb9\u534f\u5546&#xff08;\u8bed\u8a00\u3001\u8bbe\u5907\u7c7b\u578b&#xff09;<\/p>\n<\/li>\n<\/ul>\n<p>\u5ba2\u6237\u7aef\u9a71\u52a8\u91cd\u5b9a\u5411&#xff1a;<\/p>\n<ul>\n<li>\n<p>JavaScript\u91cd\u5b9a\u5411&#xff1a;window.location.href &#061; &#039;\/new&#039;<\/p>\n<\/li>\n<li>\n<p>Meta\u6807\u7b7e\u91cd\u5b9a\u5411&#xff1a;&lt;meta http-equiv&#061;&#034;refresh&#034; content&#061;&#034;5;url&#061;\/new&#034;&gt;<\/p>\n<\/li>\n<li>\n<p>HTTP\u5237\u65b0\u5934&#xff1a;Refresh: 5; url&#061;\/new-path<\/p>\n<\/li>\n<\/ul>\n<p>\u6df7\u5408\u7b56\u7565\u91cd\u5b9a\u5411&#xff1a;<\/p>\n<ul>\n<li>\n<p>Service Worker\u62e6\u622a\u548c\u91cd\u5199\u8bf7\u6c42<\/p>\n<\/li>\n<li>\n<p>\u8fb9\u7f18\u8ba1\u7b97\u5e73\u53f0&#xff08;Cloudflare Workers&#xff09;\u7684\u91cd\u5b9a\u5411\u903b\u8f91<\/p>\n<\/li>\n<li>\n<p>API\u7f51\u5173\u7684\u91cd\u5b9a\u5411\u89c4\u5219<\/p>\n<\/li>\n<\/ul>\n<h5>10.4 \u91cd\u5b9a\u5411\u7684\u5b9e\u73b0\u65b9\u5f0f<\/h5>\n<h6>10.4.1 \u670d\u52a1\u5668\u7aef\u914d\u7f6e\u5b9e\u73b0<\/h6>\n<p>Apache\u670d\u52a1\u5668 (.htaccess)&#xff1a;<\/p>\n<p>apache<\/p>\n<p># \u6c38\u4e45\u91cd\u5b9a\u5411<br \/>\nRedirect 301 \/old-path \/new-path<\/p>\n<p># \u6b63\u5219\u8868\u8fbe\u5f0f\u91cd\u5b9a\u5411<br \/>\nRedirectMatch 301 ^\/blog\/(.*)$ https:\/\/newsite.com\/articles\/$1<\/p>\n<p># \u6761\u4ef6\u91cd\u5b9a\u5411<br \/>\nRewriteEngine On<br \/>\nRewriteCond %{HTTP_HOST} ^oldsite.com$ [NC]<br \/>\nRewriteRule ^(.*)$ https:\/\/newsite.com\/$1 [R&#061;301,L]<\/p>\n<p># \u57fa\u4e8e\u67e5\u8be2\u53c2\u6570\u7684\u91cd\u5b9a\u5411<br \/>\nRewriteCond %{QUERY_STRING} ^id&#061;([0-9]&#043;)$<br \/>\nRewriteRule ^product\\\\.php$ \/products\/%1? [R&#061;301,L] <\/p>\n<p>Nginx\u914d\u7f6e&#xff1a;<\/p>\n<p>nginx<\/p>\n<p>server {<br \/>\n    listen 80;<br \/>\n    server_name example.com;<\/p>\n<p>    # \u7b80\u5355\u91cd\u5b9a\u5411<br \/>\n    location &#061; \/old-url {<br \/>\n        return 301 \/new-url;<br \/>\n    }<\/p>\n<p>    # \u6b63\u5219\u5339\u914d\u91cd\u5b9a\u5411<br \/>\n    location ~ ^\/blog\/([0-9]{4})\/([0-9]{2})\/(.*)$ {<br \/>\n        return 301 \/archives\/$1\/$2\/$3;<br \/>\n    }<\/p>\n<p>    # \u6761\u4ef6\u91cd\u5b9a\u5411<br \/>\n    if ($http_user_agent ~* &#034;bot|crawler|spider&#034;) {<br \/>\n        return 301 \/bot-page;<br \/>\n    }<\/p>\n<p>    # \u4fdd\u7559\u67e5\u8be2\u53c2\u6570<br \/>\n    location \/search {<br \/>\n        return 301 https:\/\/$server_name\/search$is_args$args;<br \/>\n    }<br \/>\n} <\/p>\n<p>IIS\u670d\u52a1\u5668 (web.config)&#xff1a;<\/p>\n<p>xml<\/p>\n<p>&lt;configuration&gt;<br \/>\n  &lt;system.webServer&gt;<br \/>\n    &lt;rewrite&gt;<br \/>\n      &lt;rules&gt;<br \/>\n        &lt;rule name&#061;&#034;Redirect to HTTPS&#034; stopProcessing&#061;&#034;true&#034;&gt;<br \/>\n          &lt;match url&#061;&#034;(.*)&#034; \/&gt;<br \/>\n          &lt;conditions&gt;<br \/>\n            &lt;add input&#061;&#034;{HTTPS}&#034; pattern&#061;&#034;^OFF$&#034; \/&gt;<br \/>\n          &lt;\/conditions&gt;<br \/>\n          &lt;action type&#061;&#034;Redirect&#034; url&#061;&#034;https:\/\/{HTTP_HOST}\/{R:1}&#034;<br \/>\n                  redirectType&#061;&#034;Permanent&#034; \/&gt;<br \/>\n        &lt;\/rule&gt;<br \/>\n        &lt;rule name&#061;&#034;Redirect old pages&#034;&gt;<br \/>\n          &lt;match url&#061;&#034;^old-folder\/(.*)$&#034; \/&gt;<br \/>\n          &lt;action type&#061;&#034;Redirect&#034; url&#061;&#034;\/new-folder\/{R:1}&#034;<br \/>\n                  redirectType&#061;&#034;Permanent&#034; \/&gt;<br \/>\n        &lt;\/rule&gt;<br \/>\n      &lt;\/rules&gt;<br \/>\n    &lt;\/rewrite&gt;<br \/>\n  &lt;\/system.webServer&gt;<br \/>\n&lt;\/configuration&gt; <\/p>\n<h6>10.4.2 \u7f16\u7a0b\u8bed\u8a00\u6846\u67b6\u5b9e\u73b0<\/h6>\n<p>Node.js\/Express\u793a\u4f8b&#xff1a;<\/p>\n<p>javascript<\/p>\n<p>const express &#061; require(&#039;express&#039;);<br \/>\nconst app &#061; express();<\/p>\n<p>\/\/ \u6c38\u4e45\u91cd\u5b9a\u5411<br \/>\napp.get(&#039;\/legacy-page&#039;, (req, res) &#061;&gt; {<br \/>\n  res.redirect(301, &#039;\/modern-page&#039;);<br \/>\n});<\/p>\n<p>\/\/ \u4e34\u65f6\u91cd\u5b9a\u5411<br \/>\napp.post(&#039;\/submit-form&#039;, (req, res) &#061;&gt; {<br \/>\n  \/\/ \u5904\u7406\u8868\u5355\u6570\u636e&#8230;<br \/>\n  res.redirect(302, &#039;\/thank-you&#039;);<br \/>\n});<\/p>\n<p>\/\/ \u4fdd\u6301\u65b9\u6cd5\u7684\u91cd\u5b9a\u5411<br \/>\napp.post(&#039;\/api\/v1\/resource&#039;, (req, res) &#061;&gt; {<br \/>\n  \/\/ \u8fc1\u79fb\u5230\u65b0\u7248\u672cAPI<br \/>\n  res.redirect(308, &#039;\/api\/v2\/resource&#039;);<br \/>\n});<\/p>\n<p>\/\/ \u6761\u4ef6\u91cd\u5b9a\u5411<br \/>\napp.get(&#039;\/user-profile&#039;, (req, res) &#061;&gt; {<br \/>\n  if (!req.session.user) {<br \/>\n    res.redirect(302, &#039;\/login&#039;);<br \/>\n  } else {<br \/>\n    res.render(&#039;profile&#039;);<br \/>\n  }<br \/>\n});<\/p>\n<p>\/\/ \u5e26\u67e5\u8be2\u53c2\u6570\u7684\u91cd\u5b9a\u5411<br \/>\napp.get(&#039;\/search&#039;, (req, res) &#061;&gt; {<br \/>\n  const { q, page } &#061; req.query;<br \/>\n  \/\/ \u89c4\u8303\u5316\u641c\u7d22URL<br \/>\n  res.redirect(301, &#096;\/search?query&#061;${encodeURIComponent(q)}&amp;page&#061;${page || 1}&#096;);<br \/>\n}); <\/p>\n<p>Python\/Django\u793a\u4f8b&#xff1a;<\/p>\n<p>python<\/p>\n<p>from django.http import HttpResponseRedirect, HttpResponsePermanentRedirect<br \/>\nfrom django.shortcuts import redirect<br \/>\nfrom django.urls import reverse<\/p>\n<p>def legacy_view(request):<br \/>\n    # \u6c38\u4e45\u91cd\u5b9a\u5411<br \/>\n    return HttpResponsePermanentRedirect(&#039;\/new-url\/&#039;)<\/p>\n<p>def temp_redirect(request):<br \/>\n    # \u4e34\u65f6\u91cd\u5b9a\u5411<br \/>\n    return HttpResponseRedirect(&#039;\/temporary-url\/&#039;)<\/p>\n<p># \u4f7f\u7528redirect\u5feb\u6377\u65b9\u5f0f<br \/>\ndef product_view(request, product_id):<br \/>\n    if product_id &lt; 1000:<br \/>\n        # \u91cd\u5b9a\u5411\u5230\u65b0\u4ea7\u54c1ID\u7cfb\u7edf<br \/>\n        return redirect(&#039;product-detail&#039;, product_id&#061;product_id&#043;10000,<br \/>\n                       permanent&#061;True)<br \/>\n    return render(request, &#039;product.html&#039;)<\/p>\n<p># \u57fa\u4e8e\u6761\u4ef6\u7684\u91cd\u5b9a\u5411<br \/>\ndef auth_required_view(request):<br \/>\n    if not request.user.is_authenticated:<br \/>\n        return redirect(&#039;%s?next&#061;%s&#039; % (settings.LOGIN_URL, request.path))<br \/>\n    return render(request, &#039;protected.html&#039;) <\/p>\n<p>PHP\u793a\u4f8b&#xff1a;<\/p>\n<p>php<\/p>\n<p>&lt;?php<br \/>\n\/\/ \u6c38\u4e45\u91cd\u5b9a\u5411<br \/>\nheader(&#034;HTTP\/1.1 301 Moved Permanently&#034;);<br \/>\nheader(&#034;Location: https:\/\/newsite.com\/new-page.php&#034;);<br \/>\nexit();<\/p>\n<p>\/\/ \u4e34\u65f6\u91cd\u5b9a\u5411<br \/>\nheader(&#034;Location: \/maintenance.php&#034;, true, 302);<br \/>\nexit();<\/p>\n<p>\/\/ \u5e26\u5ef6\u8fdf\u7684\u91cd\u5b9a\u5411<br \/>\nheader(&#034;Refresh: 10; url&#061;\/new-location.php&#034;);<br \/>\necho &#034;\u9875\u9762\u5c06\u572810\u79d2\u540e\u8df3\u8f6c&#8230;&#034;;<\/p>\n<p>\/\/ \u6761\u4ef6\u91cd\u5b9a\u5411<br \/>\nif ($_SERVER[&#039;HTTPS&#039;] !&#061; &#034;on&#034;) {<br \/>\n    $redirect_url &#061; &#034;https:\/\/&#034; . $_SERVER[&#039;HTTP_HOST&#039;] . $_SERVER[&#039;REQUEST_URI&#039;];<br \/>\n    header(&#034;Location: $redirect_url&#034;, true, 301);<br \/>\n    exit();<br \/>\n}<\/p>\n<p>\/\/ \u57fa\u4e8e\u7528\u6237\u4ee3\u7406\u7684\u91cd\u5b9a\u5411<br \/>\n$user_agent &#061; $_SERVER[&#039;HTTP_USER_AGENT&#039;];<br \/>\nif (preg_match(&#039;\/iPhone|Android|Mobile\/i&#039;, $user_agent)) {<br \/>\n    header(&#034;Location: \/mobile\/&#034;, true, 302);<br \/>\n    exit();<br \/>\n}<br \/>\n?&gt; <\/p>\n<h6>10.4.3 \u524d\u7aefJavaScript\u5b9e\u73b0<\/h6>\n<p>\u57fa\u7840\u91cd\u5b9a\u5411\u65b9\u6cd5&#xff1a;<\/p>\n<p>javascript<\/p>\n<p>\/\/ \u7acb\u5373\u91cd\u5b9a\u5411<br \/>\nwindow.location.href &#061; &#039;\/new-url&#039;;<br \/>\nwindow.location.replace(&#039;\/new-url&#039;);  \/\/ \u4e0d\u4fdd\u7559\u5386\u53f2\u8bb0\u5f55<\/p>\n<p>\/\/ \u5ef6\u8fdf\u91cd\u5b9a\u5411<br \/>\nsetTimeout(() &#061;&gt; {<br \/>\n  window.location.href &#061; &#039;\/new-url&#039;;<br \/>\n}, 3000);<\/p>\n<p>\/\/ \u6761\u4ef6\u91cd\u5b9a\u5411<br \/>\nif (!localStorage.getItem(&#039;user_token&#039;)) {<br \/>\n  window.location.href &#061; &#039;\/login&#039;;<br \/>\n}<\/p>\n<p>\/\/ \u57fa\u4e8e\u6d4f\u89c8\u5668\u7279\u6027\u7684\u91cd\u5b9a\u5411<br \/>\nif (window.innerWidth &lt; 768) {<br \/>\n  window.location.href &#061; &#039;\/mobile-version&#039;;<br \/>\n} <\/p>\n<p>\u73b0\u4ee3\u6846\u67b6\u4e2d\u7684\u8def\u7531\u91cd\u5b9a\u5411&#xff1a;<\/p>\n<p>React Router\u793a\u4f8b&#xff1a;<\/p>\n<p>jsx<\/p>\n<p>import { Navigate, useNavigate } from &#039;react-router-dom&#039;;<\/p>\n<p>\/\/ \u7ec4\u4ef6\u5185\u91cd\u5b9a\u5411<br \/>\nfunction ProtectedRoute({ children }) {<br \/>\n  const isAuthenticated &#061; useSelector(state &#061;&gt; state.auth.isAuthenticated);<\/p>\n<p>  if (!isAuthenticated) {<br \/>\n    return &lt;Navigate to&#061;&#034;\/login&#034; replace state&#061;{{ from: location }} \/&gt;;<br \/>\n  }<\/p>\n<p>  return children;<br \/>\n}<\/p>\n<p>\/\/ \u7f16\u7a0b\u5f0f\u5bfc\u822a<br \/>\nfunction ProductPage() {<br \/>\n  const navigate &#061; useNavigate();<br \/>\n  const params &#061; useParams();<\/p>\n<p>  const handleProductUpdate &#061; async () &#061;&gt; {<br \/>\n    try {<br \/>\n      await updateProduct(params.id);<br \/>\n      \/\/ \u6210\u529f\u540e\u91cd\u5b9a\u5411<br \/>\n      navigate(&#039;\/products&#039;, {<br \/>\n        replace: true,<br \/>\n        state: { message: &#039;Product updated successfully&#039; }<br \/>\n      });<br \/>\n    } catch (error) {<br \/>\n      console.error(&#039;Update failed:&#039;, error);<br \/>\n    }<br \/>\n  };<\/p>\n<p>  return (<br \/>\n    &lt;div&gt;<br \/>\n      &lt;button onClick&#061;{handleProductUpdate}&gt;Update Product&lt;\/button&gt;<br \/>\n    &lt;\/div&gt;<br \/>\n  );<br \/>\n} <\/p>\n<p>Vue Router\u793a\u4f8b&#xff1a;<\/p>\n<p>javascript<\/p>\n<p>\/\/ \u8def\u7531\u914d\u7f6e\u4e2d\u7684\u91cd\u5b9a\u5411<br \/>\nconst routes &#061; [<br \/>\n  {<br \/>\n    path: &#039;\/&#039;,<br \/>\n    redirect: &#039;\/home&#039;  \/\/ \u9ed8\u8ba4\u91cd\u5b9a\u5411<br \/>\n  },<br \/>\n  {<br \/>\n    path: &#039;\/old-product\/:id&#039;,<br \/>\n    redirect: to &#061;&gt; {<br \/>\n      \/\/ \u52a8\u6001\u91cd\u5b9a\u5411\u903b\u8f91<br \/>\n      return &#096;\/products\/${to.params.id}&#096;;<br \/>\n    }<br \/>\n  }<br \/>\n];<\/p>\n<p>\/\/ \u5bfc\u822a\u5b88\u536b\u4e2d\u7684\u91cd\u5b9a\u5411<br \/>\nrouter.beforeEach((to, from, next) &#061;&gt; {<br \/>\n  const isAuthenticated &#061; store.getters.isAuthenticated;<\/p>\n<p>  if (to.meta.requiresAuth &amp;&amp; !isAuthenticated) {<br \/>\n    next({<br \/>\n      path: &#039;\/login&#039;,<br \/>\n      query: { redirect: to.fullPath }<br \/>\n    });<br \/>\n  } else {<br \/>\n    next();<br \/>\n  }<br \/>\n}); <\/p>\n<h6>10.4.4 HTML Meta\u6807\u7b7e\u5b9e\u73b0<\/h6>\n<p>html<\/p>\n<p>&lt;!DOCTYPE html&gt;<br \/>\n&lt;html&gt;<br \/>\n&lt;head&gt;<br \/>\n  &lt;!&#8211; \u57fa\u672c\u91cd\u5b9a\u5411 &#8211;&gt;<br \/>\n  &lt;meta http-equiv&#061;&#034;refresh&#034; content&#061;&#034;0;url&#061;\/new-page.html&#034;&gt;<\/p>\n<p>  &lt;!&#8211; \u5ef6\u8fdf\u91cd\u5b9a\u5411 &#8211;&gt;<br \/>\n  &lt;meta http-equiv&#061;&#034;refresh&#034; content&#061;&#034;5;url&#061;\/new-page.html&#034;&gt;<br \/>\n  &lt;title&gt;\u9875\u9762\u8df3\u8f6c\u4e2d&#8230;&lt;\/title&gt;<br \/>\n&lt;\/head&gt;<br \/>\n&lt;body&gt;<br \/>\n  &lt;p&gt;\u9875\u9762\u5c06\u57285\u79d2\u540e\u81ea\u52a8\u8df3\u8f6c&#xff0c;&lt;a href&#061;&#034;\/new-page.html&#034;&gt;\u7acb\u5373\u8df3\u8f6c&lt;\/a&gt;&lt;\/p&gt;<br \/>\n&lt;\/body&gt;<br \/>\n&lt;\/html&gt; <\/p>\n<p>Meta\u91cd\u5b9a\u5411\u7684\u5c40\u9650\u6027&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u4e0d\u652f\u6301HTTP\u72b6\u6001\u7801&#xff08;\u603b\u662f302\u884c\u4e3a&#xff09;<\/p>\n<\/li>\n<li>\n<p>\u65e0\u6cd5\u8bbe\u7f6e\u7f13\u5b58\u7b56\u7565<\/p>\n<\/li>\n<li>\n<p>\u5bf9\u641c\u7d22\u5f15\u64ce\u4e0d\u53cb\u597d<\/p>\n<\/li>\n<li>\n<p>\u65e0\u6cd5\u5904\u7406POST\u8bf7\u6c42\u91cd\u5b9a\u5411<\/p>\n<\/li>\n<\/ul>\n<h6>10.4.5 \u8fb9\u7f18\u8ba1\u7b97\u548cCDN\u5b9e\u73b0<\/h6>\n<p>Cloudflare Workers\u91cd\u5b9a\u5411&#xff1a;<\/p>\n<p>javascript<\/p>\n<p>\/\/ Cloudflare Workers\u811a\u672c<br \/>\naddEventListener(&#039;fetch&#039;, event &#061;&gt; {<br \/>\n  event.respondWith(handleRequest(event.request));<br \/>\n});<\/p>\n<p>async function handleRequest(request) {<br \/>\n  const url &#061; new URL(request.url);<\/p>\n<p>  \/\/ \u57fa\u4e8e\u8def\u5f84\u7684\u91cd\u5b9a\u5411<br \/>\n  if (url.pathname.startsWith(&#039;\/legacy\/&#039;)) {<br \/>\n    const newPath &#061; url.pathname.replace(&#039;\/legacy\/&#039;, &#039;\/modern\/&#039;);<br \/>\n    return Response.redirect(&#096;https:\/\/${url.hostname}${newPath}&#096;, 301);<br \/>\n  }<\/p>\n<p>  \/\/ \u57fa\u4e8e\u5730\u7406\u4f4d\u7f6e\u7684\u91cd\u5b9a\u5411<br \/>\n  const country &#061; request.cf.country;<br \/>\n  if (country &#061;&#061;&#061; &#039;CN&#039;) {<br \/>\n    return Response.redirect(&#039;https:\/\/cn.example.com&#039; &#043; url.pathname, 302);<br \/>\n  }<\/p>\n<p>  \/\/ \u57fa\u4e8e\u8bbe\u5907\u7c7b\u578b\u7684\u91cd\u5b9a\u5411<br \/>\n  const userAgent &#061; request.headers.get(&#039;User-Agent&#039;);<br \/>\n  if (\/mobile|android|iphone\/i.test(userAgent)) {<br \/>\n    url.hostname &#061; &#039;m.example.com&#039;;<br \/>\n    return Response.redirect(url.toString(), 302);<br \/>\n  }<\/p>\n<p>  \/\/ \u6b63\u5e38\u8bf7\u6c42\u5904\u7406<br \/>\n  return fetch(request);<br \/>\n} <\/p>\n<p>AWS Lambda&#064;Edge\u91cd\u5b9a\u5411&#xff1a;<\/p>\n<p>javascript<\/p>\n<p>exports.handler &#061; async (event) &#061;&gt; {<br \/>\n  const request &#061; event.Records[0].cf.request;<br \/>\n  const headers &#061; request.headers;<\/p>\n<p>  \/\/ \u68c0\u67e5\u662f\u5426\u9700\u8981\u91cd\u5b9a\u5411\u5230HTTPS<br \/>\n  if (headers[&#039;cloudfront-forwarded-proto&#039;] &amp;&amp;<br \/>\n      headers[&#039;cloudfront-forwarded-proto&#039;][0].value &#061;&#061;&#061; &#039;http&#039;) {<br \/>\n    return {<br \/>\n      status: &#039;301&#039;,<br \/>\n      statusDescription: &#039;Moved Permanently&#039;,<br \/>\n      headers: {<br \/>\n        location: [{<br \/>\n          key: &#039;Location&#039;,<br \/>\n          value: &#096;https:\/\/${headers.host[0].value}${request.uri}&#096;<br \/>\n        }]<br \/>\n      }<br \/>\n    };<br \/>\n  }<\/p>\n<p>  \/\/ \u8def\u5f84\u91cd\u5199\/\u91cd\u5b9a\u5411<br \/>\n  if (request.uri.startsWith(&#039;\/old\/&#039;)) {<br \/>\n    const newUri &#061; request.uri.replace(&#039;\/old\/&#039;, &#039;\/new\/&#039;);<br \/>\n    return {<br \/>\n      status: &#039;302&#039;,<br \/>\n      statusDescription: &#039;Found&#039;,<br \/>\n      headers: {<br \/>\n        location: [{<br \/>\n          key: &#039;Location&#039;,<br \/>\n          value: newUri<br \/>\n        }]<br \/>\n      }<br \/>\n    };<br \/>\n  }<\/p>\n<p>  return request;<br \/>\n}; <\/p>\n<h5>10.5 \u91cd\u5b9a\u5411\u7684\u5e38\u89c1\u95ee\u9898\u4e0e\u8c03\u8bd5\u65b9\u6cd5<\/h5>\n<h6>10.5.1 \u5e38\u89c1\u95ee\u9898\u5206\u7c7b<\/h6>\n<p>\u91cd\u5b9a\u5411\u5faa\u73af&#xff1a;<\/p>\n<p>text<\/p>\n<p>\u5ba2\u6237\u7aef \u2192 A (301 \u2192 B) \u2192 B (301 \u2192 A) \u2192 A (301 \u2192 B) \u2192 &#8230; <\/p>\n<p>\u539f\u56e0\u5206\u6790&#xff1a;<\/p>\n<li>\n<p>\u914d\u7f6e\u9519\u8bef\u5bfc\u81f4\u76f8\u4e92\u91cd\u5b9a\u5411<\/p>\n<\/li>\n<li>\n<p>\u6761\u4ef6\u903b\u8f91\u9519\u8bef<\/p>\n<\/li>\n<li>\n<p>\u7f13\u5b58\u5bfc\u81f4\u8fc7\u65f6\u91cd\u5b9a\u5411\u89c4\u5219<\/p>\n<\/li>\n<p>\u89e3\u51b3\u65b9\u6848&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u4f7f\u7528\u5de5\u5177\u68c0\u6d4b\u5faa\u73af\u94fe<\/p>\n<\/li>\n<li>\n<p>\u68c0\u67e5\u91cd\u5b9a\u5411\u6761\u4ef6\u903b\u8f91<\/p>\n<\/li>\n<li>\n<p>\u6e05\u9664\u6d4f\u89c8\u5668\u548c\u4ee3\u7406\u7f13\u5b58<\/p>\n<\/li>\n<\/ul>\n<p>\u65b9\u6cd5\u4e22\u5931\u95ee\u9898&#xff1a;<\/p>\n<ul>\n<li>\n<p>POST\u8bf7\u6c42\u7ecf\u8fc7302\u91cd\u5b9a\u5411\u53d8\u4e3aGET<\/p>\n<\/li>\n<li>\n<p>\u8868\u5355\u6570\u636e\u4e22\u5931<\/p>\n<\/li>\n<li>\n<p>API\u8c03\u7528\u5931\u8d25<\/p>\n<\/li>\n<\/ul>\n<p>\u89e3\u51b3\u65b9\u6848&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u5bf9API\u4f7f\u7528307\/308\u72b6\u6001\u7801<\/p>\n<\/li>\n<li>\n<p>\u4f7f\u7528\u4e2d\u95f4\u9875\u4fdd\u5b58\u6570\u636e<\/p>\n<\/li>\n<li>\n<p>\u5b9e\u73b0\u5ba2\u6237\u7aef\u91cd\u8bd5\u903b\u8f91<\/p>\n<\/li>\n<\/ul>\n<p>SEO\u95ee\u9898&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u8fc7\u591a\u91cd\u5b9a\u5411\u94fe\u5f71\u54cd\u722c\u866b\u6548\u7387<\/p>\n<\/li>\n<li>\n<p>\u6743\u91cd\u4f20\u9012\u4e0d\u6b63\u786e<\/p>\n<\/li>\n<li>\n<p>\u7d22\u5f15\u6df7\u4e71<\/p>\n<\/li>\n<\/ul>\n<p>\u89e3\u51b3\u65b9\u6848&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u7b80\u5316\u91cd\u5b9a\u5411\u94fe&#xff08;\u6700\u597d1\u6b21\u8df3\u8f6c&#xff09;<\/p>\n<\/li>\n<li>\n<p>\u4f7f\u7528\u6b63\u786e\u72b6\u6001\u7801&#xff08;301\u4f20\u9012\u6743\u91cd&#xff09;<\/p>\n<\/li>\n<li>\n<p>\u5b9a\u671f\u5ba1\u6838\u91cd\u5b9a\u5411\u5730\u56fe<\/p>\n<\/li>\n<\/ul>\n<h6>10.5.2 \u8c03\u8bd5\u5de5\u5177\u4e0e\u6280\u672f<\/h6>\n<p>\u547d\u4ee4\u884c\u5de5\u5177\u8c03\u8bd5&#xff1a;<\/p>\n<p>cURL\u547d\u4ee4\u793a\u4f8b&#xff1a;<\/p>\n<p>bash<\/p>\n<p># \u57fa\u672c\u91cd\u5b9a\u5411\u8ddf\u8e2a<br \/>\ncurl -I https:\/\/example.com\/old-url<\/p>\n<p># \u8ddf\u8e2a\u91cd\u5b9a\u5411\u94fe&#xff08;-L \u8ddf\u968f\u91cd\u5b9a\u5411&#xff09;<br \/>\ncurl -L -v https:\/\/example.com\/old-url<\/p>\n<p># \u9650\u5236\u91cd\u5b9a\u5411\u6b21\u6570<br \/>\ncurl -L &#8211;max-redirs 5 https:\/\/example.com<\/p>\n<p># \u4fdd\u6301POST\u65b9\u6cd5&#xff08;-X POST&#xff09;<br \/>\ncurl -X POST -L -d &#034;param&#061;value&#034; https:\/\/example.com\/form<\/p>\n<p># \u67e5\u770b\u8be6\u7ec6\u5934\u90e8\u4fe1\u606f<br \/>\ncurl -I -H &#034;User-Agent: Mozilla\/5.0&#034; https:\/\/example.com <\/p>\n<p>wget\u547d\u4ee4\u793a\u4f8b&#xff1a;<\/p>\n<p>bash<\/p>\n<p># \u4e0b\u8f7d\u5e76\u8ddf\u968f\u91cd\u5b9a\u5411<br \/>\nwget https:\/\/example.com\/old-url<\/p>\n<p># \u67e5\u770b\u91cd\u5b9a\u5411\u8fc7\u7a0b<br \/>\nwget &#8211;server-response &#8211;spider https:\/\/example.com\/old-url<\/p>\n<p># \u4fdd\u5b58\u91cd\u5b9a\u5411\u94fe\u4fe1\u606f<br \/>\nwget &#8211;save-headers -O output.html https:\/\/example.com\/old-url <\/p>\n<p>\u6d4f\u89c8\u5668\u5f00\u53d1\u8005\u5de5\u5177\u8c03\u8bd5&#xff1a;<\/p>\n<p>Chrome DevTools\u7f51\u7edc\u9762\u677f&#xff1a;<\/p>\n<li>\n<p>\u6253\u5f00Network\u6807\u7b7e<\/p>\n<\/li>\n<li>\n<p>\u52fe\u9009&#034;Preserve log&#034;\u4fdd\u7559\u91cd\u5b9a\u5411\u65e5\u5fd7<\/p>\n<\/li>\n<li>\n<p>\u67e5\u770b\u72b6\u6001\u7801\u4e3a3xx\u7684\u8bf7\u6c42<\/p>\n<\/li>\n<li>\n<p>\u68c0\u67e5Response Headers\u4e2d\u7684Location\u5934<\/p>\n<\/li>\n<p>\u6027\u80fd\u5f71\u54cd\u5206\u6790&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u67e5\u770b\u6bcf\u4e2a\u91cd\u5b9a\u5411\u7684Timing\u6807\u7b7e<\/p>\n<\/li>\n<li>\n<p>\u5206\u6790DNS\u67e5\u627e\u3001TCP\u8fde\u63a5\u3001SSL\u63e1\u624b\u65f6\u95f4<\/p>\n<\/li>\n<li>\n<p>\u8ba1\u7b97\u603b\u91cd\u5b9a\u5411\u8017\u65f6<\/p>\n<\/li>\n<\/ul>\n<p>\u6d4f\u89c8\u5668\u6269\u5c55\u5de5\u5177&#xff1a;<\/p>\n<ul>\n<li>\n<p>Redirect Path (Chrome\u6269\u5c55)<\/p>\n<\/li>\n<li>\n<p>HTTP Header Live<\/p>\n<\/li>\n<li>\n<p>Redirect Detective<\/p>\n<\/li>\n<\/ul>\n<h6>10.5.3 \u4e13\u4e1a\u8c03\u8bd5\u5de5\u5177<\/h6>\n<p>Screaming Frog SEO Spider&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u722c\u53d6\u7f51\u7ad9\u6240\u6709\u91cd\u5b9a\u5411<\/p>\n<\/li>\n<li>\n<p>\u5206\u6790\u91cd\u5b9a\u5411\u94fe\u957f\u5ea6<\/p>\n<\/li>\n<li>\n<p>\u8bc6\u522b\u91cd\u5b9a\u5411\u5faa\u73af<\/p>\n<\/li>\n<li>\n<p>\u5bfc\u51fa\u91cd\u5b9a\u5411\u5730\u56fe<\/p>\n<\/li>\n<\/ul>\n<p>Ahrefs Site Audit&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u626b\u63cf\u5168\u7ad9\u91cd\u5b9a\u5411\u95ee\u9898<\/p>\n<\/li>\n<li>\n<p>\u68c0\u67e5301 vs 302\u4f7f\u7528<\/p>\n<\/li>\n<li>\n<p>\u5206\u6790\u91cd\u5b9a\u5411\u5bf9SEO\u7684\u5f71\u54cd<\/p>\n<\/li>\n<li>\n<p>\u63d0\u4f9b\u4fee\u590d\u5efa\u8bae<\/p>\n<\/li>\n<\/ul>\n<p>\u81ea\u5b9a\u4e49\u811a\u672c\u76d1\u63a7&#xff1a;<\/p>\n<p>python<\/p>\n<p>import requests<br \/>\nfrom urllib.parse import urljoin<br \/>\nfrom collections import defaultdict<\/p>\n<p>class RedirectAnalyzer:<br \/>\n    def __init__(self):<br \/>\n        self.redirect_chains &#061; defaultdict(list)<br \/>\n        self.max_redirects &#061; 10<\/p>\n<p>    def analyze_url(self, url):<br \/>\n        chain &#061; []<br \/>\n        current_url &#061; url<\/p>\n<p>        for _ in range(self.max_redirects):<br \/>\n            try:<br \/>\n                response &#061; requests.head(current_url, allow_redirects&#061;False, timeout&#061;5)<br \/>\n                chain.append({<br \/>\n                    &#039;url&#039;: current_url,<br \/>\n                    &#039;status&#039;: response.status_code,<br \/>\n                    &#039;headers&#039;: dict(response.headers)<br \/>\n                })<\/p>\n<p>                if response.status_code in [301, 302, 303, 307, 308]:<br \/>\n                    location &#061; response.headers.get(&#039;Location&#039;)<br \/>\n                    if location:<br \/>\n                        current_url &#061; urljoin(current_url, location)<br \/>\n                    else:<br \/>\n                        break<br \/>\n                else:<br \/>\n                    break<\/p>\n<p>                if current_url in [step[&#039;url&#039;] for step in chain]:<br \/>\n                    chain.append({&#039;error&#039;: &#039;Redirect loop detected&#039;})<br \/>\n                    break<\/p>\n<p>            except Exception as e:<br \/>\n                chain.append({&#039;error&#039;: str(e)})<br \/>\n                break<\/p>\n<p>        self.redirect_chains[url] &#061; chain<br \/>\n        return chain<\/p>\n<p>    def generate_report(self):<br \/>\n        report &#061; {<br \/>\n            &#039;total_urls&#039;: len(self.redirect_chains),<br \/>\n            &#039;chains_by_length&#039;: defaultdict(int),<br \/>\n            &#039;status_codes&#039;: defaultdict(int),<br \/>\n            &#039;issues&#039;: []<br \/>\n        }<\/p>\n<p>        for url, chain in self.redirect_chains.items():<br \/>\n            report[&#039;chains_by_length&#039;][len(chain)] &#043;&#061; 1<\/p>\n<p>            for step in chain:<br \/>\n                if &#039;status&#039; in step:<br \/>\n                    report[&#039;status_codes&#039;][step[&#039;status&#039;]] &#043;&#061; 1<\/p>\n<p>            # \u68c0\u6d4b\u95ee\u9898<br \/>\n            if len(chain) &gt; 5:<br \/>\n                report[&#039;issues&#039;].append(f&#034;Long chain detected for {url}: {len(chain)} redirects&#034;)<\/p>\n<p>            if chain and &#039;error&#039; in chain[-1]:<br \/>\n                report[&#039;issues&#039;].append(f&#034;Error for {url}: {chain[-1][&#039;error&#039;]}&#034;)<\/p>\n<p>        return report<\/p>\n<p># \u4f7f\u7528\u793a\u4f8b<br \/>\nanalyzer &#061; RedirectAnalyzer()<br \/>\nanalyzer.analyze_url(&#039;https:\/\/example.com\/old-page&#039;)<br \/>\nreport &#061; analyzer.generate_report()<br \/>\nprint(report) <\/p>\n<h6>10.5.4 \u6027\u80fd\u4f18\u5316\u7b56\u7565<\/h6>\n<p>\u51cf\u5c11\u91cd\u5b9a\u5411\u94fe\u957f\u5ea6&#xff1a;<\/p>\n<p>text<\/p>\n<p># \u4e0d\u4f73&#xff1a;\u591a\u5c42\u91cd\u5b9a\u5411<br \/>\n\u539f\u59cbURL \u2192 A \u2192 B \u2192 C \u2192 \u76ee\u6807<\/p>\n<p># \u4f18\u5316&#xff1a;\u76f4\u63a5\u91cd\u5b9a\u5411<br \/>\n\u539f\u59cbURL \u2192 \u76ee\u6807 <\/p>\n<p>\u8fde\u63a5\u590d\u7528\u4f18\u5316&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u786e\u4fdd\u91cd\u5b9a\u5411\u5728\u540c\u4e00\u57df\u540d\u4e0b<\/p>\n<\/li>\n<li>\n<p>\u4f7f\u7528HTTP\/2\u6216HTTP\/3\u7684\u591a\u8def\u590d\u7528<\/p>\n<\/li>\n<li>\n<p>\u9884\u8fde\u63a5\u4f18\u5316<\/p>\n<\/li>\n<\/ul>\n<p>\u7f13\u5b58\u7b56\u7565\u4f18\u5316&#xff1a;<\/p>\n<p>http<\/p>\n<p># \u5bf9\u6c38\u4e45\u91cd\u5b9a\u5411\u8bbe\u7f6e\u957f\u7f13\u5b58<br \/>\nHTTP\/1.1 301 Moved Permanently<br \/>\nLocation: \/new-url<br \/>\nCache-Control: public, max-age&#061;31536000  # 1\u5e74 <\/p>\n<p>CDN\u8fb9\u7f18\u91cd\u5b9a\u5411&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u5728CDN\u8fb9\u7f18\u8282\u70b9\u5904\u7406\u91cd\u5b9a\u5411<\/p>\n<\/li>\n<li>\n<p>\u51cf\u5c11\u56de\u6e90\u5ef6\u8fdf<\/p>\n<\/li>\n<li>\n<p>\u5206\u5e03\u5f0f\u5904\u7406<\/p>\n<\/li>\n<\/ul>\n<h6>10.5.5 \u5b89\u5168\u8003\u8651<\/h6>\n<p>\u91cd\u5b9a\u5411\u5b89\u5168\u5a01\u80c1&#xff1a;<\/p>\n<li>\n<p>\u5f00\u653e\u91cd\u5b9a\u5411\u6f0f\u6d1e&#xff1a;<\/p>\n<p>http<\/p>\n<p> # \u5371\u9669\u7684\u91cd\u5b9a\u5411\u5b9e\u73b0<br \/>\nGET \/redirect?url&#061;https:\/\/evil.com HTTP\/1.1<\/p>\n<p>HTTP\/1.1 302 Found<br \/>\nLocation: https:\/\/evil.com  # \u7528\u6237\u88ab\u91cd\u5b9a\u5411\u5230\u6076\u610f\u7f51\u7ad9 <\/li>\n<li>\n<p>\u9632\u5fa1\u63aa\u65bd&#xff1a;<\/p>\n<p>python<\/p>\n<p> def safe_redirect(request, default_url&#061;&#039;\/home&#039;):<br \/>\n    target &#061; request.GET.get(&#039;url&#039;, &#039;&#039;)<\/p>\n<p>    # \u9a8c\u8bc1\u76ee\u6807URL<br \/>\n    if not target:<br \/>\n        return redirect(default_url)<\/p>\n<p>    # \u767d\u540d\u5355\u9a8c\u8bc1<br \/>\n    allowed_domains &#061; [&#039;example.com&#039;, &#039;trusted-site.com&#039;]<br \/>\n    parsed_url &#061; urlparse(target)<\/p>\n<p>    if parsed_url.netloc not in allowed_domains:<br \/>\n        return redirect(default_url)<\/p>\n<p>    # \u9632\u6b62\u534f\u8bae\u6ee5\u7528<br \/>\n    if parsed_url.scheme not in [&#039;http&#039;, &#039;https&#039;]:<br \/>\n        return redirect(default_url)<\/p>\n<p>    return redirect(target) <\/li>\n<li>\n<p>HTTP\u4e25\u683c\u4f20\u8f93\u5b89\u5168&#xff08;HSTS&#xff09;&#xff1a;<\/p>\n<p>http<\/p>\n<p> # \u9632\u6b62SSL\u5265\u79bb\u653b\u51fb<br \/>\nStrict-Transport-Security: max-age&#061;31536000; includeSubDomains; preload <\/li>\n<p>\u654f\u611f\u4fe1\u606f\u4fdd\u62a4&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u91cd\u5b9a\u5411\u65f6\u4e0d\u4f20\u9012Authorization\u5934<\/p>\n<\/li>\n<li>\n<p>\u4f7f\u7528Referrer-Policy\u63a7\u5236referrer\u4fe1\u606f<\/p>\n<\/li>\n<li>\n<p>\u5bf9\u654f\u611f\u64cd\u4f5c\u4f7f\u7528POST&#043;303\u800c\u975eGET\u91cd\u5b9a\u5411<\/p>\n<\/li>\n<\/ul>\n<h4>\u7b2c11\u7ae0&#xff1a;301 Moved Permanently &#8211; \u6c38\u4e45\u91cd\u5b9a\u5411<\/h4>\n<h5>11.1 \u89c4\u8303\u5b9a\u4e49\u4e0e\u6280\u672f\u7ec6\u8282<\/h5>\n<h6>11.1.1 RFC\u89c4\u8303\u6f14\u8fdb<\/h6>\n<p>RFC 2616 (HTTP\/1.1, 1999)&#xff1a;<\/p>\n<p>text<\/p>\n<p>10.3.2 301 Moved Permanently<\/p>\n<p>The requested resource has been assigned a new permanent URI and any<br \/>\nfuture references to this resource SHOULD use one of the returned URIs. <\/p>\n<p>RFC 7231 (HTTP\/1.1 Semantics, 2014) &#8211; \u73b0\u884c\u6807\u51c6&#xff1a;<\/p>\n<p>text<\/p>\n<p>6.4.2. 301 Moved Permanently<\/p>\n<p>The 301 (Moved Permanently) status code indicates that the target<br \/>\nresource has been assigned a new permanent URI and any future<br \/>\nreferences to this resource ought to use one of the enclosed URIs. <\/p>\n<p>\u5173\u952e\u53d8\u5316&#xff1a;<\/p>\n<ul>\n<li>\n<p>&#034;SHOULD&#034;\u53d8\u4e3a&#034;ought to&#034;&#xff08;\u8bed\u6c14\u66f4\u5f3a&#xff09;<\/p>\n<\/li>\n<li>\n<p>\u5f3a\u8c03\u672a\u6765\u5f15\u7528\u90fd\u5e94\u4f7f\u7528\u65b0URI<\/p>\n<\/li>\n<li>\n<p>\u660e\u786e\u7f13\u5b58\u548c\u4ee3\u7406\u5904\u7406\u8981\u6c42<\/p>\n<\/li>\n<\/ul>\n<h6>11.1.2 \u6280\u672f\u7279\u6027\u8be6\u89e3<\/h6>\n<p>\u8bed\u4e49\u7279\u5f81&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u6c38\u4e45\u6027&#xff1a;\u8d44\u6e90\u4f4d\u7f6e\u6c38\u4e45\u6539\u53d8<\/p>\n<\/li>\n<li>\n<p>\u4e0d\u53ef\u9006\u6027&#xff1a;\u65e7URI\u4e0d\u5e94\u518d\u88ab\u4f7f\u7528<\/p>\n<\/li>\n<li>\n<p>\u53ef\u94fe\u63a5\u6027&#xff1a;\u65b0URI\u5e94\u88ab\u89c6\u4e3a\u8d44\u6e90\u7684\u89c4\u8303\u5730\u5740<\/p>\n<\/li>\n<\/ul>\n<p>\u65b9\u6cd5\u4fdd\u6301\u8981\u6c42&#xff1a;<\/p>\n<p>http<\/p>\n<p>POST \/old-endpoint HTTP\/1.1<br \/>\nContent-Type: application\/json<br \/>\nContent-Length: 32<\/p>\n<p>{&#034;action&#034;: &#034;create&#034;, &#034;data&#034;: &#034;value&#034;}<\/p>\n<p>HTTP\/1.1 301 Moved Permanently<br \/>\nLocation: \/new-endpoint <\/p>\n<p>\u89c4\u8303\u8981\u6c42&#xff1a;\u5ba2\u6237\u7aef\u5e94\u4fdd\u6301POST\u65b9\u6cd5\u91cd\u65b0\u8bf7\u6c42 \u73b0\u5b9e\u60c5\u51b5&#xff1a;\u8bb8\u591a\u6d4f\u89c8\u5668\u5c06POST\u8f6c\u4e3aGET&#xff08;\u5386\u53f2\u539f\u56e0&#xff09;<\/p>\n<p>\u7f13\u5b58\u884c\u4e3a\u89c4\u8303&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u5ba2\u6237\u7aef\u53ef\u65e0\u9650\u671f\u7f13\u5b58\u91cd\u5b9a\u5411<\/p>\n<\/li>\n<li>\n<p>\u4ee3\u7406\u670d\u52a1\u5668\u5e94\u66f4\u65b0\u7f13\u5b58\u6620\u5c04<\/p>\n<\/li>\n<li>\n<p>\u7f13\u5b58\u65f6\u95f4\u5efa\u8bae&#xff1a;1\u5e74\u6216\u66f4\u957f<\/p>\n<\/li>\n<\/ul>\n<h5>11.2 \u4f7f\u7528\u573a\u666f\u4e0e\u6700\u4f73\u5b9e\u8df5<\/h5>\n<h6>11.2.1 \u7f51\u7ad9\u91cd\u6784\u4e0e\u8fc1\u79fb<\/h6>\n<p>\u57df\u540d\u53d8\u66f4\u573a\u666f&#xff1a;<\/p>\n<p>nginx<\/p>\n<p># old-example.com \u2192 new-example.com \u5168\u9762\u8fc1\u79fb<br \/>\nserver {<br \/>\n    listen 80;<br \/>\n    server_name old-example.com www.old-example.com;<br \/>\n    return 301 https:\/\/new-example.com$request_uri;<br \/>\n}<\/p>\n<p># \u4fdd\u7559\u8def\u5f84\u7ed3\u6784<br \/>\nserver {<br \/>\n    listen 80;<br \/>\n    server_name old-example.com;<br \/>\n    location \/ {<br \/>\n        return 301 https:\/\/new-example.com$request_uri;<br \/>\n    }<br \/>\n} <\/p>\n<p>\u8def\u5f84\u7ed3\u6784\u8c03\u6574&#xff1a;<\/p>\n<p>apache<\/p>\n<p># \u6241\u5e73\u5316URL\u7ed3\u6784\u91cd\u5b9a\u5411<br \/>\nRedirectMatch 301 ^\/products\/category\/([^\/]&#043;)\/([^\/]&#043;)$ \/$1-$2<\/p>\n<p># \u65e5\u671f\u683c\u5f0f\u6807\u51c6\u5316<br \/>\nRedirectMatch 301 ^\/blog\/([0-9]{4})\/([0-9]{1,2})\/([0-9]{1,2})\/(.*)$ \\\\<br \/>\n              \/articles\/$1\/$2\/$3\/$4 <\/p>\n<p>\u534f\u8bae\u5347\u7ea7&#xff08;HTTP \u2192 HTTPS&#xff09;&#xff1a;<\/p>\n<p>apache<\/p>\n<p>&lt;VirtualHost *:80&gt;<br \/>\n    ServerName example.com<br \/>\n    ServerAlias www.example.com<\/p>\n<p>    # 301\u91cd\u5b9a\u5411\u5230HTTPS<br \/>\n    RewriteEngine On<br \/>\n    RewriteCond %{HTTPS} off<br \/>\n    RewriteRule ^(.*)$ https:\/\/%{HTTP_HOST}$1 [R&#061;301,L]<\/p>\n<p>    # \u6216\u8005\u4f7f\u7528Redirect\u6307\u4ee4<br \/>\n    Redirect permanent \/ https:\/\/example.com\/<br \/>\n&lt;\/VirtualHost&gt; <\/p>\n<h6>11.2.2 URL\u89c4\u8303\u5316\u4e0eSEO\u4f18\u5316<\/h6>\n<p>www\u4e0e\u975ewww\u89c4\u8303\u5316&#xff1a;<\/p>\n<p>nginx<\/p>\n<p># \u65b9\u68481&#xff1a;www\u91cd\u5b9a\u5411\u5230\u975ewww<br \/>\nserver {<br \/>\n    listen 80;<br \/>\n    server_name www.example.com;<br \/>\n    return 301 $scheme:\/\/example.com$request_uri;<br \/>\n}<\/p>\n<p>server {<br \/>\n    listen 80;<br \/>\n    server_name example.com;<br \/>\n    # \u4e3b\u7ad9\u70b9\u914d\u7f6e<br \/>\n}<\/p>\n<p># \u65b9\u68482&#xff1a;\u975ewww\u91cd\u5b9a\u5411\u5230www<br \/>\nserver {<br \/>\n    listen 80;<br \/>\n    server_name example.com;<br \/>\n    return 301 $scheme:\/\/www.example.com$request_uri;<br \/>\n} <\/p>\n<p>\u5c3e\u90e8\u659c\u6760\u89c4\u8303\u5316&#xff1a;<\/p>\n<p>nginx<\/p>\n<p># \u786e\u4fdd\u4e00\u81f4\u6027&#xff1a;\u65e0\u659c\u6760 \u2192 \u6709\u659c\u6760<br \/>\nlocation ~ ^(\/[^.\/]&#043;)$ {<br \/>\n    return 301 $scheme:\/\/$http_host$1\/;<br \/>\n}<\/p>\n<p># \u6216\u8005&#xff1a;\u6709\u659c\u6760 \u2192 \u65e0\u659c\u6760<br \/>\nlocation ~ ^(.&#043;)\/$ {<br \/>\n    return 301 $scheme:\/\/$http_host$1;<br \/>\n} <\/p>\n<p>\u5927\u5c0f\u5199\u89c4\u8303\u5316&#xff1a;<\/p>\n<p>nginx<\/p>\n<p># \u5c06\u6240\u6709URL\u8f6c\u4e3a\u5c0f\u5199<br \/>\nif ($request_uri ~ [A-Z]) {<br \/>\n    rewrite ^(.*)$ $scheme:\/\/$host${lowercase:$1} permanent;<br \/>\n} <\/p>\n<p>\u91cd\u590d\u5185\u5bb9\u5408\u5e76&#xff1a;<\/p>\n<p>apache<\/p>\n<p># \u591a\u4e2aURL\u6307\u5411\u540c\u4e00\u5185\u5bb9\u65f6\u9009\u62e9\u4e3b\u7248\u672c<br \/>\n&lt;IfModule mod_rewrite.c&gt;<br \/>\n    RewriteEngine On<\/p>\n<p>    # \u53c2\u6570\u89c4\u8303\u5316<br \/>\n    RewriteCond %{QUERY_STRING} ^id&#061;123&amp;lang&#061;en$<br \/>\n    RewriteRule ^product\\\\.php$ \/products\/123? [R&#061;301,L]<\/p>\n<p>    # \u6392\u5e8f\u53c2\u6570\u5ffd\u7565<br \/>\n    RewriteCond %{QUERY_STRING} ^(.*)&amp;?sort&#061;[^&amp;]&#043;&amp;?(.*)$<br \/>\n    RewriteRule ^(.*)$ \/$1?%1%2 [R&#061;301,L]<br \/>\n&lt;\/IfModule&gt; <\/p>\n<h6>11.2.3 API\u7248\u672c\u7ba1\u7406<\/h6>\n<p>API\u7aef\u70b9\u8fc1\u79fb&#xff1a;<\/p>\n<p>python<\/p>\n<p># Django API\u7248\u672c\u8fc1\u79fb\u793a\u4f8b<br \/>\nfrom django.http import JsonResponse, HttpResponsePermanentRedirect<br \/>\nfrom django.views.decorators.http import require_http_methods<\/p>\n<p>&#064;require_http_methods([&#034;GET&#034;, &#034;POST&#034;, &#034;PUT&#034;, &#034;DELETE&#034;])<br \/>\ndef legacy_api_v1(request, resource_id&#061;None):<br \/>\n    &#034;&#034;&#034;<br \/>\n    API v1 \u7aef\u70b9 &#8211; \u5df2\u5f03\u7528&#xff0c;\u91cd\u5b9a\u5411\u5230v2<br \/>\n    &#034;&#034;&#034;<br \/>\n    # \u6784\u5efav2\u7aef\u70b9URL<br \/>\n    path &#061; request.path.replace(&#039;\/api\/v1\/&#039;, &#039;\/api\/v2\/&#039;)<\/p>\n<p>    # \u5bf9\u4e8eGET\u8bf7\u6c42&#xff0c;\u76f4\u63a5\u91cd\u5b9a\u5411<br \/>\n    if request.method &#061;&#061; &#039;GET&#039;:<br \/>\n        return HttpResponsePermanentRedirect(path)<\/p>\n<p>    # \u5bf9\u4e8e\u5176\u4ed6\u65b9\u6cd5&#xff0c;\u8fd4\u56de\u8be6\u7ec6\u9519\u8bef\u4fe1\u606f<br \/>\n    return JsonResponse({<br \/>\n        &#039;error&#039;: &#039;API v1 is deprecated&#039;,<br \/>\n        &#039;message&#039;: &#039;Please use API v2&#039;,<br \/>\n        &#039;new_endpoint&#039;: path,<br \/>\n        &#039;docs&#039;: &#039;https:\/\/api.example.com\/docs\/v2&#039;<br \/>\n    }, status&#061;301) <\/p>\n<p>\u7248\u672c\u63a7\u5236\u7b56\u7565&#xff1a;<\/p>\n<p>yaml<\/p>\n<p># API\u7248\u672c\u63a7\u5236\u914d\u7f6e\u793a\u4f8b<br \/>\napi_redirects:<br \/>\n  v1_to_v2:<br \/>\n    pattern: &#034;^\/api\/v1\/(.*)&#034;<br \/>\n    replacement: &#034;\/api\/v2\/$1&#034;<br \/>\n    status: 301<br \/>\n    methods: [&#034;GET&#034;, &#034;HEAD&#034;, &#034;OPTIONS&#034;]<br \/>\n    exceptions:<br \/>\n      &#8211; &#034;\/api\/v1\/admin\/&#034;  # \u7ba1\u7406\u7aef\u70b9\u4e0d\u91cd\u5b9a\u5411<\/p>\n<p>  v2_to_v3:<br \/>\n    pattern: &#034;^\/api\/v2\/(.*)&#034;<br \/>\n    replacement: &#034;\/api\/v3\/$1&#034;<br \/>\n    status: 301<br \/>\n    sunset: &#034;2024-12-31&#034;  # \u65e5\u843d\u65f6\u95f4 <\/p>\n<h5>11.3 \u54cd\u5e94\u5934\u4e0e\u7f13\u5b58\u63a7\u5236<\/h5>\n<h6>11.3.1 \u6807\u51c6\u54cd\u5e94\u5934\u914d\u7f6e<\/h6>\n<p>\u57fa\u7840\u914d\u7f6e\u793a\u4f8b&#xff1a;<\/p>\n<p>http<\/p>\n<p>HTTP\/1.1 301 Moved Permanently<br \/>\nLocation: https:\/\/example.com\/new-path<br \/>\nDate: Mon, 21 Oct 2024 09:00:00 GMT<br \/>\nServer: nginx\/1.18.0<br \/>\nContent-Type: text\/html; charset&#061;utf-8<br \/>\nContent-Length: 178<br \/>\nConnection: close <\/p>\n<p>\u4f18\u5316\u914d\u7f6e&#xff1a;<\/p>\n<p>http<\/p>\n<p>HTTP\/1.1 301 Moved Permanently<br \/>\nLocation: https:\/\/example.com\/new-path<br \/>\nCache-Control: public, max-age&#061;31536000  # 1\u5e74\u7f13\u5b58<br \/>\nExpires: Tue, 21 Oct 2025 09:00:00 GMT<br \/>\nDate: Mon, 21 Oct 2024 09:00:00 GMT<br \/>\nServer: nginx\/1.18.0<br \/>\nContent-Type: text\/html; charset&#061;utf-8<br \/>\nContent-Length: 178<br \/>\nX-Redirect-By: ExampleApp\/2.0<br \/>\nX-Redirect-Reason: Site migration 2024 <\/p>\n<h6>11.3.2 \u7f13\u5b58\u7b56\u7565\u8be6\u89e3<\/h6>\n<p>\u5ba2\u6237\u7aef\u7f13\u5b58\u63a7\u5236&#xff1a;<\/p>\n<p>http<\/p>\n<p># \u957f\u671f\u7f13\u5b58&#xff08;\u63a8\u8350&#xff09;<br \/>\nCache-Control: public, max-age&#061;31536000, immutable<\/p>\n<p># \u4e2d\u7b49\u7f13\u5b58<br \/>\nCache-Control: public, max-age&#061;86400  # 1\u5929<\/p>\n<p># \u4e0d\u7f13\u5b58&#xff08;\u7279\u6b8a\u60c5\u51b5&#xff09;<br \/>\nCache-Control: no-cache, no-store, must-revalidate <\/p>\n<p>\u4ee3\u7406\u670d\u52a1\u5668\u7f13\u5b58&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u900f\u660e\u4ee3\u7406&#xff1a;\u9075\u5faa\u5ba2\u6237\u7aef\u7f13\u5b58\u6307\u4ee4<\/p>\n<\/li>\n<li>\n<p>\u663e\u5f0f\u4ee3\u7406&#xff1a;\u53ef\u80fd\u5b9e\u73b0\u81ea\u5df1\u7684\u7f13\u5b58\u7b56\u7565<\/p>\n<\/li>\n<li>\n<p>CDN\u8fb9\u7f18\u7f13\u5b58&#xff1a;\u914d\u7f6e\u4f20\u64ad\u548c\u5931\u6548\u7b56\u7565<\/p>\n<\/li>\n<\/ul>\n<p>\u7f13\u5b58\u5931\u6548\u7b56\u7565&#xff1a;<\/p>\n<p>nginx<\/p>\n<p># Nginx\u914d\u7f6e\u7f13\u5b58\u5934<br \/>\nlocation ~ &#034;^\/old\/(.*)$&#034; {<br \/>\n    add_header Cache-Control &#034;public, max-age&#061;31536000&#034;;<br \/>\n    add_header Expires $expires;<\/p>\n<p>    # \u6839\u636e\u6761\u4ef6\u8c03\u6574\u7f13\u5b58<br \/>\n    if ($args ~ &#034;nocache&#034;) {<br \/>\n        add_header Cache-Control &#034;no-cache&#034;;<br \/>\n    }<\/p>\n<p>    return 301 \/new\/$1;<br \/>\n} <\/p>\n<h6>11.3.3 \u54cd\u5e94\u4f53\u8bbe\u8ba1<\/h6>\n<p>\u7528\u6237\u53cb\u597d\u7684HTML\u54cd\u5e94&#xff1a;<\/p>\n<p>html<\/p>\n<p>&lt;!DOCTYPE html&gt;<br \/>\n&lt;html lang&#061;&#034;en&#034;&gt;<br \/>\n&lt;head&gt;<br \/>\n    &lt;meta charset&#061;&#034;UTF-8&#034;&gt;<br \/>\n    &lt;meta name&#061;&#034;viewport&#034; content&#061;&#034;width&#061;device-width, initial-scale&#061;1.0&#034;&gt;<br \/>\n    &lt;title&gt;Page Moved Permanently&lt;\/title&gt;<br \/>\n    &lt;style&gt;<br \/>\n        body {<br \/>\n            font-family: Arial, sans-serif;<br \/>\n            max-width: 600px;<br \/>\n            margin: 50px auto;<br \/>\n            padding: 20px;<br \/>\n        }<br \/>\n        .redirect-message {<br \/>\n            background: #f8f9fa;<br \/>\n            padding: 20px;<br \/>\n            border-radius: 5px;<br \/>\n            border-left: 4px solid #007bff;<br \/>\n        }<br \/>\n    &lt;\/style&gt;<br \/>\n&lt;\/head&gt;<br \/>\n&lt;body&gt;<br \/>\n    &lt;div class&#061;&#034;redirect-message&#034;&gt;<br \/>\n        &lt;h1&gt;Page Moved&lt;\/h1&gt;<br \/>\n        &lt;p&gt;This page has been permanently moved to a new location.&lt;\/p&gt;<br \/>\n        &lt;p&gt;You are being redirected to:<br \/>\n           &lt;a href&#061;&#034;https:\/\/example.com\/new-path&#034;&gt;https:\/\/example.com\/new-path&lt;\/a&gt;<br \/>\n        &lt;\/p&gt;<br \/>\n        &lt;p&gt;If you are not redirected automatically, please click the link above.&lt;\/p&gt;<br \/>\n        &lt;p&gt;&lt;small&gt;HTTP 301 Moved Permanently&lt;\/small&gt;&lt;\/p&gt;<br \/>\n    &lt;\/div&gt;<\/p>\n<p>    &lt;script&gt;<br \/>\n        \/\/ \u5907\u7528\u91cd\u5b9a\u5411\u673a\u5236<br \/>\n        setTimeout(function() {<br \/>\n            window.location.href &#061; &#034;https:\/\/example.com\/new-path&#034;;<br \/>\n        }, 5000);<br \/>\n    &lt;\/script&gt;<br \/>\n&lt;\/body&gt;<br \/>\n&lt;\/html&gt; <\/p>\n<p>\u673a\u5668\u53ef\u8bfb\u7684\u54cd\u5e94\u683c\u5f0f&#xff1a;<\/p>\n<p>JSON\u54cd\u5e94&#xff08;API\u573a\u666f&#xff09;&#xff1a;<\/p>\n<p>json<\/p>\n<p>{<br \/>\n  &#034;status&#034;: 301,<br \/>\n  &#034;message&#034;: &#034;Resource moved permanently&#034;,<br \/>\n  &#034;new_location&#034;: &#034;https:\/\/api.example.com\/v2\/resource\/123&#034;,<br \/>\n  &#034;documentation&#034;: &#034;https:\/\/docs.example.com\/api\/migration-guide&#034;,<br \/>\n  &#034;deprecated&#034;: true,<br \/>\n  &#034;sunset_date&#034;: &#034;2024-12-31T23:59:59Z&#034;<br \/>\n} <\/p>\n<p>XML\u54cd\u5e94&#xff1a;<\/p>\n<p>xml<\/p>\n<p>&lt;?xml version&#061;&#034;1.0&#034; encoding&#061;&#034;UTF-8&#034;?&gt;<br \/>\n&lt;redirect&gt;<br \/>\n    &lt;status&gt;301&lt;\/status&gt;<br \/>\n    &lt;message&gt;Moved Permanently&lt;\/message&gt;<br \/>\n    &lt;location&gt;https:\/\/example.com\/new-path&lt;\/location&gt;<br \/>\n    &lt;cache&gt;<br \/>\n        &lt;duration&gt;31536000&lt;\/duration&gt;<br \/>\n        &lt;unit&gt;seconds&lt;\/unit&gt;<br \/>\n    &lt;\/cache&gt;<br \/>\n    &lt;timestamp&gt;2024-10-21T09:00:00Z&lt;\/timestamp&gt;<br \/>\n&lt;\/redirect&gt; <\/p>\n<h5>11.4 SEO\u5f71\u54cd\u4e0e\u4f18\u5316\u7b56\u7565<\/h5>\n<h6>11.4.1 \u641c\u7d22\u5f15\u64ce\u5904\u7406\u673a\u5236<\/h6>\n<p>Google\u641c\u7d22\u7684301\u5904\u7406&#xff1a;<\/p>\n<li>\n<p>\u722c\u866b\u53d1\u73b0&#xff1a;Googlebot\u9047\u5230301\u72b6\u6001\u7801<\/p>\n<\/li>\n<li>\n<p>\u7d22\u5f15\u66f4\u65b0&#xff1a;\u5c06\u65e7URL\u66ff\u6362\u4e3a\u65b0URL<\/p>\n<\/li>\n<li>\n<p>\u6743\u91cd\u4f20\u9012&#xff1a;PageRank\u548c\u94fe\u63a5\u6743\u76ca\u4f20\u9012<\/p>\n<\/li>\n<li>\n<p>\u7d22\u5f15\u6e05\u7406&#xff1a;\u65e7URL\u4ece\u7d22\u5f15\u4e2d\u79fb\u9664&#xff08;\u9700\u8981\u65f6\u95f4&#xff09;<\/p>\n<\/li>\n<p>\u4f20\u9012\u56e0\u7d20&#xff1a;<\/p>\n<ul>\n<li>\n<p>90-99%\u7684PageRank\u4f20\u9012&#xff08;\u6839\u636eGoogle\u5b98\u65b9&#xff09;<\/p>\n<\/li>\n<li>\n<p>\u951a\u6587\u672c\u548c\u4e0a\u4e0b\u6587\u4fe1\u53f7<\/p>\n<\/li>\n<li>\n<p>\u6293\u53d6\u9884\u7b97\u5206\u914d\u8c03\u6574<\/p>\n<\/li>\n<\/ul>\n<p>\u65f6\u95f4\u7ebf&#xff1a;<\/p>\n<p>text<\/p>\n<p>\u53d1\u73b0301 \u2192 \u91cd\u65b0\u6293\u53d6\u65b0URL \u2192 \u9a8c\u8bc1\u5173\u7cfb \u2192<br \/>\n\u4f20\u9012\u6743\u91cd \u2192 \u66f4\u65b0\u7d22\u5f15 \u2192 \u6e05\u7406\u65e7URL<br \/>\n\u6574\u4e2a\u8fc7\u7a0b&#xff1a;\u6570\u5929\u5230\u6570\u5468 <\/p>\n<h6>11.4.2 \u6700\u4f73SEO\u5b9e\u8df5<\/h6>\n<p>\u8fc1\u79fb\u68c0\u67e5\u6e05\u5355&#xff1a;<\/p>\n<li>\n<p>\u524d\u671f\u51c6\u5907&#xff1a;<\/p>\n<p>bash<\/p>\n<p> # 1. \u521b\u5efa\u91cd\u5b9a\u5411\u6620\u5c04\u8868<br \/>\nold_url,new_url,status_code<br \/>\n\/old-page,\/new-page,301<br \/>\n\/products\/item1,\/items\/product-1,301<\/p>\n<p># 2. \u9a8c\u8bc1\u65b0URL\u53ef\u8bbf\u95ee<br \/>\ncurl -I https:\/\/example.com\/new-page<\/p>\n<p># 3. \u68c0\u67e5\u91cd\u5b9a\u5411\u94fe\u957f\u5ea6<br \/>\ncurl -L &#8211;max-redirs 10 -I https:\/\/example.com\/old-page <\/li>\n<li>\n<p>\u5b9e\u65bd\u76d1\u63a7&#xff1a;<\/p>\n<p>python<\/p>\n<p> # \u76d1\u63a7\u91cd\u5b9a\u5411\u7684SEO\u5f71\u54cd<br \/>\nimport requests<br \/>\nfrom datetime import datetime<\/p>\n<p>class RedirectMonitor:<br \/>\n    def check_redirect_status(self, url):<br \/>\n        try:<br \/>\n            response &#061; requests.head(url, allow_redirects&#061;True)<br \/>\n            final_url &#061; response.url<br \/>\n            status_history &#061; [r.status_code for r in response.history]<\/p>\n<p>            return {<br \/>\n                &#039;original_url&#039;: url,<br \/>\n                &#039;final_url&#039;: final_url,<br \/>\n                &#039;status_chain&#039;: status_history,<br \/>\n                &#039;redirect_count&#039;: len(response.history),<br \/>\n                &#039;checked_at&#039;: datetime.now().isoformat()<br \/>\n            }<br \/>\n        except Exception as e:<br \/>\n            return {&#039;error&#039;: str(e), &#039;url&#039;: url} <\/li>\n<li>\n<p>\u5de5\u5177\u9a8c\u8bc1&#xff1a;<\/p>\n<ul>\n<li>\n<p>Google Search Console\u7684URL\u68c0\u67e5\u5de5\u5177<\/p>\n<\/li>\n<li>\n<p>Screaming Frog SEO Spider\u6279\u91cf\u68c0\u67e5<\/p>\n<\/li>\n<li>\n<p>Ahrefs\/SEMrush\u7684\u7ad9\u70b9\u5ba1\u8ba1<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<p>\u907f\u514d\u5e38\u89c1SEO\u9519\u8bef&#xff1a;<\/p>\n<li>\n<p>\u91cd\u5b9a\u5411\u94fe\u8fc7\u957f&#xff1a;<\/p>\n<p>text<\/p>\n<p> # \u9519\u8bef&#xff1a;\u591a\u91cd\u8df3\u8f6c<br \/>\nA \u2192 B \u2192 C \u2192 D \u2192 E<\/p>\n<p># \u6b63\u786e&#xff1a;\u76f4\u63a5\u91cd\u5b9a\u5411<br \/>\nA \u2192 E <\/li>\n<li>\n<p>\u5faa\u73af\u91cd\u5b9a\u5411&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u4f7f\u7528\u5de5\u5177\u68c0\u6d4b\u5faa\u73af<\/p>\n<\/li>\n<li>\n<p>\u5b9a\u671f\u5ba1\u8ba1\u91cd\u5b9a\u5411\u89c4\u5219<\/p>\n<\/li>\n<li>\n<p>\u5b9e\u73b0\u76d1\u63a7\u544a\u8b66<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p>\u4e22\u5931\u53c2\u6570\u6216\u7247\u6bb5&#xff1a;<\/p>\n<p>nginx<\/p>\n<p> # \u9519\u8bef&#xff1a;\u4e22\u5931\u67e5\u8be2\u53c2\u6570<br \/>\nlocation \/search {<br \/>\n    return 301 \/new-search;  # \u4e22\u5931?q&#061;keyword<br \/>\n}<\/p>\n<p># \u6b63\u786e&#xff1a;\u4fdd\u7559\u53c2\u6570<br \/>\nlocation \/search {<br \/>\n    return 301 \/new-search$is_args$args;<br \/>\n} <\/li>\n<h6>11.4.3 \u8fc1\u79fb\u573a\u666f\u7684\u7279\u6b8a\u8003\u8651<\/h6>\n<p>\u5927\u89c4\u6a21\u7f51\u7ad9\u8fc1\u79fb&#xff1a;<\/p>\n<li>\n<p>\u5206\u9636\u6bb5\u5b9e\u65bd&#xff1a;<img loading=\"lazy\" decoding=\"async\" alt=\"\" height=\"1194\" src=\"https:\/\/www.wsisp.com\/helps\/wp-content\/uploads\/2026\/03\/20260228233637-69a37c054762a.png\" width=\"3066\" \/><\/p>\n<\/li>\n<li>\n<p>\u76d1\u63a7\u6307\u6807&#xff1a;<\/p>\n<p>yaml<\/p>\n<p> migration_metrics:<br \/>\n  crawl_errors: &lt; 0.1%  # \u6293\u53d6\u9519\u8bef\u7387<br \/>\n  redirect_chains:<br \/>\n    max_length: 2       # \u6700\u5927\u91cd\u5b9a\u5411\u94fe\u957f\u5ea6<br \/>\n    avg_length: 1.2     # \u5e73\u5747\u94fe\u957f\u5ea6<br \/>\n  index_coverage:<br \/>\n    old_urls_in_index: decreasing  # \u65e7URL\u7d22\u5f15\u6570\u91cf\u4e0b\u964d<br \/>\n    new_urls_in_index: increasing  # \u65b0URL\u7d22\u5f15\u6570\u91cf\u4e0a\u5347<br \/>\n  performance:<br \/>\n    redirect_time_p95: &lt; 200ms     # 95%\u5206\u4f4d\u91cd\u5b9a\u5411\u65f6\u95f4<br \/>\n    success_rate: &gt; 99.9%          # \u91cd\u5b9a\u5411\u6210\u529f\u7387 <\/li>\n<p>\u591a\u8bed\u8a00\/\u591a\u5730\u533a\u7f51\u7ad9&#xff1a;<\/p>\n<p>nginx<\/p>\n<p># \u591a\u8bed\u8a00\u91cd\u5b9a\u5411\u7b56\u7565<br \/>\nmap $http_accept_language $lang_redirect {<br \/>\n    default        &#034;&#034;;<br \/>\n    ~*^zh          &#034;\/zh-CN&#034;;<br \/>\n    ~*^en          &#034;\/en-US&#034;;<br \/>\n    ~*^ja          &#034;\/ja-JP&#034;;<br \/>\n}<\/p>\n<p>server {<br \/>\n    listen 80;<br \/>\n    server_name example.com;<\/p>\n<p>    # \u6839\u636e\u6d4f\u89c8\u5668\u8bed\u8a00\u91cd\u5b9a\u5411<br \/>\n    if ($lang_redirect) {<br \/>\n        return 301 $scheme:\/\/$http_host$lang_redirect$request_uri;<br \/>\n    }<\/p>\n<p>    # \u9ed8\u8ba4\u8bed\u8a00<br \/>\n    return 301 $scheme:\/\/$http_host\/en-US$request_uri;<br \/>\n} <\/p>\n<h5>11.5 \u914d\u7f6e\u65b9\u6cd5\u4e0e\u5b9e\u9645\u6848\u4f8b<\/h5>\n<h6>11.5.1 \u4e3b\u6d41\u670d\u52a1\u5668\u914d\u7f6e<\/h6>\n<p>Apache\u8be6\u7ec6\u914d\u7f6e&#xff1a;<\/p>\n<p>apache<\/p>\n<p># .htaccess \u6c38\u4e45\u91cd\u5b9a\u5411\u914d\u7f6e<\/p>\n<p># \u542f\u7528\u91cd\u5199\u5f15\u64ce<br \/>\nRewriteEngine On<\/p>\n<p># 1. \u57df\u540d\u53d8\u66f4&#xff08;\u5b8c\u6574\u8fc1\u79fb&#xff09;<br \/>\nRewriteCond %{HTTP_HOST} ^old-domain\\\\.com$ [NC,OR]<br \/>\nRewriteCond %{HTTP_HOST} ^www\\\\.old-domain\\\\.com$ [NC]<br \/>\nRewriteRule ^(.*)$ https:\/\/new-domain.com\/$1 [R&#061;301,L]<\/p>\n<p># 2. HTTP\u8f6cHTTPS<br \/>\nRewriteCond %{HTTPS} off<br \/>\nRewriteRule ^(.*)$ https:\/\/%{HTTP_HOST}\/$1 [R&#061;301,L]<\/p>\n<p># 3. www\u89c4\u8303\u5316<br \/>\nRewriteCond %{HTTP_HOST} ^example\\\\.com$ [NC]<br \/>\nRewriteRule ^(.*)$ https:\/\/www.example.com\/$1 [R&#061;301,L]<\/p>\n<p># 4. \u8def\u5f84\u91cd\u5b9a\u5411&#xff08;\u5e26\u901a\u914d\u7b26&#xff09;<br \/>\nRewriteRule ^old-path\/(.*)$ \/new-path\/$1 [R&#061;301,L]<\/p>\n<p># 5. \u67e5\u8be2\u53c2\u6570\u5904\u7406<br \/>\nRewriteCond %{QUERY_STRING} ^id&#061;([0-9]&#043;)$<br \/>\nRewriteRule ^product\\\\.php$ \/products\/%1? [R&#061;301,L]<\/p>\n<p># 6. \u6761\u4ef6\u91cd\u5b9a\u5411&#xff08;\u57fa\u4e8eIP\u6216UA&#xff09;<br \/>\nRewriteCond %{REMOTE_ADDR} ^192\\\\.168\\\\.1\\\\.<br \/>\nRewriteRule ^(.*)$ \/internal\/$1 [R&#061;301,L]<\/p>\n<p># 7. \u6392\u9664\u67d0\u4e9b\u8def\u5f84<br \/>\nRewriteCond %{REQUEST_URI} !^\/(admin|api)\/<br \/>\nRewriteRule ^blog\/(.*)$ \/articles\/$1 [R&#061;301,L]<\/p>\n<p># 8. \u4fdd\u7559\u6216\u4fee\u6539\u67e5\u8be2\u53c2\u6570<br \/>\nRewriteCond %{QUERY_STRING} ^(.&#043;)&amp;page&#061;([0-9]&#043;)&amp;?(.*)$<br \/>\nRewriteRule ^search$ \/search\/%2?%1%3 [R&#061;301,L] <\/p>\n<p>Nginx\u9ad8\u7ea7\u914d\u7f6e&#xff1a;<\/p>\n<p>nginx<\/p>\n<p># nginx.conf \u6c38\u4e45\u91cd\u5b9a\u5411\u914d\u7f6e<\/p>\n<p># \u6620\u5c04\u8868\u9a71\u52a8\u91cd\u5b9a\u5411<br \/>\nmap $request_uri $new_uri {<br \/>\n    include \/etc\/nginx\/redirects.map;<br \/>\n}<\/p>\n<p>server {<br \/>\n    listen 80;<br \/>\n    server_name example.com;<\/p>\n<p>    # \u4f7f\u7528\u6620\u5c04\u8868\u91cd\u5b9a\u5411<br \/>\n    if ($new_uri) {<br \/>\n        return 301 $new_uri;<br \/>\n    }<\/p>\n<p>    # \u57fa\u4e8e\u53d8\u91cf\u7684\u91cd\u5b9a\u5411<br \/>\n    set $redirect_reason &#034;&#034;;<\/p>\n<p>    # \u534f\u8bae\u5347\u7ea7<br \/>\n    if ($scheme &#061; &#034;http&#034;) {<br \/>\n        set $redirect_reason &#034;https_upgrade&#034;;<br \/>\n        return 301 https:\/\/$server_name$request_uri;<br \/>\n    }<\/p>\n<p>    # \u57df\u540d\u89c4\u8303\u5316<br \/>\n    if ($host &#061; &#034;example.com&#034;) {<br \/>\n        set $redirect_reason &#034;www_canonical&#034;;<br \/>\n        return 301 https:\/\/www.$host$request_uri;<br \/>\n    }<\/p>\n<p>    # \u8def\u5f84\u6a21\u5f0f\u91cd\u5b9a\u5411<br \/>\n    location ~ &#034;^\/legacy\/(.*)&#034; {<br \/>\n        # \u8bb0\u5f55\u91cd\u5b9a\u5411\u65e5\u5fd7<br \/>\n        access_log \/var\/log\/nginx\/redirect.log redirect_log;<\/p>\n<p>        # \u8bbe\u7f6e\u7f13\u5b58\u5934<br \/>\n        add_header Cache-Control &#034;public, max-age&#061;31536000&#034;;<\/p>\n<p>        # \u6267\u884c\u91cd\u5b9a\u5411<br \/>\n        return 301 \/modern\/$1;<br \/>\n    }<\/p>\n<p>    # \u6b63\u5219\u8868\u8fbe\u5f0f\u91cd\u5b9a\u5411<br \/>\n    location ~* &#034;^\/category\/([^\/]&#043;)\/([^\/]&#043;)$&#034; {<br \/>\n        return 301 \/shop\/$1-$2;<br \/>\n    }<\/p>\n<p>    # \u4ee3\u7406\u91cd\u5b9a\u5411&#xff08;\u590d\u6742\u573a\u666f&#xff09;<br \/>\n    location \/api\/old\/ {<br \/>\n        proxy_pass http:\/\/backend;<br \/>\n        proxy_redirect \/api\/new\/ \/api\/old\/;<br \/>\n    }<br \/>\n}<\/p>\n<p># \u81ea\u5b9a\u4e49\u65e5\u5fd7\u683c\u5f0f<br \/>\nlog_format redirect_log &#039;$remote_addr &#8211; $remote_user [$time_local] &#039;<br \/>\n                       &#039;&#034;$request&#034; $status $body_bytes_sent &#039;<br \/>\n                       &#039;&#034;$http_referer&#034; &#034;$http_user_agent&#034; &#039;<br \/>\n                       &#039;&#034;$redirect_reason&#034; &#034;$new_uri&#034;&#039;; <\/p>\n<p>IIS\u8be6\u7ec6\u914d\u7f6e&#xff1a;<\/p>\n<p>xml<\/p>\n<p>&lt;!&#8211; web.config \u6c38\u4e45\u91cd\u5b9a\u5411\u914d\u7f6e &#8211;&gt;<br \/>\n&lt;configuration&gt;<br \/>\n  &lt;system.webServer&gt;<br \/>\n    &lt;rewrite&gt;<br \/>\n      &lt;rules&gt;<br \/>\n        &lt;!&#8211; \u57df\u540d\u91cd\u5b9a\u5411 &#8211;&gt;<br \/>\n        &lt;rule name&#061;&#034;Domain Redirect&#034; stopProcessing&#061;&#034;true&#034;&gt;<br \/>\n          &lt;match url&#061;&#034;.*&#034; \/&gt;<br \/>\n          &lt;conditions logicalGrouping&#061;&#034;MatchAny&#034;&gt;<br \/>\n            &lt;add input&#061;&#034;{HTTP_HOST}&#034; pattern&#061;&#034;^old-domain\\\\.com$&#034; \/&gt;<br \/>\n            &lt;add input&#061;&#034;{HTTP_HOST}&#034; pattern&#061;&#034;^www\\\\.old-domain\\\\.com$&#034; \/&gt;<br \/>\n          &lt;\/conditions&gt;<br \/>\n          &lt;action type&#061;&#034;Redirect&#034; url&#061;&#034;https:\/\/new-domain.com\/{R:0}&#034;<br \/>\n                  redirectType&#061;&#034;Permanent&#034; \/&gt;<br \/>\n        &lt;\/rule&gt;<\/p>\n<p>        &lt;!&#8211; HTTP\u8f6cHTTPS &#8211;&gt;<br \/>\n        &lt;rule name&#061;&#034;Force HTTPS&#034; stopProcessing&#061;&#034;true&#034;&gt;<br \/>\n          &lt;match url&#061;&#034;(.*)&#034; \/&gt;<br \/>\n          &lt;conditions&gt;<br \/>\n            &lt;add input&#061;&#034;{HTTPS}&#034; pattern&#061;&#034;^OFF$&#034; \/&gt;<br \/>\n          &lt;\/conditions&gt;<br \/>\n          &lt;action type&#061;&#034;Redirect&#034; url&#061;&#034;https:\/\/{HTTP_HOST}\/{R:1}&#034;<br \/>\n                  redirectType&#061;&#034;Permanent&#034; \/&gt;<br \/>\n        &lt;\/rule&gt;<\/p>\n<p>        &lt;!&#8211; \u8def\u5f84\u91cd\u5b9a\u5411 &#8211;&gt;<br \/>\n        &lt;rule name&#061;&#034;Redirect old pages&#034; stopProcessing&#061;&#034;true&#034;&gt;<br \/>\n          &lt;match url&#061;&#034;^old-folder\/(.*)$&#034; \/&gt;<br \/>\n          &lt;action type&#061;&#034;Redirect&#034; url&#061;&#034;\/new-folder\/{R:1}&#034;<br \/>\n                  redirectType&#061;&#034;Permanent&#034; \/&gt;<br \/>\n        &lt;\/rule&gt;<\/p>\n<p>        &lt;!&#8211; \u67e5\u8be2\u53c2\u6570\u91cd\u5b9a\u5411 &#8211;&gt;<br \/>\n        &lt;rule name&#061;&#034;Product Redirect&#034; stopProcessing&#061;&#034;true&#034;&gt;<br \/>\n          &lt;match url&#061;&#034;^product\\\\.aspx$&#034; \/&gt;<br \/>\n          &lt;conditions&gt;<br \/>\n            &lt;add input&#061;&#034;{QUERY_STRING}&#034; pattern&#061;&#034;^id&#061;([0-9]&#043;)$&#034; \/&gt;<br \/>\n          &lt;\/conditions&gt;<br \/>\n          &lt;action type&#061;&#034;Redirect&#034; url&#061;&#034;\/products\/{C:1}&#034;<br \/>\n                  redirectType&#061;&#034;Permanent&#034; \/&gt;<br \/>\n        &lt;\/rule&gt;<br \/>\n      &lt;\/rules&gt;<\/p>\n<p>      &lt;!&#8211; \u51fa\u7ad9\u89c4\u5219&#xff08;\u4fee\u6539\u54cd\u5e94&#xff09; &#8211;&gt;<br \/>\n      &lt;outboundRules&gt;<br \/>\n        &lt;rule name&#061;&#034;Add Redirect Header&#034;&gt;<br \/>\n          &lt;match serverVariable&#061;&#034;RESPONSE_X_Redirected_By&#034; pattern&#061;&#034;.*&#034; \/&gt;<br \/>\n          &lt;action type&#061;&#034;Rewrite&#034; value&#061;&#034;IIS-Rewrite-Module&#034; \/&gt;<br \/>\n        &lt;\/rule&gt;<br \/>\n      &lt;\/outboundRules&gt;<br \/>\n    &lt;\/rewrite&gt;<\/p>\n<p>    &lt;!&#8211; HTTP\u54cd\u5e94\u5934 &#8211;&gt;<br \/>\n    &lt;httpProtocol&gt;<br \/>\n      &lt;customHeaders&gt;<br \/>\n        &lt;add name&#061;&#034;X-Redirect-Type&#034; value&#061;&#034;Permanent&#034; \/&gt;<br \/>\n        &lt;add name&#061;&#034;Cache-Control&#034; value&#061;&#034;public, max-age&#061;31536000&#034; \/&gt;<br \/>\n      &lt;\/customHeaders&gt;<br \/>\n    &lt;\/httpProtocol&gt;<br \/>\n  &lt;\/system.webServer&gt;<br \/>\n&lt;\/configuration&gt; <\/p>\n<h6>11.5.2 \u4e91\u5e73\u53f0\u914d\u7f6e<\/h6>\n<p>AWS S3\u9759\u6001\u7f51\u7ad9\u91cd\u5b9a\u5411&#xff1a;<\/p>\n<p>json<\/p>\n<p>{<br \/>\n  &#034;RedirectAllRequestsTo&#034;: {<br \/>\n    &#034;HostName&#034;: &#034;new-domain.com&#034;,<br \/>\n    &#034;Protocol&#034;: &#034;https&#034;<br \/>\n  }<br \/>\n}<\/p>\n<p>\/\/ \u6216\u5355\u4e2a\u5bf9\u8c61\u91cd\u5b9a\u5411<br \/>\n{<br \/>\n  &#034;IndexDocument&#034;: {<br \/>\n    &#034;Suffix&#034;: &#034;index.html&#034;<br \/>\n  },<br \/>\n  &#034;RoutingRules&#034;: [<br \/>\n    {<br \/>\n      &#034;Condition&#034;: {<br \/>\n        &#034;KeyPrefixEquals&#034;: &#034;old-path\/&#034;<br \/>\n      },<br \/>\n      &#034;Redirect&#034;: {<br \/>\n        &#034;HostName&#034;: &#034;new-domain.com&#034;,<br \/>\n        &#034;ReplaceKeyPrefixWith&#034;: &#034;new-path\/&#034;,<br \/>\n        &#034;HttpRedirectCode&#034;: &#034;301&#034;,<br \/>\n        &#034;Protocol&#034;: &#034;https&#034;<br \/>\n      }<br \/>\n    }<br \/>\n  ]<br \/>\n} <\/p>\n<p>Cloudflare\u9875\u9762\u89c4\u5219&#xff1a;<\/p>\n<p>text<\/p>\n<p>1. \u6a21\u5f0f: http:\/\/example.com\/old\/*<br \/>\n  \u8bbe\u7f6e: \u8f6c\u53d1URL<br \/>\n  \u72b6\u6001\u7801: 301 &#8211; \u6c38\u4e45\u91cd\u5b9a\u5411<br \/>\n  \u76ee\u6807URL: https:\/\/example.com\/new\/$1<\/p>\n<p>2. \u6a21\u5f0f: http:\/\/example.com\/*<br \/>\n  \u8bbe\u7f6e: \u5f3a\u5236HTTPS<br \/>\n  \u72b6\u6001\u7801: 301<\/p>\n<p>3. \u6a21\u5f0f: example.com\/legacy\/*<br \/>\n  \u8bbe\u7f6e: \u91cd\u5b9a\u5411<br \/>\n  \u76ee\u6807: https:\/\/example.com\/modern\/$1 <\/p>\n<p>Azure App Service&#xff1a;<\/p>\n<p>json<\/p>\n<p>{<br \/>\n  &#034;httpPlatform&#034;: {<br \/>\n    &#034;redirectRules&#034;: [<br \/>\n      {<br \/>\n        &#034;source&#034;: &#034;\/old\/&lt;segment&gt;&#034;,<br \/>\n        &#034;destination&#034;: &#034;\/new\/&lt;segment&gt;&#034;,<br \/>\n        &#034;permanent&#034;: true<br \/>\n      }<br \/>\n    ]<br \/>\n  }<br \/>\n} <\/p>\n<h6>11.5.3 \u5b9e\u9645\u6848\u4f8b\u5206\u6790<\/h6>\n<p>\u6848\u4f8b1&#xff1a;\u7535\u5546\u7f51\u7ad9URL\u7ed3\u6784\u91cd\u6784<\/p>\n<p>nginx<\/p>\n<p># \u65e7\u7ed3\u6784&#xff1a;\/product.php?id&#061;123&amp;category&#061;electronics<br \/>\n# \u65b0\u7ed3\u6784&#xff1a;\/products\/electronics\/smartphone-123<\/p>\n<p># \u91cd\u5b9a\u5411\u89c4\u5219<br \/>\nlocation &#061; \/product.php {<br \/>\n    if ($arg_id ~ &#034;^([0-9]&#043;)$&#034;) {<br \/>\n        # \u67e5\u8be2\u6570\u636e\u5e93\u83b7\u53d6\u4ea7\u54c1\u4fe1\u606f&#xff08;\u4f2a\u4ee3\u7801&#xff09;<br \/>\n        # set $product_slug get_product_slug($1);<br \/>\n        # set $category_slug get_category_slug($arg_category);<\/p>\n<p>        return 301 \/products\/$category_slug\/$product_slug-$1;<br \/>\n    }<br \/>\n    return 404;<br \/>\n}<\/p>\n<p># \u5907\u7528\u65b9\u6848&#xff1a;\u4f7f\u7528\u6620\u5c04\u6587\u4ef6<br \/>\nmap $arg_id $product_redirect {<br \/>\n    include \/etc\/nginx\/product_redirects.map;<br \/>\n}<\/p>\n<p>location &#061; \/product.php {<br \/>\n    if ($product_redirect) {<br \/>\n        return 301 $product_redirect;<br \/>\n    }<br \/>\n    return 410; # Gone<br \/>\n} <\/p>\n<p>\u6848\u4f8b2&#xff1a;\u591a\u5730\u533a\u7f51\u7ad9\u5408\u5e76<\/p>\n<p>apache<\/p>\n<p># \u5408\u5e76\u591a\u4e2a\u5730\u533a\u5b50\u57df\u540d\u5230\u4e3b\u57df\u540d\u5b50\u76ee\u5f55<br \/>\nRewriteCond %{HTTP_HOST} ^uk\\\\.example\\\\.com$ [NC]<br \/>\nRewriteRule ^(.*)$ https:\/\/example.com\/en-gb\/$1 [R&#061;301,L]<\/p>\n<p>RewriteCond %{HTTP_HOST} ^fr\\\\.example\\\\.com$ [NC]<br \/>\nRewriteRule ^(.*)$ https:\/\/example.com\/fr-fr\/$1 [R&#061;301,L]<\/p>\n<p>RewriteCond %{HTTP_HOST} ^de\\\\.example\\\\.com$ [NC]<br \/>\nRewriteRule ^(.*)$ https:\/\/example.com\/de-de\/$1 [R&#061;301,L]<\/p>\n<p># \u5904\u7406\u8bed\u8a00\u53c2\u6570<br \/>\nRewriteCond %{QUERY_STRING} (^|&amp;)lang&#061;([^&amp;]&#043;) [NC]<br \/>\nRewriteRule ^(.*)$ https:\/\/example.com\/%2\/$1? [R&#061;301,L] <\/p>\n<p>\u6848\u4f8b3&#xff1a;API\u7248\u672c\u7ba1\u7406<\/p>\n<p>python<\/p>\n<p># Flask API\u7248\u672c\u91cd\u5b9a\u5411<br \/>\nfrom flask import Flask, redirect, request<br \/>\nimport re<\/p>\n<p>app &#061; Flask(__name__)<\/p>\n<p># API\u7248\u672c\u91cd\u5b9a\u5411\u6620\u5c04<br \/>\nAPI_REDIRECTS &#061; {<br \/>\n    &#039;v1&#039;: {<br \/>\n        &#039;pattern&#039;: r&#039;^\/api\/v1\/(.*)&#039;,<br \/>\n        &#039;replacement&#039;: &#039;\/api\/v2\/\\\\\\\\1&#039;,<br \/>\n        &#039;status&#039;: 301,<br \/>\n        &#039;exclude&#039;: [&#039;\/api\/v1\/admin\/&#039;, &#039;\/api\/v1\/status&#039;]<br \/>\n    },<br \/>\n    &#039;v2&#039;: {<br \/>\n        &#039;pattern&#039;: r&#039;^\/api\/v2\/(.*)&#039;,<br \/>\n        &#039;replacement&#039;: &#039;\/api\/v3\/\\\\\\\\1&#039;,<br \/>\n        &#039;status&#039;: 301,<br \/>\n        &#039;sunset&#039;: &#039;2024-12-31&#039;<br \/>\n    }<br \/>\n}<\/p>\n<p>&#064;app.before_request<br \/>\ndef handle_api_redirects():<br \/>\n    path &#061; request.path<\/p>\n<p>    for version, config in API_REDIRECTS.items():<br \/>\n        # \u68c0\u67e5\u6392\u9664\u8def\u5f84<br \/>\n        for exclude in config.get(&#039;exclude&#039;, []):<br \/>\n            if path.startswith(exclude):<br \/>\n                return None<\/p>\n<p>        # \u68c0\u67e5\u65e5\u843d\u65f6\u95f4<br \/>\n        if &#039;sunset&#039; in config:<br \/>\n            if datetime.now() &gt; parse_date(config[&#039;sunset&#039;]):<br \/>\n                continue<\/p>\n<p>        # \u6267\u884c\u91cd\u5b9a\u5411<br \/>\n        if re.match(config[&#039;pattern&#039;], path):<br \/>\n            new_path &#061; re.sub(config[&#039;pattern&#039;], config[&#039;replacement&#039;], path)<br \/>\n            return redirect(new_path, code&#061;config[&#039;status&#039;])<\/p>\n<p>    return None <\/p>\n<h5>11.6 \u6ce8\u610f\u4e8b\u9879\u4e0e\u6700\u4f73\u5b9e\u8df5<\/h5>\n<h6>11.6.1 \u6027\u80fd\u8003\u8651<\/h6>\n<p>\u91cd\u5b9a\u5411\u6027\u80fd\u5f71\u54cd&#xff1a;<\/p>\n<p>text<\/p>\n<p>\u539f\u59cb\u8bf7\u6c42\u65f6\u95f4 &#061; DNS &#043; TCP &#043; TLS &#043; \u8bf7\u6c42 &#043; \u54cd\u5e94<br \/>\n\u91cd\u5b9a\u5411\u65f6\u95f4 &#061; \u4e0a\u8ff0\u65f6\u95f4 \u00d7 (1 &#043; \u91cd\u5b9a\u5411\u6b21\u6570)<\/p>\n<p>\u4f18\u5316\u76ee\u6807&#xff1a;\u6700\u5c0f\u5316\u91cd\u5b9a\u5411\u94fe\u957f\u5ea6 <\/p>\n<p>\u6027\u80fd\u4f18\u5316\u7b56\u7565&#xff1a;<\/p>\n<li>\n<p>\u51cf\u5c11DNS\u67e5\u8be2&#xff1a;<\/p>\n<p>nginx<\/p>\n<p> # \u786e\u4fdd\u91cd\u5b9a\u5411\u5728\u540c\u4e00\u57df\u540d\u4e0b<br \/>\nreturn 301 https:\/\/same-domain.com\/new-path;<\/p>\n<p># \u907f\u514d\u8de8\u57df\u540d\u91cd\u5b9a\u5411&#xff08;\u589e\u52a0DNS\u67e5\u8be2&#xff09;<br \/>\nreturn 301 https:\/\/different-domain.com\/new-path;  # \u4e0d\u4f73 <\/li>\n<li>\n<p>\u8fde\u63a5\u590d\u7528&#xff1a;<\/p>\n<p>http<\/p>\n<p> # HTTP\/2\u548cHTTP\/3\u652f\u6301\u591a\u8def\u590d\u7528<br \/>\n# \u786e\u4fdd\u670d\u52a1\u5668\u652f\u6301\u6700\u65b0\u534f\u8bae<\/p>\n<p># \u4fdd\u6301alive\u8fde\u63a5<br \/>\nConnection: keep-alive<br \/>\nKeep-Alive: timeout&#061;30, max&#061;100 <\/li>\n<li>\n<p>\u9884\u8fde\u63a5\u63d0\u793a&#xff1a;<\/p>\n<p>html<\/p>\n<p> &lt;!&#8211; \u63d0\u793a\u6d4f\u89c8\u5668\u9884\u8fde\u63a5\u5230\u65b0\u57df\u540d &#8211;&gt;<br \/>\n&lt;link rel&#061;&#034;preconnect&#034; href&#061;&#034;https:\/\/new-domain.com&#034;&gt;<br \/>\n&lt;link rel&#061;&#034;dns-prefetch&#034; href&#061;&#034;https:\/\/new-domain.com&#034;&gt; <\/li>\n<h6>11.6.2 \u53ef\u7ef4\u62a4\u6027\u8003\u8651<\/h6>\n<p>\u91cd\u5b9a\u5411\u7ba1\u7406\u7b56\u7565&#xff1a;<\/p>\n<li>\n<p>\u96c6\u4e2d\u5316\u7ba1\u7406&#xff1a;<\/p>\n<p>yaml<\/p>\n<p> # redirects.yaml &#8211; \u96c6\u4e2d\u7ba1\u7406\u91cd\u5b9a\u5411\u89c4\u5219<br \/>\nredirects:<br \/>\n  &#8211; from: \/old-page<br \/>\n    to: \/new-page<br \/>\n    status: 301<br \/>\n    created: 2024-01-15<br \/>\n    reason: Site restructuring<br \/>\n    owner: web-team<\/p>\n<p>  &#8211; from: \/legacy\/(.*)<br \/>\n    to: \/modern\/$1<br \/>\n    status: 301<br \/>\n    created: 2024-01-20<br \/>\n    reason: URL normalization<br \/>\n    owner: seo-team<\/p>\n<p># \u751f\u6210\u914d\u7f6e\u6587\u4ef6<br \/>\npython generate_redirects.py redirects.yaml &gt; nginx_redirects.conf <\/li>\n<li>\n<p>\u7248\u672c\u63a7\u5236&#xff1a;<\/p>\n<p>bash<\/p>\n<p> # \u91cd\u5b9a\u5411\u914d\u7f6e\u7684Git\u7ba1\u7406<br \/>\n\/configs\/<br \/>\n\u251c\u2500\u2500 redirects\/<br \/>\n\u2502   \u251c\u2500\u2500 v1.0.0\/<br \/>\n\u2502   \u2502   \u251c\u2500\u2500 apache.conf<br \/>\n\u2502   \u2502   \u251c\u2500\u2500 nginx.conf<br \/>\n\u2502   \u2502   \u2514\u2500\u2500 iis.config<br \/>\n\u2502   \u251c\u2500\u2500 v1.1.0\/<br \/>\n\u2502   \u2514\u2500\u2500 current -&gt; v1.1.0\/<br \/>\n\u2514\u2500\u2500 scripts\/<br \/>\n    \u251c\u2500\u2500 validate_redirects.py<br \/>\n    \u2514\u2500\u2500 deploy_redirects.py <\/li>\n<li>\n<p>\u6587\u6863\u5316&#xff1a;<\/p>\n<p>markdown<\/p>\n<p> # \u91cd\u5b9a\u5411\u6587\u6863\u6a21\u677f<\/p>\n<p>## \u91cd\u5b9a\u5411\u89c4\u5219: \/old-path \u2192 \/new-path<\/p>\n<p>**\u57fa\u672c\u4fe1\u606f**<br \/>\n&#8211; \u72b6\u6001\u7801: 301<br \/>\n&#8211; \u521b\u5efa\u65e5\u671f: 2024-01-15<br \/>\n&#8211; \u8d1f\u8d23\u4eba: web-team&#064;example.com<\/p>\n<p>**\u6280\u672f\u7ec6\u8282**<br \/>\n&#8211; \u5339\u914d\u6a21\u5f0f: \u7cbe\u786e\u5339\u914d<br \/>\n&#8211; \u67e5\u8be2\u53c2\u6570\u5904\u7406: \u4fdd\u7559<br \/>\n&#8211; \u7f13\u5b58\u7b56\u7565: 1\u5e74<\/p>\n<p>**\u4e1a\u52a1\u539f\u56e0**<br \/>\n&#8211; \u539f\u56e0: \u7f51\u7ad9\u91cd\u6784<br \/>\n&#8211; \u9884\u671f\u6548\u679c: \u5408\u5e76\u91cd\u590d\u5185\u5bb9<br \/>\n&#8211; SEO\u5f71\u54cd: PageRank\u4f20\u9012<\/p>\n<p>**\u76d1\u63a7\u6307\u6807**<br \/>\n&#8211; \u4f7f\u7528\u91cf: 1000\u6b21\/\u5929<br \/>\n&#8211; \u9519\u8bef\u7387: &lt; 0.1%<br \/>\n&#8211; \u6700\u540e\u9a8c\u8bc1: 2024-10-21 <\/li>\n<h6>11.6.3 \u5b89\u5168\u8003\u8651<\/h6>\n<p>\u5b89\u5168\u6700\u4f73\u5b9e\u8df5&#xff1a;<\/p>\n<li>\n<p>\u8f93\u5165\u9a8c\u8bc1&#xff1a;<\/p>\n<p>python<\/p>\n<p> def safe_redirect_url(target, base_domain&#061;&#039;example.com&#039;):<br \/>\n    # \u89e3\u6790\u76ee\u6807URL<br \/>\n    parsed &#061; urlparse(target)<\/p>\n<p>    # \u9a8c\u8bc1\u534f\u8bae<br \/>\n    if parsed.scheme not in (&#039;http&#039;, &#039;https&#039;, &#039;&#039;):<br \/>\n        raise ValueError(&#039;Invalid scheme&#039;)<\/p>\n<p>    # \u9a8c\u8bc1\u57df\u540d<br \/>\n    if parsed.netloc and parsed.netloc !&#061; base_domain:<br \/>\n        # \u53ea\u5141\u8bb8\u91cd\u5b9a\u5411\u5230\u4fe1\u4efb\u7684\u57df\u540d<br \/>\n        allowed_domains &#061; get_trusted_domains()<br \/>\n        if parsed.netloc not in allowed_domains:<br \/>\n            raise ValueError(&#039;Untrusted domain&#039;)<\/p>\n<p>    # \u9632\u6b62\u5f00\u653e\u91cd\u5b9a\u5411<br \/>\n    if parsed.path.startswith(&#039;\/\/&#039;):<br \/>\n        raise ValueError(&#039;Invalid path&#039;)<\/p>\n<p>    return urljoin(f&#039;https:\/\/{base_domain}&#039;, target) <\/li>\n<li>\n<p>HTTP\u5b89\u5168\u5934&#xff1a;<\/p>\n<p>http<\/p>\n<p> HTTP\/1.1 301 Moved Permanently<br \/>\nLocation: \/safe-new-path<br \/>\nContent-Security-Policy: default-src &#039;self&#039;<br \/>\nX-Content-Type-Options: nosniff<br \/>\nX-Frame-Options: DENY<br \/>\nReferrer-Policy: strict-origin-when-cross-origin <\/li>\n<li>\n<p>\u654f\u611f\u4fe1\u606f\u4fdd\u62a4&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u91cd\u5b9a\u5411\u65f6\u4e0d\u4f20\u9012Authorization\u5934<\/p>\n<\/li>\n<li>\n<p>\u4f7f\u7528Referrer-Policy\u63a7\u5236referrer\u6cc4\u9732<\/p>\n<\/li>\n<li>\n<p>\u5bf9\u654f\u611f\u64cd\u4f5c\u4f7f\u7528POST&#043;303\u800c\u975eGET\u91cd\u5b9a\u5411<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<h6>11.6.4 \u76d1\u63a7\u4e0e\u544a\u8b66<\/h6>\n<p>\u76d1\u63a7\u6307\u6807\u8bbe\u8ba1&#xff1a;<\/p>\n<li>\n<p>\u5065\u5eb7\u68c0\u67e5&#xff1a;<\/p>\n<p>python<\/p>\n<p> class RedirectHealthCheck:<br \/>\n    def __init__(self):<br \/>\n        self.metrics &#061; {<br \/>\n            &#039;total_redirects&#039;: 0,<br \/>\n            &#039;failed_redirects&#039;: 0,<br \/>\n            &#039;redirect_times&#039;: [],<br \/>\n            &#039;chain_lengths&#039;: []<br \/>\n        }<\/p>\n<p>    def check_redirect(self, url, expected_target):<br \/>\n        start_time &#061; time.time()<\/p>\n<p>        try:<br \/>\n            response &#061; requests.head(<br \/>\n                url,<br \/>\n                allow_redirects&#061;True,<br \/>\n                timeout&#061;5<br \/>\n            )<\/p>\n<p>            elapsed &#061; time.time() &#8211; start_time<\/p>\n<p>            # \u8bb0\u5f55\u6307\u6807<br \/>\n            self.metrics[&#039;total_redirects&#039;] &#043;&#061; 1<br \/>\n            self.metrics[&#039;redirect_times&#039;].append(elapsed)<br \/>\n            self.metrics[&#039;chain_lengths&#039;].append(len(response.history))<\/p>\n<p>            # \u9a8c\u8bc1\u91cd\u5b9a\u5411\u76ee\u6807<br \/>\n            if response.url !&#061; expected_target:<br \/>\n                self.metrics[&#039;failed_redirects&#039;] &#043;&#061; 1<br \/>\n                return False, f&#034;Wrong target: {response.url}&#034;<\/p>\n<p>            return True, &#034;OK&#034;<\/p>\n<p>        except Exception as e:<br \/>\n            self.metrics[&#039;failed_redirects&#039;] &#043;&#061; 1<br \/>\n            return False, str(e) <\/li>\n<li>\n<p>\u544a\u8b66\u89c4\u5219&#xff1a;<\/p>\n<p>yaml<\/p>\n<p> alerts:<br \/>\n  redirect_failure_rate:<br \/>\n    condition: failed_redirects \/ total_redirects &gt; 0.01<br \/>\n    severity: warning<br \/>\n    message: &#034;\u91cd\u5b9a\u5411\u5931\u8d25\u7387\u8d85\u8fc71%&#034;<\/p>\n<p>  long_redirect_chains:<br \/>\n    condition: avg(chain_lengths) &gt; 2<br \/>\n    severity: warning<br \/>\n    message: &#034;\u5e73\u5747\u91cd\u5b9a\u5411\u94fe\u957f\u5ea6\u8d85\u8fc72&#034;<\/p>\n<p>  slow_redirects:<br \/>\n    condition: p95(redirect_times) &gt; 500<br \/>\n    severity: warning<br \/>\n    message: &#034;95%\u5206\u4f4d\u91cd\u5b9a\u5411\u65f6\u95f4\u8d85\u8fc7500ms&#034; <\/li>\n<li>\n<p>\u53ef\u89c6\u5316\u4eea\u8868\u677f&#xff1a;<\/p>\n<p>json<\/p>\n<p> {<br \/>\n  &#034;dashboard&#034;: {<br \/>\n    &#034;panels&#034;: [<br \/>\n      {<br \/>\n        &#034;title&#034;: &#034;\u91cd\u5b9a\u5411\u6210\u529f\u7387&#034;,<br \/>\n        &#034;type&#034;: &#034;stat&#034;,<br \/>\n        &#034;targets&#034;: [<br \/>\n          &#034;100 * (1 &#8211; failed_redirects \/ total_redirects)&#034;<br \/>\n        ],<br \/>\n        &#034;thresholds&#034;: [90, 95]<br \/>\n      },<br \/>\n      {<br \/>\n        &#034;title&#034;: &#034;\u91cd\u5b9a\u5411\u94fe\u957f\u5ea6\u5206\u5e03&#034;,<br \/>\n        &#034;type&#034;: &#034;histogram&#034;,<br \/>\n        &#034;targets&#034;: [&#034;chain_lengths&#034;]<br \/>\n      }<br \/>\n    ]<br \/>\n  }<br \/>\n} <\/li>\n<h4>\u7b2c12\u7ae0&#xff1a;302 Found &#8211; \u4e34\u65f6\u91cd\u5b9a\u5411<\/h4>\n<h5>12.1 \u5386\u53f2\u6f14\u53d8\u4e0e\u89c4\u8303\u4e89\u8bae<\/h5>\n<h6>12.1.1 \u5386\u53f2\u80cc\u666f\u4e0e\u95ee\u9898\u6839\u6e90<\/h6>\n<p>HTTP\/1.0\u65f6\u4ee3\u7684\u6df7\u4e71&#xff1a;<\/p>\n<ul>\n<li>\n<p>1996\u5e74RFC 1945\u9996\u6b21\u5b9a\u4e49302\u72b6\u6001\u7801<\/p>\n<\/li>\n<li>\n<p>\u89c4\u8303\u63cf\u8ff0\u6a21\u7cca&#xff1a;&#034;The requested resource resides temporarily under a different URI&#034;<\/p>\n<\/li>\n<li>\n<p>\u672a\u660e\u786e\u8bf4\u660e\u8bf7\u6c42\u65b9\u6cd5\u662f\u5426\u5e94\u8be5\u4fdd\u6301<\/p>\n<\/li>\n<\/ul>\n<p>\u6d4f\u89c8\u5668\u5b9e\u73b0\u7684\u5206\u6b67&#xff1a;<\/p>\n<p>http<\/p>\n<p>POST \/form-submit HTTP\/1.0<br \/>\nContent-Type: application\/x-www-form-urlencoded<\/p>\n<p>name&#061;John&amp;action&#061;submit<\/p>\n<p>HTTP\/1.0 302 Found<br \/>\nLocation: \/thank-you<\/p>\n<p># \u65e9\u671f\u6d4f\u89c8\u5668\u884c\u4e3a&#xff1a;<br \/>\n# Netscape Navigator: POST \u2192 GET (\u4e22\u5931\u8868\u5355\u6570\u636e)<br \/>\n# Internet Explorer: \u5c1d\u8bd5\u4fdd\u6301POST (\u4e0d\u4e00\u81f4)<br \/>\n# \u5176\u4ed6\u6d4f\u89c8\u5668: \u5404\u81ea\u5b9e\u73b0 <\/p>\n<p>RFC 2616 (1999)\u7684\u6f84\u6e05\u5c1d\u8bd5&#xff1a;<\/p>\n<p>text<\/p>\n<p>10.3.3 302 Found<\/p>\n<p>The requested resource resides temporarily under a different URI.<br \/>\nSince the redirection might be altered on occasion, the client SHOULD<br \/>\ncontinue to use the Request-URI for future requests. <\/p>\n<ul>\n<li>\n<p>\u660e\u786e\u5ba2\u6237\u7aef&#034;SHOULD&#034;\u7ee7\u7eed\u4f7f\u7528\u539f\u59cbURI<\/p>\n<\/li>\n<li>\n<p>\u4f46\u65b9\u6cd5\u4fdd\u6301\u95ee\u9898\u4ecd\u672a\u5f7b\u5e95\u89e3\u51b3<\/p>\n<\/li>\n<li>\n<p>\u5b9e\u9645\u6d4f\u89c8\u5668\u5b9e\u73b0\u7ee7\u7eed\u5c06POST\u8f6c\u4e3aGET<\/p>\n<\/li>\n<\/ul>\n<h6>12.1.2 303\/307\u7684\u5f15\u5165\u4e0e\u804c\u8d23\u5212\u5206<\/h6>\n<p>RFC 2616\u7684\u89e3\u51b3\u65b9\u6848&#xff1a;<\/p>\n<p>text<\/p>\n<p>10.3.4 303 See Other<br \/>\n10.3.8 307 Temporary Redirect <\/p>\n<p>\u804c\u8d23\u660e\u786e\u5212\u5206&#xff1a;<\/p>\n<table>\n<tr>\u72b6\u6001\u7801\u65b9\u6cd5\u4fdd\u6301\u5f15\u5165\u76ee\u7684<\/tr>\n<tbody>\n<tr>\n<td>302 Found<\/td>\n<td>\u4e0d\u660e\u786e<\/td>\n<td>\u5386\u53f2\u9057\u7559&#xff0c;\u4e34\u65f6\u91cd\u5b9a\u5411<\/td>\n<\/tr>\n<tr>\n<td>303 See Other<\/td>\n<td>\u8f6c\u4e3aGET<\/td>\n<td>POST\u540e\u663e\u793a\u7ed3\u679c<\/td>\n<\/tr>\n<tr>\n<td>307 Temporary Redirect<\/td>\n<td>\u4fdd\u6301\u65b9\u6cd5<\/td>\n<td>\u4e34\u65f6\u91cd\u5b9a\u5411\u4e14\u4fdd\u6301\u65b9\u6cd5<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>\u73b0\u5b9e\u4e16\u754c\u7684\u56f0\u60d1&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u5927\u91cf\u73b0\u6709\u4ee3\u7801\u4f7f\u7528302<\/p>\n<\/li>\n<li>\n<p>\u6d4f\u89c8\u5668\u5bf9302\u7684\u884c\u4e3a\u5df2&#034;\u6807\u51c6\u5316&#034;\u4e3aPOST\u2192GET<\/p>\n<\/li>\n<li>\n<p>\u5f00\u53d1\u8005\u4e60\u60ef\u96be\u4ee5\u6539\u53d8<\/p>\n<\/li>\n<\/ul>\n<h5>12.2 \u4f7f\u7528\u573a\u666f\u4e0e\u6700\u4f73\u5b9e\u8df5<\/h5>\n<h6>12.2.1 \u4e34\u65f6\u91cd\u5b9a\u5411\u7684\u9002\u7528\u573a\u666f<\/h6>\n<p>A\/B\u6d4b\u8bd5\u4e0e\u5b9e\u9a8c&#xff1a;<\/p>\n<p>nginx<\/p>\n<p># \u5c0630%\u7528\u6237\u91cd\u5b9a\u5411\u5230\u6d4b\u8bd5\u9875\u9762<br \/>\nmap $cookie_ab_test $ab_version {<br \/>\n    default &#034;control&#034;;<br \/>\n    &#034;test&#034; &#034;test&#034;;<br \/>\n}<\/p>\n<p>split_clients &#034;${remote_addr}${http_user_agent}&#034; $ab_group {<br \/>\n    30% &#034;test&#034;;<br \/>\n    70% &#034;control&#034;;<br \/>\n}<\/p>\n<p>server {<br \/>\n    location \/home {<br \/>\n        # \u8bbe\u7f6ecookie\u6216\u57fa\u4e8eIP\u5206\u914d<br \/>\n        add_header Set-Cookie &#034;ab_test&#061;$ab_group; Path&#061;\/; Max-Age&#061;3600&#034;;<\/p>\n<p>        if ($ab_group &#061; &#034;test&#034;) {<br \/>\n            return 302 \/home-new-design;<br \/>\n        }<\/p>\n<p>        # \u63a7\u5236\u7ec4\u7ee7\u7eed\u539f\u6709\u9875\u9762<br \/>\n        try_files $uri $uri\/ &#061;404;<br \/>\n    }<br \/>\n} <\/p>\n<p>\u5730\u7406\u91cd\u5b9a\u5411\u4e0e\u672c\u5730\u5316&#xff1a;<\/p>\n<p>python<\/p>\n<p># Django\u5730\u7406\u91cd\u5b9a\u5411\u793a\u4f8b<br \/>\nfrom django.shortcuts import redirect<br \/>\nfrom django.utils.translation import activate, get_language<br \/>\nfrom geoip2.database import Reader<\/p>\n<p>class GeoRedirectMiddleware:<br \/>\n    def __init__(self, get_response):<br \/>\n        self.get_response &#061; get_response<br \/>\n        self.geoip_reader &#061; Reader(&#039;\/path\/to\/GeoLite2-Country.mmdb&#039;)<\/p>\n<p>    def __call__(self, request):<br \/>\n        # \u68c0\u67e5\u662f\u5426\u5df2\u8bbe\u7f6e\u8bed\u8a00<br \/>\n        if &#039;preferred_language&#039; in request.session:<br \/>\n            activate(request.session[&#039;preferred_language&#039;])<br \/>\n            return self.get_response(request)<\/p>\n<p>        # \u57fa\u4e8eIP\u7684\u5730\u7406\u5b9a\u4f4d<br \/>\n        ip_address &#061; self.get_client_ip(request)<br \/>\n        try:<br \/>\n            response &#061; self.geoip_reader.country(ip_address)<br \/>\n            country_code &#061; response.country.iso_code<\/p>\n<p>            # \u56fd\u5bb6\u5230\u8bed\u8a00\u7684\u6620\u5c04<br \/>\n            country_to_lang &#061; {<br \/>\n                &#039;US&#039;: &#039;en-us&#039;,<br \/>\n                &#039;GB&#039;: &#039;en-gb&#039;,<br \/>\n                &#039;FR&#039;: &#039;fr&#039;,<br \/>\n                &#039;DE&#039;: &#039;de&#039;,<br \/>\n                &#039;JP&#039;: &#039;ja&#039;,<br \/>\n                &#039;CN&#039;: &#039;zh-cn&#039;,<br \/>\n                # &#8230; \u66f4\u591a\u6620\u5c04<br \/>\n            }<\/p>\n<p>            language &#061; country_to_lang.get(country_code, &#039;en-us&#039;)<\/p>\n<p>            # \u4e34\u65f6\u91cd\u5b9a\u5411\u5230\u672c\u5730\u5316\u7248\u672c<br \/>\n            if language !&#061; &#039;en-us&#039;:<br \/>\n                new_path &#061; f&#039;\/{language}{request.path}&#039;<br \/>\n                return redirect(new_path, status&#061;302)<\/p>\n<p>        except Exception:<br \/>\n            pass<\/p>\n<p>        return self.get_response(request) <\/p>\n<p>\u7ef4\u62a4\u9875\u9762\u91cd\u5b9a\u5411&#xff1a;<\/p>\n<p>html<\/p>\n<p>&lt;!DOCTYPE html&gt;<br \/>\n&lt;html&gt;<br \/>\n&lt;head&gt;<br \/>\n    &lt;title&gt;\u7cfb\u7edf\u7ef4\u62a4\u4e2d&lt;\/title&gt;<br \/>\n    &lt;meta http-equiv&#061;&#034;refresh&#034; content&#061;&#034;30;url&#061;\/&#034;&gt;<br \/>\n    &lt;style&gt;<br \/>\n        .maintenance {<br \/>\n            text-align: center;<br \/>\n            padding: 50px;<br \/>\n            font-family: Arial, sans-serif;<br \/>\n        }<br \/>\n        .countdown {<br \/>\n            font-size: 24px;<br \/>\n            color: #e74c3c;<br \/>\n            margin: 20px 0;<br \/>\n        }<br \/>\n    &lt;\/style&gt;<br \/>\n&lt;\/head&gt;<br \/>\n&lt;body&gt;<br \/>\n    &lt;div class&#061;&#034;maintenance&#034;&gt;<br \/>\n        &lt;h1&gt;&#x1f6e0;\ufe0f \u7cfb\u7edf\u7ef4\u62a4\u4e2d&lt;\/h1&gt;<br \/>\n        &lt;p&gt;\u6211\u4eec\u6b63\u5728\u5bf9\u7cfb\u7edf\u8fdb\u884c\u7ef4\u62a4\u5347\u7ea7&#xff0c;\u4ee5\u63d0\u4f9b\u66f4\u597d\u7684\u670d\u52a1\u3002&lt;\/p&gt;<br \/>\n        &lt;div class&#061;&#034;countdown&#034;&gt;30\u79d2\u540e\u81ea\u52a8\u8fd4\u56de\u9996\u9875&lt;\/div&gt;<br \/>\n        &lt;p&gt;\u9884\u8ba1\u5b8c\u6210\u65f6\u95f4: 2024-10-22 02:00 UTC&lt;\/p&gt;<br \/>\n        &lt;p&gt;&lt;a href&#061;&#034;\/&#034;&gt;\u70b9\u51fb\u6b64\u5904\u7acb\u5373\u8fd4\u56de&lt;\/a&gt;&lt;\/p&gt;<br \/>\n    &lt;\/div&gt;<\/p>\n<p>    &lt;script&gt;<br \/>\n        let seconds &#061; 30;<br \/>\n        const countdownEl &#061; document.querySelector(&#039;.countdown&#039;);<\/p>\n<p>        const timer &#061; setInterval(() &#061;&gt; {<br \/>\n            seconds&#8211;;<br \/>\n            countdownEl.textContent &#061; &#096;${seconds}\u79d2\u540e\u81ea\u52a8\u8fd4\u56de\u9996\u9875&#096;;<\/p>\n<p>            if (seconds &lt;&#061; 0) {<br \/>\n                clearInterval(timer);<br \/>\n                window.location.href &#061; &#039;\/&#039;;<br \/>\n            }<br \/>\n        }, 1000);<br \/>\n    &lt;\/script&gt;<br \/>\n&lt;\/body&gt;<br \/>\n&lt;\/html&gt; <\/p>\n<p>\u767b\u5f55\u72b6\u6001\u9a8c\u8bc1&#xff1a;<\/p>\n<p>python<\/p>\n<p># Flask\u767b\u5f55\u91cd\u5b9a\u5411\u793a\u4f8b<br \/>\nfrom flask import Flask, redirect, request, session, url_for<br \/>\nfrom functools import wraps<\/p>\n<p>app &#061; Flask(__name__)<br \/>\napp.secret_key &#061; &#039;your-secret-key&#039;<\/p>\n<p>def login_required(f):<br \/>\n    &#064;wraps(f)<br \/>\n    def decorated_function(*args, **kwargs):<br \/>\n        if &#039;user_id&#039; not in session:<br \/>\n            # \u4e34\u65f6\u91cd\u5b9a\u5411\u5230\u767b\u5f55\u9875&#xff0c;\u8bb0\u5f55\u539f\u59cb\u76ee\u6807<br \/>\n            session[&#039;next&#039;] &#061; request.url<br \/>\n            return redirect(url_for(&#039;login&#039;), code&#061;302)<br \/>\n        return f(*args, **kwargs)<br \/>\n    return decorated_function<\/p>\n<p>&#064;app.route(&#039;\/login&#039;, methods&#061;[&#039;GET&#039;, &#039;POST&#039;])<br \/>\ndef login():<br \/>\n    if request.method &#061;&#061; &#039;POST&#039;:<br \/>\n        # \u9a8c\u8bc1\u767b\u5f55\u903b\u8f91<br \/>\n        username &#061; request.form[&#039;username&#039;]<br \/>\n        password &#061; request.form[&#039;password&#039;]<\/p>\n<p>        if validate_credentials(username, password):<br \/>\n            session[&#039;user_id&#039;] &#061; username<\/p>\n<p>            # \u91cd\u5b9a\u5411\u56de\u539f\u59cb\u8bf7\u6c42\u9875\u9762<br \/>\n            next_page &#061; session.pop(&#039;next&#039;, None)<br \/>\n            if next_page:<br \/>\n                return redirect(next_page, code&#061;302)<br \/>\n            return redirect(url_for(&#039;dashboard&#039;), code&#061;302)<\/p>\n<p>    return &#039;&#039;&#039;<br \/>\n        &lt;form method&#061;&#034;post&#034;&gt;<br \/>\n            &lt;input type&#061;&#034;text&#034; name&#061;&#034;username&#034; placeholder&#061;&#034;\u7528\u6237\u540d&#034;&gt;<br \/>\n            &lt;input type&#061;&#034;password&#034; name&#061;&#034;password&#034; placeholder&#061;&#034;\u5bc6\u7801&#034;&gt;<br \/>\n            &lt;button type&#061;&#034;submit&#034;&gt;\u767b\u5f55&lt;\/button&gt;<br \/>\n        &lt;\/form&gt;<br \/>\n    &#039;&#039;&#039;<\/p>\n<p>&#064;app.route(&#039;\/dashboard&#039;)<br \/>\n&#064;login_required<br \/>\ndef dashboard():<br \/>\n    return &#034;\u6b22\u8fce\u6765\u5230\u63a7\u5236\u9762\u677f&#034; <\/p>\n<h6>12.2.2 302 vs 301\u7684\u51b3\u7b56\u6307\u5357<\/h6>\n<p>\u51b3\u7b56\u6d41\u7a0b\u56fe&#xff1a;<img loading=\"lazy\" decoding=\"async\" alt=\"\" height=\"1940\" src=\"https:\/\/www.wsisp.com\/helps\/wp-content\/uploads\/2026\/03\/20260228233638-69a37c06ea0c7.png\" width=\"1508\" \/><\/p>\n<p>\u5177\u4f53\u573a\u666f\u51b3\u7b56&#xff1a;<\/p>\n<li>\n<p>URL\u89c4\u8303\u5316&#xff08;www\/non-www&#xff09;&#xff1a;301<\/p>\n<\/li>\n<li>\n<p>HTTP \u2192 HTTPS&#xff1a;301<\/p>\n<\/li>\n<li>\n<p>\u7f51\u7ad9\u91cd\u6784&#xff08;\u6c38\u4e45&#xff09;&#xff1a;301<\/p>\n<\/li>\n<li>\n<p>A\/B\u6d4b\u8bd5&#xff1a;302<\/p>\n<\/li>\n<li>\n<p>\u5730\u7406\u91cd\u5b9a\u5411&#xff1a;302<\/p>\n<\/li>\n<li>\n<p>\u7ef4\u62a4\u9875\u9762&#xff1a;302<\/p>\n<\/li>\n<li>\n<p>\u767b\u5f55\u91cd\u5b9a\u5411&#xff1a;302<\/p>\n<\/li>\n<li>\n<p>\u8868\u5355\u63d0\u4ea4\u540e\u663e\u793a\u7ed3\u679c&#xff1a;303<\/p>\n<\/li>\n<li>\n<p>API\u4e34\u65f6\u8fc1\u79fb&#xff1a;307<\/p>\n<\/li>\n<li>\n<p>\u4e34\u65f6\u7ef4\u62a4\u7aef\u70b9&#xff1a;307<\/p>\n<\/li>\n<h6>12.2.3 \u54cd\u5e94\u5934\u914d\u7f6e<\/h6>\n<p>\u6807\u51c6\u54cd\u5e94\u5934&#xff1a;<\/p>\n<p>http<\/p>\n<p>HTTP\/1.1 302 Found<br \/>\nLocation: \/temporary-location<br \/>\nDate: Mon, 21 Oct 2024 09:00:00 GMT<br \/>\nServer: nginx\/1.18.0<br \/>\nContent-Type: text\/html; charset&#061;utf-8<br \/>\nContent-Length: 162<br \/>\nConnection: close <\/p>\n<p>\u4f18\u5316\u914d\u7f6e&#xff08;\u660e\u786e\u4e34\u65f6\u6027&#xff09;&#xff1a;<\/p>\n<p>http<\/p>\n<p>HTTP\/1.1 302 Found<br \/>\nLocation: \/temporary-location<br \/>\nCache-Control: private, no-cache, no-store, must-revalidate<br \/>\nPragma: no-cache<br \/>\nExpires: 0<br \/>\nX-Redirect-Type: Temporary<br \/>\nX-Redirect-Expires: 2024-10-22T02:00:00Z  # \u660e\u786e\u8fc7\u671f\u65f6\u95f4<br \/>\nDate: Mon, 21 Oct 2024 09:00:00 GMT <\/p>\n<p>HTML\u54cd\u5e94\u4f53\u793a\u4f8b&#xff1a;<\/p>\n<p>html<\/p>\n<p>&lt;!DOCTYPE html&gt;<br \/>\n&lt;html lang&#061;&#034;en&#034;&gt;<br \/>\n&lt;head&gt;<br \/>\n    &lt;meta charset&#061;&#034;UTF-8&#034;&gt;<br \/>\n    &lt;meta name&#061;&#034;viewport&#034; content&#061;&#034;width&#061;device-width, initial-scale&#061;1.0&#034;&gt;<br \/>\n    &lt;title&gt;Temporary Redirect&lt;\/title&gt;<br \/>\n    &lt;meta http-equiv&#061;&#034;refresh&#034; content&#061;&#034;0;url&#061;\/temporary-location&#034;&gt;<br \/>\n    &lt;style&gt;<br \/>\n        body {<br \/>\n            font-family: Arial, sans-serif;<br \/>\n            text-align: center;<br \/>\n            padding: 50px;<br \/>\n        }<br \/>\n        .redirect-info {<br \/>\n            background: #fff3cd;<br \/>\n            border: 1px solid #ffeaa7;<br \/>\n            border-radius: 5px;<br \/>\n            padding: 20px;<br \/>\n            margin: 20px auto;<br \/>\n            max-width: 600px;<br \/>\n        }<br \/>\n    &lt;\/style&gt;<br \/>\n&lt;\/head&gt;<br \/>\n&lt;body&gt;<br \/>\n    &lt;h1&gt;&#x1f4cb; \u4e34\u65f6\u91cd\u5b9a\u5411&lt;\/h1&gt;<br \/>\n    &lt;div class&#061;&#034;redirect-info&#034;&gt;<br \/>\n        &lt;p&gt;\u6b64\u9875\u9762\u5df2\u4e34\u65f6\u79fb\u52a8\u5230\u65b0\u4f4d\u7f6e\u3002&lt;\/p&gt;<br \/>\n        &lt;p&gt;\u60a8\u5c06\u5728 &lt;strong&gt;3\u79d2&lt;\/strong&gt; \u5185\u88ab\u81ea\u52a8\u91cd\u5b9a\u5411\u3002&lt;\/p&gt;<br \/>\n        &lt;p&gt;\u5982\u679c\u672a\u81ea\u52a8\u8df3\u8f6c&#xff0c;\u8bf7\u70b9\u51fb&#xff1a;&lt;\/p&gt;<br \/>\n        &lt;p&gt;&lt;a href&#061;&#034;\/temporary-location&#034;&gt;https:\/\/example.com\/temporary-location&lt;\/a&gt;&lt;\/p&gt;<br \/>\n        &lt;p&gt;&lt;small&gt;HTTP 302 Found &#8211; \u4e34\u65f6\u91cd\u5b9a\u5411&lt;\/small&gt;&lt;\/p&gt;<br \/>\n        &lt;p&gt;&lt;small&gt;\u9884\u8ba1\u6062\u590d\u6b63\u5e38\u65f6\u95f4: 2024-10-22 02:00 UTC&lt;\/small&gt;&lt;\/p&gt;<br \/>\n    &lt;\/div&gt;<\/p>\n<p>    &lt;script&gt;<br \/>\n        \/\/ \u5907\u7528\u91cd\u5b9a\u5411\u673a\u5236<br \/>\n        setTimeout(function() {<br \/>\n            window.location.href &#061; &#034;\/temporary-location&#034;;<br \/>\n        }, 3000);<br \/>\n    &lt;\/script&gt;<br \/>\n&lt;\/body&gt;<br \/>\n&lt;\/html&gt; <\/p>\n<h5>12.3 \u65b9\u6cd5\u4fdd\u6301\u95ee\u9898\u8be6\u89e3<\/h5>\n<h6>12.3.1 \u6d4f\u89c8\u5668\u884c\u4e3a\u5206\u6790<\/h6>\n<p>\u73b0\u4ee3\u6d4f\u89c8\u5668\u6d4b\u8bd5\u7ed3\u679c&#xff08;2024\u5e74&#xff09;&#xff1a;<\/p>\n<p>javascript<\/p>\n<p>\/\/ \u6d4b\u8bd5302\u91cd\u5b9a\u5411\u7684\u65b9\u6cd5\u4fdd\u6301\u884c\u4e3a<br \/>\nasync function testRedirectBehavior() {<br \/>\n  const testCases &#061; [<br \/>\n    { method: &#039;POST&#039;, data: { test: &#039;value&#039; } },<br \/>\n    { method: &#039;PUT&#039;, data: { update: &#039;data&#039; } },<br \/>\n    { method: &#039;DELETE&#039;, data: {} },<br \/>\n    { method: &#039;PATCH&#039;, data: { change: &#039;partial&#039; } }<br \/>\n  ];<\/p>\n<p>  const results &#061; {};<\/p>\n<p>  for (const testCase of testCases) {<br \/>\n    try {<br \/>\n      const response &#061; await fetch(&#039;\/test-redirect-endpoint&#039;, {<br \/>\n        method: testCase.method,<br \/>\n        headers: {<br \/>\n          &#039;Content-Type&#039;: &#039;application\/json&#039;,<br \/>\n          &#039;X-Test-Method&#039;: testCase.method<br \/>\n        },<br \/>\n        body: JSON.stringify(testCase.data)<br \/>\n      });<\/p>\n<p>      \/\/ \u68c0\u67e5\u6700\u7ec8\u8bf7\u6c42\u65b9\u6cd5<br \/>\n      const finalMethod &#061; response.headers.get(&#039;X-Final-Method&#039;);<br \/>\n      results[testCase.method] &#061; {<br \/>\n        original: testCase.method,<br \/>\n        final: finalMethod,<br \/>\n        changed: testCase.method !&#061;&#061; finalMethod<br \/>\n      };<br \/>\n    } catch (error) {<br \/>\n      results[testCase.method] &#061; { error: error.message };<br \/>\n    }<br \/>\n  }<\/p>\n<p>  return results;<br \/>\n}<\/p>\n<p>\/\/ \u5178\u578b\u7ed3\u679c&#xff08;\u57fa\u4e8e\u771f\u5b9e\u6d4f\u89c8\u5668\u6d4b\u8bd5&#xff09;&#xff1a;<br \/>\n\/*<br \/>\n{<br \/>\n  &#034;POST&#034;: { &#034;original&#034;: &#034;POST&#034;, &#034;final&#034;: &#034;GET&#034;, &#034;changed&#034;: true },<br \/>\n  &#034;PUT&#034;: { &#034;original&#034;: &#034;PUT&#034;, &#034;final&#034;: &#034;GET&#034;, &#034;changed&#034;: true },<br \/>\n  &#034;DELETE&#034;: { &#034;original&#034;: &#034;DELETE&#034;, &#034;final&#034;: &#034;GET&#034;, &#034;changed&#034;: true },<br \/>\n  &#034;PATCH&#034;: { &#034;original&#034;: &#034;PATCH&#034;, &#034;final&#034;: &#034;GET&#034;, &#034;changed&#034;: true }<br \/>\n}<br \/>\n*\/ <\/p>\n<p>\u6d4f\u89c8\u5668\u517c\u5bb9\u6027\u8868&#xff1a;<\/p>\n<table>\n<tr>\u6d4f\u89c8\u5668\u7248\u672cPOST\u2192GETPUT\u2192GETDELETE\u2192GETPATCH\u2192GET<\/tr>\n<tbody>\n<tr>\n<td>Chrome<\/td>\n<td>120&#043;<\/td>\n<td>\u662f<\/td>\n<td>\u662f<\/td>\n<td>\u662f<\/td>\n<td>\u662f<\/td>\n<\/tr>\n<tr>\n<td>Firefox<\/td>\n<td>120&#043;<\/td>\n<td>\u662f<\/td>\n<td>\u662f<\/td>\n<td>\u662f<\/td>\n<td>\u662f<\/td>\n<\/tr>\n<tr>\n<td>Safari<\/td>\n<td>17&#043;<\/td>\n<td>\u662f<\/td>\n<td>\u662f<\/td>\n<td>\u662f<\/td>\n<td>\u662f<\/td>\n<\/tr>\n<tr>\n<td>Edge<\/td>\n<td>120&#043;<\/td>\n<td>\u662f<\/td>\n<td>\u662f<\/td>\n<td>\u662f<\/td>\n<td>\u662f<\/td>\n<\/tr>\n<tr>\n<td>\u79fb\u52a8\u6d4f\u89c8\u5668<\/td>\n<td>\u6700\u65b0<\/td>\n<td>\u662f<\/td>\n<td>\u662f<\/td>\n<td>\u662f<\/td>\n<td>\u662f<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h6>12.3.2 \u95ee\u9898\u5f71\u54cd\u4e0e\u540e\u679c<\/h6>\n<p>\u6570\u636e\u4e22\u5931\u573a\u666f&#xff1a;<\/p>\n<p>http<\/p>\n<p>POST \/submit-order HTTP\/1.1<br \/>\nContent-Type: application\/json<br \/>\nContent-Length: 87<\/p>\n<p>{<br \/>\n  &#034;items&#034;: [{&#034;id&#034;: 123, &#034;quantity&#034;: 2}],<br \/>\n  &#034;shipping&#034;: &#034;express&#034;,<br \/>\n  &#034;payment&#034;: &#034;credit_card&#034;<br \/>\n}<\/p>\n<p>HTTP\/1.1 302 Found<br \/>\nLocation: \/thank-you<\/p>\n<p># \u6d4f\u89c8\u5668\u53d1\u9001\u65b0\u8bf7\u6c42&#xff1a;<br \/>\nGET \/thank-you HTTP\/1.1<br \/>\n# \u8ba2\u5355\u6570\u636e\u5b8c\u5168\u4e22\u5931&#xff01; <\/p>\n<p>API\u8c03\u7528\u5931\u8d25&#xff1a;<\/p>\n<p>http<\/p>\n<p>DELETE \/api\/users\/123 HTTP\/1.1<br \/>\nAuthorization: Bearer xyz<\/p>\n<p>HTTP\/1.1 302 Found<br \/>\nLocation: \/api\/temp\/users\/456<\/p>\n<p># \u5b9e\u9645\u53d1\u9001&#xff1a;<br \/>\nGET \/api\/temp\/users\/456 HTTP\/1.1<br \/>\nAuthorization: Bearer xyz<br \/>\n# \u5e94\u8be5\u662fDELETE\u64cd\u4f5c&#xff0c;\u5374\u53d8\u6210\u4e86GET&#xff01; <\/p>\n<h6>12.3.3 \u89e3\u51b3\u65b9\u6848\u4e0e\u53d8\u901a\u65b9\u6cd5<\/h6>\n<p>\u65b9\u68481&#xff1a;\u4f7f\u7528307\u72b6\u6001\u7801&#xff08;\u63a8\u8350&#xff09;&#xff1a;<\/p>\n<p>python<\/p>\n<p># Django\u793a\u4f8b&#xff1a;\u4fdd\u6301\u65b9\u6cd5\u7684\u4e34\u65f6\u91cd\u5b9a\u5411<br \/>\nfrom django.http import HttpResponseRedirect<\/p>\n<p>def temporary_resource_move(request):<br \/>\n    if request.method in [&#039;POST&#039;, &#039;PUT&#039;, &#039;DELETE&#039;, &#039;PATCH&#039;]:<br \/>\n        # \u4f7f\u7528307\u4fdd\u6301\u65b9\u6cd5<br \/>\n        response &#061; HttpResponseRedirect(&#039;\/new-temp-location&#039;, status&#061;307)<br \/>\n    else:<br \/>\n        # GET\/HEAD\u7b49\u4f7f\u7528302<br \/>\n        response &#061; HttpResponseRedirect(&#039;\/new-temp-location&#039;, status&#061;302)<br \/>\n    return response <\/p>\n<p>\u65b9\u68482&#xff1a;\u5ba2\u6237\u7aef\u5904\u7406\u91cd\u5b9a\u5411&#xff1a;<\/p>\n<p>javascript<\/p>\n<p>\/\/ \u524d\u7aefJavaScript\u5904\u7406\u91cd\u5b9a\u5411&#xff0c;\u4fdd\u6301\u65b9\u6cd5<br \/>\nclass SafeRedirectHandler {<br \/>\n  constructor() {<br \/>\n    this.maxRedirects &#061; 5;<br \/>\n  }<\/p>\n<p>  async fetchWithRedirectHandling(url, options &#061; {}) {<br \/>\n    let currentUrl &#061; url;<br \/>\n    let redirectCount &#061; 0;<br \/>\n    let currentOptions &#061; { &#8230;options };<\/p>\n<p>    while (redirectCount &lt; this.maxRedirects) {<br \/>\n      const response &#061; await fetch(currentUrl, currentOptions);<\/p>\n<p>      if (response.status &#061;&#061;&#061; 302 || response.status &#061;&#061;&#061; 307) {<br \/>\n        redirectCount&#043;&#043;;<\/p>\n<p>        const location &#061; response.headers.get(&#039;Location&#039;);<br \/>\n        if (!location) break;<\/p>\n<p>        \/\/ \u89e3\u6790\u65b0URL<br \/>\n        const newUrl &#061; new URL(location, currentUrl);<br \/>\n        currentUrl &#061; newUrl.href;<\/p>\n<p>        \/\/ \u5bf9\u4e8e307&#xff0c;\u4fdd\u6301\u539f\u59cb\u65b9\u6cd5<br \/>\n        if (response.status &#061;&#061;&#061; 307) {<br \/>\n          \/\/ \u4fdd\u6301\u6240\u6709\u539f\u59cb\u9009\u9879<br \/>\n          continue;<br \/>\n        }<\/p>\n<p>        \/\/ \u5bf9\u4e8e302&#xff0c;\u6839\u636e\u89c4\u8303\u5e94\u8be5\u4fdd\u6301\u65b9\u6cd5<br \/>\n        \/\/ \u4f46\u5b9e\u9645\u53ef\u80fd\u9700\u8981\u7279\u6b8a\u5904\u7406<br \/>\n        if (currentOptions.method &#061;&#061;&#061; &#039;POST&#039;) {<br \/>\n          \/\/ \u8b66\u544a\u5f00\u53d1\u8005&#xff0c;\u5efa\u8bae\u4f7f\u7528307<br \/>\n          console.warn(&#039;302 redirect with POST method &#8211; data may be lost&#039;);<br \/>\n          \/\/ \u53ef\u4ee5\u5c1d\u8bd5\u91cd\u65b0\u53d1\u9001POST<br \/>\n          continue;<br \/>\n        }<\/p>\n<p>        continue;<br \/>\n      }<\/p>\n<p>      \/\/ \u975e\u91cd\u5b9a\u5411\u54cd\u5e94&#xff0c;\u76f4\u63a5\u8fd4\u56de<br \/>\n      return response;<br \/>\n    }<\/p>\n<p>    throw new Error(&#096;Too many redirects: ${redirectCount}&#096;);<br \/>\n  }<br \/>\n}<\/p>\n<p>\/\/ \u4f7f\u7528\u793a\u4f8b<br \/>\nconst handler &#061; new SafeRedirectHandler();<br \/>\nhandler.fetchWithRedirectHandling(&#039;\/submit-form&#039;, {<br \/>\n  method: &#039;POST&#039;,<br \/>\n  body: JSON.stringify({ data: &#039;important&#039; })<br \/>\n}); <\/p>\n<p>\u65b9\u68483&#xff1a;\u670d\u52a1\u5668\u7aef\u72b6\u6001\u4fdd\u6301&#xff1a;<\/p>\n<p>python<\/p>\n<p># \u4f7f\u7528\u4f1a\u8bdd\u6216\u6570\u636e\u5e93\u4fdd\u6301\u72b6\u6001<br \/>\nfrom flask import Flask, request, redirect, session<br \/>\nimport uuid<\/p>\n<p>app &#061; Flask(__name__)<br \/>\napp.secret_key &#061; &#039;your-secret-key&#039;<\/p>\n<p>&#064;app.route(&#039;\/submit-data&#039;, methods&#061;[&#039;POST&#039;])<br \/>\ndef submit_data():<br \/>\n    # \u751f\u6210\u552f\u4e00ID\u4fdd\u5b58\u6570\u636e<br \/>\n    request_id &#061; str(uuid.uuid4())<\/p>\n<p>    # \u5c06\u6570\u636e\u4fdd\u5b58\u5230\u6570\u636e\u5e93\u6216\u7f13\u5b58<br \/>\n    save_temporary_data(request_id, {<br \/>\n        &#039;method&#039;: request.method,<br \/>\n        &#039;data&#039;: request.form.to_dict(),<br \/>\n        &#039;headers&#039;: dict(request.headers)<br \/>\n    })<\/p>\n<p>    # \u91cd\u5b9a\u5411\u5230\u5904\u7406\u9875\u9762&#xff0c;\u4f20\u9012ID<br \/>\n    return redirect(f&#039;\/process-request\/{request_id}&#039;, code&#061;302)<\/p>\n<p>&#064;app.route(&#039;\/process-request\/&lt;request_id&gt;&#039;)<br \/>\ndef process_request(request_id):<br \/>\n    # \u6062\u590d\u539f\u59cb\u8bf7\u6c42\u6570\u636e<br \/>\n    original_request &#061; load_temporary_data(request_id)<\/p>\n<p>    if not original_request:<br \/>\n        return &#034;Request data expired&#034;, 410<\/p>\n<p>    # \u5904\u7406\u539f\u59cb\u8bf7\u6c42<br \/>\n    if original_request[&#039;method&#039;] &#061;&#061; &#039;POST&#039;:<br \/>\n        result &#061; handle_post_request(original_request[&#039;data&#039;])<br \/>\n        # \u6e05\u7406\u4e34\u65f6\u6570\u636e<br \/>\n        delete_temporary_data(request_id)<br \/>\n        return result<\/p>\n<p>    return &#034;Unsupported method&#034;, 405 <\/p>\n<h5>12.4 \u914d\u7f6e\u793a\u4f8b\u4e0e\u5b9e\u73b0<\/h5>\n<h6>12.4.1 \u670d\u52a1\u5668\u914d\u7f6e\u793a\u4f8b<\/h6>\n<p>Nginx\u914d\u7f6e&#xff1a;<\/p>\n<p>nginx<\/p>\n<p># \u4e34\u65f6\u91cd\u5b9a\u5411\u914d\u7f6e\u793a\u4f8b<br \/>\nserver {<br \/>\n    listen 80;<br \/>\n    server_name example.com;<\/p>\n<p>    # \u57fa\u7840302\u91cd\u5b9a\u5411<br \/>\n    location &#061; \/old-temp-page {<br \/>\n        return 302 \/new-temp-page;<br \/>\n    }<\/p>\n<p>    # \u5e26\u67e5\u8be2\u53c2\u6570\u7684\u4e34\u65f6\u91cd\u5b9a\u5411<br \/>\n    location \/search {<br \/>\n        # \u4e34\u65f6\u91cd\u5b9a\u5411\u5230\u65b0\u641c\u7d22\u7cfb\u7edf<br \/>\n        return 302 \/new-search$is_args$args;<br \/>\n    }<\/p>\n<p>    # \u57fa\u4e8e\u6761\u4ef6\u7684\u4e34\u65f6\u91cd\u5b9a\u5411<br \/>\n    location \/special-offer {<br \/>\n        # \u68c0\u67e5\u7528\u6237\u4ee3\u7406\u6216cookie<br \/>\n        if ($http_user_agent ~* &#034;(mobile|android|iphone)&#034;) {<br \/>\n            return 302 \/mobile-offer;<br \/>\n        }<\/p>\n<p>        # \u68c0\u67e5\u65f6\u95f4\u6761\u4ef6<br \/>\n        if ($time_iso8601 ~ &#034;^2024-10-21&#034;) {<br \/>\n            return 302 \/daily-deal;<br \/>\n        }<\/p>\n<p>        # \u9ed8\u8ba4\u884c\u4e3a<br \/>\n        try_files $uri $uri\/ &#061;404;<br \/>\n    }<\/p>\n<p>    # \u7ef4\u62a4\u6a21\u5f0f\u91cd\u5b9a\u5411<br \/>\n    location \/ {<br \/>\n        # \u68c0\u67e5\u7ef4\u62a4\u6807\u5fd7\u6587\u4ef6<br \/>\n        if (-f \/var\/www\/maintenance.flag) {<br \/>\n            # \u6392\u9664\u7ba1\u7406\u9875\u9762<br \/>\n            if ($request_uri !~ &#034;^\/admin\/&#034;) {<br \/>\n                return 302 \/maintenance.html;<br \/>\n            }<br \/>\n        }<\/p>\n<p>        try_files $uri $uri\/ &#061;404;<br \/>\n    }<\/p>\n<p>    # \u767b\u5f55\u91cd\u5b9a\u5411<br \/>\n    location \/dashboard {<br \/>\n        # \u68c0\u67e5\u8ba4\u8bc1cookie<br \/>\n        if ($cookie_session !&#061; &#034;valid&#034;) {<br \/>\n            # \u8bb0\u5f55\u539f\u59cbURL<br \/>\n            add_header Set-Cookie &#034;redirect_to&#061;$request_uri; Path&#061;\/&#034;;<br \/>\n            return 302 \/login;<br \/>\n        }<\/p>\n<p>        proxy_pass http:\/\/backend;<br \/>\n    }<br \/>\n} <\/p>\n<p>Apache\u914d\u7f6e&#xff1a;<\/p>\n<p>apache<\/p>\n<p># .htaccess \u4e34\u65f6\u91cd\u5b9a\u5411\u914d\u7f6e<\/p>\n<p># \u542f\u7528\u91cd\u5199\u5f15\u64ce<br \/>\nRewriteEngine On<\/p>\n<p># 1. \u4e34\u65f6\u9875\u9762\u91cd\u5b9a\u5411<br \/>\nRedirect 302 \/temporary-page \/new-temporary-location<\/p>\n<p># 2. \u57fa\u4e8e\u65f6\u95f4\u7684\u91cd\u5b9a\u5411<br \/>\nRewriteCond %{TIME_YEAR}%{TIME_MON}%{TIME_DAY} ^20241021$<br \/>\nRewriteRule ^event-page$ \/special-event [R&#061;302,L]<\/p>\n<p># 3. \u57fa\u4e8eIP\u7684\u91cd\u5b9a\u5411&#xff08;A\/B\u6d4b\u8bd5&#xff09;<br \/>\nRewriteCond %{REMOTE_ADDR} ^123\\\\.456\\\\.789\\\\.<br \/>\nRewriteRule ^test-page$ \/variant-b [R&#061;302,L]<\/p>\n<p># 4. \u7528\u6237\u4ee3\u7406\u68c0\u6d4b<br \/>\nRewriteCond %{HTTP_USER_AGENT} (iPhone|Android|Mobile)<br \/>\nRewriteRule ^desktop-page$ \/mobile-page [R&#061;302,L]<\/p>\n<p># 5. \u53c2\u6570\u5316\u91cd\u5b9a\u5411<br \/>\nRewriteCond %{QUERY_STRING} ^promo&#061;summer2024$<br \/>\nRewriteRule ^landing$ \/promotions\/summer-2024? [R&#061;302,L]<\/p>\n<p># 6. \u6392\u9664\u67d0\u4e9b\u8def\u5f84<br \/>\nRewriteCond %{REQUEST_URI} !^\/(admin|api)\/<br \/>\nRewriteRule ^old-section\/(.*)$ \/new-section\/$1 [R&#061;302,L]<\/p>\n<p># 7. \u4e34\u65f6\u7ef4\u62a4\u91cd\u5b9a\u5411<br \/>\nRewriteCond %{DOCUMENT_ROOT}\/maintenance.flag -f<br \/>\nRewriteCond %{REQUEST_URI} !^\/maintenance\\\\.html$<br \/>\nRewriteCond %{REQUEST_URI} !^\/admin\/<br \/>\nRewriteRule ^(.*)$ \/maintenance.html [R&#061;302,L] <\/p>\n<h6>12.4.2 \u7f16\u7a0b\u6846\u67b6\u5b9e\u73b0<\/h6>\n<p>Spring Boot\u793a\u4f8b&#xff1a;<\/p>\n<p>java<\/p>\n<p>\/\/ Spring Boot\u63a7\u5236\u5668\u4e2d\u7684302\u91cd\u5b9a\u5411<br \/>\n&#064;Controller<br \/>\npublic class RedirectController {<\/p>\n<p>    \/\/ \u7b80\u5355\u7684302\u91cd\u5b9a\u5411<br \/>\n    &#064;GetMapping(&#034;\/old-url&#034;)<br \/>\n    public String redirectOldUrl() {<br \/>\n        return &#034;redirect:\/new-url&#034;;  \/\/ Spring\u9ed8\u8ba4\u4f7f\u7528302<br \/>\n    }<\/p>\n<p>    \/\/ \u663e\u5f0f\u6307\u5b9a302\u72b6\u6001\u7801<br \/>\n    &#064;GetMapping(&#034;\/temp-redirect&#034;)<br \/>\n    public ResponseEntity&lt;Void&gt; temporaryRedirect() {<br \/>\n        HttpHeaders headers &#061; new HttpHeaders();<br \/>\n        headers.setLocation(URI.create(&#034;\/temporary-location&#034;));<br \/>\n        return new ResponseEntity&lt;&gt;(headers, HttpStatus.FOUND);  \/\/ 302<br \/>\n    }<\/p>\n<p>    \/\/ \u6761\u4ef6\u91cd\u5b9a\u5411<br \/>\n    &#064;PostMapping(&#034;\/submit-form&#034;)<br \/>\n    public String handleFormSubmission(&#064;ModelAttribute FormData formData) {<br \/>\n        if (formData.isValid()) {<br \/>\n            \/\/ \u4e34\u65f6\u91cd\u5b9a\u5411\u5230\u6210\u529f\u9875\u9762<br \/>\n            return &#034;redirect:\/success&#034;;<br \/>\n        } else {<br \/>\n            \/\/ \u91cd\u5b9a\u5411\u56de\u8868\u5355\u9875&#xff08;\u5e26\u9519\u8bef\u4fe1\u606f&#xff09;<br \/>\n            return &#034;redirect:\/form?error&#061;invalid&#034;;<br \/>\n        }<br \/>\n    }<\/p>\n<p>    \/\/ \u57fa\u4e8e\u4f1a\u8bdd\u7684\u91cd\u5b9a\u5411<br \/>\n    &#064;GetMapping(&#034;\/dashboard&#034;)<br \/>\n    public String dashboard(HttpSession session) {<br \/>\n        if (session.getAttribute(&#034;user&#034;) &#061;&#061; null) {<br \/>\n            \/\/ \u4e34\u65f6\u91cd\u5b9a\u5411\u5230\u767b\u5f55\u9875<br \/>\n            session.setAttribute(&#034;redirectAfterLogin&#034;, &#034;\/dashboard&#034;);<br \/>\n            return &#034;redirect:\/login&#034;;<br \/>\n        }<br \/>\n        return &#034;dashboard&#034;;<br \/>\n    }<\/p>\n<p>    \/\/ \u5904\u7406\u67e5\u8be2\u53c2\u6570\u7684\u91cd\u5b9a\u5411<br \/>\n    &#064;GetMapping(&#034;\/search&#034;)<br \/>\n    public String search(&#064;RequestParam String q,<br \/>\n                        &#064;RequestParam(defaultValue &#061; &#034;1&#034;) int page) {<br \/>\n        \/\/ \u4e34\u65f6\u91cd\u5b9a\u5411\u5230\u65b0\u641c\u7d22\u7cfb\u7edf<br \/>\n        return &#034;redirect:\/new-search?query&#061;&#034; &#043;<br \/>\n               URLEncoder.encode(q, StandardCharsets.UTF_8) &#043;<br \/>\n               &#034;&amp;page&#061;&#034; &#043; page;<br \/>\n    }<br \/>\n} <\/p>\n<p>Ruby on Rails\u793a\u4f8b&#xff1a;<\/p>\n<p>ruby<\/p>\n<p># Rails\u63a7\u5236\u5668\u4e2d\u7684302\u91cd\u5b9a\u5411<br \/>\nclass ApplicationController &lt; ActionController::Base<br \/>\n  # \u4e34\u65f6\u91cd\u5b9a\u5411\u65b9\u6cd5<br \/>\n  def temporary_redirect<br \/>\n    # \u57fa\u672c\u91cd\u5b9a\u5411&#xff08;Rails\u9ed8\u8ba4302&#xff09;<br \/>\n    redirect_to new_location_path<\/p>\n<p>    # \u6216\u663e\u5f0f\u6307\u5b9a\u72b6\u6001\u7801<br \/>\n    redirect_to new_location_path, status: :found  # 302<\/p>\n<p>    # \u5e26\u95ea\u5b58\u6d88\u606f<br \/>\n    redirect_to login_path, notice: &#034;\u8bf7\u5148\u767b\u5f55&#034;, status: 302<\/p>\n<p>    # \u5e26\u53c2\u6570\u7684\u4e34\u65f6\u91cd\u5b9a\u5411<br \/>\n    redirect_to product_path(&#064;product), status: 302<br \/>\n  end<\/p>\n<p>  # \u6761\u4ef6\u91cd\u5b9a\u5411<br \/>\n  def check_access<br \/>\n    unless current_user.admin?<br \/>\n      redirect_to root_path,<br \/>\n                  alert: &#034;\u6ca1\u6709\u8bbf\u95ee\u6743\u9650&#034;,<br \/>\n                  status: 302<br \/>\n    end<br \/>\n  end<\/p>\n<p>  # \u7ef4\u62a4\u6a21\u5f0f\u91cd\u5b9a\u5411<br \/>\n  before_action :check_maintenance<\/p>\n<p>  def check_maintenance<br \/>\n    if Maintenance.enabled? &amp;&amp; !admin_user?<br \/>\n      redirect_to maintenance_path, status: 302<br \/>\n    end<br \/>\n  end<\/p>\n<p>  # API\u4e34\u65f6\u7aef\u70b9<br \/>\n  def legacy_api<br \/>\n    # \u4e34\u65f6\u91cd\u5b9a\u5411\u5230\u65b0API\u7aef\u70b9<br \/>\n    redirect_to api_v2_url(params),<br \/>\n                status: 302,<br \/>\n                headers: {<br \/>\n                  &#039;X-API-Version&#039; &#061;&gt; &#039;deprecated&#039;,<br \/>\n                  &#039;X-New-Endpoint&#039; &#061;&gt; api_v2_url(params)<br \/>\n                }<br \/>\n  end<br \/>\nend<\/p>\n<p># \u8def\u7531\u5c42\u91cd\u5b9a\u5411<br \/>\nRails.application.routes.draw do<br \/>\n  # \u4e34\u65f6\u91cd\u5b9a\u5411\u8def\u7531<br \/>\n  get &#039;\/old-path&#039;, to: redirect(&#039;\/new-path&#039;, status: 302)<\/p>\n<p>  # \u52a8\u6001\u91cd\u5b9a\u5411<br \/>\n  get &#039;\/products\/:id&#039;,<br \/>\n      to: redirect { |params, req|<br \/>\n        &#034;\/items\/#{params[:id]}&#034;<br \/>\n      },<br \/>\n      status: 302<\/p>\n<p>  # \u6761\u4ef6\u91cd\u5b9a\u5411<br \/>\n  get &#039;\/special-offer&#039;,<br \/>\n      constraints: -&gt;(req) { req.env[&#039;HTTP_USER_AGENT&#039;] &#061;~ \/Mobile\/ },<br \/>\n      to: redirect(&#039;\/mobile-offer&#039;, status: 302)<br \/>\nend <\/p>\n<h6>12.4.3 \u8fb9\u7f18\u8ba1\u7b97\u5b9e\u73b0<\/h6>\n<p>Cloudflare Workers\u9ad8\u7ea7\u91cd\u5b9a\u5411&#xff1a;<\/p>\n<p>javascript<\/p>\n<p>\/\/ Cloudflare Workers\u5b9e\u73b0\u667a\u80fd\u4e34\u65f6\u91cd\u5b9a\u5411<br \/>\nexport default {<br \/>\n  async fetch(request, env, ctx) {<br \/>\n    const url &#061; new URL(request.url);<br \/>\n    const userAgent &#061; request.headers.get(&#039;User-Agent&#039;) || &#039;&#039;;<br \/>\n    const country &#061; request.cf?.country || &#039;US&#039;;<\/p>\n<p>    \/\/ 1. A\/B\u6d4b\u8bd5\u91cd\u5b9a\u5411<br \/>\n    const abTestGroup &#061; getABTestGroup(request);<br \/>\n    if (abTestGroup &#061;&#061;&#061; &#039;variant_b&#039; &amp;&amp; url.pathname &#061;&#061;&#061; &#039;\/home&#039;) {<br \/>\n      return Response.redirect(&#096;${url.origin}\/home-variant-b&#096;, 302);<br \/>\n    }<\/p>\n<p>    \/\/ 2. \u5730\u7406\u91cd\u5b9a\u5411<br \/>\n    const geoRedirects &#061; {<br \/>\n      &#039;CN&#039;: &#039;\/zh-cn&#039;,<br \/>\n      &#039;JP&#039;: &#039;\/ja&#039;,<br \/>\n      &#039;DE&#039;: &#039;\/de&#039;,<br \/>\n      &#039;FR&#039;: &#039;\/fr&#039;,<br \/>\n      \/\/ &#8230; \u5176\u4ed6\u56fd\u5bb6\u548c\u5730\u533a<br \/>\n    };<\/p>\n<p>    if (geoRedirects[country] &amp;&amp; url.pathname &#061;&#061;&#061; &#039;\/&#039;) {<br \/>\n      const newPath &#061; geoRedirects[country] &#043; url.pathname;<br \/>\n      return Response.redirect(&#096;${url.origin}${newPath}&#096;, 302);<br \/>\n    }<\/p>\n<p>    \/\/ 3. \u8bbe\u5907\u7c7b\u578b\u91cd\u5b9a\u5411<br \/>\n    const isMobile &#061; \/mobile|android|iphone\/i.test(userAgent);<br \/>\n    if (isMobile &amp;&amp; url.pathname.startsWith(&#039;\/desktop\/&#039;)) {<br \/>\n      const mobilePath &#061; url.pathname.replace(&#039;\/desktop\/&#039;, &#039;\/mobile\/&#039;);<br \/>\n      return Response.redirect(&#096;${url.origin}${mobilePath}&#096;, 302);<br \/>\n    }<\/p>\n<p>    \/\/ 4. \u4e34\u65f6\u7ef4\u62a4\u91cd\u5b9a\u5411<br \/>\n    const isUnderMaintenance &#061; await env.KV.get(&#039;maintenance_mode&#039;);<br \/>\n    if (isUnderMaintenance &#061;&#061;&#061; &#039;true&#039; &amp;&amp; !url.pathname.startsWith(&#039;\/admin&#039;)) {<br \/>\n      return Response.redirect(&#096;${url.origin}\/maintenance&#096;, 302);<br \/>\n    }<\/p>\n<p>    \/\/ 5. \u57fa\u4e8e\u65f6\u95f4\u7684\u91cd\u5b9a\u5411<br \/>\n    const now &#061; new Date();<br \/>\n    const hour &#061; now.getUTCHours();<br \/>\n    if (hour &gt;&#061; 22 || hour &lt; 6) { \/\/ \u7ef4\u62a4\u7a97\u53e3<br \/>\n      if (url.pathname.startsWith(&#039;\/api\/&#039;)) {<br \/>\n        return Response.redirect(&#096;${url.origin}\/api\/nightly-maintenance&#096;, 302);<br \/>\n      }<br \/>\n    }<\/p>\n<p>    \/\/ 6. \u901f\u7387\u9650\u5236\u91cd\u5b9a\u5411<br \/>\n    const clientKey &#061; request.headers.get(&#039;CF-Connecting-IP&#039;) || &#039;anonymous&#039;;<br \/>\n    const rateLimitKey &#061; &#096;rate_limit:${clientKey}&#096;;<br \/>\n    const requestCount &#061; await env.KV.get(rateLimitKey) || 0;<\/p>\n<p>    if (requestCount &gt; 100) { \/\/ \u8d85\u8fc7\u9650\u5236<br \/>\n      return Response.redirect(&#096;${url.origin}\/rate-limit-warning&#096;, 302);<br \/>\n    }<\/p>\n<p>    \/\/ \u66f4\u65b0\u8ba1\u6570<br \/>\n    await env.KV.put(rateLimitKey, (parseInt(requestCount) &#043; 1).toString(), {<br \/>\n      expirationTtl: 60 \/\/ 60\u79d2\u8fc7\u671f<br \/>\n    });<\/p>\n<p>    \/\/ \u6b63\u5e38\u8bf7\u6c42<br \/>\n    return fetch(request);<br \/>\n  }<br \/>\n};<\/p>\n<p>\/\/ \u8f85\u52a9\u51fd\u6570&#xff1a;\u83b7\u53d6A\/B\u6d4b\u8bd5\u5206\u7ec4<br \/>\nfunction getABTestGroup(request) {<br \/>\n  const clientId &#061; request.headers.get(&#039;CF-Connecting-IP&#039;) || &#039;default&#039;;<br \/>\n  const hash &#061; simpleHash(clientId);<br \/>\n  return hash % 100 &lt; 30 ? &#039;variant_b&#039; : &#039;control&#039;; \/\/ 30%\u7528\u6237\u8fdb\u5165B\u7ec4<br \/>\n}<\/p>\n<p>function simpleHash(str) {<br \/>\n  let hash &#061; 0;<br \/>\n  for (let i &#061; 0; i &lt; str.length; i&#043;&#043;) {<br \/>\n    hash &#061; ((hash &lt;&lt; 5) &#8211; hash) &#043; str.charCodeAt(i);<br \/>\n    hash |&#061; 0; \/\/ \u8f6c\u6362\u4e3a32\u4f4d\u6574\u6570<br \/>\n  }<br \/>\n  return Math.abs(hash);<br \/>\n} <\/p>\n<p>AWS Lambda&#064;Edge\u91cd\u5b9a\u5411&#xff1a;<\/p>\n<p>javascript<\/p>\n<p>\/\/ Lambda&#064;Edge\u5b9e\u73b0\u4e34\u65f6\u91cd\u5b9a\u5411<br \/>\nexports.handler &#061; async (event) &#061;&gt; {<br \/>\n  const request &#061; event.Records[0].cf.request;<br \/>\n  const headers &#061; request.headers;<\/p>\n<p>  \/\/ 1. \u57fa\u4e8e\u7528\u6237\u4ee3\u7406\u7684\u91cd\u5b9a\u5411<br \/>\n  const userAgent &#061; headers[&#039;user-agent&#039;]?.[0]?.value || &#039;&#039;;<br \/>\n  const isBot &#061; \/bot|crawler|spider\/i.test(userAgent);<\/p>\n<p>  if (isBot &amp;&amp; request.uri &#061;&#061;&#061; &#039;\/&#039;) {<br \/>\n    return {<br \/>\n      status: &#039;302&#039;,<br \/>\n      statusDescription: &#039;Found&#039;,<br \/>\n      headers: {<br \/>\n        &#039;location&#039;: [{<br \/>\n          key: &#039;Location&#039;,<br \/>\n          value: &#039;\/bot-landing&#039;<br \/>\n        }],<br \/>\n        &#039;cache-control&#039;: [{<br \/>\n          key: &#039;Cache-Control&#039;,<br \/>\n          value: &#039;no-cache, no-store&#039;<br \/>\n        }]<br \/>\n      }<br \/>\n    };<br \/>\n  }<\/p>\n<p>  \/\/ 2. \u57fa\u4e8e\u56fd\u5bb6\/\u5730\u533a\u7684\u91cd\u5b9a\u5411<br \/>\n  const country &#061; headers[&#039;cloudfront-viewer-country&#039;]?.[0]?.value;<br \/>\n  if (country &#061;&#061;&#061; &#039;RU&#039; &amp;&amp; request.uri.startsWith(&#039;\/news\/&#039;)) {<br \/>\n    return {<br \/>\n      status: &#039;302&#039;,<br \/>\n      statusDescription: &#039;Found&#039;,<br \/>\n      headers: {<br \/>\n        &#039;location&#039;: [{<br \/>\n          key: &#039;Location&#039;,<br \/>\n          value: &#039;\/regional-news&#039;<br \/>\n        }]<br \/>\n      }<br \/>\n    };<br \/>\n  }<\/p>\n<p>  \/\/ 3. \u4e34\u65f6\u4fc3\u9500\u91cd\u5b9a\u5411<br \/>\n  const now &#061; new Date();<br \/>\n  const promoStart &#061; new Date(&#039;2024-10-20T00:00:00Z&#039;);<br \/>\n  const promoEnd &#061; new Date(&#039;2024-10-28T23:59:59Z&#039;);<\/p>\n<p>  if (now &gt;&#061; promoStart &amp;&amp; now &lt;&#061; promoEnd &amp;&amp; request.uri &#061;&#061;&#061; &#039;\/products&#039;) {<br \/>\n    return {<br \/>\n      status: &#039;302&#039;,<br \/>\n      statusDescription: &#039;Found&#039;,<br \/>\n      headers: {<br \/>\n        &#039;location&#039;: [{<br \/>\n          key: &#039;Location&#039;,<br \/>\n          value: &#039;\/black-friday-sale&#039;<br \/>\n        }],<br \/>\n        &#039;x-promo-active&#039;: [{<br \/>\n          key: &#039;X-Promo-Active&#039;,<br \/>\n          value: &#039;true&#039;<br \/>\n        }]<br \/>\n      }<br \/>\n    };<br \/>\n  }<\/p>\n<p>  \/\/ 4. \u4e34\u65f6\u7ef4\u62a4\u91cd\u5b9a\u5411<br \/>\n  const maintenanceFlag &#061; await getMaintenanceFlag();<br \/>\n  if (maintenanceFlag &amp;&amp; !request.uri.startsWith(&#039;\/admin&#039;)) {<br \/>\n    return {<br \/>\n      status: &#039;302&#039;,<br \/>\n      statusDescription: &#039;Found&#039;,<br \/>\n      headers: {<br \/>\n        &#039;location&#039;: [{<br \/>\n          key: &#039;Location&#039;,<br \/>\n          value: &#039;\/maintenance&#039;<br \/>\n        }],<br \/>\n        &#039;retry-after&#039;: [{<br \/>\n          key: &#039;Retry-After&#039;,<br \/>\n          value: &#039;3600&#039; \/\/ 1\u5c0f\u65f6\u540e\u91cd\u8bd5<br \/>\n        }]<br \/>\n      }<br \/>\n    };<br \/>\n  }<\/p>\n<p>  \/\/ \u6b63\u5e38\u8bf7\u6c42<br \/>\n  return request;<br \/>\n};<\/p>\n<p>async function getMaintenanceFlag() {<br \/>\n  \/\/ \u4eceS3\u6216DynamoDB\u83b7\u53d6\u7ef4\u62a4\u6807\u5fd7<br \/>\n  \/\/ \u8fd4\u56detrue\/false<br \/>\n  return false;<br \/>\n} <\/p>\n<h5>12.5 \u4e0e301\u7684\u5bf9\u6bd4\u4e0e\u9009\u62e9\u6307\u5357<\/h5>\n<h6>12.5.1 \u6280\u672f\u5bf9\u6bd4\u8868<\/h6>\n<table>\n<tr>\u7279\u6027301 Moved Permanently302 Found<\/tr>\n<tbody>\n<tr>\n<td>\u72b6\u6001\u7801<\/td>\n<td>301<\/td>\n<td>302<\/td>\n<\/tr>\n<tr>\n<td>\u8bed\u4e49<\/td>\n<td>\u6c38\u4e45\u79fb\u52a8<\/td>\n<td>\u4e34\u65f6\u79fb\u52a8<\/td>\n<\/tr>\n<tr>\n<td>\u7f13\u5b58\u884c\u4e3a<\/td>\n<td>\u5ba2\u6237\u7aef\u548c\u4ee3\u7406\u53ef\u7f13\u5b58<\/td>\n<td>\u901a\u5e38\u4e0d\u7f13\u5b58<\/td>\n<\/tr>\n<tr>\n<td>\u7f13\u5b58\u65f6\u95f4<\/td>\n<td>\u957f\u671f&#xff08;\u63a8\u83501\u5e74&#xff09;<\/td>\n<td>\u77ed\u671f\u6216\u4e0d\u7f13\u5b58<\/td>\n<\/tr>\n<tr>\n<td>\u65b9\u6cd5\u4fdd\u6301<\/td>\n<td>\u89c4\u8303\u8981\u6c42\u4fdd\u6301&#xff0c;\u5b9e\u9645\u5e38\u8f6cGET<\/td>\n<td>\u89c4\u8303\u8981\u6c42\u4fdd\u6301&#xff0c;\u5b9e\u9645\u5e38\u8f6cGET<\/td>\n<\/tr>\n<tr>\n<td>SEO\u5f71\u54cd<\/td>\n<td>\u4f20\u9012\u6743\u91cd&#xff0c;\u66f4\u65b0\u7d22\u5f15<\/td>\n<td>\u4e0d\u4f20\u9012\u6743\u91cd&#xff0c;\u4fdd\u6301\u539f\u7d22\u5f15<\/td>\n<\/tr>\n<tr>\n<td>\u6d4f\u89c8\u5668\u884c\u4e3a<\/td>\n<td>\u66f4\u65b0\u4e66\u7b7e&#xff0c;\u53ef\u80fd\u66f4\u65b0\u5386\u53f2\u8bb0\u5f55<\/td>\n<td>\u4e0d\u66f4\u65b0\u4e66\u7b7e<\/td>\n<\/tr>\n<tr>\n<td>\u9002\u7528\u573a\u666f<\/td>\n<td>\u6c38\u4e45URL\u53d8\u66f4&#xff0c;\u57df\u540d\u8fc1\u79fb<\/td>\n<td>A\/B\u6d4b\u8bd5&#xff0c;\u7ef4\u62a4\u9875\u9762&#xff0c;\u767b\u5f55\u91cd\u5b9a\u5411<\/td>\n<\/tr>\n<tr>\n<td>\u5b9e\u73b0\u590d\u6742\u5ea6<\/td>\n<td>\u7b80\u5355<\/td>\n<td>\u7b80\u5355<\/td>\n<\/tr>\n<tr>\n<td>\u98ce\u9669\u7b49\u7ea7<\/td>\n<td>\u9ad8&#xff08;\u5f71\u54cdSEO\u548c\u7528\u6237\u4f53\u9a8c&#xff09;<\/td>\n<td>\u4f4e&#xff08;\u4e34\u65f6\u6027&#xff09;<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h6>12.5.2 \u51b3\u7b56\u77e9\u9635<\/h6>\n<p>yaml<\/p>\n<p>decision_matrix:<br \/>\n  permanent_changes:<br \/>\n    &#8211; domain_change: true<br \/>\n      protocol_change: true<br \/>\n      url_restructure: true<br \/>\n      canonicalization: true<br \/>\n      recommended_status: 301<\/p>\n<p>  temporary_changes:<br \/>\n    &#8211; ab_testing: true<br \/>\n      geo_redirect: true<br \/>\n      maintenance: true<br \/>\n      login_redirect: true<br \/>\n      seasonal_promo: true<br \/>\n      recommended_status: 302<\/p>\n<p>  special_cases:<br \/>\n    &#8211; post_after_submit:<br \/>\n        description: &#034;\u8868\u5355\u63d0\u4ea4\u540e\u663e\u793a\u7ed3\u679c\u9875&#034;<br \/>\n        recommended_status: 303<\/p>\n<p>    &#8211; api_temp_redirect:<br \/>\n        description: &#034;API\u7aef\u70b9\u4e34\u65f6\u8fc1\u79fb\u4e14\u9700\u4fdd\u6301\u65b9\u6cd5&#034;<br \/>\n        recommended_status: 307<\/p>\n<p>    &#8211; unsure_permanent:<br \/>\n        description: &#034;\u4e0d\u786e\u5b9a\u662f\u5426\u6c38\u4e45\u53d8\u66f4&#034;<br \/>\n        recommendation: &#034;\u4f7f\u7528302&#xff0c;\u786e\u8ba4\u540e\u518d\u6539\u4e3a301&#034;<\/p>\n<p>    &#8211; large_scale_migration:<br \/>\n        description: &#034;\u5927\u89c4\u6a21\u7f51\u7ad9\u8fc1\u79fb&#034;<br \/>\n        recommendation: &#034;\u5206\u6279\u4f7f\u7528301&#xff0c;\u76d1\u63a7\u540e\u518d\u5168\u9762\u5b9e\u65bd&#034; <\/p>\n<h6>12.5.3 \u8fc1\u79fb\u7b56\u7565\u4e0e\u8f6c\u6362<\/h6>\n<p>\u4ece302\u8f6c\u6362\u4e3a301\u7684\u65f6\u673a&#xff1a;<\/p>\n<p>python<\/p>\n<p>class RedirectUpgrader:<br \/>\n    &#034;&#034;&#034;\u76d1\u63a7302\u91cd\u5b9a\u5411\u4f7f\u7528\u60c5\u51b5&#xff0c;\u5efa\u8bae\u5347\u7ea7\u5230301&#034;&#034;&#034;<\/p>\n<p>    def __init__(self, redirect_logs):<br \/>\n        self.logs &#061; redirect_logs<br \/>\n        self.analysis_results &#061; {}<\/p>\n<p>    def analyze_redirects(self):<br \/>\n        &#034;&#034;&#034;\u5206\u6790\u91cd\u5b9a\u5411\u4f7f\u7528\u6a21\u5f0f&#034;&#034;&#034;<\/p>\n<p>        for url, data in self.logs.items():<br \/>\n            # \u8ba1\u7b97\u6307\u6807<br \/>\n            usage_duration &#061; self.get_usage_duration(data[&#039;first_seen&#039;], data[&#039;last_seen&#039;])<br \/>\n            request_count &#061; data[&#039;request_count&#039;]<br \/>\n            user_agents &#061; data[&#039;user_agents&#039;]<\/p>\n<p>            # \u5206\u6790\u6a21\u5f0f<br \/>\n            is_stable &#061; usage_duration.days &gt; 30<br \/>\n            is_high_traffic &#061; request_count &gt; 1000<br \/>\n            is_consistent &#061; self.check_consistency(user_agents)<\/p>\n<p>            # \u5224\u65ad\u662f\u5426\u5e94\u8be5\u5347\u7ea7\u5230301<br \/>\n            should_upgrade &#061; (<br \/>\n                is_stable and<br \/>\n                is_high_traffic and<br \/>\n                is_consistent and<br \/>\n                not data.get(&#039;temporary_reason&#039;)<br \/>\n            )<\/p>\n<p>            self.analysis_results[url] &#061; {<br \/>\n                &#039;should_upgrade&#039;: should_upgrade,<br \/>\n                &#039;confidence_score&#039;: self.calculate_confidence(<br \/>\n                    is_stable, is_high_traffic, is_consistent<br \/>\n                ),<br \/>\n                &#039;metrics&#039;: {<br \/>\n                    &#039;duration_days&#039;: usage_duration.days,<br \/>\n                    &#039;request_count&#039;: request_count,<br \/>\n                    &#039;unique_users&#039;: len(user_agents)<br \/>\n                }<br \/>\n            }<\/p>\n<p>        return self.analysis_results<\/p>\n<p>    def generate_upgrade_plan(self, threshold&#061;0.8):<br \/>\n        &#034;&#034;&#034;\u751f\u6210\u5347\u7ea7\u8ba1\u5212&#034;&#034;&#034;<\/p>\n<p>        plan &#061; {<br \/>\n            &#039;immediate&#039;: [],<br \/>\n            &#039;monitor&#039;: [],<br \/>\n            &#039;keep_as_is&#039;: []<br \/>\n        }<\/p>\n<p>        for url, analysis in self.analysis_results.items():<br \/>\n            if analysis[&#039;confidence_score&#039;] &gt;&#061; threshold:<br \/>\n                plan[&#039;immediate&#039;].append(url)<br \/>\n            elif analysis[&#039;confidence_score&#039;] &gt;&#061; 0.6:<br \/>\n                plan[&#039;monitor&#039;].append(url)<br \/>\n            else:<br \/>\n                plan[&#039;keep_as_is&#039;].append(url)<\/p>\n<p>        return plan <\/p>\n<p>\u5b89\u5168\u8f6c\u6362\u6d41\u7a0b&#xff1a;<img loading=\"lazy\" decoding=\"async\" alt=\"\" height=\"1972\" src=\"https:\/\/www.wsisp.com\/helps\/wp-content\/uploads\/2026\/03\/20260228233640-69a37c0834cfe.png\" width=\"766\" \/><\/p>\n<h6>12.5.4 \u76d1\u63a7\u4e0e\u5ea6\u91cf<\/h6>\n<p>\u5173\u952e\u6027\u80fd\u6307\u6807&#xff1a;<\/p>\n<p>yaml<\/p>\n<p>monitoring_metrics:<br \/>\n  basic_metrics:<br \/>\n    &#8211; total_302_redirects: &#034;\u603b302\u91cd\u5b9a\u5411\u6b21\u6570&#034;<br \/>\n    &#8211; unique_sources: &#034;\u552f\u4e00\u6765\u6e90URL\u6570\u91cf&#034;<br \/>\n    &#8211; success_rate: &#034;\u91cd\u5b9a\u5411\u6210\u529f\u7387&#034;<\/p>\n<p>  performance_metrics:<br \/>\n    &#8211; avg_redirect_time: &#034;\u5e73\u5747\u91cd\u5b9a\u5411\u65f6\u95f4&#034;<br \/>\n    &#8211; p95_redirect_time: &#034;95%\u5206\u4f4d\u91cd\u5b9a\u5411\u65f6\u95f4&#034;<br \/>\n    &#8211; chain_length_distribution: &#034;\u91cd\u5b9a\u5411\u94fe\u957f\u5ea6\u5206\u5e03&#034;<\/p>\n<p>  business_metrics:<br \/>\n    &#8211; user_experience_score: &#034;\u7528\u6237\u4f53\u9a8c\u8bc4\u5206&#034;<br \/>\n    &#8211; conversion_rate_impact: &#034;\u8f6c\u5316\u7387\u5f71\u54cd&#034;<br \/>\n    &#8211; seo_impact: &#034;SEO\u5f71\u54cd\u6307\u6807&#034;<\/p>\n<p>  alerting_thresholds:<br \/>\n    &#8211; high_failure_rate: &#034;\u5931\u8d25\u7387 &gt; 1%&#034;<br \/>\n    &#8211; slow_redirects: &#034;p95\u91cd\u5b9a\u5411\u65f6\u95f4 &gt; 500ms&#034;<br \/>\n    &#8211; long_chains: &#034;\u5e73\u5747\u94fe\u957f\u5ea6 &gt; 2&#034; <\/p>\n<p>\u76d1\u63a7\u4eea\u8868\u677f\u793a\u4f8b&#xff1a;<\/p>\n<p>python<\/p>\n<p>class RedirectDashboard:<br \/>\n    def __init__(self):<br \/>\n        self.metrics &#061; {<br \/>\n            &#039;status_codes&#039;: defaultdict(int),<br \/>\n            &#039;response_times&#039;: [],<br \/>\n            &#039;chain_lengths&#039;: []<br \/>\n        }<\/p>\n<p>    def update_metrics(self, redirect_data):<br \/>\n        &#034;&#034;&#034;\u66f4\u65b0\u76d1\u63a7\u6307\u6807&#034;&#034;&#034;<\/p>\n<p>        # \u72b6\u6001\u7801\u5206\u5e03<br \/>\n        self.metrics[&#039;status_codes&#039;][redirect_data.status] &#043;&#061; 1<\/p>\n<p>        # \u54cd\u5e94\u65f6\u95f4<br \/>\n        if redirect_data.response_time:<br \/>\n            self.metrics[&#039;response_times&#039;].append(redirect_data.response_time)<\/p>\n<p>        # \u94fe\u957f\u5ea6<br \/>\n        self.metrics[&#039;chain_lengths&#039;].append(redirect_data.chain_length)<\/p>\n<p>        # \u4fdd\u7559\u6700\u8fd11000\u4e2a\u6837\u672c<br \/>\n        for key in [&#039;response_times&#039;, &#039;chain_lengths&#039;]:<br \/>\n            if len(self.metrics[key]) &gt; 1000:<br \/>\n                self.metrics[key] &#061; self.metrics[key][-1000:]<\/p>\n<p>    def generate_report(self):<br \/>\n        &#034;&#034;&#034;\u751f\u6210\u76d1\u63a7\u62a5\u544a&#034;&#034;&#034;<\/p>\n<p>        report &#061; {<br \/>\n            &#039;summary&#039;: {<br \/>\n                &#039;total_redirects&#039;: sum(self.metrics[&#039;status_codes&#039;].values()),<br \/>\n                &#039;status_distribution&#039;: dict(self.metrics[&#039;status_codes&#039;]),<br \/>\n                &#039;avg_response_time&#039;: self._calculate_avg(self.metrics[&#039;response_times&#039;]),<br \/>\n                &#039;avg_chain_length&#039;: self._calculate_avg(self.metrics[&#039;chain_lengths&#039;])<br \/>\n            },<br \/>\n            &#039;percentiles&#039;: {<br \/>\n                &#039;response_time_p95&#039;: self._calculate_percentile(<br \/>\n                    self.metrics[&#039;response_times&#039;], 95<br \/>\n                ),<br \/>\n                &#039;chain_length_p95&#039;: self._calculate_percentile(<br \/>\n                    self.metrics[&#039;chain_lengths&#039;], 95<br \/>\n                )<br \/>\n            },<br \/>\n            &#039;alerts&#039;: self._check_alerts()<br \/>\n        }<\/p>\n<p>        return report<\/p>\n<p>    def _check_alerts(self):<br \/>\n        &#034;&#034;&#034;\u68c0\u67e5\u544a\u8b66\u6761\u4ef6&#034;&#034;&#034;<\/p>\n<p>        alerts &#061; []<\/p>\n<p>        # \u68c0\u67e5\u5931\u8d25\u7387<br \/>\n        total &#061; sum(self.metrics[&#039;status_codes&#039;].values())<br \/>\n        if total &gt; 0:<br \/>\n            failure_rate &#061; self.metrics[&#039;status_codes&#039;].get(500, 0) \/ total<br \/>\n            if failure_rate &gt; 0.01:<br \/>\n                alerts.append({<br \/>\n                    &#039;level&#039;: &#039;warning&#039;,<br \/>\n                    &#039;message&#039;: f&#039;\u91cd\u5b9a\u5411\u5931\u8d25\u7387\u8fc7\u9ad8: {failure_rate:.2%}&#039;,<br \/>\n                    &#039;metric&#039;: &#039;failure_rate&#039;<br \/>\n                })<\/p>\n<p>        # \u68c0\u67e5\u54cd\u5e94\u65f6\u95f4<br \/>\n        p95_response &#061; self._calculate_percentile(self.metrics[&#039;response_times&#039;], 95)<br \/>\n        if p95_response &gt; 500:<br \/>\n            alerts.append({<br \/>\n                &#039;level&#039;: &#039;warning&#039;,<br \/>\n                &#039;message&#039;: f&#039;95%\u5206\u4f4d\u54cd\u5e94\u65f6\u95f4\u8fc7\u957f: {p95_response:.0f}ms&#039;,<br \/>\n                &#039;metric&#039;: &#039;response_time&#039;<br \/>\n            })<\/p>\n<p>        return alerts <\/p>\n<h4>\u7b2c13\u7ae0&#xff1a;304 Not Modified &#8211; \u7f13\u5b58\u9a8c\u8bc1<\/h4>\n<h5>13.1 \u7279\u6b8a\u6027\u8d28\u4e0e\u8bbe\u8ba1\u54f2\u5b66<\/h5>\n<h6>13.1.1 \u7f13\u5b58\u9a8c\u8bc1\u673a\u5236\u7684\u672c\u8d28<\/h6>\n<p>304 Not Modified\u7684\u72ec\u7279\u5730\u4f4d&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u5e76\u975e\u771f\u6b63\u7684&#034;\u91cd\u5b9a\u5411&#034;&#xff0c;\u800c\u662f\u6761\u4ef6GET\u7684\u4f18\u5316\u54cd\u5e94<\/p>\n<\/li>\n<li>\n<p>\u8bbe\u8ba1\u76ee\u7684&#xff1a;\u51cf\u5c11\u7f51\u7edc\u4f20\u8f93&#xff0c;\u63d0\u9ad8\u6027\u80fd<\/p>\n<\/li>\n<li>\n<p>\u8bed\u4e49&#xff1a;\u8d44\u6e90\u672a\u6539\u53d8&#xff0c;\u53ef\u4f7f\u7528\u5ba2\u6237\u7aef\u7f13\u5b58\u526f\u672c<\/p>\n<\/li>\n<\/ul>\n<p>\u6761\u4ef6\u8bf7\u6c42-\u54cd\u5e94\u5faa\u73af&#xff1a;<img loading=\"lazy\" decoding=\"async\" alt=\"\" height=\"1770\" src=\"https:\/\/www.wsisp.com\/helps\/wp-content\/uploads\/2026\/03\/20260228233640-69a37c0897e56.png\" width=\"976\" \/><\/p>\n<p>\u8bbe\u8ba1\u54f2\u5b66&#xff1a;RESTful\u7f13\u5b58&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u9075\u5faaREST\u67b6\u6784\u7684&#034;\u65e0\u72b6\u6001&#034;\u539f\u5219<\/p>\n<\/li>\n<li>\n<p>\u5ba2\u6237\u7aef\u8d1f\u8d23\u7f13\u5b58\u7ba1\u7406<\/p>\n<\/li>\n<li>\n<p>\u670d\u52a1\u5668\u8d1f\u8d23\u9a8c\u8bc1\u903b\u8f91<\/p>\n<\/li>\n<li>\n<p>\u51cf\u5c11\u4e0d\u5fc5\u8981\u7684\u6570\u636e\u4f20\u8f93<\/p>\n<\/li>\n<\/ul>\n<h6>13.1.2 \u4e0e\u771f\u6b63\u91cd\u5b9a\u5411\u7684\u533a\u522b<\/h6>\n<p>\u6bd4\u8f83\u8868&#xff1a;<\/p>\n<table>\n<tr>\u7279\u6027301\/302\u91cd\u5b9a\u5411304\u7f13\u5b58\u9a8c\u8bc1<\/tr>\n<tbody>\n<tr>\n<td>\u76ee\u7684<\/td>\n<td>\u4f4d\u7f6e\u8f6c\u79fb<\/td>\n<td>\u7f13\u5b58\u4f18\u5316<\/td>\n<\/tr>\n<tr>\n<td>\u54cd\u5e94\u4f53<\/td>\n<td>\u901a\u5e38\u6709HTML\u4e3b\u4f53<\/td>\n<td>\u65e0\u54cd\u5e94\u4f53<\/td>\n<\/tr>\n<tr>\n<td>Location\u5934<\/td>\n<td>\u5fc5\u9700<\/td>\n<td>\u65e0<\/td>\n<\/tr>\n<tr>\n<td>\u7f13\u5b58\u884c\u4e3a<\/td>\n<td>\u53ef\u80fd\u7f13\u5b58\u91cd\u5b9a\u5411\u672c\u8eab<\/td>\n<td>\u9a8c\u8bc1\u7f13\u5b58\u6709\u6548\u6027<\/td>\n<\/tr>\n<tr>\n<td>\u5ba2\u6237\u7aef\u52a8\u4f5c<\/td>\n<td>\u5411\u65b0URL\u53d1\u8d77\u8bf7\u6c42<\/td>\n<td>\u4f7f\u7528\u672c\u5730\u7f13\u5b58<\/td>\n<\/tr>\n<tr>\n<td>\u7f51\u7edc\u4f20\u8f93<\/td>\n<td>\u81f3\u5c11\u4e24\u6b21\u5b8c\u6574\u8bf7\u6c42<\/td>\n<td>\u4e00\u6b21\u8f7b\u91cf\u9a8c\u8bc1<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>\u5b9e\u9645\u7f51\u7edc\u6d41\u91cf\u5bf9\u6bd4&#xff1a;<\/p>\n<p>text<\/p>\n<p># 301\u91cd\u5b9a\u5411\u6d41\u91cf<br \/>\n\u5ba2\u6237\u7aef \u2192 \u8bf7\u6c42A \u2192 \u670d\u52a1\u5668<br \/>\n\u5ba2\u6237\u7aef \u2190 301\u54cd\u5e94 (300\u5b57\u8282) \u2190 \u670d\u52a1\u5668<br \/>\n\u5ba2\u6237\u7aef \u2192 \u8bf7\u6c42B \u2192 \u670d\u52a1\u5668<br \/>\n\u5ba2\u6237\u7aef \u2190 200\u54cd\u5e94 (50KB) \u2190 \u670d\u52a1\u5668<br \/>\n\u603b\u6d41\u91cf: ~50.3KB<\/p>\n<p># 304\u7f13\u5b58\u9a8c\u8bc1\u6d41\u91cf<br \/>\n\u5ba2\u6237\u7aef \u2192 \u6761\u4ef6\u8bf7\u6c42 \u2192 \u670d\u52a1\u5668 (\u5e26\u9a8c\u8bc1\u5934)<br \/>\n\u5ba2\u6237\u7aef \u2190 304\u54cd\u5e94 (200\u5b57\u8282) \u2190 \u670d\u52a1\u5668<br \/>\n\u5ba2\u6237\u7aef\u4f7f\u7528\u672c\u5730\u7f13\u5b58 (50KB)<br \/>\n\u603b\u6d41\u91cf: ~0.2KB<br \/>\n\u8282\u7701: 99.6% \u7684\u6d41\u91cf <\/p>\n<h5>13.2 \u5de5\u4f5c\u539f\u7406\u4e0e\u6280\u672f\u5b9e\u73b0<\/h5>\n<h6>13.2.1 \u9a8c\u8bc1\u5668\u7c7b\u578b\u4e0e\u673a\u5236<\/h6>\n<p>\u5f3a\u9a8c\u8bc1\u5668 vs \u5f31\u9a8c\u8bc1\u5668&#xff1a;<\/p>\n<table>\n<tr>\u7c7b\u578b\u793a\u4f8b\u7279\u70b9\u9002\u7528\u573a\u666f<\/tr>\n<tbody>\n<tr>\n<td>\u5f3a\u9a8c\u8bc1\u5668<\/td>\n<td>ETag: &#034;abc123&#034;<\/td>\n<td>\u5b57\u8282\u7ea7\u7cbe\u786e\u5339\u914d<\/td>\n<td>\u7cbe\u786e\u5185\u5bb9\u9a8c\u8bc1<\/td>\n<\/tr>\n<tr>\n<td><\/td>\n<td>ETag: W\/&#034;abc123&#034;<\/td>\n<td>\u5f31\u9a8c\u8bc1\u5668&#xff0c;\u8bed\u4e49\u76f8\u540c\u5373\u53ef<\/td>\n<td>\u5185\u5bb9\u8bed\u4e49\u76f8\u540c<\/td>\n<\/tr>\n<tr>\n<td>\u65f6\u95f4\u9a8c\u8bc1\u5668<\/td>\n<td>Last-Modified<\/td>\n<td>\u57fa\u4e8e\u4fee\u6539\u65f6\u95f4<\/td>\n<td>\u65f6\u95f4\u6233\u6bd4\u8f83<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>ETag\u751f\u6210\u7b56\u7565&#xff1a;<\/p>\n<p>python<\/p>\n<p>import hashlib<br \/>\nimport json<br \/>\nfrom datetime import datetime<\/p>\n<p>class ETagGenerator:<br \/>\n    &#034;&#034;&#034;ETag\u751f\u6210\u5668&#xff0c;\u652f\u6301\u4e0d\u540c\u7b56\u7565&#034;&#034;&#034;<\/p>\n<p>    &#064;staticmethod<br \/>\n    def generate_strong_etag(content):<br \/>\n        &#034;&#034;&#034;\u751f\u6210\u5f3aETag&#xff08;\u57fa\u4e8e\u5185\u5bb9\u54c8\u5e0c&#xff09;&#034;&#034;&#034;<br \/>\n        content_hash &#061; hashlib.md5(content).hexdigest()<br \/>\n        return f&#039;&#034;{content_hash}&#034;&#039;<\/p>\n<p>    &#064;staticmethod<br \/>\n    def generate_weak_etag(content):<br \/>\n        &#034;&#034;&#034;\u751f\u6210\u5f31ETag&#xff08;W\/\u524d\u7f00&#xff09;&#034;&#034;&#034;<br \/>\n        # \u5f31ETag&#xff1a;\u8bed\u4e49\u76f8\u540c\u5373\u53ef&#xff0c;\u53ef\u5ffd\u7565\u5fae\u5c0f\u5dee\u5f02&#xff08;\u5982\u7a7a\u683c\u3001\u65f6\u95f4\u6233&#xff09;<br \/>\n        normalized &#061; content.strip().replace(&#039;\\\\r\\\\n&#039;, &#039;\\\\n&#039;)<br \/>\n        content_hash &#061; hashlib.md5(normalized.encode()).hexdigest()[:8]<br \/>\n        return f&#039;W\/&#034;{content_hash}&#034;&#039;<\/p>\n<p>    &#064;staticmethod<br \/>\n    def generate_version_etag(version):<br \/>\n        &#034;&#034;&#034;\u57fa\u4e8e\u7248\u672c\u53f7\u7684ETag&#034;&#034;&#034;<br \/>\n        return f&#039;&#034;v{version}&#034;&#039;<\/p>\n<p>    &#064;staticmethod<br \/>\n    def generate_composite_etag(content, last_modified):<br \/>\n        &#034;&#034;&#034;\u590d\u5408ETag&#xff1a;\u5185\u5bb9\u54c8\u5e0c &#043; \u65f6\u95f4\u6233&#034;&#034;&#034;<br \/>\n        content_hash &#061; hashlib.md5(content).hexdigest()[:8]<br \/>\n        timestamp &#061; int(last_modified.timestamp())<br \/>\n        return f&#039;&#034;{content_hash}-{timestamp}&#034;&#039; <\/p>\n<h6>13.2.2 \u6761\u4ef6\u8bf7\u6c42\u5934\u8be6\u89e3<\/h6>\n<p>\u5ba2\u6237\u7aef\u53d1\u9001\u7684\u9a8c\u8bc1\u5934&#xff1a;<\/p>\n<li>\n<p>If-None-Match&#xff08;ETag\u9a8c\u8bc1&#xff09;&#xff1a;<\/p>\n<p>http<\/p>\n<p> GET \/resource HTTP\/1.1<br \/>\nIf-None-Match: &#034;abc123&#034;, &#034;def456&#034;, W\/&#034;ghi789&#034; <\/p>\n<ul>\n<li>\n<p>\u670d\u52a1\u5668\u68c0\u67e5\u5f53\u524dETag\u662f\u5426\u5339\u914d\u5217\u8868\u4e2d\u7684\u4efb\u4f55\u4e00\u4e2a<\/p>\n<\/li>\n<li>\n<p>\u4f7f\u7528\u00a0*\u00a0\u5339\u914d\u4efb\u4f55ETag&#xff08;\u65e0\u6761\u4ef6\u8bf7\u6c42&#xff09;<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p>If-Modified-Since&#xff08;\u65f6\u95f4\u9a8c\u8bc1&#xff09;&#xff1a;<\/p>\n<p>http<\/p>\n<p> GET \/resource HTTP\/1.1<br \/>\nIf-Modified-Since: Mon, 21 Oct 2024 09:00:00 GMT <\/p>\n<ul>\n<li>\n<p>\u670d\u52a1\u5668\u68c0\u67e5\u8d44\u6e90\u662f\u5426\u5728\u6307\u5b9a\u65f6\u95f4\u540e\u4fee\u6539\u8fc7<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p>\u7ec4\u5408\u4f7f\u7528&#xff1a;<\/p>\n<p>http<\/p>\n<p> GET \/resource HTTP\/1.1<br \/>\nIf-None-Match: &#034;abc123&#034;<br \/>\nIf-Modified-Since: Mon, 21 Oct 2024 09:00:00 GMT <\/p>\n<ul>\n<li>\n<p>ETag\u4f18\u5148&#xff1a;\u5982\u679c\u63d0\u4f9bETag&#xff0c;\u5ffd\u7565\u65f6\u95f4\u6233<\/p>\n<\/li>\n<li>\n<p>\u4fdd\u5b88\u7b56\u7565&#xff1a;\u53ea\u6709\u4e24\u4e2a\u6761\u4ef6\u90fd\u6ee1\u8db3\u672a\u4fee\u6539&#xff0c;\u624d\u8fd4\u56de304<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<p>\u670d\u52a1\u5668\u9a8c\u8bc1\u903b\u8f91&#xff1a;<\/p>\n<p>python<\/p>\n<p>def validate_request(headers, resource_info):<br \/>\n    &#034;&#034;&#034;<br \/>\n    \u9a8c\u8bc1\u6761\u4ef6\u8bf7\u6c42&#xff0c;\u51b3\u5b9a\u8fd4\u56de304\u8fd8\u662f200<br \/>\n    &#034;&#034;&#034;<\/p>\n<p>    # \u68c0\u67e5If-None-Match<br \/>\n    if_none_match &#061; headers.get(&#039;If-None-Match&#039;)<br \/>\n    if if_none_match:<br \/>\n        if if_none_match &#061;&#061; &#039;*&#039;:<br \/>\n            # \u65e0\u6761\u4ef6\u5339\u914d\u4efb\u4f55ETag<br \/>\n            return False  # \u9700\u8981\u8fd4\u56de\u5b8c\u6574\u54cd\u5e94<\/p>\n<p>        # \u89e3\u6790ETag\u5217\u8868<br \/>\n        etags &#061; [etag.strip().strip(&#039;&#034;&#039;) for etag in if_none_match.split(&#039;,&#039;)]<\/p>\n<p>        # \u68c0\u67e5\u662f\u5426\u6709\u5339\u914d\u7684ETag<br \/>\n        current_etag &#061; resource_info.get(&#039;etag&#039;, &#039;&#039;).strip(&#039;&#034;&#039;)<br \/>\n        for etag in etags:<br \/>\n            # \u5f31\u9a8c\u8bc1\u5668\u6bd4\u8f83&#xff08;\u5ffd\u7565W\/\u524d\u7f00&#xff09;<br \/>\n            if etag.startswith(&#039;W\/&#039;):<br \/>\n                etag_value &#061; etag[2:]<br \/>\n                if current_etag.startswith(&#039;W\/&#039;):<br \/>\n                    current_value &#061; current_etag[2:]<br \/>\n                else:<br \/>\n                    current_value &#061; current_etag<\/p>\n<p>                if etag_value &#061;&#061; current_value:<br \/>\n                    return True  # \u8fd4\u56de304<br \/>\n            else:<br \/>\n                # \u5f3a\u9a8c\u8bc1\u5668\u7cbe\u786e\u5339\u914d<br \/>\n                if etag &#061;&#061; current_etag:<br \/>\n                    return True  # \u8fd4\u56de304<\/p>\n<p>    # \u68c0\u67e5If-Modified-Since<br \/>\n    if_modified_since &#061; headers.get(&#039;If-Modified-Since&#039;)<br \/>\n    if if_modified_since:<br \/>\n        try:<br \/>\n            client_time &#061; parse_http_date(if_modified_since)<br \/>\n            server_time &#061; resource_info.get(&#039;last_modified&#039;)<\/p>\n<p>            if server_time and server_time &lt;&#061; client_time:<br \/>\n                return True  # \u8fd4\u56de304<br \/>\n        except ValueError:<br \/>\n            pass<\/p>\n<p>    return False  # \u9700\u8981\u8fd4\u56de\u5b8c\u6574\u54cd\u5e94 <\/p>\n<h6>13.2.3 \u54cd\u5e94\u5934\u914d\u7f6e<\/h6>\n<p>\u6807\u51c6304\u54cd\u5e94&#xff1a;<\/p>\n<p>http<\/p>\n<p>HTTP\/1.1 304 Not Modified<br \/>\nDate: Mon, 21 Oct 2024 09:00:00 GMT<br \/>\nETag: &#034;abc123&#034;<br \/>\nCache-Control: public, max-age&#061;3600<br \/>\nExpires: Mon, 21 Oct 2024 10:00:00 GMT<br \/>\nLast-Modified: Mon, 21 Oct 2024 08:00:00 GMT <\/p>\n<p>\u91cd\u8981\u6ce8\u610f\u4e8b\u9879&#xff1a;<\/p>\n<li>\n<p>\u65e0\u54cd\u5e94\u4f53&#xff1a;304\u54cd\u5e94\u5fc5\u987b\u6ca1\u6709\u6d88\u606f\u4f53<\/p>\n<\/li>\n<li>\n<p>\u5fc5\u987b\u5305\u542b\u7684\u5934\u90e8&#xff1a;<\/p>\n<ul>\n<li>\n<p>Date&#xff1a;\u54cd\u5e94\u751f\u6210\u65f6\u95f4<\/p>\n<\/li>\n<li>\n<p>\u7f13\u5b58\u9a8c\u8bc1\u5934&#xff08;ETag\u6216Last-Modified\u81f3\u5c11\u4e00\u4e2a&#xff09;<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p>\u53ef\u9009\u5934\u90e8&#xff1a;<\/p>\n<ul>\n<li>\n<p>Cache-Control&#xff1a;\u66f4\u65b0\u7f13\u5b58\u6307\u4ee4<\/p>\n<\/li>\n<li>\n<p>Expires&#xff1a;\u66f4\u65b0\u8fc7\u671f\u65f6\u95f4<\/p>\n<\/li>\n<li>\n<p>\u5176\u4ed6\u7f13\u5b58\u76f8\u5173\u5934\u90e8<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<p>\u670d\u52a1\u5668\u914d\u7f6e\u793a\u4f8b&#xff1a;<\/p>\n<p>nginx<\/p>\n<p># Nginx\u9759\u6001\u6587\u4ef6304\u914d\u7f6e<br \/>\nlocation ~* \\\\.(jpg|jpeg|png|gif|ico|css|js)$ {<br \/>\n    # \u542f\u7528ETag<br \/>\n    etag on;<\/p>\n<p>    # \u542f\u7528\u6700\u540e\u4fee\u6539\u65f6\u95f4<br \/>\n    if_modified_since exact;<\/p>\n<p>    # \u7f13\u5b58\u63a7\u5236<br \/>\n    add_header Cache-Control &#034;public, max-age&#061;31536000, immutable&#034;;<\/p>\n<p>    # \u5bf9\u9759\u6001\u6587\u4ef6\u81ea\u52a8\u5904\u7406\u6761\u4ef6\u8bf7\u6c42<br \/>\n    # Nginx\u4f1a\u81ea\u52a8\u5904\u7406If-Modified-Since\u548cIf-None-Match<br \/>\n}<\/p>\n<p># \u52a8\u6001\u5185\u5bb9\u7684304\u5904\u7406<br \/>\nlocation \/api\/data {<br \/>\n    proxy_pass http:\/\/backend;<\/p>\n<p>    # \u4f20\u9012\u6761\u4ef6\u8bf7\u6c42\u5934\u5230\u540e\u7aef<br \/>\n    proxy_set_header If-Modified-Since $http_if_modified_since;<br \/>\n    proxy_set_header If-None-Match $http_if_none_match;<\/p>\n<p>    # \u5904\u7406\u540e\u7aef\u7684304\u54cd\u5e94<br \/>\n    proxy_intercept_errors on;<br \/>\n    error_page 304 &#061; &#064;handle_304;<br \/>\n}<\/p>\n<p>location &#064;handle_304 {<br \/>\n    # \u8fd4\u56de304&#xff0c;\u4fdd\u6301\u7f13\u5b58\u5934<br \/>\n    return 304;<br \/>\n} <\/p>\n<h5>13.3 \u7f13\u5b58\u5934\u4e0e\u7f13\u5b58\u7b56\u7565<\/h5>\n<h6>13.3.1 \u7f13\u5b58\u63a7\u5236\u5934\u8be6\u89e3<\/h6>\n<p>Cache-Control\u6307\u4ee4&#xff1a;<\/p>\n<table>\n<tr>\u6307\u4ee4\u503c\u542b\u4e49\u793a\u4f8b<\/tr>\n<tbody>\n<tr>\n<td>public<\/td>\n<td>&#8211;<\/td>\n<td>\u54cd\u5e94\u53ef\u88ab\u4efb\u4f55\u7f13\u5b58\u5b58\u50a8<\/td>\n<td>Cache-Control: public<\/td>\n<\/tr>\n<tr>\n<td>private<\/td>\n<td>&#8211;<\/td>\n<td>\u54cd\u5e94\u4ec5\u4f9b\u5355\u4e2a\u7528\u6237\u7f13\u5b58<\/td>\n<td>Cache-Control: private<\/td>\n<\/tr>\n<tr>\n<td>max-age<\/td>\n<td>\u79d2\u6570<\/td>\n<td>\u65b0\u9c9c\u5ea6\u751f\u547d\u5468\u671f<\/td>\n<td>max-age&#061;3600<\/td>\n<\/tr>\n<tr>\n<td>s-maxage<\/td>\n<td>\u79d2\u6570<\/td>\n<td>\u5171\u4eab\u7f13\u5b58&#xff08;CDN&#xff09;\u6709\u6548\u671f<\/td>\n<td>s-maxage&#061;86400<\/td>\n<\/tr>\n<tr>\n<td>no-cache<\/td>\n<td>&#8211;<\/td>\n<td>\u6bcf\u6b21\u9700\u5411\u670d\u52a1\u5668\u9a8c\u8bc1<\/td>\n<td>Cache-Control: no-cache<\/td>\n<\/tr>\n<tr>\n<td>no-store<\/td>\n<td>&#8211;<\/td>\n<td>\u4e0d\u7f13\u5b58\u4efb\u4f55\u90e8\u5206<\/td>\n<td>Cache-Control: no-store<\/td>\n<\/tr>\n<tr>\n<td>must-revalidate<\/td>\n<td>&#8211;<\/td>\n<td>\u8fc7\u671f\u540e\u5fc5\u987b\u9a8c\u8bc1<\/td>\n<td>must-revalidate<\/td>\n<\/tr>\n<tr>\n<td>immutable<\/td>\n<td>&#8211;<\/td>\n<td>\u5185\u5bb9\u6c38\u4e0d\u53d8&#xff0c;\u65e0\u9700\u9a8c\u8bc1<\/td>\n<td>immutable<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>\u7ec4\u5408\u7b56\u7565\u793a\u4f8b&#xff1a;<\/p>\n<p>http<\/p>\n<p># \u9759\u6001\u8d44\u6e90&#xff1a;\u957f\u671f\u7f13\u5b58&#xff0c;\u5185\u5bb9\u4e0d\u53d8<br \/>\nCache-Control: public, max-age&#061;31536000, immutable<br \/>\nETag: &#034;abc123&#034;<\/p>\n<p># \u7528\u6237\u4e2a\u4eba\u6570\u636e&#xff1a;\u79c1\u6709\u7f13\u5b58&#xff0c;\u77ed\u671f<br \/>\nCache-Control: private, max-age&#061;300, must-revalidate<br \/>\nETag: W\/&#034;def456&#034;<\/p>\n<p># \u52a8\u6001\u5185\u5bb9&#xff1a;\u9700\u6bcf\u6b21\u9a8c\u8bc1<br \/>\nCache-Control: no-cache<br \/>\nLast-Modified: Mon, 21 Oct 2024 09:00:00 GMT<\/p>\n<p># CDN\u4f18\u5316&#xff1a;\u4e0d\u540c\u7f13\u5b58\u7b56\u7565<br \/>\nCache-Control: public, s-maxage&#061;86400, max-age&#061;3600 <\/p>\n<h6>13.3.2 ETag\u751f\u6210\u6700\u4f73\u5b9e\u8df5<\/h6>\n<p>\u4e0d\u540c\u8d44\u6e90\u7c7b\u578b\u7684ETag\u7b56\u7565&#xff1a;<\/p>\n<p>python<\/p>\n<p>class ResourceETagStrategy:<br \/>\n    &#034;&#034;&#034;\u6839\u636e\u8d44\u6e90\u7c7b\u578b\u9009\u62e9\u5408\u9002\u7684ETag\u7b56\u7565&#034;&#034;&#034;<\/p>\n<p>    strategies &#061; {<br \/>\n        &#039;static_file&#039;: {<br \/>\n            &#039;type&#039;: &#039;strong&#039;,<br \/>\n            &#039;method&#039;: &#039;content_hash&#039;,<br \/>\n            &#039;description&#039;: &#039;\u9759\u6001\u6587\u4ef6\u4f7f\u7528\u5185\u5bb9\u54c8\u5e0c&#039;<br \/>\n        },<br \/>\n        &#039;api_json&#039;: {<br \/>\n            &#039;type&#039;: &#039;weak&#039;,<br \/>\n            &#039;method&#039;: &#039;semantic_hash&#039;,<br \/>\n            &#039;description&#039;: &#039;API\u54cd\u5e94\u4f7f\u7528\u8bed\u4e49\u54c8\u5e0c&#039;<br \/>\n        },<br \/>\n        &#039;database_content&#039;: {<br \/>\n            &#039;type&#039;: &#039;version&#039;,<br \/>\n            &#039;method&#039;: &#039;row_version&#039;,<br \/>\n            &#039;description&#039;: &#039;\u6570\u636e\u5e93\u5185\u5bb9\u4f7f\u7528\u884c\u7248\u672c\u53f7&#039;<br \/>\n        },<br \/>\n        &#039;computed_result&#039;: {<br \/>\n            &#039;type&#039;: &#039;composite&#039;,<br \/>\n            &#039;method&#039;: &#039;input_hash&#039;,<br \/>\n            &#039;description&#039;: &#039;\u8ba1\u7b97\u7ed3\u679c\u4f7f\u7528\u8f93\u5165\u53c2\u6570\u54c8\u5e0c&#039;<br \/>\n        }<br \/>\n    }<\/p>\n<p>    &#064;staticmethod<br \/>\n    def generate_etag(resource_type, content, metadata&#061;None):<br \/>\n        &#034;&#034;&#034;\u6839\u636e\u8d44\u6e90\u7c7b\u578b\u751f\u6210ETag&#034;&#034;&#034;<\/p>\n<p>        strategy &#061; ResourceETagStrategy.strategies.get(resource_type, {})<\/p>\n<p>        if strategy[&#039;method&#039;] &#061;&#061; &#039;content_hash&#039;:<br \/>\n            # \u9759\u6001\u6587\u4ef6&#xff1a;\u57fa\u4e8e\u5b8c\u6574\u5185\u5bb9<br \/>\n            return hashlib.md5(content).hexdigest()<\/p>\n<p>        elif strategy[&#039;method&#039;] &#061;&#061; &#039;semantic_hash&#039;:<br \/>\n            # JSON API&#xff1a;\u89c4\u8303\u5316\u540e\u54c8\u5e0c<br \/>\n            if isinstance(content, dict):<br \/>\n                # \u89c4\u8303\u5316JSON&#xff08;\u6392\u5e8f\u952e&#xff0c;\u6807\u51c6\u5316\u683c\u5f0f&#xff09;<br \/>\n                normalized &#061; json.dumps(<br \/>\n                    content,<br \/>\n                    sort_keys&#061;True,<br \/>\n                    separators&#061;(&#039;,&#039;, &#039;:&#039;)<br \/>\n                )<br \/>\n                return &#039;W\/&#034;&#039; &#043; hashlib.md5(normalized.encode()).hexdigest()[:8] &#043; &#039;&#034;&#039;<\/p>\n<p>        elif strategy[&#039;method&#039;] &#061;&#061; &#039;row_version&#039;:<br \/>\n            # \u6570\u636e\u5e93\u5185\u5bb9&#xff1a;\u7248\u672c\u53f7<br \/>\n            version &#061; metadata.get(&#039;version&#039;, 0) if metadata else 0<br \/>\n            return f&#039;&#034;v{version}&#034;&#039;<\/p>\n<p>        elif strategy[&#039;method&#039;] &#061;&#061; &#039;input_hash&#039;:<br \/>\n            # \u8ba1\u7b97\u7ed3\u679c&#xff1a;\u8f93\u5165\u53c2\u6570\u54c8\u5e0c<br \/>\n            input_hash &#061; hashlib.md5(str(metadata).encode()).hexdigest()[:12]<br \/>\n            return f&#039;&#034;{input_hash}&#034;&#039;<\/p>\n<p>        # \u9ed8\u8ba4&#xff1a;\u5f3aETag<br \/>\n        return &#039;&#034;&#039; &#043; hashlib.md5(content).hexdigest() &#043; &#039;&#034;&#039; <\/p>\n<p>ETag\u751f\u6210\u7684\u6210\u672c\u8003\u91cf&#xff1a;<\/p>\n<p>python<\/p>\n<p>class CostAwareETagGenerator:<br \/>\n    &#034;&#034;&#034;\u8003\u8651\u8ba1\u7b97\u6210\u672c\u7684ETag\u751f\u6210\u5668&#034;&#034;&#034;<\/p>\n<p>    def __init__(self):<br \/>\n        self.cache &#061; {}  # \u7f13\u5b58\u5df2\u8ba1\u7b97\u7684ETag<\/p>\n<p>    def generate_etag(self, content, strategy&#061;&#039;auto&#039;):<br \/>\n        &#034;&#034;&#034;<br \/>\n        \u667a\u80fd\u751f\u6210ETag&#xff0c;\u5e73\u8861\u51c6\u786e\u6027\u548c\u6027\u80fd<br \/>\n        &#034;&#034;&#034;<\/p>\n<p>        # \u5c0f\u5185\u5bb9&#xff1a;\u76f4\u63a5\u8ba1\u7b97<br \/>\n        if len(content) &lt; 1024:  # \u5c0f\u4e8e1KB<br \/>\n            return self._generate_strong_etag(content)<\/p>\n<p>        # \u5927\u5185\u5bb9&#xff1a;\u6839\u636e\u7b56\u7565\u9009\u62e9<br \/>\n        if strategy &#061;&#061; &#039;auto&#039;:<br \/>\n            # \u81ea\u52a8\u9009\u62e9&#xff1a;\u57fa\u4e8e\u5185\u5bb9\u7c7b\u578b\u548c\u5927\u5c0f<br \/>\n            if self._is_binary(content):<br \/>\n                # \u4e8c\u8fdb\u5236\u6587\u4ef6&#xff1a;\u4f7f\u7528\u6587\u4ef6\u5c5e\u6027\u548c\u90e8\u5206\u54c8\u5e0c<br \/>\n                return self._generate_composite_etag(content)<br \/>\n            else:<br \/>\n                # \u6587\u672c\u6587\u4ef6&#xff1a;\u4f7f\u7528\u5f31ETag&#xff08;\u66f4\u9ad8\u6548&#xff09;<br \/>\n                return self._generate_weak_etag(content)<\/p>\n<p>        elif strategy &#061;&#061; &#039;fast&#039;:<br \/>\n            # \u5feb\u901f\u6a21\u5f0f&#xff1a;\u4ec5\u4f7f\u7528\u6587\u4ef6\u5c5e\u6027\u548c\u5927\u5c0f<br \/>\n            return self._generate_lightweight_etag(content)<\/p>\n<p>        elif strategy &#061;&#061; &#039;accurate&#039;:<br \/>\n            # \u7cbe\u786e\u6a21\u5f0f&#xff1a;\u5b8c\u6574\u5185\u5bb9\u54c8\u5e0c<br \/>\n            return self._generate_strong_etag(content)<\/p>\n<p>    def _generate_lightweight_etag(self, content):<br \/>\n        &#034;&#034;&#034;\u8f7b\u91cf\u7ea7ETag&#xff1a;\u57fa\u4e8e\u5927\u5c0f\u548c\u4fee\u6539\u65f6\u95f4&#034;&#034;&#034;<br \/>\n        size &#061; len(content)<br \/>\n        mtime &#061; os.path.getmtime(filename) if hasattr(content, &#039;filename&#039;) else 0<br \/>\n        return f&#039;W\/&#034;s{size}-t{int(mtime)}&#034;&#039;<\/p>\n<p>    def _generate_composite_etag(self, content):<br \/>\n        &#034;&#034;&#034;\u590d\u5408ETag&#xff1a;\u5934\u90e8\u54c8\u5e0c &#043; \u6587\u4ef6\u5c5e\u6027&#034;&#034;&#034;<br \/>\n        # \u53ea\u54c8\u5e0c\u6587\u4ef6\u5934\u90e8&#xff08;\u63d0\u9ad8\u6027\u80fd&#xff09;<br \/>\n        header_hash &#061; hashlib.md5(content[:1024]).hexdigest()[:8]<br \/>\n        size &#061; len(content)<br \/>\n        return f&#039;&#034;{header_hash}-{size}&#034;&#039; <\/p>\n<h6>13.3.3 \u7f13\u5b58\u5c42\u6b21\u4e0e\u9a8c\u8bc1\u6d41\u7a0b<\/h6>\n<p>\u591a\u5c42\u6b21\u7f13\u5b58\u67b6\u6784&#xff1a;<\/p>\n<p>text<\/p>\n<p>\u5ba2\u6237\u7aef\u7f13\u5b58 \u2192 \u4ee3\u7406\u7f13\u5b58 \u2192 CDN\u8fb9\u7f18\u7f13\u5b58 \u2192 \u6e90\u670d\u52a1\u5668 <\/p>\n<p>\u9a8c\u8bc1\u8bf7\u6c42\u4f20\u64ad&#xff1a;<img loading=\"lazy\" decoding=\"async\" alt=\"\" height=\"1950\" src=\"https:\/\/www.wsisp.com\/helps\/wp-content\/uploads\/2026\/03\/20260228233641-69a37c094e18a.png\" width=\"764\" \/><\/p>\n<p>\u7f13\u5b58\u9a8c\u8bc1\u4f18\u5316\u7b56\u7565&#xff1a;<\/p>\n<p>python<\/p>\n<p>class CacheValidationOptimizer:<br \/>\n    &#034;&#034;&#034;\u7f13\u5b58\u9a8c\u8bc1\u4f18\u5316\u5668&#034;&#034;&#034;<\/p>\n<p>    def __init__(self):<br \/>\n        self.stats &#061; {<br \/>\n            &#039;cache_hits&#039;: 0,<br \/>\n            &#039;cache_misses&#039;: 0,<br \/>\n            &#039;validation_requests&#039;: 0<br \/>\n        }<\/p>\n<p>    def should_revalidate(self, cache_entry, request_headers):<br \/>\n        &#034;&#034;&#034;<br \/>\n        \u667a\u80fd\u51b3\u5b9a\u662f\u5426\u9700\u8981\u9a8c\u8bc1<br \/>\n        &#034;&#034;&#034;<\/p>\n<p>        # 1. \u68c0\u67e5\u7f13\u5b58\u662f\u5426\u8fc7\u671f<br \/>\n        if self._is_fresh(cache_entry):<br \/>\n            self.stats[&#039;cache_hits&#039;] &#043;&#061; 1<br \/>\n            return False  # \u65e0\u9700\u9a8c\u8bc1<\/p>\n<p>        # 2. \u68c0\u67e5\u662f\u5426\u6709\u6761\u4ef6\u8bf7\u6c42\u5934<br \/>\n        has_validation_headers &#061; any(<br \/>\n            header in request_headers<br \/>\n            for header in [&#039;If-None-Match&#039;, &#039;If-Modified-Since&#039;]<br \/>\n        )<\/p>\n<p>        if not has_validation_headers:<br \/>\n            self.stats[&#039;cache_misses&#039;] &#043;&#061; 1<br \/>\n            return True  # \u9700\u8981\u5b8c\u6574\u8bf7\u6c42<\/p>\n<p>        # 3. \u68c0\u67e5\u7f13\u5b58\u6761\u76ee\u7684\u9a8c\u8bc1\u5668\u5f3a\u5ea6<br \/>\n        cache_has_strong_validator &#061; cache_entry.get(&#039;etag&#039;, &#039;&#039;).startswith(&#039;&#034;&#039;)<br \/>\n        cache_has_weak_validator &#061; cache_entry.get(&#039;etag&#039;, &#039;&#039;).startswith(&#039;W\/&#039;)<br \/>\n        cache_has_timestamp &#061; &#039;last_modified&#039; in cache_entry<\/p>\n<p>        # \u5982\u679c\u53ea\u6709\u5f31\u9a8c\u8bc1\u5668\u4e14\u8d44\u6e90\u5f88\u91cd\u8981&#xff0c;\u53ef\u4ee5\u91cd\u65b0\u83b7\u53d6<br \/>\n        if cache_has_weak_validator and not cache_has_timestamp:<br \/>\n            # \u91cd\u8981\u8d44\u6e90&#xff1a;\u5373\u4f7f\u6709\u5f31\u9a8c\u8bc1\u5668\u4e5f\u91cd\u65b0\u83b7\u53d6<br \/>\n            if self._is_important_resource(cache_entry[&#039;url&#039;]):<br \/>\n                self.stats[&#039;validation_requests&#039;] &#043;&#061; 1<br \/>\n                return True<\/p>\n<p>        # \u9ed8\u8ba4&#xff1a;\u53d1\u9001\u9a8c\u8bc1\u8bf7\u6c42<br \/>\n        self.stats[&#039;validation_requests&#039;] &#043;&#061; 1<br \/>\n        return True<\/p>\n<p>    def _is_fresh(self, cache_entry):<br \/>\n        &#034;&#034;&#034;\u68c0\u67e5\u7f13\u5b58\u662f\u5426\u65b0\u9c9c&#034;&#034;&#034;<br \/>\n        max_age &#061; cache_entry.get(&#039;max_age&#039;, 0)<br \/>\n        cached_at &#061; cache_entry.get(&#039;cached_at&#039;, 0)<\/p>\n<p>        if max_age &#061;&#061; 0:<br \/>\n            return False<\/p>\n<p>        current_time &#061; time.time()<br \/>\n        age &#061; current_time &#8211; cached_at<\/p>\n<p>        return age &lt; max_age <\/p>\n<h5>13.4 \u4f7f\u7528\u573a\u666f\u4e0e\u6700\u4f73\u5b9e\u8df5<\/h5>\n<h6>13.4.1 \u9002\u7528\u573a\u666f\u5206\u6790<\/h6>\n<p>\u9ad8\u7f13\u5b58\u547d\u4e2d\u7387\u573a\u666f&#xff1a;<\/p>\n<li>\n<p>\u9759\u6001\u8d44\u6e90\u6587\u4ef6&#xff1a;<\/p>\n<p>nginx<\/p>\n<p> # \u56fe\u7247\u3001CSS\u3001JS\u7b49\u9759\u6001\u8d44\u6e90<br \/>\nlocation ~* \\\\.(css|js|jpg|jpeg|png|gif|ico|woff|woff2|ttf|svg)$ {<br \/>\n    expires 1y;<br \/>\n    add_header Cache-Control &#034;public, immutable&#034;;<br \/>\n    etag on;<\/p>\n<p>    # \u91cd\u8981\u7684&#xff1a;\u4e0d\u8981\u8bbe\u7f6eno-cache&#xff0c;\u5426\u5219\u6bcf\u6b21\u90fd\u4f1a\u9a8c\u8bc1<br \/>\n    # \u4f7f\u7528immutable\u8868\u793a\u5185\u5bb9\u6c38\u4e0d\u6539\u53d8<br \/>\n} <\/li>\n<li>\n<p>API\u54cd\u5e94\u7f13\u5b58&#xff1a;<\/p>\n<p>python<\/p>\n<p> # Django REST Framework API\u7f13\u5b58<br \/>\nfrom django.views.decorators.cache import cache_page<br \/>\nfrom django.views.decorators.vary import vary_on_headers<\/p>\n<p>&#064;api_view([&#039;GET&#039;])<br \/>\n&#064;cache_page(60 * 15)  # \u7f13\u5b5815\u5206\u949f<br \/>\n&#064;vary_on_headers(&#039;Authorization&#039;, &#039;Accept-Language&#039;)<br \/>\ndef product_list(request):<br \/>\n    products &#061; Product.objects.all()<br \/>\n    serializer &#061; ProductSerializer(products, many&#061;True)<\/p>\n<p>    # \u8bbe\u7f6eETag<br \/>\n    response &#061; Response(serializer.data)<br \/>\n    response[&#039;ETag&#039;] &#061; generate_etag(serializer.data)<br \/>\n    response[&#039;Cache-Control&#039;] &#061; &#039;public, max-age&#061;900&#039;<\/p>\n<p>    return response <\/li>\n<li>\n<p>\u5185\u5bb9\u5206\u53d1\u7f51\u7edc&#xff08;CDN&#xff09;&#xff1a;<\/p>\n<p>http<\/p>\n<p> # CDN\u8fb9\u7f18\u8282\u70b9\u914d\u7f6e<br \/>\nCache-Control: public, s-maxage&#061;86400, max-age&#061;3600<br \/>\nETag: &#034;abc123&#034;<br \/>\nVary: Accept-Encoding, User-Agent<\/p>\n<p># CDN\u5904\u7406\u903b\u8f91&#xff1a;<br \/>\n# 1. \u68c0\u67e5s-maxage&#xff08;CDN\u7f13\u5b58\u65f6\u95f4&#xff09;<br \/>\n# 2. \u68c0\u67e5If-None-Match<br \/>\n# 3. \u5fc5\u8981\u65f6\u56de\u6e90\u9a8c\u8bc1 <\/li>\n<p>\u4e0d\u9002\u5408304\u7684\u573a\u666f&#xff1a;<\/p>\n<li>\n<p>\u9ad8\u5ea6\u52a8\u6001\u5185\u5bb9&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u5b9e\u65f6\u80a1\u7968\u4ef7\u683c<\/p>\n<\/li>\n<li>\n<p>\u804a\u5929\u6d88\u606f<\/p>\n<\/li>\n<li>\n<p>\u5728\u7ebf\u6e38\u620f\u72b6\u6001<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p>\u4e2a\u6027\u5316\u6570\u636e&#xff1a;<\/p>\n<p>python<\/p>\n<p> # \u7528\u6237\u4e2a\u4eba\u6570\u636e\u4e0d\u9002\u5408\u516c\u5171\u7f13\u5b58<br \/>\n&#064;cache_page(60)<br \/>\n&#064;vary_on_cookie  # \u57fa\u4e8e\u7528\u6237\u4f1a\u8bdd<br \/>\ndef user_profile(request):<br \/>\n    # \u6bcf\u4e2a\u7528\u6237\u7684\u7f13\u5b58\u72ec\u7acb<br \/>\n    pass <\/li>\n<li>\n<p>POST\/PUT\/DELETE\u8bf7\u6c42&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u4fee\u6539\u64cd\u4f5c\u4e0d\u5e94\u8fd4\u56de304<\/p>\n<\/li>\n<li>\n<p>\u5e94\u4f7f\u7528200\/201\/204\u7b49\u72b6\u6001\u7801<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<h6>13.4.2 \u6027\u80fd\u4f18\u5316\u7b56\u7565<\/h6>\n<p>\u7f13\u5b58\u9884\u70ed\u7b56\u7565&#xff1a;<\/p>\n<p>python<\/p>\n<p>class CacheWarmer:<br \/>\n    &#034;&#034;&#034;\u7f13\u5b58\u9884\u70ed\u7ba1\u7406\u5668&#034;&#034;&#034;<\/p>\n<p>    def __init__(self, cache_client):<br \/>\n        self.cache &#061; cache_client<br \/>\n        self.warmup_queue &#061; []<\/p>\n<p>    def schedule_warmup(self, urls, priority&#061;&#039;medium&#039;):<br \/>\n        &#034;&#034;&#034;\u8ba1\u5212\u7f13\u5b58\u9884\u70ed\u4efb\u52a1&#034;&#034;&#034;<\/p>\n<p>        for url in urls:<br \/>\n            self.warmup_queue.append({<br \/>\n                &#039;url&#039;: url,<br \/>\n                &#039;priority&#039;: priority,<br \/>\n                &#039;scheduled_at&#039;: time.time()<br \/>\n            })<\/p>\n<p>        # \u6309\u4f18\u5148\u7ea7\u6392\u5e8f<br \/>\n        self.warmup_queue.sort(key&#061;lambda x: (<br \/>\n            0 if x[&#039;priority&#039;] &#061;&#061; &#039;high&#039; else<br \/>\n            1 if x[&#039;priority&#039;] &#061;&#061; &#039;medium&#039; else 2<br \/>\n        ))<\/p>\n<p>    def execute_warmup(self, concurrency&#061;5):<br \/>\n        &#034;&#034;&#034;\u6267\u884c\u7f13\u5b58\u9884\u70ed&#034;&#034;&#034;<\/p>\n<p>        async def warm_url(url):<br \/>\n            try:<br \/>\n                # \u53d1\u9001\u8bf7\u6c42&#xff0c;\u89e6\u53d1\u7f13\u5b58<br \/>\n                async with aiohttp.ClientSession() as session:<br \/>\n                    async with session.get(url) as response:<br \/>\n                        if response.status &#061;&#061; 200:<br \/>\n                            print(f&#034;\u9884\u70ed\u6210\u529f: {url}&#034;)<br \/>\n                            return True<br \/>\n            except Exception as e:<br \/>\n                print(f&#034;\u9884\u70ed\u5931\u8d25 {url}: {e}&#034;)<br \/>\n                return False<\/p>\n<p>        # \u5e76\u53d1\u9884\u70ed<br \/>\n        tasks &#061; []<br \/>\n        for item in self.warmup_queue[:concurrency]:<br \/>\n            tasks.append(warm_url(item[&#039;url&#039;]))<\/p>\n<p>        # \u79fb\u9664\u5df2\u5904\u7406\u7684\u4efb\u52a1<br \/>\n        self.warmup_queue &#061; self.warmup_queue[concurrency:]<\/p>\n<p>        return tasks <\/p>\n<p>\u6761\u4ef6\u8bf7\u6c42\u4f18\u5316&#xff1a;<\/p>\n<p>nginx<\/p>\n<p># Nginx\u6761\u4ef6\u8bf7\u6c42\u4f18\u5316\u914d\u7f6e<br \/>\nhttp {<br \/>\n    # \u542f\u7528\u6761\u4ef6\u8bf7\u6c42\u5904\u7406<br \/>\n    if_modified_since before;<\/p>\n<p>    # ETag\u914d\u7f6e<br \/>\n    etag on;<\/p>\n<p>    # \u9759\u6001\u6587\u4ef6\u5904\u7406\u4f18\u5316<br \/>\n    location ~* \\\\.(jpg|jpeg|png|gif|ico|css|js)$ {<br \/>\n        # \u4f7f\u7528sendfile\u9ad8\u6548\u4f20\u8f93<br \/>\n        sendfile on;<br \/>\n        sendfile_max_chunk 1m;<\/p>\n<p>        # \u542f\u7528\u7f13\u5b58<br \/>\n        open_file_cache max&#061;1000 inactive&#061;20s;<br \/>\n        open_file_cache_valid 30s;<br \/>\n        open_file_cache_min_uses 2;<br \/>\n        open_file_cache_errors on;<\/p>\n<p>        # \u7f13\u5b58\u63a7\u5236<br \/>\n        expires 1y;<br \/>\n        add_header Cache-Control &#034;public, immutable&#034;;<br \/>\n    }<\/p>\n<p>    # \u52a8\u6001\u5185\u5bb9\u7f13\u5b58<br \/>\n    location \/api\/ {<br \/>\n        proxy_cache api_cache;<br \/>\n        proxy_cache_key &#034;$scheme$request_method$host$request_uri&#034;;<br \/>\n        proxy_cache_valid 200 302 5m;<br \/>\n        proxy_cache_valid 404 1m;<br \/>\n        proxy_cache_use_stale error timeout invalid_header updating<br \/>\n                             http_500 http_502 http_503 http_504;<\/p>\n<p>        # \u4f20\u9012\u6761\u4ef6\u8bf7\u6c42\u5934<br \/>\n        proxy_set_header If-Modified-Since $http_if_modified_since;<br \/>\n        proxy_set_header If-None-Match $http_if_none_match;<\/p>\n<p>        proxy_pass http:\/\/backend;<br \/>\n    }<br \/>\n} <\/p>\n<p>\u5ba2\u6237\u7aef\u7f13\u5b58\u7b56\u7565&#xff1a;<\/p>\n<p>javascript<\/p>\n<p>\/\/ \u524d\u7aef\u7f13\u5b58\u63a7\u5236\u6700\u4f73\u5b9e\u8df5<br \/>\nclass ClientCacheManager {<br \/>\n    constructor() {<br \/>\n        this.cacheName &#061; &#039;app-cache-v1&#039;;<br \/>\n        this.precacheList &#061; [<br \/>\n            &#039;\/&#039;,<br \/>\n            &#039;\/styles\/main.css&#039;,<br \/>\n            &#039;\/scripts\/app.js&#039;,<br \/>\n            &#039;\/images\/logo.png&#039;<br \/>\n        ];<br \/>\n    }<\/p>\n<p>    async setupCaching() {<br \/>\n        \/\/ 1. \u6ce8\u518cService Worker<br \/>\n        if (&#039;serviceWorker&#039; in navigator) {<br \/>\n            try {<br \/>\n                const registration &#061; await navigator.serviceWorker.register(&#039;\/sw.js&#039;);<br \/>\n                console.log(&#039;ServiceWorker \u6ce8\u518c\u6210\u529f&#039;);<br \/>\n            } catch (error) {<br \/>\n                console.log(&#039;ServiceWorker \u6ce8\u518c\u5931\u8d25:&#039;, error);<br \/>\n            }<br \/>\n        }<\/p>\n<p>        \/\/ 2. \u9884\u7f13\u5b58\u5173\u952e\u8d44\u6e90<br \/>\n        await this.precacheCriticalResources();<\/p>\n<p>        \/\/ 3. \u8bbe\u7f6e\u7f13\u5b58\u7b56\u7565<br \/>\n        this.setupCacheHeaders();<br \/>\n    }<\/p>\n<p>    async precacheCriticalResources() {<br \/>\n        const cache &#061; await caches.open(this.cacheName);<\/p>\n<p>        \/\/ \u9884\u7f13\u5b58\u5173\u952e\u8d44\u6e90<br \/>\n        await cache.addAll(this.precacheList);<\/p>\n<p>        \/\/ \u8bbe\u7f6e\u7f13\u5b58\u7248\u672c<br \/>\n        await cache.put(<br \/>\n            new Request(&#039;\/cache-version&#039;),<br \/>\n            new Response(JSON.stringify({ version: &#039;1.0.0&#039; }))<br \/>\n        );<br \/>\n    }<\/p>\n<p>    setupCacheHeaders() {<br \/>\n        \/\/ \u901a\u8fc7fetch API\u63a7\u5236\u7f13\u5b58\u884c\u4e3a<br \/>\n        const originalFetch &#061; window.fetch;<\/p>\n<p>        window.fetch &#061; async function(resource, init &#061; {}) {<br \/>\n            \/\/ \u5bf9GET\u8bf7\u6c42\u6dfb\u52a0\u7f13\u5b58\u63a7\u5236<br \/>\n            const isGet &#061; !init.method || init.method.toUpperCase() &#061;&#061;&#061; &#039;GET&#039;;<\/p>\n<p>            if (isGet) {<br \/>\n                \/\/ \u6dfb\u52a0\u6761\u4ef6\u8bf7\u6c42\u5934&#xff08;\u5982\u679c\u672c\u5730\u6709\u7f13\u5b58&#xff09;<br \/>\n                const cached &#061; await caches.match(resource);<br \/>\n                if (cached) {<br \/>\n                    const etag &#061; cached.headers.get(&#039;ETag&#039;);<br \/>\n                    const lastModified &#061; cached.headers.get(&#039;Last-Modified&#039;);<\/p>\n<p>                    if (!init.headers) init.headers &#061; {};<\/p>\n<p>                    if (etag) {<br \/>\n                        init.headers[&#039;If-None-Match&#039;] &#061; etag;<br \/>\n                    }<br \/>\n                    if (lastModified) {<br \/>\n                        init.headers[&#039;If-Modified-Since&#039;] &#061; lastModified;<br \/>\n                    }<br \/>\n                }<\/p>\n<p>                \/\/ \u8bbe\u7f6e\u7f13\u5b58\u6a21\u5f0f<br \/>\n                init.cache &#061; &#039;default&#039;; \/\/ \u6216 &#039;no-store&#039;, &#039;reload&#039;, &#039;no-cache&#039;, &#039;force-cache&#039;, &#039;only-if-cached&#039;<br \/>\n            }<\/p>\n<p>            return originalFetch(resource, init);<br \/>\n        };<br \/>\n    }<\/p>\n<p>    async getWithCache(url, options &#061; {}) {<br \/>\n        \/\/ \u9996\u5148\u5c1d\u8bd5\u4ece\u7f13\u5b58\u83b7\u53d6<br \/>\n        const cache &#061; await caches.open(this.cacheName);<br \/>\n        const cachedResponse &#061; await cache.match(url);<\/p>\n<p>        if (cachedResponse &amp;&amp; this.isCacheFresh(cachedResponse)) {<br \/>\n            \/\/ \u7f13\u5b58\u65b0\u9c9c&#xff0c;\u76f4\u63a5\u4f7f\u7528<br \/>\n            return cachedResponse;<br \/>\n        }<\/p>\n<p>        \/\/ \u7f13\u5b58\u8fc7\u671f\u6216\u4e0d\u5b58\u5728&#xff0c;\u53d1\u8d77\u7f51\u7edc\u8bf7\u6c42<br \/>\n        const networkResponse &#061; await fetch(url, options);<\/p>\n<p>        if (networkResponse.ok) {<br \/>\n            \/\/ \u66f4\u65b0\u7f13\u5b58<br \/>\n            await cache.put(url, networkResponse.clone());<br \/>\n        }<\/p>\n<p>        return networkResponse;<br \/>\n    }<\/p>\n<p>    isCacheFresh(cachedResponse) {<br \/>\n        \/\/ \u68c0\u67e5\u7f13\u5b58\u662f\u5426\u65b0\u9c9c<br \/>\n        const cacheControl &#061; cachedResponse.headers.get(&#039;Cache-Control&#039;);<br \/>\n        const date &#061; cachedResponse.headers.get(&#039;Date&#039;);<\/p>\n<p>        if (!cacheControl || !date) return false;<\/p>\n<p>        \/\/ \u89e3\u6790max-age<br \/>\n        const maxAgeMatch &#061; cacheControl.match(\/max-age&#061;(\\\\d&#043;)\/);<br \/>\n        if (!maxAgeMatch) return false;<\/p>\n<p>        const maxAge &#061; parseInt(maxAgeMatch[1], 10);<br \/>\n        const cachedTime &#061; new Date(date).getTime();<br \/>\n        const currentTime &#061; Date.now();<\/p>\n<p>        return (currentTime &#8211; cachedTime) &lt; maxAge * 1000;<br \/>\n    }<br \/>\n} <\/p>\n<h6>13.4.3 \u76d1\u63a7\u4e0e\u8c03\u8bd5<\/h6>\n<p>\u7f13\u5b58\u547d\u4e2d\u7387\u76d1\u63a7&#xff1a;<\/p>\n<p>python<\/p>\n<p>class CacheMonitor:<br \/>\n    &#034;&#034;&#034;\u7f13\u5b58\u6027\u80fd\u76d1\u63a7\u5668&#034;&#034;&#034;<\/p>\n<p>    def __init__(self):<br \/>\n        self.metrics &#061; {<br \/>\n            &#039;requests&#039;: 0,<br \/>\n            &#039;cache_hits&#039;: 0,<br \/>\n            &#039;cache_misses&#039;: 0,<br \/>\n            &#039;conditional_requests&#039;: 0,<br \/>\n            &#039;304_responses&#039;: 0,<br \/>\n            &#039;response_times&#039;: []<br \/>\n        }<\/p>\n<p>    def record_request(self, request_headers):<br \/>\n        &#034;&#034;&#034;\u8bb0\u5f55\u8bf7\u6c42\u6307\u6807&#034;&#034;&#034;<br \/>\n        self.metrics[&#039;requests&#039;] &#043;&#061; 1<\/p>\n<p>        # \u68c0\u67e5\u662f\u5426\u4e3a\u6761\u4ef6\u8bf7\u6c42<br \/>\n        if any(h in request_headers for h in [&#039;If-None-Match&#039;, &#039;If-Modified-Since&#039;]):<br \/>\n            self.metrics[&#039;conditional_requests&#039;] &#043;&#061; 1<\/p>\n<p>    def record_response(self, response_status, response_time):<br \/>\n        &#034;&#034;&#034;\u8bb0\u5f55\u54cd\u5e94\u6307\u6807&#034;&#034;&#034;<br \/>\n        if response_status &#061;&#061; 304:<br \/>\n            self.metrics[&#039;304_responses&#039;] &#043;&#061; 1<br \/>\n            self.metrics[&#039;cache_hits&#039;] &#043;&#061; 1<br \/>\n        elif response_status &#061;&#061; 200:<br \/>\n            self.metrics[&#039;cache_misses&#039;] &#043;&#061; 1<\/p>\n<p>        self.metrics[&#039;response_times&#039;].append(response_time)<\/p>\n<p>        # \u4fdd\u7559\u6700\u8fd11000\u4e2a\u6837\u672c<br \/>\n        if len(self.metrics[&#039;response_times&#039;]) &gt; 1000:<br \/>\n            self.metrics[&#039;response_times&#039;] &#061; self.metrics[&#039;response_times&#039;][-1000:]<\/p>\n<p>    def get_stats(self):<br \/>\n        &#034;&#034;&#034;\u83b7\u53d6\u7edf\u8ba1\u4fe1\u606f&#034;&#034;&#034;<br \/>\n        total &#061; self.metrics[&#039;requests&#039;]<br \/>\n        hits &#061; self.metrics[&#039;cache_hits&#039;]<br \/>\n        misses &#061; self.metrics[&#039;cache_misses&#039;]<\/p>\n<p>        hit_rate &#061; hits \/ total if total &gt; 0 else 0<\/p>\n<p>        return {<br \/>\n            &#039;total_requests&#039;: total,<br \/>\n            &#039;cache_hit_rate&#039;: f&#034;{hit_rate:.1%}&#034;,<br \/>\n            &#039;conditional_requests&#039;: self.metrics[&#039;conditional_requests&#039;],<br \/>\n            &#039;304_responses&#039;: self.metrics[&#039;304_responses&#039;],<br \/>\n            &#039;avg_response_time&#039;: sum(self.metrics[&#039;response_times&#039;]) \/ len(self.metrics[&#039;response_times&#039;]) if self.metrics[&#039;response_times&#039;] else 0,<br \/>\n            &#039;p95_response_time&#039;: self._calculate_percentile(self.metrics[&#039;response_times&#039;], 95)<br \/>\n        }<\/p>\n<p>    def _calculate_percentile(self, values, percentile):<br \/>\n        &#034;&#034;&#034;\u8ba1\u7b97\u767e\u5206\u4f4d\u6570&#034;&#034;&#034;<br \/>\n        if not values:<br \/>\n            return 0<\/p>\n<p>        sorted_values &#061; sorted(values)<br \/>\n        index &#061; (percentile \/ 100) * (len(sorted_values) &#8211; 1)<\/p>\n<p>        if index.is_integer():<br \/>\n            return sorted_values[int(index)]<br \/>\n        else:<br \/>\n            lower &#061; int(index)<br \/>\n            upper &#061; lower &#043; 1<br \/>\n            weight &#061; index &#8211; lower<br \/>\n            return sorted_values[lower] * (1 &#8211; weight) &#043; sorted_values[upper] * weight <\/p>\n<p>\u8c03\u8bd5\u5de5\u5177\u4e0e\u6280\u672f&#xff1a;<\/p>\n<li>\n<p>\u6d4f\u89c8\u5668\u5f00\u53d1\u8005\u5de5\u5177&#xff1a;<\/p>\n<p>javascript<\/p>\n<p> \/\/ \u68c0\u67e5\u7f13\u5b58\u72b6\u6001\u7684JavaScript\u4ee3\u7801<br \/>\nasync function checkCacheStatus(url) {<br \/>\n    console.group(&#039;\u7f13\u5b58\u72b6\u6001\u68c0\u67e5:&#039;, url);<\/p>\n<p>    \/\/ 1. \u68c0\u67e5Service Worker\u7f13\u5b58<br \/>\n    if (&#039;caches&#039; in window) {<br \/>\n        const cache &#061; await caches.open(&#039;app-cache&#039;);<br \/>\n        const cached &#061; await cache.match(url);<\/p>\n<p>        if (cached) {<br \/>\n            console.log(&#039;Service Worker\u7f13\u5b58:&#039;, cached);<br \/>\n            console.log(&#039;ETag:&#039;, cached.headers.get(&#039;ETag&#039;));<br \/>\n            console.log(&#039;Cache-Control:&#039;, cached.headers.get(&#039;Cache-Control&#039;));<br \/>\n            console.log(&#039;Last-Modified:&#039;, cached.headers.get(&#039;Last-Modified&#039;));<br \/>\n        }<br \/>\n    }<\/p>\n<p>    \/\/ 2. \u53d1\u8d77\u6761\u4ef6\u8bf7\u6c42<br \/>\n    const response &#061; await fetch(url, {<br \/>\n        headers: {<br \/>\n            &#039;Cache-Control&#039;: &#039;no-cache&#039;<br \/>\n        }<br \/>\n    });<\/p>\n<p>    console.log(&#039;\u7f51\u7edc\u54cd\u5e94\u72b6\u6001:&#039;, response.status);<br \/>\n    console.log(&#039;\u54cd\u5e94\u5934:&#039;, Object.fromEntries(response.headers.entries()));<\/p>\n<p>    console.groupEnd();<br \/>\n    return response;<br \/>\n} <\/li>\n<li>\n<p>\u547d\u4ee4\u884c\u8c03\u8bd5&#xff1a;<\/p>\n<p>bash<\/p>\n<p> # \u4f7f\u7528curl\u68c0\u67e5\u7f13\u5b58\u5934<br \/>\ncurl -I https:\/\/example.com\/resource<\/p>\n<p># \u68c0\u67e5ETag\u548cLast-Modified<br \/>\ncurl -I -H &#034;Cache-Control: no-cache&#034; https:\/\/example.com\/resource<\/p>\n<p># \u53d1\u9001\u6761\u4ef6\u8bf7\u6c42<br \/>\ncurl -I -H &#039;If-None-Match: &#034;abc123&#034;&#039; https:\/\/example.com\/resource<br \/>\ncurl -I -H &#039;If-Modified-Since: Mon, 21 Oct 2024 09:00:00 GMT&#039; https:\/\/example.com\/resource<\/p>\n<p># \u8be6\u7ec6\u8ddf\u8e2a<br \/>\ncurl -v -H &#039;If-None-Match: &#034;abc123&#034;&#039; https:\/\/example.com\/resource <\/li>\n<li>\n<p>\u6d4f\u89c8\u5668\u6269\u5c55\u5de5\u5177&#xff1a;<\/p>\n<ul>\n<li>\n<p>Chrome: &#034;Cache Killer&#034; \u6269\u5c55<\/p>\n<\/li>\n<li>\n<p>Firefox: &#034;Cache Status&#034; \u6269\u5c55<\/p>\n<\/li>\n<li>\n<p>\u901a\u7528: &#034;ModHeader&#034; \u4fee\u6539\u8bf7\u6c42\u5934<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<h5>13.5 \u5e38\u89c1\u95ee\u9898\u4e0e\u89e3\u51b3\u65b9\u6848<\/h5>\n<h6>13.5.1 \u7f13\u5b58\u4e00\u81f4\u6027\u95ee\u9898<\/h6>\n<p>\u95ee\u9898\u573a\u666f&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u591a\u4e2aCDN\u8282\u70b9\u7f13\u5b58\u4e0d\u4e00\u81f4<\/p>\n<\/li>\n<li>\n<p>\u5ba2\u6237\u7aef\u7f13\u5b58\u4e0e\u670d\u52a1\u7aef\u6570\u636e\u4e0d\u540c\u6b65<\/p>\n<\/li>\n<li>\n<p>\u7f13\u5b58\u6c61\u67d3\u6216\u5931\u6548<\/p>\n<\/li>\n<\/ul>\n<p>\u89e3\u51b3\u65b9\u6848&#xff1a;<\/p>\n<li>\n<p>\u7f13\u5b58\u6e05\u9664\u7b56\u7565&#xff1a;<\/p>\n<p>python<\/p>\n<p> class CacheInvalidationManager:<br \/>\n    &#034;&#034;&#034;\u7f13\u5b58\u5931\u6548\u7ba1\u7406\u5668&#034;&#034;&#034;<\/p>\n<p>    def __init__(self):<br \/>\n        self.invalidation_queue &#061; []<\/p>\n<p>    def invalidate_cache(self, url_pattern, reason&#061;&#039;content_update&#039;):<br \/>\n        &#034;&#034;&#034;\u6807\u8bb0\u7f13\u5b58\u9700\u8981\u5931\u6548&#034;&#034;&#034;<\/p>\n<p>        self.invalidation_queue.append({<br \/>\n            &#039;pattern&#039;: url_pattern,<br \/>\n            &#039;reason&#039;: reason,<br \/>\n            &#039;timestamp&#039;: time.time(),<br \/>\n            &#039;status&#039;: &#039;pending&#039;<br \/>\n        })<\/p>\n<p>    async def execute_invalidation(self):<br \/>\n        &#034;&#034;&#034;\u6267\u884c\u7f13\u5b58\u5931\u6548&#034;&#034;&#034;<\/p>\n<p>        for item in self.invalidation_queue:<br \/>\n            if item[&#039;status&#039;] &#061;&#061; &#039;pending&#039;:<br \/>\n                try:<br \/>\n                    # 1. \u6e05\u9664CDN\u7f13\u5b58<br \/>\n                    await self.purge_cdn_cache(item[&#039;pattern&#039;])<\/p>\n<p>                    # 2. \u6e05\u9664\u53cd\u5411\u4ee3\u7406\u7f13\u5b58<br \/>\n                    await self.purge_proxy_cache(item[&#039;pattern&#039;])<\/p>\n<p>                    # 3. \u901a\u77e5\u5ba2\u6237\u7aef&#xff08;\u901a\u8fc7\u7248\u672c\u53f7&#xff09;<br \/>\n                    await self.update_client_cache_version()<\/p>\n<p>                    item[&#039;status&#039;] &#061; &#039;completed&#039;<br \/>\n                    print(f&#034;\u7f13\u5b58\u5931\u6548\u5b8c\u6210: {item[&#039;pattern&#039;]}&#034;)<\/p>\n<p>                except Exception as e:<br \/>\n                    item[&#039;status&#039;] &#061; &#039;failed&#039;<br \/>\n                    item[&#039;error&#039;] &#061; str(e)<br \/>\n                    print(f&#034;\u7f13\u5b58\u5931\u6548\u5931\u8d25: {e}&#034;)<\/p>\n<p>    async def purge_cdn_cache(self, pattern):<br \/>\n        &#034;&#034;&#034;\u6e05\u9664CDN\u7f13\u5b58&#034;&#034;&#034;<br \/>\n        # \u8c03\u7528CDN API\u6e05\u9664\u7f13\u5b58<br \/>\n        pass<\/p>\n<p>    async def update_client_cache_version(self):<br \/>\n        &#034;&#034;&#034;\u66f4\u65b0\u5ba2\u6237\u7aef\u7f13\u5b58\u7248\u672c&#034;&#034;&#034;<br \/>\n        # \u66f4\u65b0Service Worker\u7f13\u5b58\u7248\u672c<br \/>\n        # \u6216\u901a\u8fc7API\u54cd\u5e94\u5934\u901a\u77e5\u5ba2\u6237\u7aef<br \/>\n        pass <\/li>\n<li>\n<p>\u7248\u672c\u5316\u7f13\u5b58\u7b56\u7565&#xff1a;<\/p>\n<p>nginx<\/p>\n<p> # \u7248\u672c\u5316\u9759\u6001\u8d44\u6e90URL<br \/>\nlocation ~* &#034;^\/static\/(v\\\\d&#043;\/.*)$&#034; {<br \/>\n    alias \/var\/www\/static\/$1;<br \/>\n    expires 1y;<br \/>\n    add_header Cache-Control &#034;public, immutable&#034;;<\/p>\n<p>    # \u7248\u672c\u53f7\u53d8\u5316\u65f6&#xff0c;URL\u53d8\u5316&#xff0c;\u81ea\u7136\u7f13\u5b58\u5931\u6548<br \/>\n} <\/li>\n<h6>13.5.2 \u6027\u80fd\u95ee\u9898<\/h6>\n<p>\u95ee\u9898\u573a\u666f&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u8fc7\u591a\u7684304\u54cd\u5e94&#xff08;\u9a8c\u8bc1\u8bf7\u6c42\u5f00\u9500&#xff09;<\/p>\n<\/li>\n<li>\n<p>ETag\u8ba1\u7b97\u6210\u672c\u9ad8<\/p>\n<\/li>\n<li>\n<p>\u7f13\u5b58\u9a8c\u8bc1\u5bfc\u81f4\u5ef6\u8fdf<\/p>\n<\/li>\n<\/ul>\n<p>\u4f18\u5316\u65b9\u6848&#xff1a;<\/p>\n<li>\n<p>\u667a\u80fdETag\u751f\u6210&#xff1a;<\/p>\n<p>python<\/p>\n<p> class SmartETagGenerator:<br \/>\n    &#034;&#034;&#034;\u667a\u80fdETag\u751f\u6210&#xff0c;\u5e73\u8861\u51c6\u786e\u6027\u548c\u6027\u80fd&#034;&#034;&#034;<\/p>\n<p>    def __init__(self, config):<br \/>\n        self.config &#061; config<br \/>\n        self.cache &#061; {}  # \u7f13\u5b58ETag\u8ba1\u7b97\u7ed3\u679c<\/p>\n<p>    def generate_etag(self, content, content_type):<br \/>\n        &#034;&#034;&#034;\u6839\u636e\u5185\u5bb9\u7c7b\u578b\u667a\u80fd\u751f\u6210ETag&#034;&#034;&#034;<\/p>\n<p>        # \u68c0\u67e5\u7f13\u5b58<br \/>\n        cache_key &#061; f&#034;{hash(content)}_{content_type}&#034;<br \/>\n        if cache_key in self.cache:<br \/>\n            return self.cache[cache_key]<\/p>\n<p>        etag &#061; None<\/p>\n<p>        # \u6839\u636e\u5185\u5bb9\u7c7b\u578b\u9009\u62e9\u7b56\u7565<br \/>\n        if content_type.startswith(&#039;image\/&#039;) or content_type.startswith(&#039;video\/&#039;):<br \/>\n            # \u5927\u6587\u4ef6&#xff1a;\u4f7f\u7528\u5f31ETag&#xff08;\u6587\u4ef6\u5927\u5c0f &#043; \u4fee\u6539\u65f6\u95f4&#xff09;<br \/>\n            etag &#061; self._generate_lightweight_etag(content)<\/p>\n<p>        elif content_type &#061;&#061; &#039;application\/json&#039;:<br \/>\n            # JSON&#xff1a;\u89c4\u8303\u5316\u540e\u751f\u6210\u5f31ETag<br \/>\n            etag &#061; self._generate_json_etag(content)<\/p>\n<p>        elif content_type.startswith(&#039;text\/&#039;):<br \/>\n            # \u6587\u672c&#xff1a;\u5b8c\u6574\u5185\u5bb9\u54c8\u5e0c<br \/>\n            etag &#061; self._generate_strong_etag(content)<\/p>\n<p>        else:<br \/>\n            # \u9ed8\u8ba4&#xff1a;\u5f31ETag<br \/>\n            etag &#061; self._generate_weak_etag(content)<\/p>\n<p>        # \u7f13\u5b58\u7ed3\u679c<br \/>\n        if etag and len(content) &gt; 1024:  # \u53ea\u7f13\u5b58\u5927\u6587\u4ef6\u7684ETag<br \/>\n            self.cache[cache_key] &#061; etag<\/p>\n<p>        return etag <\/li>\n<li>\n<p>\u7f13\u5b58\u9884\u70ed\u4e0e\u9884\u9a8c\u8bc1&#xff1a;<\/p>\n<p>python<\/p>\n<p> class CachePreValidator:<br \/>\n    &#034;&#034;&#034;\u7f13\u5b58\u9884\u9a8c\u8bc1&#xff0c;\u51cf\u5c11304\u8bf7\u6c42&#034;&#034;&#034;<\/p>\n<p>    def __init__(self):<br \/>\n        self.validation_schedule &#061; {}<\/p>\n<p>    def schedule_prevalidation(self, url, ttl):<br \/>\n        &#034;&#034;&#034;\u8ba1\u5212\u9884\u9a8c\u8bc1&#034;&#034;&#034;<\/p>\n<p>        expiration_time &#061; time.time() &#043; ttl * 0.8  # \u5728\u8fc7\u671f\u524d20%\u65f6\u9884\u9a8c\u8bc1<\/p>\n<p>        self.validation_schedule[url] &#061; {<br \/>\n            &#039;expiration_time&#039;: expiration_time,<br \/>\n            &#039;last_validated&#039;: time.time(),<br \/>\n            &#039;ttl&#039;: ttl<br \/>\n        }<\/p>\n<p>    async def run_prevalidation(self):<br \/>\n        &#034;&#034;&#034;\u6267\u884c\u9884\u9a8c\u8bc1&#034;&#034;&#034;<\/p>\n<p>        current_time &#061; time.time()<br \/>\n        urls_to_validate &#061; []<\/p>\n<p>        for url, info in self.validation_schedule.items():<br \/>\n            if current_time &gt;&#061; info[&#039;expiration_time&#039;]:<br \/>\n                urls_to_validate.append(url)<\/p>\n<p>        # \u5e76\u53d1\u9884\u9a8c\u8bc1<br \/>\n        for url in urls_to_validate:<br \/>\n            try:<br \/>\n                response &#061; await self.validate_url(url)<\/p>\n<p>                if response.status &#061;&#061; 304:<br \/>\n                    # \u7f13\u5b58\u4ecd\u7136\u6709\u6548&#xff0c;\u66f4\u65b0\u9884\u9a8c\u8bc1\u65f6\u95f4<br \/>\n                    self.validation_schedule[url][&#039;last_validated&#039;] &#061; current_time<br \/>\n                    self.validation_schedule[url][&#039;expiration_time&#039;] &#061; current_time &#043; self.validation_schedule[url][&#039;ttl&#039;]<\/p>\n<p>                elif response.status &#061;&#061; 200:<br \/>\n                    # \u7f13\u5b58\u5931\u6548&#xff0c;\u6e05\u9664\u76f8\u5173\u7f13\u5b58<br \/>\n                    await self.invalidate_cache(url)<br \/>\n                    # \u91cd\u65b0\u7f13\u5b58\u65b0\u5185\u5bb9<br \/>\n                    await self.cache_new_content(url, response)<\/p>\n<p>            except Exception as e:<br \/>\n                print(f&#034;\u9884\u9a8c\u8bc1\u5931\u8d25 {url}: {e}&#034;) <\/li>\n<h6>13.5.3 \u5b89\u5168\u6027\u95ee\u9898<\/h6>\n<p>\u7f13\u5b58\u5b89\u5168\u8003\u8651&#xff1a;<\/p>\n<li>\n<p>\u654f\u611f\u4fe1\u606f\u7f13\u5b58&#xff1a;<\/p>\n<p>http<\/p>\n<p> # \u4e2a\u4eba\u6570\u636e&#xff1a;\u79c1\u6709\u7f13\u5b58<br \/>\nCache-Control: private, max-age&#061;300<br \/>\n# \u6216\u5b8c\u5168\u907f\u514d\u7f13\u5b58<br \/>\nCache-Control: no-store <\/li>\n<li>\n<p>\u7f13\u5b58\u6295\u6bd2\u9632\u62a4&#xff1a;<\/p>\n<p>nginx<\/p>\n<p> # \u4f7f\u7528Vary\u5934\u9632\u6b62\u7f13\u5b58\u6df7\u4e71<br \/>\nlocation \/api\/data {<br \/>\n    proxy_pass http:\/\/backend;<\/p>\n<p>    # \u6839\u636e\u8fd9\u4e9b\u5934\u90e8\u533a\u5206\u7f13\u5b58<br \/>\n    proxy_set_header Vary &#034;Accept-Encoding, User-Agent, Authorization&#034;;<\/p>\n<p>    # \u9632\u6b62\u7f13\u5b58\u6295\u6bd2<br \/>\n    proxy_cache_key &#034;$scheme$request_method$host$request_uri$http_authorization&#034;;<br \/>\n} <\/li>\n<li>\n<p>HTTPS\u4e0e\u7f13\u5b58&#xff1a;<\/p>\n<p>http<\/p>\n<p> # HTTPS\u54cd\u5e94\u9ed8\u8ba4\u4e0d\u7f13\u5b58&#xff08;\u9664\u975e\u660e\u786e\u8bbe\u7f6e&#xff09;<br \/>\n# \u9700\u8981\u663e\u5f0f\u8bbe\u7f6e\u7f13\u5b58\u5934<br \/>\nCache-Control: public, max-age&#061;3600 <\/li>\n<h4>\u7b2c14\u7ae0&#xff1a;307\/308 &#8211; \u65b9\u6cd5\u4fdd\u6301\u91cd\u5b9a\u5411<\/h4>\n<h5>14.1 307 Temporary Redirect &#8211; \u4e34\u65f6\u91cd\u5b9a\u5411&#xff08;\u4fdd\u6301\u65b9\u6cd5&#xff09;<\/h5>\n<h6>14.1.1 \u89c4\u8303\u5b9a\u4e49\u4e0e\u8bbe\u8ba1\u76ee\u7684<\/h6>\n<p>RFC 7231\u5b9a\u4e49&#xff1a;<\/p>\n<p>text<\/p>\n<p>6.4.7.  307 Temporary Redirect<\/p>\n<p>The 307 (Temporary Redirect) status code indicates that the target<br \/>\nresource resides temporarily under a different URI and the user agent<br \/>\nMUST NOT change the request method if it performs an automatic<br \/>\nredirection to that URI. <\/p>\n<p>\u5173\u952e\u7279\u6027&#xff1a;<\/p>\n<li>\n<p>\u4e34\u65f6\u6027&#xff1a;\u4e0e302\u76f8\u540c&#xff0c;\u8d44\u6e90\u4e34\u65f6\u79fb\u52a8<\/p>\n<\/li>\n<li>\n<p>\u65b9\u6cd5\u4fdd\u6301&#xff1a;\u5fc5\u987b\u4fdd\u6301\u539f\u59cbHTTP\u65b9\u6cd5<\/p>\n<\/li>\n<li>\n<p>\u660e\u786e\u6027&#xff1a;\u89e3\u51b3\u4e86302\u7684\u65b9\u6cd5\u4fdd\u6301\u6a21\u7cca\u95ee\u9898<\/p>\n<\/li>\n<p>\u8bbe\u8ba1\u52a8\u673a&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u6f84\u6e05302\u7684\u6a21\u7cca\u8bed\u4e49<\/p>\n<\/li>\n<li>\n<p>\u4e3a\u9700\u8981\u4fdd\u6301\u65b9\u6cd5\u7684\u91cd\u5b9a\u5411\u63d0\u4f9b\u660e\u786e\u9009\u9879<\/p>\n<\/li>\n<li>\n<p>\u652f\u6301API\u548cRESTful\u670d\u52a1<\/p>\n<\/li>\n<\/ul>\n<h6>14.1.2 \u6d4f\u89c8\u5668\u517c\u5bb9\u6027\u4e0e\u5b9e\u73b0<\/h6>\n<p>\u73b0\u4ee3\u6d4f\u89c8\u5668\u652f\u6301&#xff08;2024\u5e74&#xff09;&#xff1a;<\/p>\n<p>javascript<\/p>\n<p>\/\/ \u6d4b\u8bd5\u6d4f\u89c8\u5668\u5bf9307\u7684\u652f\u6301<br \/>\nasync function test307Support() {<br \/>\n  const testEndpoint &#061; &#039;https:\/\/httpbin.org\/status\/307&#039;;<\/p>\n<p>  const methods &#061; [&#039;GET&#039;, &#039;POST&#039;, &#039;PUT&#039;, &#039;DELETE&#039;, &#039;PATCH&#039;];<br \/>\n  const results &#061; {};<\/p>\n<p>  for (const method of methods) {<br \/>\n    try {<br \/>\n      const response &#061; await fetch(testEndpoint, {<br \/>\n        method: method,<br \/>\n        redirect: &#039;manual&#039;  \/\/ \u624b\u52a8\u5904\u7406\u91cd\u5b9a\u5411<br \/>\n      });<\/p>\n<p>      results[method] &#061; {<br \/>\n        status: response.status,<br \/>\n        location: response.headers.get(&#039;Location&#039;),<br \/>\n        supported: response.status &#061;&#061;&#061; 307<br \/>\n      };<\/p>\n<p>    } catch (error) {<br \/>\n      results[method] &#061; { error: error.message };<br \/>\n    }<br \/>\n  }<\/p>\n<p>  return results;<br \/>\n}<\/p>\n<p>\/\/ \u5178\u578b\u6d4b\u8bd5\u7ed3\u679c&#xff1a;<br \/>\n\/*<br \/>\n{<br \/>\n  &#034;GET&#034;: { &#034;status&#034;: 307, &#034;supported&#034;: true },<br \/>\n  &#034;POST&#034;: { &#034;status&#034;: 307, &#034;supported&#034;: true },<br \/>\n  &#034;PUT&#034;: { &#034;status&#034;: 307, &#034;supported&#034;: true },<br \/>\n  &#034;DELETE&#034;: { &#034;status&#034;: 307, &#034;supported&#034;: true },<br \/>\n  &#034;PATCH&#034;: { &#034;status&#034;: 307, &#034;supported&#034;: true }<br \/>\n}<br \/>\n*\/ <\/p>\n<p>\u517c\u5bb9\u6027\u8868&#xff1a;<\/p>\n<table>\n<tr>\u6d4f\u89c8\u5668\/\u5e73\u53f0\u652f\u6301\u7248\u672c\u5907\u6ce8<\/tr>\n<tbody>\n<tr>\n<td>Chrome<\/td>\n<td>\u6240\u6709\u73b0\u4ee3\u7248\u672c<\/td>\n<td>\u5b8c\u5168\u652f\u6301<\/td>\n<\/tr>\n<tr>\n<td>Firefox<\/td>\n<td>\u6240\u6709\u73b0\u4ee3\u7248\u672c<\/td>\n<td>\u5b8c\u5168\u652f\u6301<\/td>\n<\/tr>\n<tr>\n<td>Safari<\/td>\n<td>\u6240\u6709\u73b0\u4ee3\u7248\u672c<\/td>\n<td>\u5b8c\u5168\u652f\u6301<\/td>\n<\/tr>\n<tr>\n<td>Edge<\/td>\n<td>\u6240\u6709\u73b0\u4ee3\u7248\u672c<\/td>\n<td>\u5b8c\u5168\u652f\u6301<\/td>\n<\/tr>\n<tr>\n<td>Node.js (fetch)<\/td>\n<td>18&#043;<\/td>\n<td>\u9700\u8981\u624b\u52a8\u5904\u7406<\/td>\n<\/tr>\n<tr>\n<td>curl<\/td>\n<td>7.49.0&#043;<\/td>\n<td>\u652f\u6301-L\u8ddf\u968f<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h6>14.1.3 \u4f7f\u7528\u573a\u666f<\/h6>\n<p>API\u7aef\u70b9\u4e34\u65f6\u7ef4\u62a4&#xff1a;<\/p>\n<p>python<\/p>\n<p># Flask API\u4e34\u65f6\u7ef4\u62a4\u91cd\u5b9a\u5411<br \/>\nfrom flask import Flask, request, redirect, jsonify<\/p>\n<p>app &#061; Flask(__name__)<\/p>\n<p>&#064;app.route(&#039;\/api\/v1\/users\/&lt;user_id&gt;&#039;, methods&#061;[&#039;GET&#039;, &#039;POST&#039;, &#039;PUT&#039;, &#039;DELETE&#039;])<br \/>\ndef legacy_user_api(user_id):<br \/>\n    # \u4e34\u65f6\u91cd\u5b9a\u5411\u5230\u65b0\u7aef\u70b9&#xff0c;\u4fdd\u6301\u65b9\u6cd5<br \/>\n    new_url &#061; f&#039;\/api\/v2\/users\/{user_id}&#039;<\/p>\n<p>    # \u6784\u5efa307\u54cd\u5e94<br \/>\n    response &#061; redirect(new_url, code&#061;307)<\/p>\n<p>    # \u6dfb\u52a0\u989d\u5916\u4fe1\u606f\u5934<br \/>\n    response.headers[&#039;X-API-Version&#039;] &#061; &#039;v1 (deprecated)&#039;<br \/>\n    response.headers[&#039;X-New-Endpoint&#039;] &#061; new_url<br \/>\n    response.headers[&#039;Sunset&#039;] &#061; &#039;Mon, 31 Dec 2024 23:59:59 GMT&#039;<\/p>\n<p>    return response<\/p>\n<p>&#064;app.route(&#039;\/api\/v2\/users\/&lt;user_id&gt;&#039;, methods&#061;[&#039;GET&#039;, &#039;POST&#039;, &#039;PUT&#039;, &#039;DELETE&#039;])<br \/>\ndef new_user_api(user_id):<br \/>\n    # \u65b0API\u5b9e\u73b0<br \/>\n    if request.method &#061;&#061; &#039;GET&#039;:<br \/>\n        return jsonify({&#039;id&#039;: user_id, &#039;name&#039;: &#039;John Doe&#039;})<br \/>\n    elif request.method &#061;&#061; &#039;POST&#039;:<br \/>\n        return jsonify({&#039;status&#039;: &#039;created&#039;, &#039;id&#039;: user_id}), 201<br \/>\n    # &#8230; \u5176\u4ed6\u65b9\u6cd5\u5904\u7406 <\/p>\n<p>\u8d1f\u8f7d\u5747\u8861\u4e0e\u6545\u969c\u8f6c\u79fb&#xff1a;<\/p>\n<p>nginx<\/p>\n<p># Nginx\u914d\u7f6e&#xff1a;\u4e34\u65f6\u5c06\u8bf7\u6c42\u91cd\u5b9a\u5411\u5230\u5907\u7528\u670d\u52a1\u5668<br \/>\nupstream primary_backend {<br \/>\n    server backend1.example.com;<br \/>\n    server backend2.example.com;<br \/>\n}<\/p>\n<p>upstream backup_backend {<br \/>\n    server backup1.example.com;<br \/>\n    server backup2.example.com;<br \/>\n}<\/p>\n<p>server {<br \/>\n    location \/api\/ {<br \/>\n        # \u68c0\u67e5\u4e3b\u540e\u7aef\u5065\u5eb7\u72b6\u6001<br \/>\n        if ($backend_health &#061; &#034;unhealthy&#034;) {<br \/>\n            # \u4e34\u65f6\u91cd\u5b9a\u5411\u5230\u5907\u7528\u670d\u52a1\u5668&#xff0c;\u4fdd\u6301\u65b9\u6cd5<br \/>\n            return 307 https:\/\/backup-cluster.example.com$request_uri;<br \/>\n        }<\/p>\n<p>        proxy_pass http:\/\/primary_backend;<br \/>\n        proxy_redirect off;<br \/>\n    }<br \/>\n} <\/p>\n<p>A\/B\u6d4b\u8bd5\u9700\u8981\u4fdd\u6301\u65b9\u6cd5&#xff1a;<\/p>\n<p>javascript<\/p>\n<p>\/\/ Node.js Express&#xff1a;A\/B\u6d4b\u8bd5\u4fdd\u6301POST\u65b9\u6cd5<br \/>\nconst express &#061; require(&#039;express&#039;);<br \/>\nconst app &#061; express();<\/p>\n<p>app.post(&#039;\/submit-order&#039;, (req, res) &#061;&gt; {<br \/>\n  \/\/ A\/B\u6d4b\u8bd5&#xff1a;50%\u7528\u6237\u4f7f\u7528\u65b0\u6d41\u7a0b<br \/>\n  const useNewFlow &#061; Math.random() &lt; 0.5;<\/p>\n<p>  if (useNewFlow) {<br \/>\n    \/\/ \u4e34\u65f6\u91cd\u5b9a\u5411\u5230\u65b0\u8ba2\u5355\u5904\u7406\u7aef\u70b9&#xff0c;\u4fdd\u6301POST\u65b9\u6cd5<br \/>\n    res.redirect(307, &#039;\/new-order-flow&#039;);<br \/>\n  } else {<br \/>\n    \/\/ \u539f\u6709\u5904\u7406\u903b\u8f91<br \/>\n    processOrder(req.body);<br \/>\n    res.json({ status: &#039;success&#039; });<br \/>\n  }<br \/>\n});<\/p>\n<p>app.post(&#039;\/new-order-flow&#039;, (req, res) &#061;&gt; {<br \/>\n  \/\/ \u65b0\u8ba2\u5355\u5904\u7406\u903b\u8f91<br \/>\n  console.log(&#039;New flow &#8211; Order data:&#039;, req.body);<br \/>\n  processOrderNew(req.body);<br \/>\n  res.json({ status: &#039;success&#039;, flow: &#039;new&#039; });<br \/>\n}); <\/p>\n<h5>14.2 308 Permanent Redirect &#8211; \u6c38\u4e45\u91cd\u5b9a\u5411&#xff08;\u4fdd\u6301\u65b9\u6cd5&#xff09;<\/h5>\n<h6>14.2.1 \u89c4\u8303\u5b9a\u4e49\u4e0e\u7279\u6027<\/h6>\n<p>RFC 7538\u5b9a\u4e49&#xff1a;<\/p>\n<p>text<\/p>\n<p>3. 308 Permanent Redirect<\/p>\n<p>The 308 (Permanent Redirect) status code indicates that the target<br \/>\nresource has been assigned a new permanent URI and any future<br \/>\nreferences to this resource ought to use one of the enclosed URIs.<br \/>\nClients with link-editing capabilities ought to automatically re-link<br \/>\nreferences to the effective request URI to one or more of the new<br \/>\nreferences sent by the server, where possible. <\/p>\n<p>\u5173\u952e\u7279\u6027&#xff1a;<\/p>\n<li>\n<p>\u6c38\u4e45\u6027&#xff1a;\u4e0e301\u76f8\u540c&#xff0c;\u8d44\u6e90\u6c38\u4e45\u79fb\u52a8<\/p>\n<\/li>\n<li>\n<p>\u65b9\u6cd5\u4fdd\u6301&#xff1a;\u5fc5\u987b\u4fdd\u6301\u539f\u59cbHTTP\u65b9\u6cd5<\/p>\n<\/li>\n<li>\n<p>\u660e\u786e\u6027&#xff1a;\u89e3\u51b3\u4e86301\u7684\u65b9\u6cd5\u4fdd\u6301\u6a21\u7cca\u95ee\u9898<\/p>\n<\/li>\n<p>\u4e0e301\u7684\u6838\u5fc3\u533a\u522b&#xff1a;<\/p>\n<p>text<\/p>\n<p># 301&#xff1a;\u6c38\u4e45\u91cd\u5b9a\u5411&#xff0c;\u65b9\u6cd5\u53ef\u80fd\u6539\u53d8&#xff08;POST\u2192GET&#xff09;<br \/>\nPOST \/old \u2192 301 \u2192 GET \/new<\/p>\n<p># 308&#xff1a;\u6c38\u4e45\u91cd\u5b9a\u5411&#xff0c;\u65b9\u6cd5\u5fc5\u987b\u4fdd\u6301<br \/>\nPOST \/old \u2192 308 \u2192 POST \/new <\/p>\n<h6>14.2.2 \u4f7f\u7528\u573a\u666f\u4e0e\u6700\u4f73\u5b9e\u8df5<\/h6>\n<p>API\u7248\u672c\u6c38\u4e45\u8fc1\u79fb&#xff1a;<\/p>\n<p>python<\/p>\n<p># Django API\u6c38\u4e45\u8fc1\u79fb\u793a\u4f8b<br \/>\nfrom django.http import HttpResponseRedirect<br \/>\nfrom django.views.decorators.http import require_http_methods<\/p>\n<p>&#064;require_http_methods([&#034;GET&#034;, &#034;POST&#034;, &#034;PUT&#034;, &#034;DELETE&#034;, &#034;PATCH&#034;])<br \/>\ndef legacy_api(request, endpoint):<br \/>\n    &#034;&#034;&#034;<br \/>\n    \u65e7API\u7248\u672c&#xff0c;\u6c38\u4e45\u91cd\u5b9a\u5411\u5230\u65b0\u7248\u672c<br \/>\n    &#034;&#034;&#034;<\/p>\n<p>    # \u6784\u5efa\u65b0API\u7aef\u70b9URL<br \/>\n    new_endpoint &#061; endpoint.replace(&#039;\/api\/v1\/&#039;, &#039;\/api\/v2\/&#039;)<br \/>\n    new_url &#061; f&#039;https:\/\/api.example.com{new_endpoint}&#039;<\/p>\n<p>    # \u521b\u5efa308\u54cd\u5e94<br \/>\n    response &#061; HttpResponseRedirect(new_url, status&#061;308)<\/p>\n<p>    # \u6dfb\u52a0\u8fc1\u79fb\u4fe1\u606f\u5934<br \/>\n    response[&#039;X-API-Migration&#039;] &#061; &#039;v1\u2192v2&#039;<br \/>\n    response[&#039;X-Deprecation-Date&#039;] &#061; &#039;2024-12-31&#039;<br \/>\n    response[&#039;X-New-Endpoint&#039;] &#061; new_url<br \/>\n    response[&#039;Link&#039;] &#061; f&#039;&lt;{new_url}&gt;; rel&#061;&#034;successor-version&#034;&#039;<\/p>\n<p>    return response <\/p>\n<p>HTTPS\u5f3a\u5236\u5347\u7ea7\u4fdd\u6301\u65b9\u6cd5&#xff1a;<\/p>\n<p>nginx<\/p>\n<p># Nginx\u914d\u7f6e&#xff1a;HTTP\u5230HTTPS\u6c38\u4e45\u91cd\u5b9a\u5411&#xff0c;\u4fdd\u6301\u65b9\u6cd5<br \/>\nserver {<br \/>\n    listen 80;<br \/>\n    server_name api.example.com;<\/p>\n<p>    # \u5bf9\u6240\u6709\u8bf7\u6c42\u8fdb\u884c308\u91cd\u5b9a\u5411\u5230HTTPS<br \/>\n    location \/ {<br \/>\n        # 308\u6c38\u4e45\u91cd\u5b9a\u5411&#xff0c;\u4fdd\u6301\u539f\u59cb\u65b9\u6cd5<br \/>\n        return 308 https:\/\/$server_name$request_uri;<br \/>\n    }<br \/>\n}<\/p>\n<p># \u5bf9\u6bd4301\u65b9\u6848\u7684\u95ee\u9898&#xff1a;<br \/>\n# \u4f7f\u7528301\u65f6&#xff0c;POST\u8bf7\u6c42\u53ef\u80fd\u53d8\u4e3aGET&#xff0c;\u5bfc\u81f4API\u8c03\u7528\u5931\u8d25<br \/>\n# \u4f7f\u7528308\u786e\u4fdd\u6240\u6709\u65b9\u6cd5\u90fd\u6b63\u786e\u91cd\u5b9a\u5411 <\/p>\n<p>RESTful\u8d44\u6e90\u6c38\u4e45\u79fb\u52a8&#xff1a;<\/p>\n<p>java<\/p>\n<p>\/\/ Spring Boot REST API\u8d44\u6e90\u6c38\u4e45\u79fb\u52a8<br \/>\n&#064;RestController<br \/>\n&#064;RequestMapping(&#034;\/api\/v1&#034;)<br \/>\npublic class LegacyResourceController {<\/p>\n<p>    &#064;RequestMapping(value &#061; &#034;\/resources\/{id}&#034;, method &#061; {RequestMethod.GET,<br \/>\n                                                         RequestMethod.POST,<br \/>\n                                                         RequestMethod.PUT,<br \/>\n                                                         RequestMethod.DELETE})<br \/>\n    public ResponseEntity&lt;Void&gt; handleLegacyResource(<br \/>\n            HttpServletRequest request,<br \/>\n            &#064;PathVariable String id) {<\/p>\n<p>        \/\/ \u6784\u5efa\u65b0\u8d44\u6e90URL<br \/>\n        String newUrl &#061; &#034;\/api\/v2\/resources\/&#034; &#043; id;<\/p>\n<p>        \/\/ \u521b\u5efa308\u54cd\u5e94<br \/>\n        HttpHeaders headers &#061; new HttpHeaders();<br \/>\n        headers.setLocation(URI.create(newUrl));<br \/>\n        headers.set(&#034;X-Resource-Moved&#034;, &#034;permanently&#034;);<br \/>\n        headers.set(&#034;X-New-Location&#034;, newUrl);<\/p>\n<p>        return new ResponseEntity&lt;&gt;(headers, HttpStatus.PERMANENT_REDIRECT);<br \/>\n    }<br \/>\n} <\/p>\n<h6>14.2.3 \u6d4f\u89c8\u5668\u4e0e\u5ba2\u6237\u7aef\u5b9e\u73b0<\/h6>\n<p>\u6d4f\u89c8\u5668\u652f\u6301\u72b6\u6001&#xff1a;<\/p>\n<p>javascript<\/p>\n<p>\/\/ \u68c0\u6d4b308\u652f\u6301\u7684\u5ba2\u6237\u7aef\u4ee3\u7801<br \/>\nfunction check308Support() {<br \/>\n  const support &#061; {<br \/>\n    browser: false,<br \/>\n    fetch: false,<br \/>\n    xhr: false<br \/>\n  };<\/p>\n<p>  \/\/ \u68c0\u67e5fetch API\u652f\u6301<br \/>\n  if (typeof fetch &#061;&#061;&#061; &#039;function&#039;) {<br \/>\n    support.fetch &#061; true;<\/p>\n<p>    \/\/ \u6ce8\u610f&#xff1a;fetch API\u9ed8\u8ba4\u81ea\u52a8\u5904\u7406\u91cd\u5b9a\u5411<br \/>\n    \/\/ \u9700\u8981\u624b\u52a8\u6a21\u5f0f\u624d\u80fd\u770b\u5230308\u72b6\u6001\u7801<br \/>\n  }<\/p>\n<p>  \/\/ \u68c0\u67e5XMLHttpRequest\u652f\u6301<br \/>\n  if (typeof XMLHttpRequest !&#061;&#061; &#039;undefined&#039;) {<br \/>\n    const xhr &#061; new XMLHttpRequest();<br \/>\n    try {<br \/>\n      \/\/ \u5927\u591a\u6570\u73b0\u4ee3\u6d4f\u89c8\u5668\u652f\u6301308<br \/>\n      support.xhr &#061; true;<br \/>\n    } catch (e) {<br \/>\n      support.xhr &#061; false;<br \/>\n    }<br \/>\n  }<\/p>\n<p>  \/\/ \u6d4f\u89c8\u5668\u603b\u4f53\u652f\u6301&#xff08;\u57fa\u4e8eUser Agent\u68c0\u6d4b&#xff09;<br \/>\n  const ua &#061; navigator.userAgent;<br \/>\n  const chromeMatch &#061; ua.match(\/Chrome\\\\\/(\\\\d&#043;)\/);<br \/>\n  const firefoxMatch &#061; ua.match(\/Firefox\\\\\/(\\\\d&#043;)\/);<br \/>\n  const safariMatch &#061; ua.match(\/Version\\\\\/(\\\\d&#043;).*Safari\/);<\/p>\n<p>  if (chromeMatch &amp;&amp; parseInt(chromeMatch[1]) &gt;&#061; 49) {<br \/>\n    support.browser &#061; true;<br \/>\n  } else if (firefoxMatch &amp;&amp; parseInt(firefoxMatch[1]) &gt;&#061; 51) {<br \/>\n    support.browser &#061; true;<br \/>\n  } else if (safariMatch &amp;&amp; parseInt(safariMatch[1]) &gt;&#061; 10) {<br \/>\n    support.browser &#061; true;<br \/>\n  }<\/p>\n<p>  return support;<br \/>\n} <\/p>\n<p>\u5ba2\u6237\u7aef\u5904\u7406\u903b\u8f91&#xff1a;<\/p>\n<p>javascript<\/p>\n<p>class RedirectHandler308 {<br \/>\n  constructor() {<br \/>\n    this.maxRedirects &#061; 5;<br \/>\n    this.visitedUrls &#061; new Set();<br \/>\n  }<\/p>\n<p>  async fetchWith308Support(url, options &#061; {}) {<br \/>\n    let currentUrl &#061; url;<br \/>\n    let redirectCount &#061; 0;<br \/>\n    let currentOptions &#061; { &#8230;options };<\/p>\n<p>    while (redirectCount &lt; this.maxRedirects) {<br \/>\n      \/\/ \u68c0\u67e5\u5faa\u73af<br \/>\n      if (this.visitedUrls.has(currentUrl)) {<br \/>\n        throw new Error(&#096;Redirect loop detected: ${currentUrl}&#096;);<br \/>\n      }<br \/>\n      this.visitedUrls.add(currentUrl);<\/p>\n<p>      \/\/ \u53d1\u9001\u8bf7\u6c42&#xff0c;\u4e0d\u81ea\u52a8\u5904\u7406\u91cd\u5b9a\u5411<br \/>\n      const response &#061; await fetch(currentUrl, {<br \/>\n        &#8230;currentOptions,<br \/>\n        redirect: &#039;manual&#039;  \/\/ \u624b\u52a8\u5904\u7406\u91cd\u5b9a\u5411<br \/>\n      });<\/p>\n<p>      \/\/ \u5904\u7406\u91cd\u5b9a\u5411<br \/>\n      if (response.status &#061;&#061;&#061; 301 || response.status &#061;&#061;&#061; 302 ||<br \/>\n          response.status &#061;&#061;&#061; 303 || response.status &#061;&#061;&#061; 307 ||<br \/>\n          response.status &#061;&#061;&#061; 308) {<\/p>\n<p>        redirectCount&#043;&#043;;<br \/>\n        const location &#061; response.headers.get(&#039;Location&#039;);<\/p>\n<p>        if (!location) {<br \/>\n          throw new Error(&#039;Redirect response missing Location header&#039;);<br \/>\n        }<\/p>\n<p>        \/\/ \u89e3\u6790\u65b0URL<br \/>\n        const newUrl &#061; new URL(location, currentUrl);<br \/>\n        currentUrl &#061; newUrl.href;<\/p>\n<p>        \/\/ \u5bf9\u4e8e303&#xff0c;\u603b\u662f\u8f6c\u4e3aGET<br \/>\n        if (response.status &#061;&#061;&#061; 303) {<br \/>\n          currentOptions.method &#061; &#039;GET&#039;;<br \/>\n          delete currentOptions.body;<br \/>\n          delete currentOptions.headers[&#039;Content-Type&#039;];<br \/>\n        }<\/p>\n<p>        \/\/ \u5bf9\u4e8e301\u548c302&#xff0c;\u6d4f\u89c8\u5668\u901a\u5e38\u8f6cGET&#xff0c;\u4f46\u6211\u4eec\u5c1d\u8bd5\u4fdd\u6301<br \/>\n        if (response.status &#061;&#061;&#061; 301 || response.status &#061;&#061;&#061; 302) {<br \/>\n          \/\/ \u8b66\u544a&#xff1a;\u53ef\u80fd\u4e22\u5931\u6570\u636e<br \/>\n          console.warn(&#096;${response.status} redirect &#8211; method may change to GET&#096;);<br \/>\n        }<\/p>\n<p>        \/\/ \u5bf9\u4e8e307\u548c308&#xff0c;\u4fdd\u6301\u539f\u59cb\u65b9\u6cd5<br \/>\n        if (response.status &#061;&#061;&#061; 307 || response.status &#061;&#061;&#061; 308) {<br \/>\n          \/\/ \u4fdd\u6301\u6240\u6709\u539f\u59cb\u9009\u9879<br \/>\n          continue;<br \/>\n        }<\/p>\n<p>        continue;<br \/>\n      }<\/p>\n<p>      \/\/ \u975e\u91cd\u5b9a\u5411\u54cd\u5e94<br \/>\n      return response;<br \/>\n    }<\/p>\n<p>    throw new Error(&#096;Too many redirects: ${redirectCount}&#096;);<br \/>\n  }<br \/>\n} <\/p>\n<h5>14.3 \u4e0e301\/302\u7684\u8be6\u7ec6\u5bf9\u6bd4<\/h5>\n<h6>14.3.1 \u6280\u672f\u7279\u6027\u5bf9\u6bd4\u8868<\/h6>\n<table>\n<tr>\u7279\u6027301302303307308<\/tr>\n<tbody>\n<tr>\n<td>\u72b6\u6001\u7801<\/td>\n<td>301<\/td>\n<td>302<\/td>\n<td>303<\/td>\n<td>307<\/td>\n<td>308<\/td>\n<\/tr>\n<tr>\n<td>\u8bed\u4e49<\/td>\n<td>\u6c38\u4e45\u79fb\u52a8<\/td>\n<td>\u4e34\u65f6\u79fb\u52a8<\/td>\n<td>See Other<\/td>\n<td>\u4e34\u65f6\u91cd\u5b9a\u5411<\/td>\n<td>\u6c38\u4e45\u91cd\u5b9a\u5411<\/td>\n<\/tr>\n<tr>\n<td>\u65b9\u6cd5\u4fdd\u6301<\/td>\n<td>\u7406\u8bba\u4e0a\u5e94\u4fdd\u6301&#xff0c;\u5b9e\u9645\u5e38\u8f6cGET<\/td>\n<td>\u7406\u8bba\u4e0a\u5e94\u4fdd\u6301&#xff0c;\u5b9e\u9645\u5e38\u8f6cGET<\/td>\n<td>\u603b\u662f\u8f6c\u4e3aGET<\/td>\n<td>\u5fc5\u987b\u4fdd\u6301<\/td>\n<td>\u5fc5\u987b\u4fdd\u6301<\/td>\n<\/tr>\n<tr>\n<td>\u7f13\u5b58\u6027<\/td>\n<td>\u53ef\u7f13\u5b58<\/td>\n<td>\u901a\u5e38\u4e0d\u7f13\u5b58<\/td>\n<td>\u901a\u5e38\u4e0d\u7f13\u5b58<\/td>\n<td>\u901a\u5e38\u4e0d\u7f13\u5b58<\/td>\n<td>\u53ef\u7f13\u5b58<\/td>\n<\/tr>\n<tr>\n<td>SEO\u5f71\u54cd<\/td>\n<td>\u4f20\u9012\u6743\u91cd<\/td>\n<td>\u4e0d\u4f20\u9012\u6743\u91cd<\/td>\n<td>\u4e0d\u4f20\u9012\u6743\u91cd<\/td>\n<td>\u4e0d\u4f20\u9012\u6743\u91cd<\/td>\n<td>\u4f20\u9012\u6743\u91cd<\/td>\n<\/tr>\n<tr>\n<td>\u5f15\u5165\u65f6\u95f4<\/td>\n<td>HTTP\/1.0<\/td>\n<td>HTTP\/1.0<\/td>\n<td>HTTP\/1.1<\/td>\n<td>HTTP\/1.1<\/td>\n<td>HTTP\/1.1 (RFC 7538)<\/td>\n<\/tr>\n<tr>\n<td>\u4e3b\u8981\u7528\u9014<\/td>\n<td>\u6c38\u4e45URL\u53d8\u66f4<\/td>\n<td>\u4e34\u65f6\u91cd\u5b9a\u5411<\/td>\n<td>POST\u540e\u663e\u793a\u7ed3\u679c<\/td>\n<td>\u4e34\u65f6\u91cd\u5b9a\u5411\u4e14\u9700\u4fdd\u6301\u65b9\u6cd5<\/td>\n<td>\u6c38\u4e45\u91cd\u5b9a\u5411\u4e14\u9700\u4fdd\u6301\u65b9\u6cd5<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h6>14.3.2 \u65b9\u6cd5\u4fdd\u6301\u884c\u4e3a\u5bf9\u6bd4<\/h6>\n<p>\u4e0d\u540c\u72b6\u6001\u7801\u7684\u65b9\u6cd5\u4fdd\u6301\u884c\u4e3a&#xff1a;<\/p>\n<p>python<\/p>\n<p># \u6d4b\u8bd5\u4e0d\u540c\u91cd\u5b9a\u5411\u72b6\u6001\u7801\u7684\u65b9\u6cd5\u4fdd\u6301\u884c\u4e3a<br \/>\ndef test_method_preservation():<br \/>\n    test_cases &#061; [<br \/>\n        {&#039;method&#039;: &#039;POST&#039;, &#039;data&#039;: {&#039;test&#039;: &#039;value&#039;}},<br \/>\n        {&#039;method&#039;: &#039;PUT&#039;, &#039;data&#039;: {&#039;update&#039;: &#039;data&#039;}},<br \/>\n        {&#039;method&#039;: &#039;DELETE&#039;, &#039;data&#039;: {}},<br \/>\n        {&#039;method&#039;: &#039;PATCH&#039;, &#039;data&#039;: {&#039;change&#039;: &#039;partial&#039;}}<br \/>\n    ]<\/p>\n<p>    results &#061; {}<\/p>\n<p>    for status_code in [301, 302, 303, 307, 308]:<br \/>\n        results[status_code] &#061; {}<\/p>\n<p>        for test in test_cases:<br \/>\n            # \u6a21\u62df\u91cd\u5b9a\u5411\u5904\u7406<br \/>\n            final_method &#061; simulate_redirect(status_code, test[&#039;method&#039;])<\/p>\n<p>            results[status_code][test[&#039;method&#039;]] &#061; {<br \/>\n                &#039;original&#039;: test[&#039;method&#039;],<br \/>\n                &#039;final&#039;: final_method,<br \/>\n                &#039;changed&#039;: test[&#039;method&#039;] !&#061; final_method,<br \/>\n                &#039;data_preserved&#039;: test[&#039;method&#039;] &#061;&#061; final_method<br \/>\n            }<\/p>\n<p>    return results<\/p>\n<p>def simulate_redirect(status_code, original_method):<br \/>\n    &#034;&#034;&#034;\u6a21\u62df\u6d4f\u89c8\u5668\u5bf9\u4e0d\u540c\u72b6\u6001\u7801\u7684\u5904\u7406&#034;&#034;&#034;<\/p>\n<p>    if status_code &#061;&#061; 303:<br \/>\n        # 303&#xff1a;\u603b\u662f\u8f6c\u4e3aGET<br \/>\n        return &#039;GET&#039;<\/p>\n<p>    elif status_code &#061;&#061; 301 or status_code &#061;&#061; 302:<br \/>\n        # 301\/302&#xff1a;\u89c4\u8303\u8981\u6c42\u4fdd\u6301&#xff0c;\u4f46\u6d4f\u89c8\u5668\u901a\u5e38\u8f6cGET<br \/>\n        # \u5b9e\u9645\u884c\u4e3a&#xff1a;\u5927\u591a\u6570\u6d4f\u89c8\u5668\u4f1a\u5c06POST\u8f6c\u4e3aGET<br \/>\n        if original_method in [&#039;POST&#039;, &#039;PUT&#039;, &#039;DELETE&#039;, &#039;PATCH&#039;]:<br \/>\n            return &#039;GET&#039;  # \u5b9e\u9645\u6d4f\u89c8\u5668\u884c\u4e3a<br \/>\n        else:<br \/>\n            return original_method<\/p>\n<p>    elif status_code &#061;&#061; 307 or status_code &#061;&#061; 308:<br \/>\n        # 307\/308&#xff1a;\u5fc5\u987b\u4fdd\u6301\u539f\u59cb\u65b9\u6cd5<br \/>\n        return original_method<\/p>\n<p>    return original_method<\/p>\n<p># \u9884\u671f\u7ed3\u679c&#xff1a;<br \/>\n&#034;&#034;&#034;<br \/>\n{<br \/>\n  301: {<br \/>\n    &#039;POST&#039;: {&#039;original&#039;: &#039;POST&#039;, &#039;final&#039;: &#039;GET&#039;, &#039;changed&#039;: True},<br \/>\n    &#039;PUT&#039;: {&#039;original&#039;: &#039;PUT&#039;, &#039;final&#039;: &#039;GET&#039;, &#039;changed&#039;: True},<br \/>\n    &#8230;<br \/>\n  },<br \/>\n  302: { &#8230; \u7c7b\u4f3c301 &#8230; },<br \/>\n  303: { &#8230; \u5168\u90e8\u8f6c\u4e3aGET &#8230; },<br \/>\n  307: { &#8230; \u5168\u90e8\u4fdd\u6301\u539f\u65b9\u6cd5 &#8230; },<br \/>\n  308: { &#8230; \u5168\u90e8\u4fdd\u6301\u539f\u65b9\u6cd5 &#8230; }<br \/>\n}<br \/>\n&#034;&#034;&#034; <\/p>\n<h6>14.3.3 \u7f13\u5b58\u884c\u4e3a\u5bf9\u6bd4<\/h6>\n<p>\u7f13\u5b58\u5934\u914d\u7f6e\u5dee\u5f02&#xff1a;<\/p>\n<p>http<\/p>\n<p># 301 &#8211; \u53ef\u7f13\u5b58&#xff0c;\u957f\u671f<br \/>\nHTTP\/1.1 301 Moved Permanently<br \/>\nLocation: \/new-location<br \/>\nCache-Control: public, max-age&#061;31536000<\/p>\n<p># 302 &#8211; \u901a\u5e38\u4e0d\u7f13\u5b58<br \/>\nHTTP\/1.1 302 Found<br \/>\nLocation: \/temporary-location<br \/>\nCache-Control: no-cache, no-store<\/p>\n<p># 303 &#8211; \u901a\u5e38\u4e0d\u7f13\u5b58<br \/>\nHTTP\/1.1 303 See Other<br \/>\nLocation: \/result-page<br \/>\nCache-Control: no-cache<\/p>\n<p># 307 &#8211; \u901a\u5e38\u4e0d\u7f13\u5b58&#xff08;\u4e34\u65f6&#xff09;<br \/>\nHTTP\/1.1 307 Temporary Redirect<br \/>\nLocation: \/temp-redirect<br \/>\nCache-Control: no-cache, no-store<\/p>\n<p># 308 &#8211; \u53ef\u7f13\u5b58&#xff0c;\u957f\u671f<br \/>\nHTTP\/1.1 308 Permanent Redirect<br \/>\nLocation: \/permanent-new-location<br \/>\nCache-Control: public, max-age&#061;31536000 <\/p>\n<p>\u4ee3\u7406\u670d\u52a1\u5668\u5904\u7406\u5dee\u5f02&#xff1a;<\/p>\n<p>nginx<\/p>\n<p># Nginx\u4ee3\u7406\u7f13\u5b58\u914d\u7f6e\u793a\u4f8b<br \/>\nproxy_cache_path \/var\/cache\/nginx levels&#061;1:2 keys_zone&#061;redirect_cache:10m;<\/p>\n<p>server {<br \/>\n    location \/ {<br \/>\n        proxy_pass http:\/\/backend;<br \/>\n        proxy_cache redirect_cache;<\/p>\n<p>        # \u53ea\u7f13\u5b58301\u548c308\u54cd\u5e94<br \/>\n        proxy_cache_valid 301 308 1h;<\/p>\n<p>        # \u4e0d\u7f13\u5b58302\u3001303\u3001307<br \/>\n        proxy_cache_valid 302 303 307 0s;<\/p>\n<p>        # \u7f13\u5b58\u952e\u5305\u542b\u72b6\u6001\u7801<br \/>\n        proxy_cache_key &#034;$scheme$request_method$host$request_uri$status&#034;;<br \/>\n    }<br \/>\n} <\/p>\n<h5>14.4 \u4f7f\u7528\u573a\u666f\u8be6\u89e3<\/h5>\n<h6>14.4.1 API\u8bbe\u8ba1\u4e0e\u7248\u672c\u63a7\u5236<\/h6>\n<p>\u6e10\u8fdb\u5f0fAPI\u8fc1\u79fb&#xff1a;<\/p>\n<p>python<\/p>\n<p># API\u7248\u672c\u8fc1\u79fb\u7b56\u7565\u793a\u4f8b<br \/>\nfrom flask import Flask, request, redirect, jsonify<br \/>\nfrom datetime import datetime<\/p>\n<p>app &#061; Flask(__name__)<\/p>\n<p>class APIVersionManager:<br \/>\n    def __init__(self):<br \/>\n        self.migration_schedule &#061; {<br \/>\n            &#039;v1&#039;: {<br \/>\n                &#039;status&#039;: &#039;deprecated&#039;,<br \/>\n                &#039;sunset_date&#039;: &#039;2024-12-31&#039;,<br \/>\n                &#039;redirect_to&#039;: &#039;v2&#039;,<br \/>\n                &#039;redirect_type&#039;: 308  # \u6c38\u4e45\u91cd\u5b9a\u5411&#xff0c;\u4fdd\u6301\u65b9\u6cd5<br \/>\n            },<br \/>\n            &#039;v2&#039;: {<br \/>\n                &#039;status&#039;: &#039;current&#039;,<br \/>\n                &#039;sunset_date&#039;: None,<br \/>\n                &#039;redirect_to&#039;: None<br \/>\n            }<br \/>\n        }<\/p>\n<p>    def should_redirect(self, version):<br \/>\n        &#034;&#034;&#034;\u68c0\u67e5\u662f\u5426\u9700\u8981\u91cd\u5b9a\u5411&#034;&#034;&#034;<br \/>\n        config &#061; self.migration_schedule.get(version)<\/p>\n<p>        if not config or config[&#039;status&#039;] !&#061; &#039;deprecated&#039;:<br \/>\n            return None<\/p>\n<p>        # \u68c0\u67e5\u662f\u5426\u5df2\u8fc7\u65e5\u843d\u671f<br \/>\n        if config[&#039;sunset_date&#039;]:<br \/>\n            sunset &#061; datetime.fromisoformat(config[&#039;sunset_date&#039;])<br \/>\n            if datetime.now() &gt; sunset:<br \/>\n                return 410  # Gone &#8211; \u5b8c\u5168\u79fb\u9664<\/p>\n<p>        return config[&#039;redirect_type&#039;]<\/p>\n<p># API\u7248\u672c\u4e2d\u95f4\u4ef6<br \/>\nversion_manager &#061; APIVersionManager()<\/p>\n<p>&#064;app.before_request<br \/>\ndef handle_api_version():<br \/>\n    # \u68c0\u67e5\u8bf7\u6c42\u8def\u5f84\u4e2d\u7684API\u7248\u672c<br \/>\n    if request.path.startswith(&#039;\/api\/&#039;):<br \/>\n        path_parts &#061; request.path.split(&#039;\/&#039;)<\/p>\n<p>        if len(path_parts) &gt; 2 and path_parts[1] &#061;&#061; &#039;api&#039;:<br \/>\n            version &#061; path_parts[2]  # \u4f8b\u5982&#xff1a;v1, v2<\/p>\n<p>            redirect_status &#061; version_manager.should_redirect(version)<\/p>\n<p>            if redirect_status:<br \/>\n                # \u6784\u5efa\u65b0URL<br \/>\n                new_path &#061; request.path.replace(f&#039;\/{version}\/&#039;, f&#039;\/v2\/&#039;)<\/p>\n<p>                if redirect_status &#061;&#061; 410:<br \/>\n                    # \u5b8c\u5168\u79fb\u9664&#xff0c;\u8fd4\u56de410 Gone<br \/>\n                    return jsonify({<br \/>\n                        &#039;error&#039;: &#039;API version removed&#039;,<br \/>\n                        &#039;message&#039;: f&#039;API {version} has been sunset&#039;,<br \/>\n                        &#039;current_version&#039;: &#039;v2&#039;,<br \/>\n                        &#039;docs&#039;: &#039;https:\/\/api.example.com\/docs\/v2&#039;<br \/>\n                    }), 410<\/p>\n<p>                elif redirect_status in [301, 308]:<br \/>\n                    # \u6c38\u4e45\u91cd\u5b9a\u5411<br \/>\n                    response &#061; redirect(new_path, code&#061;redirect_status)<\/p>\n<p>                    # \u6dfb\u52a0\u8fc1\u79fb\u4fe1\u606f<br \/>\n                    response.headers[&#039;X-API-Version&#039;] &#061; version<br \/>\n                    response.headers[&#039;X-New-Version&#039;] &#061; &#039;v2&#039;<br \/>\n                    response.headers[&#039;Link&#039;] &#061; f&#039;&lt;{new_path}&gt;; rel&#061;&#034;successor-version&#034;&#039;<\/p>\n<p>                    if redirect_status &#061;&#061; 308:<br \/>\n                        response.headers[&#039;X-Method-Preserved&#039;] &#061; &#039;true&#039;<\/p>\n<p>                    return response<\/p>\n<p>                elif redirect_status in [302, 307]:<br \/>\n                    # \u4e34\u65f6\u91cd\u5b9a\u5411<br \/>\n                    return redirect(new_path, code&#061;redirect_status)<\/p>\n<p># \u5f53\u524d\u7248\u672cAPI\u7aef\u70b9<br \/>\n&#064;app.route(&#039;\/api\/v2\/users\/&lt;user_id&gt;&#039;, methods&#061;[&#039;GET&#039;, &#039;POST&#039;, &#039;PUT&#039;, &#039;DELETE&#039;])<br \/>\ndef user_endpoint_v2(user_id):<br \/>\n    # \u5b9e\u73b0\u903b\u8f91&#8230;<br \/>\n    pass <\/p>\n<h6>14.4.2 \u8d1f\u8f7d\u5747\u8861\u4e0e\u6545\u969c\u8f6c\u79fb<\/h6>\n<p>\u667a\u80fd\u6545\u969c\u8f6c\u79fb\u7b56\u7565&#xff1a;<\/p>\n<p>nginx<\/p>\n<p># Nginx\u9ad8\u7ea7\u8d1f\u8f7d\u5747\u8861\u4e0e\u91cd\u5b9a\u5411\u914d\u7f6e<br \/>\nhttp {<br \/>\n    upstream primary_cluster {<br \/>\n        zone backend 64k;<\/p>\n<p>        server backend1.example.com max_fails&#061;3 fail_timeout&#061;30s;<br \/>\n        server backend2.example.com max_fails&#061;3 fail_timeout&#061;30s;<br \/>\n        server backend3.example.com max_fails&#061;3 fail_timeout&#061;30s;<\/p>\n<p>        # \u5065\u5eb7\u68c0\u67e5<br \/>\n        health_check interval&#061;5s fails&#061;3 passes&#061;2 uri&#061;\/health;<br \/>\n    }<\/p>\n<p>    upstream backup_cluster {<br \/>\n        server backup1.example.com;<br \/>\n        server backup2.example.com;<br \/>\n    }<\/p>\n<p>    # \u5171\u4eab\u5185\u5b58\u533a\u57df\u5b58\u50a8\u5065\u5eb7\u72b6\u6001<br \/>\n    lua_shared_dict health_status 10m;<\/p>\n<p>    server {<br \/>\n        listen 80;<\/p>\n<p>        location \/api\/ {<br \/>\n            # \u68c0\u67e5\u4e3b\u96c6\u7fa4\u5065\u5eb7\u72b6\u6001<br \/>\n            access_by_lua_block {<br \/>\n                local health &#061; ngx.shared.health_status<br \/>\n                local is_healthy &#061; health:get(&#034;primary_healthy&#034;)<\/p>\n<p>                if is_healthy &#061;&#061; false then<br \/>\n                    &#8212; \u4e3b\u96c6\u7fa4\u4e0d\u5065\u5eb7&#xff0c;\u4e34\u65f6\u91cd\u5b9a\u5411\u5230\u5907\u4efd\u96c6\u7fa4<br \/>\n                    &#8212; \u4f7f\u7528307\u4fdd\u6301\u8bf7\u6c42\u65b9\u6cd5<br \/>\n                    return ngx.redirect(&#034;https:\/\/backup.example.com&#034; .. ngx.var.request_uri, 307)<br \/>\n                end<br \/>\n            }<\/p>\n<p>            proxy_pass http:\/\/primary_cluster;<\/p>\n<p>            # \u9519\u8bef\u5904\u7406<br \/>\n            proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;<br \/>\n            proxy_next_upstream_tries 3;<\/p>\n<p>            # \u5904\u7406\u4ee3\u7406\u9519\u8bef<br \/>\n            proxy_intercept_errors on;<br \/>\n            error_page 500 502 503 504 &#061; &#064;handle_backend_error;<br \/>\n        }<\/p>\n<p>        location &#064;handle_backend_error {<br \/>\n            # \u8bb0\u5f55\u9519\u8bef<br \/>\n            ngx.log(ngx.WARN, &#034;Primary backend error, marking as unhealthy&#034;)<\/p>\n<p>            &#8212; \u6807\u8bb0\u4e3b\u96c6\u7fa4\u4e3a\u4e0d\u5065\u5eb7<br \/>\n            local health &#061; ngx.shared.health_status<br \/>\n            health:set(&#034;primary_healthy&#034;, false, 60) &#8212; 60\u79d2\u5185\u8ba4\u4e3a\u4e0d\u5065\u5eb7<\/p>\n<p>            &#8212; \u4e34\u65f6\u91cd\u5b9a\u5411\u5230\u5907\u4efd\u96c6\u7fa4&#xff0c;\u4fdd\u6301\u65b9\u6cd5<br \/>\n            return 307 https:\/\/backup.example.com$request_uri;<br \/>\n        }<br \/>\n    }<br \/>\n} <\/p>\n<h6>14.4.3 \u5b89\u5168\u91cd\u5b9a\u5411\u573a\u666f<\/h6>\n<p>\u5b89\u5168\u8ba4\u8bc1\u91cd\u5b9a\u5411&#xff1a;<\/p>\n<p>python<\/p>\n<p># Django\u5b89\u5168\u91cd\u5b9a\u5411\u4e2d\u95f4\u4ef6<br \/>\nfrom django.shortcuts import redirect<br \/>\nfrom django.utils.deprecation import MiddlewareMixin<br \/>\nfrom urllib.parse import urlparse, urlunparse<\/p>\n<p>class SecureRedirectMiddleware(MiddlewareMixin):<br \/>\n    &#034;&#034;&#034;\u5b89\u5168\u91cd\u5b9a\u5411\u4e2d\u95f4\u4ef6&#xff0c;\u9632\u6b62\u5f00\u653e\u91cd\u5b9a\u5411\u6f0f\u6d1e&#034;&#034;&#034;<\/p>\n<p>    ALLOWED_DOMAINS &#061; [&#039;example.com&#039;, &#039;trusted-partner.com&#039;]<br \/>\n    ALLOWED_SCHEMES &#061; [&#039;http&#039;, &#039;https&#039;]<\/p>\n<p>    def process_response(self, request, response):<br \/>\n        # \u68c0\u67e5\u662f\u5426\u662f\u91cd\u5b9a\u5411\u54cd\u5e94<br \/>\n        if response.status_code in [301, 302, 303, 307, 308]:<br \/>\n            location &#061; response.get(&#039;Location&#039;, &#039;&#039;)<\/p>\n<p>            if location:<br \/>\n                # \u9a8c\u8bc1\u91cd\u5b9a\u5411\u76ee\u6807<br \/>\n                if not self.is_safe_redirect(location, request):<br \/>\n                    # \u4e0d\u5b89\u5168\u7684\u91cd\u5b9a\u5411&#xff0c;\u91cd\u5b9a\u5411\u5230\u9ed8\u8ba4\u9875\u9762<br \/>\n                    response.status_code &#061; 302<br \/>\n                    response[&#039;Location&#039;] &#061; &#039;\/&#039;<\/p>\n<p>                    # \u8bb0\u5f55\u5b89\u5168\u4e8b\u4ef6<br \/>\n                    self.log_security_event(request, location)<\/p>\n<p>        return response<\/p>\n<p>    def is_safe_redirect(self, target_url, request):<br \/>\n        &#034;&#034;&#034;\u9a8c\u8bc1\u91cd\u5b9a\u5411\u76ee\u6807\u662f\u5426\u5b89\u5168&#034;&#034;&#034;<\/p>\n<p>        try:<br \/>\n            parsed &#061; urlparse(target_url)<\/p>\n<p>            # \u68c0\u67e5\u534f\u8bae<br \/>\n            if parsed.scheme and parsed.scheme not in self.ALLOWED_SCHEMES:<br \/>\n                return False<\/p>\n<p>            # \u68c0\u67e5\u57df\u540d<br \/>\n            if parsed.netloc:<br \/>\n                domain &#061; parsed.netloc.split(&#039;:&#039;)[0]  # \u79fb\u9664\u7aef\u53e3<\/p>\n<p>                # \u5141\u8bb8\u76f8\u5bf9\u8def\u5f84\u548c\u5f53\u524d\u57df\u540d<br \/>\n                if domain and domain not in self.ALLOWED_DOMAINS:<br \/>\n                    # \u68c0\u67e5\u662f\u5426\u5f53\u524d\u57df\u540d\u7684\u5b50\u57df\u540d<br \/>\n                    current_domain &#061; request.get_host().split(&#039;:&#039;)[0]<\/p>\n<p>                    if not domain.endswith(&#039;.&#039; &#043; current_domain):<br \/>\n                        return False<\/p>\n<p>            # \u9632\u6b62\u6076\u610f\u8def\u5f84<br \/>\n            if parsed.path and &#039;\/\/&#039; in parsed.path:<br \/>\n                return False<\/p>\n<p>            return True<\/p>\n<p>        except Exception:<br \/>\n            return False<\/p>\n<p>    def log_security_event(self, request, malicious_url):<br \/>\n        &#034;&#034;&#034;\u8bb0\u5f55\u5b89\u5168\u4e8b\u4ef6&#034;&#034;&#034;<br \/>\n        import logging<br \/>\n        security_logger &#061; logging.getLogger(&#039;security&#039;)<\/p>\n<p>        security_logger.warning(<br \/>\n            &#039;Blocked unsafe redirect attempt&#039;,<br \/>\n            extra&#061;{<br \/>\n                &#039;ip&#039;: request.META.get(&#039;REMOTE_ADDR&#039;),<br \/>\n                &#039;user_agent&#039;: request.META.get(&#039;HTTP_USER_AGENT&#039;),<br \/>\n                &#039;target_url&#039;: malicious_url,<br \/>\n                &#039;referer&#039;: request.META.get(&#039;HTTP_REFERER&#039;),<br \/>\n                &#039;path&#039;: request.path<br \/>\n            }<br \/>\n        ) <\/p>\n<h5>14.5 \u914d\u7f6e\u793a\u4f8b\u4e0e\u5b9e\u73b0<\/h5>\n<h6>14.5.1 \u670d\u52a1\u5668\u914d\u7f6e\u793a\u4f8b<\/h6>\n<p>Apache\u914d\u7f6e&#xff1a;<\/p>\n<p>apache<\/p>\n<p># .htaccess 307\/308\u91cd\u5b9a\u5411\u914d\u7f6e<\/p>\n<p># \u542f\u7528\u91cd\u5199\u5f15\u64ce<br \/>\nRewriteEngine On<\/p>\n<p># 1. \u4e34\u65f6\u91cd\u5b9a\u5411\u4fdd\u6301\u65b9\u6cd5&#xff08;307&#xff09;<br \/>\n# \u5c06POST\u8bf7\u6c42\u4e34\u65f6\u91cd\u5b9a\u5411\u5230\u65b0\u7aef\u70b9<br \/>\nRewriteCond %{REQUEST_METHOD} ^(POST|PUT|DELETE|PATCH)$<br \/>\nRewriteRule ^old-api-endpoint$ \/new-api-endpoint [R&#061;307,L]<\/p>\n<p># 2. \u6c38\u4e45\u91cd\u5b9a\u5411\u4fdd\u6301\u65b9\u6cd5&#xff08;308&#xff09;<br \/>\n# API\u7248\u672c\u8fc1\u79fb<br \/>\nRewriteRule ^api\/v1\/(.*)$ https:\/\/api.example.com\/v2\/$1 [R&#061;308,L]<\/p>\n<p># 3. \u57fa\u4e8e\u6761\u4ef6\u7684307\u91cd\u5b9a\u5411<br \/>\n# \u4ec5\u5728\u7ef4\u62a4\u6a21\u5f0f\u4e0b\u91cd\u5b9a\u5411<br \/>\nRewriteCond %{ENV:MAINTENANCE_MODE} ^true$<br \/>\nRewriteCond %{REQUEST_URI} !^\/maintenance\\\\.html$<br \/>\nRewriteRule ^(.*)$ \/maintenance.html [R&#061;307,L]<\/p>\n<p># 4. \u8d1f\u8f7d\u5747\u8861\u6545\u969c\u8f6c\u79fb<br \/>\nRewriteCond %{ENV:BACKEND_STATUS} ^unhealthy$<br \/>\nRewriteRule ^api\/(.*)$ https:\/\/backup-cluster.example.com\/api\/$1 [R&#061;307,L]<\/p>\n<p># 5. A\/B\u6d4b\u8bd5\u4fdd\u6301\u65b9\u6cd5<br \/>\nRewriteCond %{QUERY_STRING} ^ab&#061;test$<br \/>\nRewriteCond %{REQUEST_METHOD} ^(POST|PUT|DELETE|PATCH)$<br \/>\nRewriteRule ^submit-endpoint$ \/new-submit-flow [R&#061;307,L] <\/p>\n<p>Nginx\u914d\u7f6e&#xff1a;<\/p>\n<p>nginx<\/p>\n<p># nginx.conf 307\/308\u914d\u7f6e\u793a\u4f8b<\/p>\n<p># 1. API\u7248\u672c\u6c38\u4e45\u8fc1\u79fb&#xff08;308&#xff09;<br \/>\nserver {<br \/>\n    listen 80;<br \/>\n    server_name api.example.com;<\/p>\n<p>    location ~ &#034;^\/api\/v1\/(.*)&#034; {<br \/>\n        # \u6c38\u4e45\u91cd\u5b9a\u5411\u5230v2&#xff0c;\u4fdd\u6301\u65b9\u6cd5<br \/>\n        return 308 https:\/\/api.example.com\/api\/v2\/$1;<br \/>\n    }<br \/>\n}<\/p>\n<p># 2. \u4e34\u65f6\u7ef4\u62a4\u91cd\u5b9a\u5411&#xff08;307&#xff09;<br \/>\nserver {<br \/>\n    listen 80;<br \/>\n    server_name example.com;<\/p>\n<p>    # \u68c0\u67e5\u7ef4\u62a4\u6807\u5fd7<br \/>\n    if (-f \/var\/www\/maintenance.flag) {<br \/>\n        # \u4e34\u65f6\u91cd\u5b9a\u5411\u5230\u7ef4\u62a4\u9875\u9762&#xff0c;\u4fdd\u6301\u65b9\u6cd5<br \/>\n        return 307 \/maintenance.html;<br \/>\n    }<\/p>\n<p>    location \/ {<br \/>\n        # \u6b63\u5e38\u5904\u7406<br \/>\n        proxy_pass http:\/\/backend;<br \/>\n    }<br \/>\n}<\/p>\n<p># 3. \u8d1f\u8f7d\u5747\u8861\u6545\u969c\u8f6c\u79fb<br \/>\nupstream primary {<br \/>\n    server backend1.example.com;<br \/>\n    server backend2.example.com;<br \/>\n}<\/p>\n<p>upstream backup {<br \/>\n    server backup1.example.com;<br \/>\n    server backup2.example.com;<br \/>\n}<\/p>\n<p>server {<br \/>\n    location \/api\/ {<br \/>\n        # \u68c0\u67e5\u4e3b\u96c6\u7fa4\u5065\u5eb7<br \/>\n        if ($backend_health &#061; &#034;unhealthy&#034;) {<br \/>\n            # \u4e34\u65f6\u91cd\u5b9a\u5411\u5230\u5907\u4efd\u96c6\u7fa4&#xff0c;\u4fdd\u6301\u65b9\u6cd5<br \/>\n            return 307 https:\/\/backup.example.com$request_uri;<br \/>\n        }<\/p>\n<p>        proxy_pass http:\/\/primary;<br \/>\n    }<br \/>\n}<\/p>\n<p># 4. HTTPS\u5f3a\u5236\u5347\u7ea7&#xff08;308\u4fdd\u6301\u65b9\u6cd5&#xff09;<br \/>\nserver {<br \/>\n    listen 80;<br \/>\n    server_name secure.example.com;<\/p>\n<p>    # \u6c38\u4e45\u91cd\u5b9a\u5411\u5230HTTPS&#xff0c;\u4fdd\u6301\u6240\u6709\u8bf7\u6c42\u65b9\u6cd5<br \/>\n    return 308 https:\/\/$server_name$request_uri;<br \/>\n} <\/p>\n<h6>14.5.2 \u4e91\u670d\u52a1\u914d\u7f6e<\/h6>\n<p>AWS API Gateway&#xff1a;<\/p>\n<p>yaml<\/p>\n<p># AWS API Gateway OpenAPI\u5b9a\u4e49\u4e2d\u7684308\u91cd\u5b9a\u5411<br \/>\nopenapi: 3.0.1<br \/>\ninfo:<br \/>\n  title: API with 308 Redirects<br \/>\n  version: 1.0.0<\/p>\n<p>paths:<br \/>\n  \/api\/v1\/users\/{userId}:<br \/>\n    get:<br \/>\n      responses:<br \/>\n        &#039;308&#039;:<br \/>\n          description: Permanent Redirect to v2<br \/>\n          headers:<br \/>\n            Location:<br \/>\n              schema:<br \/>\n                type: string<br \/>\n              example: \/api\/v2\/users\/{userId}<br \/>\n    post:<br \/>\n      responses:<br \/>\n        &#039;308&#039;:<br \/>\n          description: Permanent Redirect to v2<br \/>\n          headers:<br \/>\n            Location:<br \/>\n              schema:<br \/>\n                type: string<br \/>\n              example: \/api\/v2\/users\/{userId}<\/p>\n<p>  \/api\/v2\/users\/{userId}:<br \/>\n    get:<br \/>\n      # v2\u5b9e\u73b0&#8230;<br \/>\n    post:<br \/>\n      # v2\u5b9e\u73b0&#8230;<\/p>\n<p># AWS CloudFormation\u914d\u7f6eAPI Gateway\u91cd\u5b9a\u5411<br \/>\nAWSTemplateFormatVersion: &#039;2010-09-09&#039;<br \/>\nResources:<br \/>\n  ApiGatewayRestApi:<br \/>\n    Type: AWS::ApiGateway::RestApi<br \/>\n    Properties:<br \/>\n      Name: MyApi<br \/>\n      Body:<br \/>\n        openapi: 3.0.1<br \/>\n        # &#8230; \u4e0a\u9762\u7684OpenAPI\u5b9a\u4e49 &#8230;<\/p>\n<p>  ApiGatewayMethod:<br \/>\n    Type: AWS::ApiGateway::Method<br \/>\n    Properties:<br \/>\n      RestApiId: !Ref ApiGatewayRestApi<br \/>\n      ResourceId: !GetAtt ApiGatewayRestApi.RootResourceId<br \/>\n      HttpMethod: ANY<br \/>\n      AuthorizationType: NONE<br \/>\n      Integration:<br \/>\n        Type: HTTP<br \/>\n        Uri: http:\/\/example.com<br \/>\n        IntegrationHttpMethod: ANY<br \/>\n        ConnectionType: INTERNET<br \/>\n        PassthroughBehavior: WHEN_NO_MATCH<br \/>\n      MethodResponses:<br \/>\n        &#8211; StatusCode: &#039;308&#039;<br \/>\n          ResponseParameters:<br \/>\n            method.response.header.Location: true <\/p>\n<p>Cloudflare Workers&#xff1a;<\/p>\n<p>javascript<\/p>\n<p>\/\/ Cloudflare Workers\u5b9e\u73b0307\/308\u91cd\u5b9a\u5411<br \/>\nexport default {<br \/>\n  async fetch(request, env, ctx) {<br \/>\n    const url &#061; new URL(request.url);<br \/>\n    const method &#061; request.method;<\/p>\n<p>    \/\/ 1. API\u7248\u672c\u8fc1\u79fb&#xff08;308\u6c38\u4e45\u91cd\u5b9a\u5411&#xff09;<br \/>\n    if (url.pathname.startsWith(&#039;\/api\/v1\/&#039;)) {<br \/>\n      const newPath &#061; url.pathname.replace(&#039;\/api\/v1\/&#039;, &#039;\/api\/v2\/&#039;);<br \/>\n      const newUrl &#061; new URL(newPath, url);<\/p>\n<p>      return Response.redirect(newUrl.toString(), 308);<br \/>\n    }<\/p>\n<p>    \/\/ 2. \u4e34\u65f6\u7ef4\u62a4\u6a21\u5f0f&#xff08;307\u4e34\u65f6\u91cd\u5b9a\u5411&#xff09;<br \/>\n    const maintenanceMode &#061; await env.KV.get(&#039;maintenance_mode&#039;);<br \/>\n    if (maintenanceMode &#061;&#061;&#061; &#039;true&#039; &amp;&amp; method !&#061;&#061; &#039;GET&#039;) {<br \/>\n      \/\/ \u5bf9\u4e8e\u975eGET\u8bf7\u6c42&#xff0c;\u4f7f\u7528307\u4fdd\u6301\u65b9\u6cd5<br \/>\n      return Response.redirect(&#096;${url.origin}\/maintenance&#096;, 307);<br \/>\n    }<\/p>\n<p>    \/\/ 3. A\/B\u6d4b\u8bd5\u4fdd\u6301\u65b9\u6cd5<br \/>\n    if (url.pathname &#061;&#061;&#061; &#039;\/submit&#039; &amp;&amp; method &#061;&#061;&#061; &#039;POST&#039;) {<br \/>\n      const abTestGroup &#061; getABTestGroup(request);<\/p>\n<p>      if (abTestGroup &#061;&#061;&#061; &#039;experimental&#039;) {<br \/>\n        \/\/ \u4fdd\u6301POST\u65b9\u6cd5\u91cd\u5b9a\u5411\u5230\u5b9e\u9a8c\u7aef\u70b9<br \/>\n        return Response.redirect(&#096;${url.origin}\/submit-experimental&#096;, 307);<br \/>\n      }<br \/>\n    }<\/p>\n<p>    \/\/ 4. \u8d1f\u8f7d\u5747\u8861\u6545\u969c\u8f6c\u79fb<br \/>\n    const primaryHealthy &#061; await checkBackendHealth(&#039;primary&#039;);<br \/>\n    if (!primaryHealthy &amp;&amp; method !&#061;&#061; &#039;GET&#039;) {<br \/>\n      \/\/ \u975eGET\u8bf7\u6c42\u6545\u969c\u8f6c\u79fb\u5230\u5907\u4efd&#xff0c;\u4fdd\u6301\u65b9\u6cd5<br \/>\n      return Response.redirect(&#039;https:\/\/backup.example.com&#039; &#043; url.pathname, 307);<br \/>\n    }<\/p>\n<p>    \/\/ \u6b63\u5e38\u8bf7\u6c42\u5904\u7406<br \/>\n    return fetch(request);<br \/>\n  }<br \/>\n};<\/p>\n<p>\/\/ \u8f85\u52a9\u51fd\u6570<br \/>\nfunction getABTestGroup(request) {<br \/>\n  const cookie &#061; request.headers.get(&#039;Cookie&#039;) || &#039;&#039;;<br \/>\n  const match &#061; cookie.match(\/ab_test&#061;([^;]&#043;)\/);<br \/>\n  return match ? match[1] : &#039;control&#039;;<br \/>\n}<\/p>\n<p>async function checkBackendHealth(backend) {<br \/>\n  try {<br \/>\n    const response &#061; await fetch(&#096;https:\/\/${backend}.example.com\/health&#096;);<br \/>\n    return response.ok;<br \/>\n  } catch {<br \/>\n    return false;<br \/>\n  }<br \/>\n} <\/p>\n<h6>14.5.3 \u7f16\u7a0b\u6846\u67b6\u5b9e\u73b0<\/h6>\n<p>Spring Boot\u5b8c\u6574\u793a\u4f8b&#xff1a;<\/p>\n<p>java<\/p>\n<p>\/\/ Spring Boot 307\/308\u91cd\u5b9a\u5411\u63a7\u5236\u5668<br \/>\n&#064;RestController<br \/>\n&#064;RequestMapping(&#034;\/api&#034;)<br \/>\npublic class RedirectController {<\/p>\n<p>    private final RedirectService redirectService;<\/p>\n<p>    public RedirectController(RedirectService redirectService) {<br \/>\n        this.redirectService &#061; redirectService;<br \/>\n    }<\/p>\n<p>    \/\/ 1. API\u7248\u672c\u8fc1\u79fb &#8211; 308\u6c38\u4e45\u91cd\u5b9a\u5411<br \/>\n    &#064;RequestMapping(value &#061; &#034;\/v1\/**&#034;, method &#061; {RequestMethod.GET, RequestMethod.POST,<br \/>\n                                               RequestMethod.PUT, RequestMethod.DELETE,<br \/>\n                                               RequestMethod.PATCH})<br \/>\n    public ResponseEntity&lt;Void&gt; redirectV1ToV2(HttpServletRequest request) {<br \/>\n        String originalPath &#061; request.getRequestURI();<br \/>\n        String newPath &#061; originalPath.replace(&#034;\/api\/v1\/&#034;, &#034;\/api\/v2\/&#034;);<\/p>\n<p>        \/\/ \u6784\u5efa\u65b0URL<br \/>\n        URI newUri &#061; UriComponentsBuilder<br \/>\n            .fromHttpUrl(getBaseUrl(request))<br \/>\n            .path(newPath)<br \/>\n            .query(request.getQueryString())<br \/>\n            .build()<br \/>\n            .toUri();<\/p>\n<p>        \/\/ \u521b\u5efa308\u54cd\u5e94<br \/>\n        HttpHeaders headers &#061; new HttpHeaders();<br \/>\n        headers.setLocation(newUri);<br \/>\n        headers.set(&#034;X-API-Version&#034;, &#034;v1 (deprecated)&#034;);<br \/>\n        headers.set(&#034;X-New-Version&#034;, &#034;v2&#034;);<br \/>\n        headers.set(&#034;Link&#034;, String.format(&#034;&lt;%s&gt;; rel&#061;\\\\&#034;successor-version\\\\&#034;&#034;, newUri));<\/p>\n<p>        return new ResponseEntity&lt;&gt;(headers, HttpStatus.PERMANENT_REDIRECT);<br \/>\n    }<\/p>\n<p>    \/\/ 2. \u4e34\u65f6\u7ef4\u62a4\u91cd\u5b9a\u5411 &#8211; 307\u4e34\u65f6\u91cd\u5b9a\u5411<br \/>\n    &#064;PostMapping(&#034;\/submit&#034;)<br \/>\n    public ResponseEntity&lt;?&gt; submitData(&#064;RequestBody SubmitData data,<br \/>\n                                        HttpServletRequest request) {<\/p>\n<p>        if (redirectService.isUnderMaintenance()) {<br \/>\n            \/\/ \u4e34\u65f6\u91cd\u5b9a\u5411\u5230\u7ef4\u62a4\u7aef\u70b9&#xff0c;\u4fdd\u6301POST\u65b9\u6cd5<br \/>\n            URI maintenanceUri &#061; UriComponentsBuilder<br \/>\n                .fromHttpUrl(getBaseUrl(request))<br \/>\n                .path(&#034;\/api\/maintenance-submit&#034;)<br \/>\n                .build()<br \/>\n                .toUri();<\/p>\n<p>            HttpHeaders headers &#061; new HttpHeaders();<br \/>\n            headers.setLocation(maintenanceUri);<br \/>\n            headers.set(&#034;X-Maintenance-Mode&#034;, &#034;true&#034;);<br \/>\n            headers.set(&#034;Retry-After&#034;, &#034;3600&#034;); \/\/ 1\u5c0f\u65f6\u540e\u91cd\u8bd5<\/p>\n<p>            return new ResponseEntity&lt;&gt;(headers, HttpStatus.TEMPORARY_REDIRECT);<br \/>\n        }<\/p>\n<p>        \/\/ \u6b63\u5e38\u5904\u7406\u903b\u8f91<br \/>\n        return ResponseEntity.ok(redirectService.processData(data));<br \/>\n    }<\/p>\n<p>    \/\/ 3. A\/B\u6d4b\u8bd5\u91cd\u5b9a\u5411 &#8211; 307\u4fdd\u6301\u65b9\u6cd5<br \/>\n    &#064;PostMapping(&#034;\/checkout&#034;)<br \/>\n    public ResponseEntity&lt;?&gt; checkout(&#064;RequestBody Order order,<br \/>\n                                      &#064;RequestHeader(&#034;User-Agent&#034;) String userAgent) {<\/p>\n<p>        \/\/ \u6839\u636e\u7528\u6237\u51b3\u5b9a\u4f7f\u7528\u54ea\u4e2a\u6d41\u7a0b<br \/>\n        String flow &#061; redirectService.determineFlow(userAgent);<\/p>\n<p>        if (&#034;new&#034;.equals(flow)) {<br \/>\n            \/\/ \u91cd\u5b9a\u5411\u5230\u65b0\u7ed3\u8d26\u6d41\u7a0b&#xff0c;\u4fdd\u6301POST\u65b9\u6cd5<br \/>\n            URI newCheckoutUri &#061; UriComponentsBuilder<br \/>\n                .fromPath(&#034;\/api\/checkout-new&#034;)<br \/>\n                .build()<br \/>\n                .toUri();<\/p>\n<p>            HttpHeaders headers &#061; new HttpHeaders();<br \/>\n            headers.setLocation(newCheckoutUri);<br \/>\n            headers.set(&#034;X-Flow-Version&#034;, &#034;new&#034;);<\/p>\n<p>            return new ResponseEntity&lt;&gt;(headers, HttpStatus.TEMPORARY_REDIRECT);<br \/>\n        }<\/p>\n<p>        \/\/ \u539f\u6709\u6d41\u7a0b<br \/>\n        return ResponseEntity.ok(redirectService.processCheckout(order));<br \/>\n    }<\/p>\n<p>    private String getBaseUrl(HttpServletRequest request) {<br \/>\n        String scheme &#061; request.getScheme();<br \/>\n        String serverName &#061; request.getServerName();<br \/>\n        int serverPort &#061; request.getServerPort();<\/p>\n<p>        StringBuilder url &#061; new StringBuilder();<br \/>\n        url.append(scheme).append(&#034;:\/\/&#034;).append(serverName);<\/p>\n<p>        if (serverPort !&#061; 80 &amp;&amp; serverPort !&#061; 443) {<br \/>\n            url.append(&#034;:&#034;).append(serverPort);<br \/>\n        }<\/p>\n<p>        return url.toString();<br \/>\n    }<br \/>\n}<\/p>\n<p>\/\/ \u91cd\u5b9a\u5411\u670d\u52a1\u7c7b<br \/>\n&#064;Service<br \/>\nclass RedirectService {<\/p>\n<p>    &#064;Value(&#034;${app.maintenance.enabled:false}&#034;)<br \/>\n    private boolean maintenanceEnabled;<\/p>\n<p>    &#064;Value(&#034;${app.ab-test.new-flow-percentage:50}&#034;)<br \/>\n    private int newFlowPercentage;<\/p>\n<p>    public boolean isUnderMaintenance() {<br \/>\n        return maintenanceEnabled;<br \/>\n    }<\/p>\n<p>    public String determineFlow(String userAgent) {<br \/>\n        \/\/ \u7b80\u5355\u7684A\/B\u6d4b\u8bd5\u903b\u8f91<br \/>\n        Random random &#061; new Random(userAgent.hashCode());<br \/>\n        int roll &#061; random.nextInt(100);<\/p>\n<p>        return roll &lt; newFlowPercentage ? &#034;new&#034; : &#034;old&#034;;<br \/>\n    }<\/p>\n<p>    public ProcessResult processData(SubmitData data) {<br \/>\n        \/\/ \u6570\u636e\u5904\u7406\u903b\u8f91<br \/>\n        return new ProcessResult(&#034;success&#034;, &#034;Data processed&#034;);<br \/>\n    }<\/p>\n<p>    public OrderResult processCheckout(Order order) {<br \/>\n        \/\/ \u7ed3\u8d26\u5904\u7406\u903b\u8f91<br \/>\n        return new OrderResult(&#034;order_processed&#034;, order.getId());<br \/>\n    }<br \/>\n} <\/p>\n<p>Node.js\/Express\u9ad8\u7ea7\u793a\u4f8b&#xff1a;<\/p>\n<p>javascript<\/p>\n<p>\/\/ Express.js 307\/308\u91cd\u5b9a\u5411\u4e2d\u95f4\u4ef6\u548c\u8def\u7531<br \/>\nconst express &#061; require(&#039;express&#039;);<br \/>\nconst app &#061; express();<br \/>\nconst crypto &#061; require(&#039;crypto&#039;);<\/p>\n<p>\/\/ \u4e2d\u95f4\u4ef6&#xff1a;\u5b89\u5168\u91cd\u5b9a\u5411\u9a8c\u8bc1<br \/>\nconst validateRedirect &#061; (req, res, next) &#061;&gt; {<br \/>\n  const allowedDomains &#061; [&#039;example.com&#039;, &#039;trusted.com&#039;];<\/p>\n<p>  \/\/ \u68c0\u67e5\u91cd\u5b9a\u5411\u76ee\u6807&#xff08;\u5982\u679c\u5b58\u5728&#xff09;<br \/>\n  if (req.query.redirect) {<br \/>\n    try {<br \/>\n      const url &#061; new URL(req.query.redirect, &#096;${req.protocol}:\/\/${req.get(&#039;host&#039;)}&#096;);<br \/>\n      const domain &#061; url.hostname;<\/p>\n<p>      if (!allowedDomains.includes(domain)) {<br \/>\n        \/\/ \u4e0d\u5b89\u5168\u7684\u91cd\u5b9a\u5411&#xff0c;\u62d2\u7edd<br \/>\n        return res.status(400).json({<br \/>\n          error: &#039;Unsafe redirect destination&#039;,<br \/>\n          allowedDomains<br \/>\n        });<br \/>\n      }<\/p>\n<p>      \/\/ \u5b89\u5168\u7684\u91cd\u5b9a\u5411&#xff0c;\u7ee7\u7eed<br \/>\n      req.safeRedirect &#061; url.toString();<br \/>\n    } catch (error) {<br \/>\n      return res.status(400).json({ error: &#039;Invalid redirect URL&#039; });<br \/>\n    }<br \/>\n  }<\/p>\n<p>  next();<br \/>\n};<\/p>\n<p>\/\/ \u4e2d\u95f4\u4ef6&#xff1a;API\u7248\u672c\u5904\u7406<br \/>\nconst apiVersionHandler &#061; (req, res, next) &#061;&gt; {<br \/>\n  const path &#061; req.path;<\/p>\n<p>  \/\/ \u68c0\u67e5\u662f\u5426\u662f\u5df2\u5f03\u7528\u7684v1 API<br \/>\n  if (path.startsWith(&#039;\/api\/v1\/&#039;)) {<br \/>\n    const newPath &#061; path.replace(&#039;\/api\/v1\/&#039;, &#039;\/api\/v2\/&#039;);<\/p>\n<p>    \/\/ \u6839\u636e\u8bf7\u6c42\u65b9\u6cd5\u9009\u62e9\u91cd\u5b9a\u5411\u72b6\u6001\u7801<br \/>\n    const method &#061; req.method;<\/p>\n<p>    if (method &#061;&#061;&#061; &#039;GET&#039; || method &#061;&#061;&#061; &#039;HEAD&#039;) {<br \/>\n      \/\/ \u5bf9GET\/HEAD\u53ef\u4ee5\u4f7f\u7528301<br \/>\n      res.redirect(301, newPath);<br \/>\n    } else {<br \/>\n      \/\/ \u5bf9POST\/PUT\/DELETE\u7b49\u4f7f\u7528308\u4fdd\u6301\u65b9\u6cd5<br \/>\n      res.redirect(308, newPath);<br \/>\n    }<\/p>\n<p>    \/\/ \u6dfb\u52a0\u4fe1\u606f\u5934<br \/>\n    res.setHeader(&#039;X-API-Version&#039;, &#039;v1 (deprecated)&#039;);<br \/>\n    res.setHeader(&#039;X-New-Version&#039;, &#039;v2&#039;);<br \/>\n    res.setHeader(&#039;Sunset&#039;, &#039;Mon, 31 Dec 2024 23:59:59 GMT&#039;);<\/p>\n<p>    return;<br \/>\n  }<\/p>\n<p>  next();<br \/>\n};<\/p>\n<p>\/\/ \u5e94\u7528\u4e2d\u95f4\u4ef6<br \/>\napp.use(validateRedirect);<br \/>\napp.use(apiVersionHandler);<\/p>\n<p>\/\/ \u8def\u7531&#xff1a;\u4e34\u65f6\u7ef4\u62a4\u91cd\u5b9a\u5411<br \/>\napp.post(&#039;\/api\/submit&#039;, (req, res) &#061;&gt; {<br \/>\n  \/\/ \u68c0\u67e5\u7ef4\u62a4\u6a21\u5f0f<br \/>\n  if (process.env.MAINTENANCE_MODE &#061;&#061;&#061; &#039;true&#039;) {<br \/>\n    \/\/ \u4e34\u65f6\u91cd\u5b9a\u5411\u5230\u7ef4\u62a4\u7aef\u70b9&#xff0c;\u4fdd\u6301POST\u65b9\u6cd5<br \/>\n    res.status(307);<br \/>\n    res.setHeader(&#039;Location&#039;, &#039;\/api\/maintenance-submit&#039;);<br \/>\n    res.setHeader(&#039;Retry-After&#039;, &#039;3600&#039;);<br \/>\n    res.setHeader(&#039;X-Maintenance&#039;, &#039;true&#039;);<br \/>\n    res.end();<br \/>\n    return;<br \/>\n  }<\/p>\n<p>  \/\/ \u6b63\u5e38\u5904\u7406<br \/>\n  res.json({ status: &#039;submitted&#039;, data: req.body });<br \/>\n});<\/p>\n<p>\/\/ \u8def\u7531&#xff1a;A\/B\u6d4b\u8bd5\u91cd\u5b9a\u5411&#xff08;\u4fdd\u6301\u65b9\u6cd5&#xff09;<br \/>\napp.post(&#039;\/api\/order&#039;, (req, res) &#061;&gt; {<br \/>\n  \/\/ \u786e\u5b9a\u7528\u6237\u5206\u7ec4<br \/>\n  const userId &#061; req.headers[&#039;x-user-id&#039;] || req.ip;<br \/>\n  const hash &#061; crypto.createHash(&#039;md5&#039;).update(userId).digest(&#039;hex&#039;);<br \/>\n  const group &#061; parseInt(hash.slice(0, 2), 16) % 100; \/\/ 0-99<\/p>\n<p>  if (group &lt; 30) { \/\/ 30%\u7528\u6237\u8fdb\u5165\u65b0\u6d41\u7a0b<br \/>\n    \/\/ \u4e34\u65f6\u91cd\u5b9a\u5411\u5230\u65b0\u8ba2\u5355\u6d41\u7a0b&#xff0c;\u4fdd\u6301POST\u65b9\u6cd5<br \/>\n    res.redirect(307, &#039;\/api\/order-new&#039;);<br \/>\n    return;<br \/>\n  }<\/p>\n<p>  \/\/ 70%\u7528\u6237\u4f7f\u7528\u539f\u6709\u6d41\u7a0b<br \/>\n  processOrder(req.body, (result) &#061;&gt; {<br \/>\n    res.json(result);<br \/>\n  });<br \/>\n});<\/p>\n<p>\/\/ \u8def\u7531&#xff1a;\u8d1f\u8f7d\u5747\u8861\u6545\u969c\u8f6c\u79fb<br \/>\napp.all(&#039;\/api\/data\/*&#039;, async (req, res) &#061;&gt; {<br \/>\n  const method &#061; req.method;<br \/>\n  const originalUrl &#061; req.originalUrl;<\/p>\n<p>  try {<br \/>\n    \/\/ \u5c1d\u8bd5\u4e3b\u540e\u7aef<br \/>\n    const response &#061; await fetch(&#096;http:\/\/primary-backend${originalUrl}&#096;, {<br \/>\n      method: method,<br \/>\n      headers: req.headers,<br \/>\n      body: method !&#061;&#061; &#039;GET&#039; &amp;&amp; method !&#061;&#061; &#039;HEAD&#039; ? JSON.stringify(req.body) : undefined<br \/>\n    });<\/p>\n<p>    \/\/ \u8f6c\u53d1\u54cd\u5e94<br \/>\n    res.status(response.status);<br \/>\n    for (const [key, value] of response.headers) {<br \/>\n      res.setHeader(key, value);<br \/>\n    }<\/p>\n<p>    const body &#061; await response.text();<br \/>\n    res.send(body);<\/p>\n<p>  } catch (error) {<br \/>\n    \/\/ \u4e3b\u540e\u7aef\u5931\u8d25&#xff0c;\u4e34\u65f6\u91cd\u5b9a\u5411\u5230\u5907\u4efd<br \/>\n    console.error(&#039;Primary backend failed, redirecting to backup:&#039;, error);<\/p>\n<p>    if (method &#061;&#061;&#061; &#039;GET&#039; || method &#061;&#061;&#061; &#039;HEAD&#039;) {<br \/>\n      \/\/ GET\/HEAD\u4f7f\u7528302<br \/>\n      res.redirect(302, &#096;http:\/\/backup-backend${originalUrl}&#096;);<br \/>\n    } else {<br \/>\n      \/\/ \u5176\u4ed6\u65b9\u6cd5\u4f7f\u7528307\u4fdd\u6301\u65b9\u6cd5<br \/>\n      res.redirect(307, &#096;http:\/\/backup-backend${originalUrl}&#096;);<br \/>\n    }<br \/>\n  }<br \/>\n});<\/p>\n<p>\/\/ \u542f\u52a8\u670d\u52a1\u5668<br \/>\nconst PORT &#061; process.env.PORT || 3000;<br \/>\napp.listen(PORT, () &#061;&gt; {<br \/>\n  console.log(&#096;Server running on port ${PORT}&#096;);<br \/>\n});<\/p>\n<p>\/\/ \u8f85\u52a9\u51fd\u6570<br \/>\nfunction processOrder(orderData, callback) {<br \/>\n  \/\/ \u8ba2\u5355\u5904\u7406\u903b\u8f91<br \/>\n  setTimeout(() &#061;&gt; {<br \/>\n    callback({ status: &#039;processed&#039;, orderId: Date.now() });<br \/>\n  }, 100);<br \/>\n} <\/p>\n<h5>14.6 \u76d1\u63a7\u3001\u8c03\u8bd5\u4e0e\u6700\u4f73\u5b9e\u8df5<\/h5>\n<h6>14.6.1 \u76d1\u63a7\u6307\u6807<\/h6>\n<p>\u5173\u952e\u76d1\u63a7\u6307\u6807&#xff1a;<\/p>\n<p>python<\/p>\n<p># \u91cd\u5b9a\u5411\u76d1\u63a7\u6307\u6807\u6536\u96c6\u5668<br \/>\nclass RedirectMetricsCollector:<br \/>\n    def __init__(self):<br \/>\n        self.metrics &#061; {<br \/>\n            &#039;by_status_code&#039;: defaultdict(int),<br \/>\n            &#039;by_method&#039;: defaultdict(int),<br \/>\n            &#039;response_times&#039;: defaultdict(list),<br \/>\n            &#039;error_codes&#039;: defaultdict(int),<br \/>\n            &#039;redirect_chains&#039;: []<br \/>\n        }<\/p>\n<p>    def record_redirect(self, status_code, method, response_time,<br \/>\n                       source_url, target_url, error_code&#061;None):<br \/>\n        &#034;&#034;&#034;\u8bb0\u5f55\u91cd\u5b9a\u5411\u4e8b\u4ef6&#034;&#034;&#034;<\/p>\n<p>        # \u8bb0\u5f55\u72b6\u6001\u7801\u5206\u5e03<br \/>\n        self.metrics[&#039;by_status_code&#039;][status_code] &#043;&#061; 1<\/p>\n<p>        # \u8bb0\u5f55\u65b9\u6cd5\u5206\u5e03<br \/>\n        self.metrics[&#039;by_method&#039;][method] &#043;&#061; 1<\/p>\n<p>        # \u8bb0\u5f55\u54cd\u5e94\u65f6\u95f4<br \/>\n        self.metrics[&#039;response_times&#039;][status_code].append(response_time)<\/p>\n<p>        # \u8bb0\u5f55\u9519\u8bef&#xff08;\u5982\u679c\u6709&#xff09;<br \/>\n        if error_code:<br \/>\n            self.metrics[&#039;error_codes&#039;][error_code] &#043;&#061; 1<\/p>\n<p>        # \u8bb0\u5f55\u91cd\u5b9a\u5411\u94fe&#xff08;\u7b80\u5316&#xff09;<br \/>\n        self.metrics[&#039;redirect_chains&#039;].append({<br \/>\n            &#039;from&#039;: source_url,<br \/>\n            &#039;to&#039;: target_url,<br \/>\n            &#039;status&#039;: status_code,<br \/>\n            &#039;method&#039;: method,<br \/>\n            &#039;timestamp&#039;: time.time()<br \/>\n        })<\/p>\n<p>        # \u4fdd\u7559\u6700\u8fd11000\u6761\u8bb0\u5f55<br \/>\n        if len(self.metrics[&#039;redirect_chains&#039;]) &gt; 1000:<br \/>\n            self.metrics[&#039;redirect_chains&#039;] &#061; self.metrics[&#039;redirect_chains&#039;][-1000:]<\/p>\n<p>    def get_stats(self):<br \/>\n        &#034;&#034;&#034;\u83b7\u53d6\u7edf\u8ba1\u62a5\u544a&#034;&#034;&#034;<\/p>\n<p>        stats &#061; {<br \/>\n            &#039;total_redirects&#039;: sum(self.metrics[&#039;by_status_code&#039;].values()),<br \/>\n            &#039;status_code_distribution&#039;: dict(self.metrics[&#039;by_status_code&#039;]),<br \/>\n            &#039;method_distribution&#039;: dict(self.metrics[&#039;by_method&#039;]),<br \/>\n            &#039;avg_response_times&#039;: {},<br \/>\n            &#039;error_summary&#039;: dict(self.metrics[&#039;error_codes&#039;])<br \/>\n        }<\/p>\n<p>        # \u8ba1\u7b97\u5e73\u5747\u54cd\u5e94\u65f6\u95f4<br \/>\n        for status_code, times in self.metrics[&#039;response_times&#039;].items():<br \/>\n            if times:<br \/>\n                stats[&#039;avg_response_times&#039;][status_code] &#061; sum(times) \/ len(times)<\/p>\n<p>        # \u68c0\u6d4b\u95ee\u9898<br \/>\n        stats[&#039;issues&#039;] &#061; self.detect_issues()<\/p>\n<p>        return stats<\/p>\n<p>    def detect_issues(self):<br \/>\n        &#034;&#034;&#034;\u68c0\u6d4b\u6f5c\u5728\u95ee\u9898&#034;&#034;&#034;<\/p>\n<p>        issues &#061; []<\/p>\n<p>        # \u68c0\u67e5\u91cd\u5b9a\u5411\u5faa\u73af<br \/>\n        chains &#061; self.metrics[&#039;redirect_chains&#039;]<br \/>\n        if len(chains) &gt; 10:<br \/>\n            # \u7b80\u5316\u5faa\u73af\u68c0\u6d4b&#xff1a;\u68c0\u67e5\u91cd\u590d\u7684\u6e90-\u76ee\u6807\u5bf9<br \/>\n            pairs &#061; {}<br \/>\n            for chain in chains:<br \/>\n                pair_key &#061; f&#034;{chain[&#039;from&#039;]}\u2192{chain[&#039;to&#039;]}&#034;<br \/>\n                pairs[pair_key] &#061; pairs.get(pair_key, 0) &#043; 1<\/p>\n<p>            for pair_key, count in pairs.items():<br \/>\n                if count &gt; 5:  # \u540c\u4e00\u91cd\u5b9a\u5411\u53d1\u751f\u591a\u6b21<br \/>\n                    issues.append({<br \/>\n                        &#039;type&#039;: &#039;possible_loop&#039;,<br \/>\n                        &#039;pair&#039;: pair_key,<br \/>\n                        &#039;count&#039;: count<br \/>\n                    })<\/p>\n<p>        # \u68c0\u67e5\u65b9\u6cd5\u4e0d\u5339\u914d<br \/>\n        for chain in chains[-20:]:  # \u68c0\u67e5\u6700\u8fd120\u4e2a<br \/>\n            if chain[&#039;status&#039;] in [307, 308]:<br \/>\n                # 307\/308\u5e94\u8be5\u4fdd\u6301\u65b9\u6cd5&#xff0c;\u68c0\u67e5\u5ba2\u6237\u7aef\u662f\u5426\u6b63\u786e\u5904\u7406<br \/>\n                pass<\/p>\n<p>        return issues <\/p>\n<h6>14.6.2 \u8c03\u8bd5\u5de5\u5177\u4e0e\u6280\u672f<\/h6>\n<p>\u6d4f\u89c8\u5668\u5f00\u53d1\u8005\u5de5\u5177\u8c03\u8bd5&#xff1a;<\/p>\n<p>javascript<\/p>\n<p>\/\/ \u5ba2\u6237\u7aef\u8c03\u8bd5\u811a\u672c<br \/>\nclass RedirectDebugger {<br \/>\n    constructor() {<br \/>\n        this.requests &#061; [];<br \/>\n        this.originalFetch &#061; window.fetch;<\/p>\n<p>        this.interceptRequests();<br \/>\n    }<\/p>\n<p>    interceptRequests() {<br \/>\n        \/\/ \u62e6\u622afetch\u8bf7\u6c42<br \/>\n        window.fetch &#061; async (&#8230;args) &#061;&gt; {<br \/>\n            const startTime &#061; Date.now();<br \/>\n            const [resource, init &#061; {}] &#061; args;<\/p>\n<p>            \/\/ \u8bb0\u5f55\u8bf7\u6c42<br \/>\n            const requestInfo &#061; {<br \/>\n                url: resource,<br \/>\n                method: init.method || &#039;GET&#039;,<br \/>\n                timestamp: new Date().toISOString(),<br \/>\n                headers: { &#8230;init.headers }<br \/>\n            };<\/p>\n<p>            try {<br \/>\n                const response &#061; await this.originalFetch(&#8230;args);<\/p>\n<p>                \/\/ \u8bb0\u5f55\u54cd\u5e94<br \/>\n                requestInfo.response &#061; {<br \/>\n                    status: response.status,<br \/>\n                    statusText: response.statusText,<br \/>\n                    headers: Object.fromEntries(response.headers.entries()),<br \/>\n                    duration: Date.now() &#8211; startTime<br \/>\n                };<\/p>\n<p>                \/\/ \u5904\u7406\u91cd\u5b9a\u5411<br \/>\n                if (response.redirected) {<br \/>\n                    requestInfo.redirected &#061; true;<br \/>\n                    requestInfo.finalUrl &#061; response.url;<br \/>\n                }<\/p>\n<p>                this.requests.push(requestInfo);<br \/>\n                console.log(&#039;Redirect debug:&#039;, requestInfo);<\/p>\n<p>                return response;<\/p>\n<p>            } catch (error) {<br \/>\n                requestInfo.error &#061; error.message;<br \/>\n                this.requests.push(requestInfo);<br \/>\n                console.error(&#039;Redirect debug error:&#039;, requestInfo);<br \/>\n                throw error;<br \/>\n            }<br \/>\n        };<br \/>\n    }<\/p>\n<p>    getRedirectLog() {<br \/>\n        return this.requests.filter(req &#061;&gt;<br \/>\n            req.response &amp;&amp;<br \/>\n            (req.response.status &gt;&#061; 300 &amp;&amp; req.response.status &lt; 400) ||<br \/>\n            req.redirected<br \/>\n        );<br \/>\n    }<\/p>\n<p>    clearLog() {<br \/>\n        this.requests &#061; [];<br \/>\n    }<br \/>\n}<\/p>\n<p>\/\/ \u4f7f\u7528\u793a\u4f8b<br \/>\nconst debugger &#061; new RedirectDebugger();<\/p>\n<p>\/\/ \u68c0\u67e5\u91cd\u5b9a\u5411\u65e5\u5fd7<br \/>\nconsole.log(&#039;Redirects:&#039;, debugger.getRedirectLog()); <\/p>\n<p>\u547d\u4ee4\u884c\u8c03\u8bd5&#xff1a;<\/p>\n<p>bash<\/p>\n<p># \u4f7f\u7528curl\u6d4b\u8bd5307\/308\u91cd\u5b9a\u5411<\/p>\n<p># 1. \u6d4b\u8bd5GET\u8bf7\u6c42\u7684307\u91cd\u5b9a\u5411<br \/>\ncurl -v -L http:\/\/example.com\/redirect-307<\/p>\n<p># 2. \u6d4b\u8bd5POST\u8bf7\u6c42\u7684307\u91cd\u5b9a\u5411&#xff08;\u4fdd\u6301\u65b9\u6cd5&#xff09;<br \/>\ncurl -v -L -X POST -d &#039;{&#034;test&#034;:&#034;data&#034;}&#039; \\\\<br \/>\n  -H &#034;Content-Type: application\/json&#034; \\\\<br \/>\n  http:\/\/example.com\/redirect-307<\/p>\n<p># 3. \u6d4b\u8bd5308\u6c38\u4e45\u91cd\u5b9a\u5411<br \/>\ncurl -v -L http:\/\/example.com\/redirect-308<\/p>\n<p># 4. \u6d4b\u8bd5POST\u8bf7\u6c42\u7684308\u91cd\u5b9a\u5411<br \/>\ncurl -v -L -X POST -d &#039;{&#034;test&#034;:&#034;data&#034;}&#039; \\\\<br \/>\n  -H &#034;Content-Type: application\/json&#034; \\\\<br \/>\n  http:\/\/example.com\/redirect-308<\/p>\n<p># 5. \u624b\u52a8\u5904\u7406\u91cd\u5b9a\u5411&#xff08;\u67e5\u770b\u4e2d\u95f4\u72b6\u6001&#xff09;<br \/>\ncurl -v -i &#8211;post301 &#8211;post302 &#8211;post303 \\\\<br \/>\n  -X POST -d &#039;data&#039; http:\/\/example.com\/redirect<\/p>\n<p># 6. \u4f7f\u7528wget\u6d4b\u8bd5<br \/>\nwget &#8211;method&#061;POST &#8211;body-data&#061;&#039;test&#061;data&#039; \\\\<br \/>\n  &#8211;header&#061;&#039;Content-Type: application\/x-www-form-urlencoded&#039; \\\\<br \/>\n  &#8211;server-response http:\/\/example.com\/redirect-307 <\/p>\n<h6>14.6.3 \u6700\u4f73\u5b9e\u8df5\u603b\u7ed3<\/h6>\n<p>\u9009\u62e9\u6b63\u786e\u7684\u72b6\u6001\u7801&#xff1a;<img loading=\"lazy\" decoding=\"async\" alt=\"\" height=\"1764\" src=\"https:\/\/www.wsisp.com\/helps\/wp-content\/uploads\/2026\/03\/20260228233641-69a37c09ade7f.png\" width=\"1458\" \/><\/p>\n<p>\u5b89\u5168\u6700\u4f73\u5b9e\u8df5&#xff1a;<\/p>\n<li>\n<p>\u8f93\u5165\u9a8c\u8bc1&#xff1a;<\/p>\n<p>python<\/p>\n<p> def safe_redirect_url(url, request):<br \/>\n    &#034;&#034;&#034;\u5b89\u5168\u7684\u91cd\u5b9a\u5411URL\u9a8c\u8bc1&#034;&#034;&#034;<\/p>\n<p>    # \u89e3\u6790URL<br \/>\n    parsed &#061; urlparse(url)<\/p>\n<p>    # 1. \u9a8c\u8bc1\u534f\u8bae<br \/>\n    allowed_schemes &#061; [&#039;http&#039;, &#039;https&#039;, &#039;&#039;]<br \/>\n    if parsed.scheme not in allowed_schemes:<br \/>\n        raise ValueError(f&#034;Invalid scheme: {parsed.scheme}&#034;)<\/p>\n<p>    # 2. \u9a8c\u8bc1\u57df\u540d<br \/>\n    allowed_domains &#061; [&#039;example.com&#039;, &#039;trusted.com&#039;]<br \/>\n    if parsed.netloc:<br \/>\n        domain &#061; parsed.netloc.split(&#039;:&#039;)[0]<br \/>\n        if domain not in allowed_domains:<br \/>\n            # \u68c0\u67e5\u5b50\u57df\u540d<br \/>\n            current_domain &#061; request.get_host().split(&#039;:&#039;)[0]<br \/>\n            if not domain.endswith(&#039;.&#039; &#043; current_domain):<br \/>\n                raise ValueError(f&#034;Untrusted domain: {domain}&#034;)<\/p>\n<p>    # 3. \u9632\u6b62\u5f00\u653e\u91cd\u5b9a\u5411<br \/>\n    if parsed.path and &#039;\/\/&#039; in parsed.path:<br \/>\n        raise ValueError(&#034;Invalid path&#034;)<\/p>\n<p>    # 4. \u6784\u5efa\u5b89\u5168URL<br \/>\n    safe_url &#061; urlunparse((<br \/>\n        parsed.scheme or &#039;https&#039;,<br \/>\n        parsed.netloc or request.get_host(),<br \/>\n        parsed.path,<br \/>\n        parsed.params,<br \/>\n        parsed.query,<br \/>\n        parsed.fragment<br \/>\n    ))<\/p>\n<p>    return safe_url <\/li>\n<li>\n<p>HTTPS\u5f3a\u5236&#xff1a;<\/p>\n<p>nginx<\/p>\n<p> # \u603b\u662f\u91cd\u5b9a\u5411\u5230HTTPS&#xff0c;\u4f7f\u7528308\u4fdd\u6301\u65b9\u6cd5<br \/>\nserver {<br \/>\n    listen 80;<br \/>\n    server_name api.example.com;<\/p>\n<p>    location \/ {<br \/>\n        return 308 https:\/\/$server_name$request_uri;<br \/>\n    }<br \/>\n} <\/li>\n<li>\n<p>\u5b89\u5168\u5934\u8bbe\u7f6e&#xff1a;<\/p>\n<p>http<\/p>\n<p> HTTP\/1.1 308 Permanent Redirect<br \/>\nLocation: https:\/\/example.com\/new-path<br \/>\nContent-Security-Policy: default-src &#039;self&#039;<br \/>\nX-Content-Type-Options: nosniff<br \/>\nX-Frame-Options: DENY<br \/>\nReferrer-Policy: strict-origin-when-cross-origin<br \/>\nX-Redirect-By: MyApp\/1.0 <\/li>\n<p>\u6027\u80fd\u4f18\u5316\u5b9e\u8df5&#xff1a;<\/p>\n<li>\n<p>\u51cf\u5c11\u91cd\u5b9a\u5411\u94fe&#xff1a;<\/p>\n<p>nginx<\/p>\n<p> # \u4e0d\u4f73&#xff1a;\u591a\u91cd\u91cd\u5b9a\u5411<br \/>\nlocation \/old {<br \/>\n    return 308 \/intermediate;<br \/>\n}<\/p>\n<p>location \/intermediate {<br \/>\n    return 308 \/new;<br \/>\n}<\/p>\n<p># \u4f18\u5316&#xff1a;\u76f4\u63a5\u91cd\u5b9a\u5411<br \/>\nlocation \/old {<br \/>\n    return 308 \/new;<br \/>\n} <\/li>\n<li>\n<p>\u8fde\u63a5\u590d\u7528&#xff1a;<\/p>\n<p>nginx<\/p>\n<p> # \u786e\u4fdd\u91cd\u5b9a\u5411\u5728\u540c\u4e00\u57df\u540d\u4e0b&#xff0c;\u590d\u7528\u8fde\u63a5<br \/>\nlocation \/api\/v1\/ {<br \/>\n    return 308 https:\/\/$server_name\/api\/v2\/$1;<br \/>\n} <\/li>\n<li>\n<p>\u7f13\u5b58\u7b56\u7565&#xff1a;<\/p>\n<p>http<\/p>\n<p> # \u5bf9308\u8bbe\u7f6e\u5408\u7406\u7f13\u5b58<br \/>\nHTTP\/1.1 308 Permanent Redirect<br \/>\nLocation: \/new-location<br \/>\nCache-Control: public, max-age&#061;31536000  # 1\u5e74<\/p>\n<p># \u5bf9307\u901a\u5e38\u4e0d\u7f13\u5b58<br \/>\nHTTP\/1.1 307 Temporary Redirect<br \/>\nLocation: \/temp-location<br \/>\nCache-Control: no-cache, no-store <\/li>\n<p>\u76d1\u63a7\u544a\u8b66&#xff1a;<\/p>\n<p>yaml<\/p>\n<p># \u76d1\u63a7\u544a\u8b66\u914d\u7f6e\u793a\u4f8b<br \/>\nalerts:<br \/>\n  high_redirect_rate:<br \/>\n    condition: rate(redirects_total[5m]) &gt; 1000<br \/>\n    severity: warning<br \/>\n    message: &#034;\u9ad8\u91cd\u5b9a\u5411\u7387\u68c0\u6d4b&#034;<\/p>\n<p>  redirect_errors:<br \/>\n    condition: rate(redirect_errors_total[5m]) &gt; 10<br \/>\n    severity: critical<br \/>\n    message: &#034;\u91cd\u5b9a\u5411\u9519\u8bef\u7387\u8fc7\u9ad8&#034;<\/p>\n<p>  long_redirect_chains:<br \/>\n    condition: avg(redirect_chain_length) &gt; 3<br \/>\n    severity: warning<br \/>\n    message: &#034;\u91cd\u5b9a\u5411\u94fe\u8fc7\u957f\u5f71\u54cd\u6027\u80fd&#034;<\/p>\n<p>  method_changes:<br \/>\n    condition: rate(method_change_events[5m]) &gt; 50<br \/>\n    severity: warning<br \/>\n    message: &#034;HTTP\u65b9\u6cd5\u53d8\u66f4\u9891\u7e41&#xff0c;\u53ef\u80fd\u5f71\u54cdAPI\u529f\u80fd&#034; <\/p>\n<hr \/>\n<h3>\u603b\u7ed3<\/h3>\n<p>HTTP\u91cd\u5b9a\u5411\u72b6\u6001\u78013xx\u662fWeb\u67b6\u6784\u4e2d\u7684\u91cd\u8981\u7ec4\u6210\u90e8\u5206&#xff0c;\u6bcf\u79cd\u72b6\u6001\u7801\u90fd\u6709\u5176\u7279\u5b9a\u7684\u8bed\u4e49\u548c\u7528\u9014&#xff1a;<\/p>\n<ul>\n<li>\n<p>301 Moved Permanently&#xff1a;\u6c38\u4e45\u91cd\u5b9a\u5411&#xff0c;\u9002\u5408\u7f51\u7ad9\u91cd\u6784\u3001\u57df\u540d\u8fc1\u79fb\u7b49\u6c38\u4e45\u6027\u53d8\u66f4<\/p>\n<\/li>\n<li>\n<p>302 Found&#xff1a;\u4e34\u65f6\u91cd\u5b9a\u5411&#xff0c;\u4f20\u7edf\u4f46\u65b9\u6cd5\u4fdd\u6301\u6a21\u7cca&#xff0c;\u9002\u5408A\/B\u6d4b\u8bd5\u3001\u7ef4\u62a4\u9875\u9762\u7b49\u4e34\u65f6\u573a\u666f<\/p>\n<\/li>\n<li>\n<p>303 See Other&#xff1a;\u660e\u786e\u8f6c\u4e3aGET\u7684\u91cd\u5b9a\u5411&#xff0c;\u9002\u5408POST\u540e\u663e\u793a\u7ed3\u679c\u9875<\/p>\n<\/li>\n<li>\n<p>304 Not Modified&#xff1a;\u7f13\u5b58\u9a8c\u8bc1\u673a\u5236&#xff0c;\u4f18\u5316\u6027\u80fd&#xff0c;\u51cf\u5c11\u4e0d\u5fc5\u8981\u7684\u6570\u636e\u4f20\u8f93<\/p>\n<\/li>\n<li>\n<p>307 Temporary Redirect&#xff1a;\u4e34\u65f6\u91cd\u5b9a\u5411\u4e14\u4fdd\u6301\u65b9\u6cd5&#xff0c;\u89e3\u51b3302\u7684\u6a21\u7cca\u6027&#xff0c;\u9002\u5408API\u4e34\u65f6\u8fc1\u79fb<\/p>\n<\/li>\n<li>\n<p>308 Permanent Redirect&#xff1a;\u6c38\u4e45\u91cd\u5b9a\u5411\u4e14\u4fdd\u6301\u65b9\u6cd5&#xff0c;\u89e3\u51b3301\u7684\u6a21\u7cca\u6027&#xff0c;\u9002\u5408API\u7248\u672c\u8fc1\u79fb<\/p>\n<\/li>\n<\/ul>\n<p>\u6838\u5fc3\u5efa\u8bae&#xff1a;<\/p>\n<li>\n<p>\u660e\u786e\u610f\u56fe&#xff1a;\u6839\u636e\u8d44\u6e90\u79fb\u52a8\u7684\u6c38\u4e45\u6027\u548c\u65b9\u6cd5\u4fdd\u6301\u9700\u6c42\u9009\u62e9\u5408\u9002\u7684\u72b6\u6001\u7801<\/p>\n<\/li>\n<li>\n<p>\u65b9\u6cd5\u5b89\u5168&#xff1a;\u5bf9API\u548c\u8868\u5355\u63d0\u4ea4\u4f7f\u7528307\/308\u786e\u4fdd\u65b9\u6cd5\u4fdd\u6301<\/p>\n<\/li>\n<li>\n<p>\u6027\u80fd\u4f18\u5316&#xff1a;\u5c3d\u91cf\u51cf\u5c11\u91cd\u5b9a\u5411\u94fe\u957f\u5ea6&#xff0c;\u5408\u7406\u8bbe\u7f6e\u7f13\u5b58\u5934<\/p>\n<\/li>\n<li>\n<p>\u5b89\u5168\u8003\u8651&#xff1a;\u9a8c\u8bc1\u91cd\u5b9a\u5411\u76ee\u6807&#xff0c;\u9632\u6b62\u5f00\u653e\u91cd\u5b9a\u5411\u6f0f\u6d1e<\/p>\n<\/li>\n<li>\n<p>\u76d1\u63a7\u7ef4\u62a4&#xff1a;\u5efa\u7acb\u76d1\u63a7\u544a\u8b66&#xff0c;\u5b9a\u671f\u5ba1\u8ba1\u91cd\u5b9a\u5411\u89c4\u5219<\/p>\n<\/li>\n<h2>\u7b2c15\u7ae0&#xff1a;\u5176\u4ed63xx\u72b6\u6001\u7801&#xff08;300\u3001303\u3001305\u3001306&#xff09;\u4e07\u5b57\u8be6\u89e3<\/h2>\n<h3>15.1 300 Multiple Choices&#xff08;\u591a\u79cd\u9009\u62e9&#xff09;<\/h3>\n<h4>15.1.1 \u5b9a\u4e49\u4e0e\u8bed\u4e49<\/h4>\n<p>300 Multiple Choices\u00a0\u72b6\u6001\u7801\u8868\u793a\u76ee\u6807\u8d44\u6e90\u6709\u591a\u4e2a\u8868\u793a\u5f62\u5f0f&#xff0c;\u6bcf\u4e2a\u8868\u793a\u90fd\u6709\u81ea\u5df1\u7684\u7279\u5b9a\u4f4d\u7f6e\u3002\u670d\u52a1\u5668\u53ef\u4ee5\u63d0\u4f9b\u8d44\u6e90\u7279\u5f81\u548c\u4f4d\u7f6e\u7684\u5217\u8868&#xff0c;\u4ee5\u4fbf\u7528\u6237\u6216\u7528\u6237\u4ee3\u7406&#xff08;\u5982\u6d4f\u89c8\u5668&#xff09;\u53ef\u4ee5\u9009\u62e9\u6700\u5408\u9002\u7684\u8868\u793a\u5e76\u8fdb\u884c\u91cd\u5b9a\u5411\u3002<\/p>\n<h4>15.1.2 \u5386\u53f2\u6f14\u53d8<\/h4>\n<ul>\n<li>\n<p>HTTP\/1.0&#xff1a;300\u72b6\u6001\u7801\u9996\u6b21\u5b9a\u4e49&#xff0c;\u4f46\u89c4\u8303\u8f83\u4e3a\u6a21\u7cca<\/p>\n<\/li>\n<li>\n<p>RFC 1945&#xff08;1996&#xff09;&#xff1a;\u521d\u6b65\u89c4\u8303<\/p>\n<\/li>\n<li>\n<p>RFC 2616&#xff08;1999&#xff09;&#xff1a;HTTP\/1.1\u7684\u6b63\u5f0f\u5b9a\u4e49<\/p>\n<\/li>\n<li>\n<p>RFC 7231&#xff08;2014&#xff09;&#xff1a;\u5f53\u524d\u6807\u51c6\u5b9a\u4e49&#xff0c;\u8bed\u4e49\u6709\u6240\u8c03\u6574<\/p>\n<\/li>\n<\/ul>\n<h4>15.1.3 \u6280\u672f\u89c4\u8303<\/h4>\n<h5>\u72b6\u6001\u884c<\/h5>\n<p>text<\/p>\n<p>HTTP\/1.1 300 Multiple Choices <\/p>\n<h5>\u5fc5\u9700\u7684\u5934\u5b57\u6bb5<\/h5>\n<p>\u670d\u52a1\u5668\u5e94\u8be5\u5305\u542b\u4ee5\u4e0b\u5934\u5b57\u6bb5\u4e4b\u4e00&#xff1a;<\/p>\n<ul>\n<li>\n<p>Location&#xff1a;\u5982\u679c\u670d\u52a1\u5668\u6709\u9996\u9009\u7684\u8868\u793a\u5f62\u5f0f<\/p>\n<\/li>\n<li>\n<p>\u5b9e\u4f53\u4e3b\u4f53&#xff1a;\u5305\u542b\u9009\u62e9\u5217\u8868&#xff08;\u901a\u5e38\u662fHTML\u683c\u5f0f&#xff09;<\/p>\n<\/li>\n<\/ul>\n<h5>\u53ef\u9009\u7684\u5934\u5b57\u6bb5<\/h5>\n<ul>\n<li>\n<p>Content-Type&#xff1a;\u63cf\u8ff0\u5b9e\u4f53\u4e3b\u4f53\u7684\u5a92\u4f53\u7c7b\u578b<\/p>\n<\/li>\n<li>\n<p>Content-Language&#xff1a;\u63cf\u8ff0\u5b9e\u4f53\u4e3b\u4f53\u7684\u8bed\u8a00<\/p>\n<\/li>\n<li>\n<p>Vary&#xff1a;\u6307\u793a\u670d\u52a1\u5668\u6839\u636e\u54ea\u4e9b\u8bf7\u6c42\u5934\u9009\u62e9\u8868\u793a<\/p>\n<\/li>\n<\/ul>\n<h4>15.1.4 \u5de5\u4f5c\u539f\u7406<\/h4>\n<h5>\u670d\u52a1\u5668\u884c\u4e3a<\/h5>\n<li>\n<p>\u68c0\u6d4b\u5230\u5ba2\u6237\u7aef\u8bf7\u6c42\u7684\u8d44\u6e90\u6709\u591a\u4e2a\u8868\u793a\u5f62\u5f0f<\/p>\n<\/li>\n<li>\n<p>\u6839\u636e\u8bf7\u6c42\u5934&#xff08;\u5982Accept\u3001Accept-Language&#xff09;\u65e0\u6cd5\u81ea\u52a8\u9009\u62e9\u6700\u5408\u9002\u7684<\/p>\n<\/li>\n<li>\n<p>\u8fd4\u56de300\u72b6\u6001\u7801\u5e76\u63d0\u4f9b\u9009\u62e9\u5217\u8868<\/p>\n<\/li>\n<h5>\u5ba2\u6237\u7aef\u884c\u4e3a<\/h5>\n<li>\n<p>\u63a5\u6536300\u54cd\u5e94<\/p>\n<\/li>\n<li>\n<p>\u89e3\u6790\u5b9e\u4f53\u4e3b\u4f53\u4e2d\u7684\u9009\u62e9\u5217\u8868<\/p>\n<\/li>\n<li>\n<p>\u7528\u6237\u6216\u81ea\u52a8\u903b\u8f91\u9009\u62e9\u5176\u4e2d\u4e00\u4e2a<\/p>\n<\/li>\n<li>\n<p>\u5411\u9009\u62e9\u7684URI\u53d1\u9001\u65b0\u8bf7\u6c42<\/p>\n<\/li>\n<h4>15.1.5 \u8be6\u7ec6\u793a\u4f8b<\/h4>\n<h5>\u573a\u666f\u63cf\u8ff0<\/h5>\n<p>\u4e00\u4e2a\u56fd\u9645\u5316\u7684\u6587\u6863\u8d44\u6e90&#xff0c;\u652f\u6301\u4e09\u79cd\u8bed\u8a00\u7248\u672c&#xff1a;\u82f1\u8bed\u3001\u6cd5\u8bed\u548c\u897f\u73ed\u7259\u8bed\u3002\u670d\u52a1\u5668\u65e0\u6cd5\u6839\u636e\u5ba2\u6237\u7aef\u7684Accept-Language\u5934\u786e\u5b9a\u9996\u9009\u8bed\u8a00&#xff08;\u6216\u5ba2\u6237\u7aef\u672a\u53d1\u9001\u8be5\u5934&#xff09;\u3002<\/p>\n<h5>\u8bf7\u6c42\u793a\u4f8b<\/h5>\n<p>http<\/p>\n<p>GET \/document HTTP\/1.1<br \/>\nHost: example.com<br \/>\nUser-Agent: Mozilla\/5.0<br \/>\nAccept: text\/html <\/p>\n<h5>\u54cd\u5e94\u793a\u4f8b<\/h5>\n<p>http<\/p>\n<p>HTTP\/1.1 300 Multiple Choices<br \/>\nDate: Mon, 15 Jan 2024 12:00:00 GMT<br \/>\nContent-Type: text\/html; charset&#061;utf-8<br \/>\nContent-Language: en<br \/>\nVary: Accept-Language<\/p>\n<p>&lt;!DOCTYPE html&gt;<br \/>\n&lt;html lang&#061;&#034;en&#034;&gt;<br \/>\n&lt;head&gt;<br \/>\n    &lt;title&gt;Multiple Representations Available&lt;\/title&gt;<br \/>\n&lt;\/head&gt;<br \/>\n&lt;body&gt;<br \/>\n    &lt;h1&gt;Available Representations&lt;\/h1&gt;<br \/>\n    &lt;p&gt;The resource you requested is available in multiple languages:&lt;\/p&gt;<br \/>\n    &lt;ul&gt;<br \/>\n        &lt;li&gt;&lt;a href&#061;&#034;\/document\/en&#034;&gt;English version&lt;\/a&gt;&lt;\/li&gt;<br \/>\n        &lt;li&gt;&lt;a href&#061;&#034;\/document\/fr&#034;&gt;French version&lt;\/a&gt;&lt;\/li&gt;<br \/>\n        &lt;li&gt;&lt;a href&#061;&#034;\/document\/es&#034;&gt;Spanish version&lt;\/a&gt;&lt;\/li&gt;<br \/>\n    &lt;\/ul&gt;<br \/>\n    &lt;p&gt;Please select one of the options above.&lt;\/p&gt;<br \/>\n&lt;\/body&gt;<br \/>\n&lt;\/html&gt; <\/p>\n<h5>\u66f4\u590d\u6742\u7684\u793a\u4f8b&#xff08;\u5305\u542b\u5a92\u4f53\u7c7b\u578b\u9009\u62e9&#xff09;<\/h5>\n<p>http<\/p>\n<p>HTTP\/1.1 300 Multiple Choices<br \/>\nDate: Mon, 15 Jan 2024 12:00:00 GMT<br \/>\nContent-Type: application\/json<br \/>\nVary: Accept<\/p>\n<p>{<br \/>\n  &#034;message&#034;: &#034;Multiple representations available&#034;,<br \/>\n  &#034;choices&#034;: [<br \/>\n    {<br \/>\n      &#034;uri&#034;: &#034;\/resource\/document.pdf&#034;,<br \/>\n      &#034;content_type&#034;: &#034;application\/pdf&#034;,<br \/>\n      &#034;description&#034;: &#034;PDF document (formatted)&#034;,<br \/>\n      &#034;size&#034;: &#034;245KB&#034;<br \/>\n    },<br \/>\n    {<br \/>\n      &#034;uri&#034;: &#034;\/resource\/document.html&#034;,<br \/>\n      &#034;content_type&#034;: &#034;text\/html&#034;,<br \/>\n      &#034;description&#034;: &#034;HTML version (web view)&#034;,<br \/>\n      &#034;size&#034;: &#034;45KB&#034;<br \/>\n    },<br \/>\n    {<br \/>\n      &#034;uri&#034;: &#034;\/resource\/document.json&#034;,<br \/>\n      &#034;content_type&#034;: &#034;application\/json&#034;,<br \/>\n      &#034;description&#034;: &#034;JSON data (structured)&#034;,<br \/>\n      &#034;size&#034;: &#034;32KB&#034;<br \/>\n    }<br \/>\n  ],<br \/>\n  &#034;default&#034;: &#034;\/resource\/document.html&#034;<br \/>\n} <\/p>\n<h4>15.1.6 \u4f7f\u7528\u573a\u666f\u4e0e\u6700\u4f73\u5b9e\u8df5<\/h4>\n<h5>\u9002\u7528\u573a\u666f<\/h5>\n<li>\n<p>\u5185\u5bb9\u534f\u5546\u5931\u8d25\u65f6&#xff1a;\u5f53\u670d\u52a1\u5668\u65e0\u6cd5\u6839\u636eAccept*\u5934\u5b57\u6bb5\u786e\u5b9a\u6700\u4f73\u8868\u793a<\/p>\n<\/li>\n<li>\n<p>\u591a\u683c\u5f0f\u8d44\u6e90&#xff1a;\u8d44\u6e90\u540c\u65f6\u63d0\u4f9bPDF\u3001HTML\u3001JSON\u7b49\u591a\u79cd\u683c\u5f0f<\/p>\n<\/li>\n<li>\n<p>\u591a\u8bed\u8a00\u5185\u5bb9&#xff1a;\u8d44\u6e90\u6709\u591a\u79cd\u8bed\u8a00\u7248\u672c&#xff0c;\u4e14\u65e0\u6cd5\u81ea\u52a8\u9009\u62e9<\/p>\n<\/li>\n<li>\n<p>\u8bbe\u5907\u7279\u5b9a\u5185\u5bb9&#xff1a;\u8d44\u6e90\u6709\u9488\u5bf9\u684c\u9762\u3001\u79fb\u52a8\u3001\u5e73\u677f\u7684\u4e0d\u540c\u7248\u672c<\/p>\n<\/li>\n<h5>\u6700\u4f73\u5b9e\u8df5<\/h5>\n<li>\n<p>\u63d0\u4f9b\u6e05\u6670\u7684\u7528\u6237\u754c\u9762&#xff1a;\u5982\u679c\u8fd4\u56deHTML&#xff0c;\u786e\u4fdd\u9009\u62e9\u5217\u8868\u6613\u4e8e\u7406\u89e3<\/p>\n<\/li>\n<li>\n<p>\u5305\u542b\u5143\u6570\u636e&#xff1a;\u5728\u6bcf\u4e2a\u9009\u9879\u4e2d\u63d0\u4f9b\u5185\u5bb9\u7c7b\u578b\u3001\u8bed\u8a00\u3001\u5927\u5c0f\u7b49\u4fe1\u606f<\/p>\n<\/li>\n<li>\n<p>\u8bbe\u7f6e\u9ed8\u8ba4\u9009\u9879&#xff1a;\u5982\u679c\u6709\u63a8\u8350\u9009\u9879&#xff0c;\u901a\u8fc7Location\u5934\u6216\u6807\u8bb0\u9ed8\u8ba4\u9879<\/p>\n<\/li>\n<li>\n<p>\u7f13\u5b58\u8003\u8651&#xff1a;\u901a\u5e38\u4e0d\u5e94\u7f13\u5b58300\u54cd\u5e94&#xff0c;\u6216\u8bbe\u7f6e\u8f83\u77ed\u7f13\u5b58\u65f6\u95f4<\/p>\n<\/li>\n<li>\n<p>SEO\u4f18\u5316&#xff1a;\u5bf9\u4e8e\u641c\u7d22\u5f15\u64ce&#xff0c;\u8003\u8651\u4f7f\u7528\u5176\u4ed6\u65b9\u6cd5&#xff08;\u5982hreflang&#xff09;<\/p>\n<\/li>\n<h5>\u4e0d\u63a8\u8350\u7684\u4f7f\u7528\u65b9\u5f0f<\/h5>\n<li>\n<p>\u4f5c\u4e3a\u5e38\u89c4\u91cd\u5b9a\u5411\u4f7f\u7528&#xff08;\u5e94\u4f7f\u7528301\/302\/303&#xff09;<\/p>\n<\/li>\n<li>\n<p>\u66ff\u4ee3\u670d\u52a1\u5668\u7aef\u5185\u5bb9\u534f\u5546<\/p>\n<\/li>\n<li>\n<p>\u5f53\u53ef\u4ee5\u81ea\u52a8\u9009\u62e9\u65f6\u4ecd\u8fd4\u56de300<\/p>\n<\/li>\n<h4>15.1.7 \u5b9e\u73b0\u793a\u4f8b<\/h4>\n<h5>Python Flask\u5b9e\u73b0<\/h5>\n<p>python<\/p>\n<p>from flask import Flask, request, jsonify, make_response<\/p>\n<p>app &#061; Flask(__name__)<\/p>\n<p>&#064;app.route(&#039;\/document&#039;)<br \/>\ndef get_document():<br \/>\n    accept_language &#061; request.headers.get(&#039;Accept-Language&#039;, &#039;&#039;)<\/p>\n<p>    # \u5982\u679c\u5ba2\u6237\u7aef\u6307\u5b9a\u4e86\u660e\u786e\u7684\u8bed\u8a00\u504f\u597d<br \/>\n    if &#039;en&#039; in accept_language:<br \/>\n        return redirect_to(&#039;\/document\/en&#039;)<br \/>\n    elif &#039;fr&#039; in accept_language:<br \/>\n        return redirect_to(&#039;\/document\/fr&#039;)<br \/>\n    elif &#039;es&#039; in accept_language:<br \/>\n        return redirect_to(&#039;\/document\/es&#039;)<br \/>\n    else:<br \/>\n        # \u65e0\u6cd5\u786e\u5b9a&#xff0c;\u8fd4\u56de\u9009\u62e9\u5217\u8868<br \/>\n        response &#061; make_response(&#039;&#039;&#039;<br \/>\n        &lt;html&gt;<br \/>\n        &lt;body&gt;<br \/>\n            &lt;h1&gt;Select Language&lt;\/h1&gt;<br \/>\n            &lt;ul&gt;<br \/>\n                &lt;li&gt;&lt;a href&#061;&#034;\/document\/en&#034;&gt;English&lt;\/a&gt;&lt;\/li&gt;<br \/>\n                &lt;li&gt;&lt;a href&#061;&#034;\/document\/fr&#034;&gt;French&lt;\/a&gt;&lt;\/li&gt;<br \/>\n                &lt;li&gt;&lt;a href&#061;&#034;\/document\/es&#034;&gt;Spanish&lt;\/a&gt;&lt;\/li&gt;<br \/>\n            &lt;\/ul&gt;<br \/>\n        &lt;\/body&gt;<br \/>\n        &lt;\/html&gt;<br \/>\n        &#039;&#039;&#039;)<br \/>\n        response.status_code &#061; 300<br \/>\n        response.headers[&#039;Content-Type&#039;] &#061; &#039;text\/html&#039;<br \/>\n        return response<\/p>\n<p>def redirect_to(url):<br \/>\n    from flask import redirect<br \/>\n    return redirect(url, code&#061;302) <\/p>\n<h5>Node.js Express\u5b9e\u73b0<\/h5>\n<p>javascript<\/p>\n<p>const express &#061; require(&#039;express&#039;);<br \/>\nconst app &#061; express();<\/p>\n<p>app.get(&#039;\/api\/data&#039;, (req, res) &#061;&gt; {<br \/>\n    const acceptHeader &#061; req.headers.accept || &#039;&#039;;<\/p>\n<p>    const representations &#061; [<br \/>\n        {<br \/>\n            url: &#039;\/api\/data.json&#039;,<br \/>\n            type: &#039;application\/json&#039;,<br \/>\n            description: &#039;JSON representation&#039;<br \/>\n        },<br \/>\n        {<br \/>\n            url: &#039;\/api\/data.xml&#039;,<br \/>\n            type: &#039;application\/xml&#039;,<br \/>\n            description: &#039;XML representation&#039;<br \/>\n        },<br \/>\n        {<br \/>\n            url: &#039;\/api\/data.csv&#039;,<br \/>\n            type: &#039;text\/csv&#039;,<br \/>\n            description: &#039;CSV representation&#039;<br \/>\n        }<br \/>\n    ];<\/p>\n<p>    \/\/ \u68c0\u67e5\u662f\u5426\u63a5\u53d7JSON<br \/>\n    if (acceptHeader.includes(&#039;application\/json&#039;)) {<br \/>\n        res.redirect(&#039;\/api\/data.json&#039;);<br \/>\n    }<br \/>\n    \/\/ \u68c0\u67e5\u662f\u5426\u63a5\u53d7XML<br \/>\n    else if (acceptHeader.includes(&#039;application\/xml&#039;)) {<br \/>\n        res.redirect(&#039;\/api\/data.xml&#039;);<br \/>\n    }<br \/>\n    \/\/ \u65e0\u6cd5\u786e\u5b9a&#xff0c;\u8fd4\u56de300<br \/>\n    else {<br \/>\n        res.status(300);<br \/>\n        res.set(&#039;Content-Type&#039;, &#039;application\/json&#039;);<br \/>\n        res.json({<br \/>\n            message: &#039;Multiple representations available&#039;,<br \/>\n            choices: representations,<br \/>\n            default: &#039;\/api\/data.json&#039;<br \/>\n        });<br \/>\n    }<br \/>\n}); <\/p>\n<h4>15.1.8 \u5ba2\u6237\u7aef\u5904\u7406\u7b56\u7565<\/h4>\n<h5>\u6d4f\u89c8\u5668\u5904\u7406<\/h5>\n<p>\u5927\u591a\u6570\u73b0\u4ee3\u6d4f\u89c8\u5668\u5c06300\u54cd\u5e94\u89c6\u4e3a\u9700\u8981\u7528\u6237\u4ea4\u4e92\u7684\u60c5\u51b5&#xff1a;<\/p>\n<li>\n<p>\u663e\u793a\u670d\u52a1\u5668\u8fd4\u56de\u7684HTML\u9875\u9762<\/p>\n<\/li>\n<li>\n<p>\u7528\u6237\u70b9\u51fb\u94fe\u63a5\u540e\u7ee7\u7eed<\/p>\n<\/li>\n<li>\n<p>\u4e0d\u4f1a\u81ea\u52a8\u91cd\u5b9a\u5411&#xff08;\u9664\u975e\u6709Location\u5934&#xff09;<\/p>\n<\/li>\n<h5>API\u5ba2\u6237\u7aef\u5904\u7406<\/h5>\n<p>API\u5ba2\u6237\u7aef\u5e94\u80fd\u89e3\u6790300\u54cd\u5e94\u5e76\u505a\u51fa\u51b3\u7b56&#xff1a;<\/p>\n<p>python<\/p>\n<p>import requests<\/p>\n<p>def handle_300_response(response):<br \/>\n    if response.status_code &#061;&#061; 300:<br \/>\n        content_type &#061; response.headers.get(&#039;Content-Type&#039;, &#039;&#039;)<\/p>\n<p>        if &#039;application\/json&#039; in content_type:<br \/>\n            data &#061; response.json()<br \/>\n            # \u81ea\u52a8\u9009\u62e9\u9ed8\u8ba4\u9009\u9879\u6216\u7b2c\u4e00\u4e2a\u9009\u9879<br \/>\n            if &#039;default&#039; in data:<br \/>\n                return requests.get(data[&#039;default&#039;])<br \/>\n            elif &#039;choices&#039; in data and len(data[&#039;choices&#039;]) &gt; 0:<br \/>\n                return requests.get(data[&#039;choices&#039;][0][&#039;uri&#039;])<br \/>\n        elif &#039;text\/html&#039; in content_type:<br \/>\n            # \u5bf9\u4e8eHTML&#xff0c;\u53ef\u80fd\u9700\u8981\u7528\u6237\u4ea4\u4e92<br \/>\n            # \u8fd9\u91cc\u7b80\u5355\u9009\u62e9\u7b2c\u4e00\u4e2a\u94fe\u63a5<br \/>\n            import re<br \/>\n            links &#061; re.findall(r&#039;href&#061;&#034;([^&#034;]&#043;)&#034;&#039;, response.text)<br \/>\n            if links:<br \/>\n                return requests.get(links[0])<\/p>\n<p>    return response<\/p>\n<p># \u4f7f\u7528\u793a\u4f8b<br \/>\nresponse &#061; requests.get(&#039;http:\/\/example.com\/document&#039;)<br \/>\nif response.status_code &#061;&#061; 300:<br \/>\n    response &#061; handle_300_response(response) <\/p>\n<h4>15.1.9 \u7f13\u5b58\u884c\u4e3a<\/h4>\n<p>300\u54cd\u5e94\u7684\u7f13\u5b58\u7279\u6027&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u9ed8\u8ba4\u4e0d\u53ef\u7f13\u5b58&#xff1a;\u9664\u975e\u660e\u786e\u8bbe\u7f6e\u7f13\u5b58\u5934<\/p>\n<\/li>\n<li>\n<p>\u7f13\u5b58\u952e&#xff1a;\u5e94\u8003\u8651Vary\u5934\u5b57\u6bb5<\/p>\n<\/li>\n<li>\n<p>\u5efa\u8bae&#xff1a;\u901a\u5e38\u8bbe\u7f6eCache-Control: no-cache\u6216\u8f83\u77ed\u7684\u6700\u5927\u5e74\u9f84<\/p>\n<\/li>\n<\/ul>\n<p>http<\/p>\n<p>HTTP\/1.1 300 Multiple Choices<br \/>\nCache-Control: max-age&#061;3600, public<br \/>\nVary: Accept, Accept-Language <\/p>\n<h4>15.1.10 \u5b89\u5168\u8003\u8651<\/h4>\n<li>\n<p>\u4fe1\u606f\u6cc4\u9732&#xff1a;\u9009\u62e9\u5217\u8868\u53ef\u80fd\u66b4\u9732\u5185\u90e8URL\u7ed3\u6784<\/p>\n<\/li>\n<li>\n<p>\u91cd\u5b9a\u5411\u5b89\u5168&#xff1a;\u786e\u4fdd\u6240\u6709\u9009\u9879\u6307\u5411\u53ef\u4fe1\u4f4d\u7f6e<\/p>\n<\/li>\n<li>\n<p>CSRF\u4fdd\u62a4&#xff1a;\u5982\u679c\u6d89\u53ca\u654f\u611f\u64cd\u4f5c&#xff0c;\u9700\u9a8c\u8bc1\u91cd\u5b9a\u5411\u76ee\u6807<\/p>\n<\/li>\n<li>\n<p>\u70b9\u51fb\u52ab\u6301&#xff1a;\u786e\u4fdd\u9009\u62e9\u9875\u9762\u6709\u9002\u5f53\u7684X-Frame-Options<\/p>\n<\/li>\n<h4>15.1.11 \u4e0e\u5176\u4ed6\u72b6\u6001\u7801\u7684\u6bd4\u8f83<\/h4>\n<table>\n<tr>\u7279\u6027300 Multiple Choices302 Found303 See Other<\/tr>\n<tbody>\n<tr>\n<td>\u81ea\u52a8\u91cd\u5b9a\u5411<\/td>\n<td>\u5426&#xff08;\u901a\u5e38&#xff09;<\/td>\n<td>\u662f<\/td>\n<td>\u662f<\/td>\n<\/tr>\n<tr>\n<td>\u65b9\u6cd5\u4fdd\u6301<\/td>\n<td>\u4e0d\u9002\u7528<\/td>\n<td>\u901a\u5e38\u6539\u53d8\u4e3aGET<\/td>\n<td>\u5fc5\u987b\u6539\u4e3aGET<\/td>\n<\/tr>\n<tr>\n<td>\u7528\u6237\u4ea4\u4e92<\/td>\n<td>\u901a\u5e38\u9700\u8981<\/td>\n<td>\u4e0d\u9700\u8981<\/td>\n<td>\u4e0d\u9700\u8981<\/td>\n<\/tr>\n<tr>\n<td>\u7f13\u5b58\u6027<\/td>\n<td>\u901a\u5e38\u4e0d\u7f13\u5b58<\/td>\n<td>\u901a\u5e38\u4e0d\u7f13\u5b58<\/td>\n<td>\u901a\u5e38\u4e0d\u7f13\u5b58<\/td>\n<\/tr>\n<tr>\n<td>\u5178\u578b\u7528\u9014<\/td>\n<td>\u5185\u5bb9\u534f\u5546<\/td>\n<td>\u4e34\u65f6\u91cd\u5b9a\u5411<\/td>\n<td>POST\u540e\u91cd\u5b9a\u5411<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h4>15.1.12 \u5b9e\u9645\u5e94\u7528\u6848\u4f8b<\/h4>\n<h5>\u6848\u4f8b1&#xff1a;REST API\u7248\u672c\u534f\u5546<\/h5>\n<p>http<\/p>\n<p>GET \/api\/users HTTP\/1.1<br \/>\nHost: api.example.com<br \/>\nAccept: application\/vnd.example.user&#043;json <\/p>\n<p>http<\/p>\n<p>HTTP\/1.1 300 Multiple Choices<br \/>\nContent-Type: application\/json<\/p>\n<p>{<br \/>\n  &#034;error&#034;: &#034;API version not specified&#034;,<br \/>\n  &#034;available_versions&#034;: [<br \/>\n    {<br \/>\n      &#034;version&#034;: &#034;1.0&#034;,<br \/>\n      &#034;url&#034;: &#034;\/api\/v1.0\/users&#034;,<br \/>\n      &#034;status&#034;: &#034;stable&#034;<br \/>\n    },<br \/>\n    {<br \/>\n      &#034;version&#034;: &#034;2.0&#034;,<br \/>\n      &#034;url&#034;: &#034;\/api\/v2.0\/users&#034;,<br \/>\n      &#034;status&#034;: &#034;beta&#034;<br \/>\n    },<br \/>\n    {<br \/>\n      &#034;version&#034;: &#034;3.0-alpha&#034;,<br \/>\n      &#034;url&#034;: &#034;\/api\/v3.0\/users&#034;,<br \/>\n      &#034;status&#034;: &#034;alpha&#034;<br \/>\n    }<br \/>\n  ],<br \/>\n  &#034;documentation&#034;: &#034;https:\/\/api.example.com\/versions&#034;<br \/>\n} <\/p>\n<h5>\u6848\u4f8b2&#xff1a;\u591a\u683c\u5f0f\u6587\u6863\u670d\u52a1<\/h5>\n<p>http<\/p>\n<p>GET \/reports\/annual-2023 HTTP\/1.1<br \/>\nHost: docs.company.com <\/p>\n<p>http<\/p>\n<p>HTTP\/1.1 300 Multiple Choices<br \/>\nContent-Type: text\/html; charset&#061;utf-8<\/p>\n<p>&lt;!DOCTYPE html&gt;<br \/>\n&lt;html&gt;<br \/>\n&lt;head&gt;<br \/>\n    &lt;title&gt;Annual Report 2023 &#8211; Available Formats&lt;\/title&gt;<br \/>\n&lt;\/head&gt;<br \/>\n&lt;body&gt;<br \/>\n    &lt;h1&gt;Annual Report 2023&lt;\/h1&gt;<br \/>\n    &lt;p&gt;Available in multiple formats:&lt;\/p&gt;<br \/>\n    &lt;div class&#061;&#034;format-options&#034;&gt;<br \/>\n        &lt;div class&#061;&#034;option&#034;&gt;<br \/>\n            &lt;h2&gt;&lt;a href&#061;&#034;\/reports\/annual-2023.pdf&#034;&gt;PDF Version&lt;\/a&gt;&lt;\/h2&gt;<br \/>\n            &lt;p&gt;High-quality printable document (15MB)&lt;\/p&gt;<br \/>\n            &lt;p&gt;&lt;small&gt;Includes charts and high-resolution images&lt;\/small&gt;&lt;\/p&gt;<br \/>\n        &lt;\/div&gt;<br \/>\n        &lt;div class&#061;&#034;option&#034;&gt;<br \/>\n            &lt;h2&gt;&lt;a href&#061;&#034;\/reports\/annual-2023.html&#034;&gt;Web Version&lt;\/a&gt;&lt;\/h2&gt;<br \/>\n            &lt;p&gt;Accessible HTML version (1.2MB)&lt;\/p&gt;<br \/>\n            &lt;p&gt;&lt;small&gt;Optimized for screen readers and mobile devices&lt;\/small&gt;&lt;\/p&gt;<br \/>\n        &lt;\/div&gt;<br \/>\n        &lt;div class&#061;&#034;option&#034;&gt;<br \/>\n            &lt;h2&gt;&lt;a href&#061;&#034;\/reports\/annual-2023.txt&#034;&gt;Plain Text&lt;\/a&gt;&lt;\/h2&gt;<br \/>\n            &lt;p&gt;Text-only version (450KB)&lt;\/p&gt;<br \/>\n            &lt;p&gt;&lt;small&gt;Minimal formatting, fastest download&lt;\/small&gt;&lt;\/p&gt;<br \/>\n        &lt;\/div&gt;<br \/>\n    &lt;\/div&gt;<br \/>\n&lt;\/body&gt;<br \/>\n&lt;\/html&gt; <\/p>\n<hr \/>\n<h3>15.2 303 See Other&#xff08;\u53c2\u89c1\u5176\u4ed6&#xff09;<\/h3>\n<h4>15.2.1 \u5b9a\u4e49\u4e0e\u8bed\u4e49<\/h4>\n<p>303 See Other\u00a0\u72b6\u6001\u7801\u8868\u793a\u670d\u52a1\u5668\u5c06\u5ba2\u6237\u7aef\u91cd\u5b9a\u5411\u5230\u53e6\u4e00\u4e2a\u8d44\u6e90&#xff0c;\u8be5\u8d44\u6e90\u4f4d\u4e8e\u4e0d\u540c\u7684URI&#xff0c;\u5e76\u4e14\u5ba2\u6237\u7aef\u5e94\u8be5\u4f7f\u7528GET\u65b9\u6cd5&#xff08;\u65e0\u8bba\u539f\u59cb\u8bf7\u6c42\u4f7f\u7528\u4ec0\u4e48\u65b9\u6cd5&#xff09;\u8bf7\u6c42\u8be5\u8d44\u6e90\u3002<\/p>\n<h4>15.2.2 \u5386\u53f2\u80cc\u666f\u4e0e\u8bbe\u8ba1\u76ee\u7684<\/h4>\n<p>303\u72b6\u6001\u7801\u662f\u5728HTTP\/1.1\u4e2d\u5f15\u5165\u7684&#xff0c;\u4e3b\u8981\u76ee\u7684\u662f\u89e3\u51b3302\u72b6\u6001\u7801\u7684\u8bed\u4e49\u6a21\u7cca\u6027&#xff1a;<\/p>\n<li>\n<p>HTTP\/1.0\u4e2d\u7684302\u95ee\u9898&#xff1a;<\/p>\n<ul>\n<li>\n<p>302\u6700\u521d\u5b9a\u4e49\u4e3a&#034;Temporary Redirect&#034;<\/p>\n<\/li>\n<li>\n<p>\u4f46\u5b9e\u9645\u5b9e\u73b0\u4e2d&#xff0c;\u6709\u4e9b\u5ba2\u6237\u7aef\u4fdd\u6301\u539f\u59cb\u65b9\u6cd5&#xff0c;\u6709\u4e9b\u6539\u4e3aGET<\/p>\n<\/li>\n<li>\n<p>\u8fd9\u5bfc\u81f4\u4e86\u4e0d\u4e00\u81f4\u6027\u548c\u6f5c\u5728\u7684\u5b89\u5168\u95ee\u9898<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p>HTTP\/1.1\u7684\u89e3\u51b3\u65b9\u6848&#xff1a;<\/p>\n<ul>\n<li>\n<p>302\u88ab\u91cd\u65b0\u5b9a\u4e49\u4e3a&#034;Found&#034;&#xff0c;\u8bed\u4e49\u4fdd\u6301\u4e0d\u53d8\u4f46\u6a21\u7cca<\/p>\n<\/li>\n<li>\n<p>\u5f15\u5165303&#xff0c;\u660e\u786e\u8981\u6c42\u5fc5\u987b\u4f7f\u7528GET\u65b9\u6cd5<\/p>\n<\/li>\n<li>\n<p>\u5f15\u5165307&#xff0c;\u660e\u786e\u8981\u6c42\u5fc5\u987b\u4fdd\u6301\u539f\u59cb\u65b9\u6cd5<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<h4>15.2.3 \u6280\u672f\u89c4\u8303<\/h4>\n<h5>\u72b6\u6001\u884c<\/h5>\n<p>text<\/p>\n<p>HTTP\/1.1 303 See Other <\/p>\n<h5>\u5fc5\u9700\u7684\u5934\u5b57\u6bb5<\/h5>\n<ul>\n<li>\n<p>Location&#xff1a;\u5fc5\u987b\u5305\u542b&#xff0c;\u6307\u5b9a\u5ba2\u6237\u7aef\u5e94\u8be5\u8bf7\u6c42\u7684\u65b0URI<\/p>\n<\/li>\n<\/ul>\n<h5>\u5efa\u8bae\u7684\u5934\u5b57\u6bb5<\/h5>\n<ul>\n<li>\n<p>Content-Type&#xff1a;\u5982\u679c\u5305\u542b\u54cd\u5e94\u5b9e\u4f53<\/p>\n<\/li>\n<li>\n<p>Cache-Control&#xff1a;\u901a\u5e38\u8bbe\u7f6e\u4e3a\u4e0d\u7f13\u5b58<\/p>\n<\/li>\n<\/ul>\n<h4>15.2.4 \u6838\u5fc3\u7279\u6027<\/h4>\n<h5>1. \u65b9\u6cd5\u53d8\u66f4<\/h5>\n<ul>\n<li>\n<p>\u539f\u59cb\u8bf7\u6c42&#xff1a;\u53ef\u4ee5\u662f\u4efb\u4f55HTTP\u65b9\u6cd5&#xff08;POST\u3001PUT\u3001DELETE\u7b49&#xff09;<\/p>\n<\/li>\n<li>\n<p>\u91cd\u5b9a\u5411\u8bf7\u6c42&#xff1a;\u5fc5\u987b\u4f7f\u7528GET\u65b9\u6cd5<\/p>\n<\/li>\n<\/ul>\n<h5>2. \u81ea\u52a8\u91cd\u5b9a\u5411<\/h5>\n<p>\u5927\u591a\u6570\u7528\u6237\u4ee3\u7406&#xff08;\u6d4f\u89c8\u5668&#xff09;\u4f1a\u81ea\u52a8\u5904\u7406303\u91cd\u5b9a\u5411&#xff0c;\u65e0\u9700\u7528\u6237\u5e72\u9884\u3002<\/p>\n<h5>3. \u4e0d\u7f13\u5b58\u6027<\/h5>\n<p>303\u54cd\u5e94\u901a\u5e38\u4e0d\u5e94\u8be5\u88ab\u7f13\u5b58&#xff0c;\u9664\u975e\u6709\u660e\u786e\u7684\u7f13\u5b58\u6307\u4ee4\u3002<\/p>\n<h4>15.2.5 \u8be6\u7ec6\u5de5\u4f5c\u6d41\u7a0b<\/h4>\n<h5>\u5178\u578b\u573a\u666f&#xff1a;\u8868\u5355\u63d0\u4ea4\u540e\u7684\u91cd\u5b9a\u5411<\/h5>\n<p>text<\/p>\n<p>\u7528\u6237\u586b\u5199\u8868\u5355 \u2192 POST\u63d0\u4ea4 \u2192 \u670d\u52a1\u5668\u5904\u7406 \u2192 \u8fd4\u56de303 \u2192 \u6d4f\u89c8\u5668GET\u91cd\u5b9a\u5411 \u2192 \u663e\u793a\u7ed3\u679c\u9875\u9762 <\/p>\n<h5>\u8bf7\u6c42-\u54cd\u5e94\u5e8f\u5217<\/h5>\n<p>text<\/p>\n<p>1. \u5ba2\u6237\u7aef \u2192 \u670d\u52a1\u5668: POST \/submit-form<br \/>\n   Content-Type: application\/x-www-form-urlencoded<\/p>\n<p>   name&#061;John&amp;email&#061;john&#064;example.com<\/p>\n<p>2. \u670d\u52a1\u5668 \u2192 \u5ba2\u6237\u7aef: HTTP\/1.1 303 See Other<br \/>\n   Location: \/success-page<br \/>\n   Content-Type: text\/html<\/p>\n<p>   &lt;html&gt;&#8230;&lt;\/html&gt;<\/p>\n<p>3. \u5ba2\u6237\u7aef \u2192 \u670d\u52a1\u5668: GET \/success-page<\/p>\n<p>4. \u670d\u52a1\u5668 \u2192 \u5ba2\u6237\u7aef: HTTP\/1.1 200 OK<br \/>\n   Content-Type: text\/html<\/p>\n<p>   &lt;html&gt;Thank you for your submission!&lt;\/html&gt; <\/p>\n<h4>15.2.6 \u5b9e\u9645\u5e94\u7528\u793a\u4f8b<\/h4>\n<h5>\u793a\u4f8b1&#xff1a;\u7528\u6237\u6ce8\u518c\u6d41\u7a0b<\/h5>\n<p>python<\/p>\n<p># Flask\u793a\u4f8b<br \/>\nfrom flask import Flask, request, redirect, url_for, session<\/p>\n<p>app &#061; Flask(__name__)<br \/>\napp.secret_key &#061; &#039;your-secret-key&#039;<\/p>\n<p>&#064;app.route(&#039;\/register&#039;, methods&#061;[&#039;POST&#039;])<br \/>\ndef register_user():<br \/>\n    # \u5904\u7406\u7528\u6237\u6ce8\u518c<br \/>\n    username &#061; request.form[&#039;username&#039;]<br \/>\n    email &#061; request.form[&#039;email&#039;]<br \/>\n    password &#061; request.form[&#039;password&#039;]<\/p>\n<p>    # \u4fdd\u5b58\u7528\u6237\u5230\u6570\u636e\u5e93<br \/>\n    user_id &#061; save_user_to_database(username, email, password)<\/p>\n<p>    # \u8bbe\u7f6e\u4f1a\u8bdd<br \/>\n    session[&#039;user_id&#039;] &#061; user_id<br \/>\n    session[&#039;username&#039;] &#061; username<\/p>\n<p>    # \u4f7f\u7528303\u91cd\u5b9a\u5411\u5230\u6b22\u8fce\u9875\u9762<br \/>\n    return redirect(url_for(&#039;welcome&#039;), code&#061;303)<\/p>\n<p>&#064;app.route(&#039;\/welcome&#039;)<br \/>\ndef welcome():<br \/>\n    if &#039;user_id&#039; not in session:<br \/>\n        return redirect(url_for(&#039;login&#039;))<\/p>\n<p>    return f&#039;&#039;&#039;<br \/>\n    &lt;html&gt;<br \/>\n    &lt;head&gt;&lt;title&gt;Welcome&lt;\/title&gt;&lt;\/head&gt;<br \/>\n    &lt;body&gt;<br \/>\n        &lt;h1&gt;Welcome, {session[&#039;username&#039;]}!&lt;\/h1&gt;<br \/>\n        &lt;p&gt;Your registration was successful.&lt;\/p&gt;<br \/>\n    &lt;\/body&gt;<br \/>\n    &lt;\/html&gt;<br \/>\n    &#039;&#039;&#039; <\/p>\n<h5>\u793a\u4f8b2&#xff1a;\u6587\u4ef6\u4e0a\u4f20\u5904\u7406<\/h5>\n<p>javascript<\/p>\n<p>\/\/ Node.js Express\u793a\u4f8b<br \/>\nconst express &#061; require(&#039;express&#039;);<br \/>\nconst multer &#061; require(&#039;multer&#039;);<br \/>\nconst app &#061; express();<\/p>\n<p>const upload &#061; multer({ dest: &#039;uploads\/&#039; });<\/p>\n<p>app.post(&#039;\/upload&#039;, upload.single(&#039;file&#039;), (req, res) &#061;&gt; {<br \/>\n    if (!req.file) {<br \/>\n        return res.status(400).send(&#039;No file uploaded&#039;);<br \/>\n    }<\/p>\n<p>    \/\/ \u5904\u7406\u6587\u4ef6&#xff08;\u5982\u4fdd\u5b58\u5230\u6570\u636e\u5e93\u3001\u5904\u7406\u5185\u5bb9\u7b49&#xff09;<br \/>\n    const fileInfo &#061; {<br \/>\n        filename: req.file.originalname,<br \/>\n        size: req.file.size,<br \/>\n        mimetype: req.file.mimetype,<br \/>\n        uploadedAt: new Date()<br \/>\n    };<\/p>\n<p>    \/\/ \u4fdd\u5b58\u6587\u4ef6\u4fe1\u606f<br \/>\n    saveFileInfo(fileInfo);<\/p>\n<p>    \/\/ 303\u91cd\u5b9a\u5411\u5230\u6587\u4ef6\u8be6\u60c5\u9875\u9762<br \/>\n    res.redirect(303, &#096;\/files\/${req.file.filename}&#096;);<br \/>\n});<\/p>\n<p>app.get(&#039;\/files\/:filename&#039;, (req, res) &#061;&gt; {<br \/>\n    const fileInfo &#061; getFileInfo(req.params.filename);<\/p>\n<p>    res.send(&#096;<br \/>\n        &lt;h1&gt;File Uploaded Successfully&lt;\/h1&gt;<br \/>\n        &lt;p&gt;&lt;strong&gt;Name:&lt;\/strong&gt; ${fileInfo.filename}&lt;\/p&gt;<br \/>\n        &lt;p&gt;&lt;strong&gt;Size:&lt;\/strong&gt; ${fileInfo.size} bytes&lt;\/p&gt;<br \/>\n        &lt;p&gt;&lt;strong&gt;Type:&lt;\/strong&gt; ${fileInfo.mimetype}&lt;\/p&gt;<br \/>\n        &lt;p&gt;&lt;strong&gt;Uploaded:&lt;\/strong&gt; ${fileInfo.uploadedAt}&lt;\/p&gt;<br \/>\n    &#096;);<br \/>\n}); <\/p>\n<h4>15.2.7 \u4e0ePOST\/\u91cd\u5b9a\u5411\/GET\u6a21\u5f0f&#xff08;PRG\u6a21\u5f0f&#xff09;<\/h4>\n<h5>PRG\u6a21\u5f0f\u6982\u8ff0<\/h5>\n<p>POST\/Redirect\/GET\u662f\u4e00\u79cdWeb\u5f00\u53d1\u6a21\u5f0f&#xff0c;\u7528\u4e8e\u9632\u6b62\u8868\u5355\u91cd\u590d\u63d0\u4ea4&#xff1a;<\/p>\n<li>\n<p>POST&#xff1a;\u5ba2\u6237\u7aef\u63d0\u4ea4\u8868\u5355\u6570\u636e<\/p>\n<\/li>\n<li>\n<p>Redirect&#xff1a;\u670d\u52a1\u5668\u5904\u7406\u6570\u636e\u540e\u8fd4\u56de\u91cd\u5b9a\u5411&#xff08;\u901a\u5e38303&#xff09;<\/p>\n<\/li>\n<li>\n<p>GET&#xff1a;\u5ba2\u6237\u7aef\u83b7\u53d6\u7ed3\u679c\u9875\u9762<\/p>\n<\/li>\n<h5>PRG\u6a21\u5f0f\u7684\u4f18\u52bf<\/h5>\n<li>\n<p>\u9632\u6b62\u91cd\u590d\u63d0\u4ea4&#xff1a;\u5237\u65b0GET\u8bf7\u6c42\u4e0d\u4f1a\u91cd\u65b0\u63d0\u4ea4\u8868\u5355<\/p>\n<\/li>\n<li>\n<p>\u4e66\u7b7e\u53cb\u597d&#xff1a;\u7ed3\u679c\u9875\u9762\u53ef\u4ee5\u6dfb\u52a0\u5230\u4e66\u7b7e<\/p>\n<\/li>\n<li>\n<p>\u540e\u9000\u6309\u94ae\u53cb\u597d&#xff1a;\u7528\u6237\u53ef\u4ee5\u4f7f\u7528\u540e\u9000\u6309\u94ae\u800c\u4e0d\u89e6\u53d1\u8b66\u544a<\/p>\n<\/li>\n<li>\n<p>\u6e05\u6670\u7684URL&#xff1a;\u7ed3\u679c\u9875\u9762\u6709\u72ec\u7acb\u7684URL<\/p>\n<\/li>\n<h5>PRG\u6a21\u5f0f\u5b9e\u73b0<\/h5>\n<p>python<\/p>\n<p># Django\u793a\u4f8b<br \/>\nfrom django.shortcuts import render, redirect<br \/>\nfrom django.views.decorators.http import require_POST<\/p>\n<p>&#064;require_POST<br \/>\ndef process_order(request):<br \/>\n    # \u5904\u7406\u8ba2\u5355\u903b\u8f91<br \/>\n    order_id &#061; create_order(request.POST)<\/p>\n<p>    # \u8bbe\u7f6e\u95ea\u5b58\u6d88\u606f<br \/>\n    from django.contrib import messages<br \/>\n    messages.success(request, &#039;Order placed successfully!&#039;)<\/p>\n<p>    # 303\u91cd\u5b9a\u5411\u5230\u8ba2\u5355\u786e\u8ba4\u9875\u9762<br \/>\n    return redirect(&#039;order_confirmation&#039;, order_id&#061;order_id)<\/p>\n<p>def order_confirmation(request, order_id):<br \/>\n    # \u83b7\u53d6\u8ba2\u5355\u4fe1\u606f<br \/>\n    order &#061; get_order(order_id)<\/p>\n<p>    # \u663e\u793a\u786e\u8ba4\u9875\u9762<br \/>\n    return render(request, &#039;orders\/confirmation.html&#039;, {<br \/>\n        &#039;order&#039;: order<br \/>\n    }) <\/p>\n<h4>15.2.8 \u4e0e302\u72b6\u6001\u7801\u7684\u533a\u522b<\/h4>\n<table>\n<tr>\u7279\u6027303 See Other302 Found<\/tr>\n<tbody>\n<tr>\n<td>HTTP\u7248\u672c<\/td>\n<td>HTTP\/1.1\u5f15\u5165<\/td>\n<td>HTTP\/1.0\u5df2\u6709<\/td>\n<\/tr>\n<tr>\n<td>\u65b9\u6cd5\u5904\u7406<\/td>\n<td>\u5fc5\u987b\u6539\u4e3aGET<\/td>\n<td>\u5e94\u8be5\u6539\u4e3aGET&#xff08;\u4f46\u4e0d\u5f3a\u5236&#xff09;<\/td>\n<\/tr>\n<tr>\n<td>\u8bed\u4e49\u6e05\u6670\u5ea6<\/td>\n<td>\u660e\u786e<\/td>\n<td>\u6a21\u7cca<\/td>\n<\/tr>\n<tr>\n<td>\u5178\u578b\u7528\u9014<\/td>\n<td>POST\u540e\u7684\u91cd\u5b9a\u5411<\/td>\n<td>\u4e34\u65f6\u91cd\u5b9a\u5411<\/td>\n<\/tr>\n<tr>\n<td>\u7f13\u5b58\u884c\u4e3a<\/td>\n<td>\u901a\u5e38\u4e0d\u7f13\u5b58<\/td>\n<td>\u901a\u5e38\u4e0d\u7f13\u5b58<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h4>15.2.9 \u5ba2\u6237\u7aef\u5904\u7406<\/h4>\n<h5>\u6d4f\u89c8\u5668\u884c\u4e3a<\/h5>\n<p>\u73b0\u4ee3\u6d4f\u89c8\u5668\u5bf9303\u7684\u5904\u7406&#xff1a;<\/p>\n<li>\n<p>\u81ea\u52a8\u8ddf\u968fLocation\u5934\u91cd\u5b9a\u5411<\/p>\n<\/li>\n<li>\n<p>\u4f7f\u7528GET\u65b9\u6cd5\u8bf7\u6c42\u65b0URL<\/p>\n<\/li>\n<li>\n<p>\u4e0d\u643a\u5e26\u539f\u59cb\u8bf7\u6c42\u7684\u6b63\u6587<\/p>\n<\/li>\n<li>\n<p>\u53ef\u80fd\u643a\u5e26\u539f\u59cb\u8bf7\u6c42\u7684\u67d0\u4e9b\u5934&#xff08;\u5982Referer&#xff09;<\/p>\n<\/li>\n<h5>JavaScript Fetch API\u5904\u7406<\/h5>\n<p>javascript<\/p>\n<p>\/\/ \u4f7f\u7528Fetch API\u5904\u7406303\u91cd\u5b9a\u5411<br \/>\nasync function submitForm(formData) {<br \/>\n    try {<br \/>\n        const response &#061; await fetch(&#039;\/api\/submit&#039;, {<br \/>\n            method: &#039;POST&#039;,<br \/>\n            body: formData,<br \/>\n            redirect: &#039;follow&#039; \/\/ \u81ea\u52a8\u8ddf\u968f\u91cd\u5b9a\u5411<br \/>\n        });<\/p>\n<p>        \/\/ \u6ce8\u610f&#xff1a;\u5982\u679c\u8bbe\u7f6e\u4e86redirect: &#039;follow&#039;<br \/>\n        \/\/ \u6700\u7ec8\u7684response\u662f\u91cd\u5b9a\u5411\u540e\u7684\u54cd\u5e94<br \/>\n        const result &#061; await response.text();<br \/>\n        console.log(&#039;Result:&#039;, result);<\/p>\n<p>    } catch (error) {<br \/>\n        console.error(&#039;Error:&#039;, error);<br \/>\n    }<br \/>\n}<\/p>\n<p>\/\/ \u624b\u52a8\u5904\u7406\u91cd\u5b9a\u5411<br \/>\nasync function submitFormManual(formData) {<br \/>\n    const response &#061; await fetch(&#039;\/api\/submit&#039;, {<br \/>\n        method: &#039;POST&#039;,<br \/>\n        body: formData,<br \/>\n        redirect: &#039;manual&#039; \/\/ \u4e0d\u81ea\u52a8\u8ddf\u968f\u91cd\u5b9a\u5411<br \/>\n    });<\/p>\n<p>    if (response.status &#061;&#061;&#061; 303) {<br \/>\n        const redirectUrl &#061; response.headers.get(&#039;Location&#039;);<br \/>\n        \/\/ \u624b\u52a8\u8fdb\u884cGET\u8bf7\u6c42<br \/>\n        const getResponse &#061; await fetch(redirectUrl);<br \/>\n        const result &#061; await getResponse.json();<br \/>\n        return result;<br \/>\n    }<\/p>\n<p>    return await response.json();<br \/>\n} <\/p>\n<h4>15.2.10 \u7f13\u5b58\u6ce8\u610f\u4e8b\u9879<\/h4>\n<p>303\u54cd\u5e94\u901a\u5e38\u4e0d\u5e94\u8be5\u88ab\u7f13\u5b58&#xff0c;\u4f46\u53ef\u4ee5\u8bbe\u7f6e\u7279\u5b9a\u7f13\u5b58\u5934&#xff1a;<\/p>\n<p>http<\/p>\n<p>HTTP\/1.1 303 See Other<br \/>\nLocation: \/new-location<br \/>\nCache-Control: no-cache, no-store, must-revalidate<br \/>\nPragma: no-cache<br \/>\nExpires: 0 <\/p>\n<p>\u5982\u679c\u786e\u5b9e\u9700\u8981\u7f13\u5b58&#xff08;\u7f55\u89c1\u60c5\u51b5&#xff09;&#xff1a;<\/p>\n<p>http<\/p>\n<p>HTTP\/1.1 303 See Other<br \/>\nLocation: \/new-location<br \/>\nCache-Control: public, max-age&#061;3600<br \/>\nVary: Cookie, Authorization <\/p>\n<h4>15.2.11 \u5b89\u5168\u8003\u8651<\/h4>\n<h5>1. \u5f00\u653e\u91cd\u5b9a\u5411\u98ce\u9669<\/h5>\n<p>python<\/p>\n<p># \u4e0d\u5b89\u5168\u7684\u5b9e\u73b0 &#8211; \u53ef\u80fd\u88ab\u7528\u4e8e\u9493\u9c7c\u653b\u51fb<br \/>\n&#064;app.route(&#039;\/redirect&#039;)<br \/>\ndef unsafe_redirect():<br \/>\n    target &#061; request.args.get(&#039;url&#039;)<br \/>\n    # \u5371\u9669&#xff1a;\u76f4\u63a5\u91cd\u5b9a\u5411\u5230\u7528\u6237\u63d0\u4f9b\u7684URL<br \/>\n    return redirect(target, code&#061;303)<\/p>\n<p># \u5b89\u5168\u7684\u5b9e\u73b0<br \/>\n&#064;app.route(&#039;\/safe-redirect&#039;)<br \/>\ndef safe_redirect():<br \/>\n    target &#061; request.args.get(&#039;url&#039;)<\/p>\n<p>    # \u9a8c\u8bc1\u76ee\u6807URL\u662f\u5426\u5728\u767d\u540d\u5355\u5185<br \/>\n    allowed_domains &#061; [&#039;example.com&#039;, &#039;trusted-site.org&#039;]<\/p>\n<p>    from urllib.parse import urlparse<br \/>\n    parsed_url &#061; urlparse(target)<\/p>\n<p>    if parsed_url.netloc in allowed_domains:<br \/>\n        return redirect(target, code&#061;303)<br \/>\n    else:<br \/>\n        return redirect(&#039;\/default-safe-page&#039;, code&#061;303) <\/p>\n<h5>2. CSRF\u4fdd\u62a4<\/h5>\n<p>\u4f7f\u7528303\u65f6\u4ecd\u9700\u8003\u8651CSRF\u9632\u62a4&#xff1a;<\/p>\n<p>python<\/p>\n<p># Django\u793a\u4f8b &#8211; \u7ed3\u5408CSRF\u4fdd\u62a4<br \/>\nfrom django.views.decorators.csrf import csrf_exempt, csrf_protect<\/p>\n<p>&#064;csrf_protect<br \/>\ndef process_payment(request):<br \/>\n    if request.method &#061;&#061; &#039;POST&#039;:<br \/>\n        # \u9a8c\u8bc1CSRF\u4ee4\u724c<br \/>\n        # \u5904\u7406\u652f\u4ed8<br \/>\n        payment_id &#061; process_payment_transaction(request.POST)<\/p>\n<p>        # 303\u91cd\u5b9a\u5411\u5230\u6536\u636e\u9875\u9762<br \/>\n        return redirect(f&#039;\/receipt\/{payment_id}&#039;, code&#061;303)<\/p>\n<p>    return render(request, &#039;payment_form.html&#039;) <\/p>\n<h4>15.2.12 \u6700\u4f73\u5b9e\u8df5<\/h4>\n<li>\n<p>\u59cb\u7ec8\u7528\u4e8ePOST\u540e\u91cd\u5b9a\u5411&#xff1a;\u786e\u4fdd\u8868\u5355\u6570\u636e\u4e0d\u4f1a\u56e0\u5237\u65b0\u800c\u91cd\u590d\u63d0\u4ea4<\/p>\n<\/li>\n<li>\n<p>\u8bbe\u7f6e\u9002\u5f53\u7684\u7f13\u5b58\u5934&#xff1a;\u901a\u5e38\u8bbe\u4e3ano-cache<\/p>\n<\/li>\n<li>\n<p>\u9a8c\u8bc1\u91cd\u5b9a\u5411\u76ee\u6807&#xff1a;\u9632\u6b62\u5f00\u653e\u91cd\u5b9a\u5411\u6f0f\u6d1e<\/p>\n<\/li>\n<li>\n<p>\u63d0\u4f9b\u6709\u610f\u4e49\u7684Location URL&#xff1a;\u7ed3\u679c\u9875\u9762\u5e94\u53cd\u6620\u64cd\u4f5c\u7ed3\u679c<\/p>\n<\/li>\n<li>\n<p>\u8003\u8651\u7528\u6237\u4f53\u9a8c&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u5728\u91cd\u5b9a\u5411\u524d\u53ef\u8bbe\u7f6e\u95ea\u5b58\u6d88\u606f<\/p>\n<\/li>\n<li>\n<p>\u786e\u4fdd\u91cd\u5b9a\u5411\u76ee\u6807\u5feb\u901f\u52a0\u8f7d<\/p>\n<\/li>\n<li>\n<p>\u4e3aJavaScript\u5ba2\u6237\u7aef\u63d0\u4f9bAPI\u517c\u5bb9\u6027<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<h5>\u5b8c\u6574\u793a\u4f8b&#xff1a;\u7535\u5b50\u5546\u52a1\u8ba2\u5355\u5904\u7406<\/h5>\n<p>python<\/p>\n<p># Flask\u5b8c\u6574\u793a\u4f8b<br \/>\nfrom flask import Flask, request, redirect, url_for, flash, session<br \/>\nfrom flask.views import MethodView<\/p>\n<p>app &#061; Flask(__name__)<br \/>\napp.secret_key &#061; &#039;your-secret-key-here&#039;<\/p>\n<p>class CheckoutView(MethodView):<br \/>\n    def get(self):<br \/>\n        &#034;&#034;&#034;\u663e\u793a\u7ed3\u8d26\u9875\u9762&#034;&#034;&#034;<br \/>\n        cart &#061; session.get(&#039;cart&#039;, [])<br \/>\n        total &#061; sum(item[&#039;price&#039;] * item[&#039;quantity&#039;] for item in cart)<\/p>\n<p>        return f&#039;&#039;&#039;<br \/>\n        &lt;h1&gt;Checkout&lt;\/h1&gt;<br \/>\n        &lt;p&gt;Total: ${total:.2f}&lt;\/p&gt;<br \/>\n        &lt;form method&#061;&#034;post&#034;&gt;<br \/>\n            &lt;input type&#061;&#034;text&#034; name&#061;&#034;card_number&#034; placeholder&#061;&#034;Card Number&#034; required&gt;<br \/>\n            &lt;input type&#061;&#034;text&#034; name&#061;&#034;expiry&#034; placeholder&#061;&#034;MM\/YY&#034; required&gt;<br \/>\n            &lt;input type&#061;&#034;text&#034; name&#061;&#034;cvc&#034; placeholder&#061;&#034;CVC&#034; required&gt;<br \/>\n            &lt;button type&#061;&#034;submit&#034;&gt;Place Order&lt;\/button&gt;<br \/>\n        &lt;\/form&gt;<br \/>\n        &#039;&#039;&#039;<\/p>\n<p>    def post(self):<br \/>\n        &#034;&#034;&#034;\u5904\u7406\u8ba2\u5355\u5e76\u91cd\u5b9a\u5411&#034;&#034;&#034;<br \/>\n        # \u9a8c\u8bc1\u652f\u4ed8\u4fe1\u606f<br \/>\n        card_number &#061; request.form.get(&#039;card_number&#039;)<br \/>\n        expiry &#061; request.form.get(&#039;expiry&#039;)<br \/>\n        cvc &#061; request.form.get(&#039;cvc&#039;)<\/p>\n<p>        if not validate_payment(card_number, expiry, cvc):<br \/>\n            flash(&#039;Invalid payment information&#039;, &#039;error&#039;)<br \/>\n            return redirect(url_for(&#039;checkout&#039;))<\/p>\n<p>        # \u521b\u5efa\u8ba2\u5355<br \/>\n        cart &#061; session.get(&#039;cart&#039;, [])<br \/>\n        order_id &#061; create_order(cart, request.form)<\/p>\n<p>        # \u6e05\u7a7a\u8d2d\u7269\u8f66<br \/>\n        session.pop(&#039;cart&#039;, None)<\/p>\n<p>        # \u8bbe\u7f6e\u6210\u529f\u6d88\u606f<br \/>\n        flash(&#039;Order placed successfully!&#039;, &#039;success&#039;)<\/p>\n<p>        # 303\u91cd\u5b9a\u5411\u5230\u8ba2\u5355\u786e\u8ba4\u9875\u9762<br \/>\n        return redirect(url_for(&#039;order_confirmation&#039;, order_id&#061;order_id), code&#061;303)<\/p>\n<p>class OrderConfirmationView(MethodView):<br \/>\n    def get(self, order_id):<br \/>\n        &#034;&#034;&#034;\u663e\u793a\u8ba2\u5355\u786e\u8ba4\u9875\u9762&#034;&#034;&#034;<br \/>\n        order &#061; get_order(order_id)<\/p>\n<p>        return f&#039;&#039;&#039;<br \/>\n        &lt;h1&gt;Order Confirmation&lt;\/h1&gt;<br \/>\n        &lt;p&gt;Order ID: {order[&#039;id&#039;]}&lt;\/p&gt;<br \/>\n        &lt;p&gt;Status: {order[&#039;status&#039;]}&lt;\/p&gt;<br \/>\n        &lt;p&gt;Total: ${order[&#039;total&#039;]:.2f}&lt;\/p&gt;<br \/>\n        &lt;a href&#061;&#034;\/&#034;&gt;Continue Shopping&lt;\/a&gt;<br \/>\n        &#039;&#039;&#039;<\/p>\n<p># \u6ce8\u518c\u8def\u7531<br \/>\napp.add_url_rule(&#039;\/checkout&#039;, view_func&#061;CheckoutView.as_view(&#039;checkout&#039;))<br \/>\napp.add_url_rule(&#039;\/order\/&lt;order_id&gt;&#039;, view_func&#061;OrderConfirmationView.as_view(&#039;order_confirmation&#039;)) <\/p>\n<hr \/>\n<h3>15.3 305 Use Proxy&#xff08;\u4f7f\u7528\u4ee3\u7406&#xff09;<\/h3>\n<h4>15.3.1 \u5b9a\u4e49\u4e0e\u8bed\u4e49<\/h4>\n<p>305 Use Proxy\u00a0\u72b6\u6001\u7801\u8868\u793a\u8bf7\u6c42\u7684\u8d44\u6e90\u5fc5\u987b\u901a\u8fc7\u4ee3\u7406\u8bbf\u95ee\u3002\u4ee3\u7406\u7684\u4f4d\u7f6e\u5728\u54cd\u5e94\u7684Location\u5934\u5b57\u6bb5\u4e2d\u6307\u5b9a\u3002\u5ba2\u6237\u7aef\u5e94\u8be5\u91cd\u65b0\u53d1\u9001\u8bf7\u6c42\u5230\u6307\u5b9a\u7684\u4ee3\u7406\u3002<\/p>\n<h4>15.3.2 \u5386\u53f2\u80cc\u666f<\/h4>\n<ul>\n<li>\n<p>\u5f15\u5165&#xff1a;\u5728HTTP\/1.1\u4e2d\u9996\u6b21\u5b9a\u4e49<\/p>\n<\/li>\n<li>\n<p>\u5e9f\u5f03&#xff1a;\u7531\u4e8e\u5b89\u5168\u8003\u8651&#xff0c;\u5728\u540e\u7eed\u89c4\u8303\u4e2d\u88ab\u6807\u8bb0\u4e3a&#034;\u5df2\u5e9f\u5f03&#034;<\/p>\n<\/li>\n<li>\n<p>\u73b0\u72b6&#xff1a;\u73b0\u4ee3\u6d4f\u89c8\u5668\u548c\u5ba2\u6237\u7aef\u901a\u5e38\u4e0d\u652f\u6301\u6b64\u72b6\u6001\u7801<\/p>\n<\/li>\n<\/ul>\n<h4>15.3.3 \u5e9f\u5f03\u539f\u56e0<\/h4>\n<li>\n<p>\u5b89\u5168\u98ce\u9669&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u53ef\u80fd\u88ab\u7528\u4e8e\u4e2d\u95f4\u4eba\u653b\u51fb<\/p>\n<\/li>\n<li>\n<p>\u4ee3\u7406\u53ef\u80fd\u8bb0\u5f55\u6216\u4fee\u6539\u654f\u611f\u6570\u636e<\/p>\n<\/li>\n<li>\n<p>\u7f3a\u4e4f\u4ee3\u7406\u8eab\u4efd\u9a8c\u8bc1\u673a\u5236<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p>\u914d\u7f6e\u95ee\u9898&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u4e0e\u7cfb\u7edf\u7ea7\u4ee3\u7406\u914d\u7f6e\u51b2\u7a81<\/p>\n<\/li>\n<li>\n<p>\u96be\u4ee5\u7ba1\u7406\u591a\u4e2a\u4ee3\u7406\u8bbe\u7f6e<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p>\u5b9e\u9645\u4f7f\u7528\u6709\u9650&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u5f88\u5c11\u88ab\u5b9e\u9645\u90e8\u7f72<\/p>\n<\/li>\n<li>\n<p>\u66ff\u4ee3\u65b9\u6848\u66f4\u53d7\u6b22\u8fce&#xff08;\u5982PAC\u6587\u4ef6\u3001WPAD&#xff09;<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<h4>15.3.4 \u6280\u672f\u89c4\u8303<\/h4>\n<h5>\u72b6\u6001\u884c<\/h5>\n<p>text<\/p>\n<p>HTTP\/1.1 305 Use Proxy <\/p>\n<h5>\u5fc5\u9700\u7684\u5934\u5b57\u6bb5<\/h5>\n<ul>\n<li>\n<p>Location&#xff1a;\u5fc5\u987b\u5305\u542b\u4ee3\u7406\u7684URI<\/p>\n<\/li>\n<\/ul>\n<h5>\u54cd\u5e94\u5b9e\u4f53<\/h5>\n<p>\u5e94\u8be5\u5305\u542b\u4e00\u4e2a\u7b80\u77ed\u7684\u4eba\u7c7b\u53ef\u8bfb\u6d88\u606f&#xff0c;\u89e3\u91ca\u9700\u8981\u4f7f\u7528\u4ee3\u7406<\/p>\n<h4>15.3.5 \u5de5\u4f5c\u539f\u7406<\/h4>\n<h5>\u6b63\u5e38\u6d41\u7a0b<\/h5>\n<p>text<\/p>\n<p>\u5ba2\u6237\u7aef \u2192 \u670d\u52a1\u5668: GET \/resource<br \/>\n\u670d\u52a1\u5668 \u2192 \u5ba2\u6237\u7aef: 305 Use Proxy<br \/>\n                Location: http:\/\/proxy.example.com:8080<br \/>\n\u5ba2\u6237\u7aef \u2192 \u4ee3\u7406: GET http:\/\/proxy.example.com:8080\/resource<br \/>\n\u4ee3\u7406 \u2192 \u670d\u52a1\u5668: GET \/resource<br \/>\n\u670d\u52a1\u5668 \u2192 \u4ee3\u7406: 200 OK<br \/>\n\u4ee3\u7406 \u2192 \u5ba2\u6237\u7aef: 200 OK <\/p>\n<h5>\u793a\u4f8b\u5b9e\u73b0<\/h5>\n<p>python<\/p>\n<p># \u670d\u52a1\u5668\u7aef\u5b9e\u73b0&#xff08;\u7406\u8bba\u4e0a&#xff09;<br \/>\nfrom flask import Flask, request, make_response<\/p>\n<p>app &#061; Flask(__name__)<\/p>\n<p>&#064;app.route(&#039;\/restricted-resource&#039;)<br \/>\ndef get_restricted_resource():<br \/>\n    # \u68c0\u67e5\u5ba2\u6237\u7aefIP\u662f\u5426\u5728\u5141\u8bb8\u7684\u76f4\u63a5\u8bbf\u95ee\u5217\u8868\u4e2d<br \/>\n    client_ip &#061; request.remote_addr<\/p>\n<p>    if client_ip not in ALLOWED_DIRECT_IPS:<br \/>\n        # \u9700\u8981\u4ee3\u7406\u8bbf\u95ee<br \/>\n        response &#061; make_response(&#039;&#039;&#039;<br \/>\n        &lt;html&gt;<br \/>\n        &lt;head&gt;&lt;title&gt;Use Proxy Required&lt;\/title&gt;&lt;\/head&gt;<br \/>\n        &lt;body&gt;<br \/>\n            &lt;h1&gt;Proxy Required&lt;\/h1&gt;<br \/>\n            &lt;p&gt;This resource must be accessed through the corporate proxy.&lt;\/p&gt;<br \/>\n            &lt;p&gt;Proxy URL: http:\/\/proxy.corporate.com:3128&lt;\/p&gt;<br \/>\n        &lt;\/body&gt;<br \/>\n        &lt;\/html&gt;<br \/>\n        &#039;&#039;&#039;)<br \/>\n        response.status_code &#061; 305<br \/>\n        response.headers[&#039;Location&#039;] &#061; &#039;http:\/\/proxy.corporate.com:3128&#039;<br \/>\n        return response<\/p>\n<p>    # \u76f4\u63a5\u8bbf\u95ee\u5141\u8bb8<br \/>\n    return &#039;Restricted resource content&#039; <\/p>\n<h4>15.3.6 \u73b0\u4ee3\u66ff\u4ee3\u65b9\u6848<\/h4>\n<h5>1. \u4ee3\u7406\u81ea\u52a8\u914d\u7f6e&#xff08;PAC&#xff09;<\/h5>\n<p>javascript<\/p>\n<p>\/\/ proxy.pac \u6587\u4ef6<br \/>\nfunction FindProxyForURL(url, host) {<br \/>\n    \/\/ \u5bf9\u5185\u90e8\u8d44\u6e90\u4f7f\u7528\u4ee3\u7406<br \/>\n    if (shExpMatch(host, &#034;*.internal.company.com&#034;)) {<br \/>\n        return &#034;PROXY proxy.internal.company.com:8080&#034;;<br \/>\n    }<\/p>\n<p>    \/\/ \u5bf9\u5176\u4ed6\u8d44\u6e90\u76f4\u63a5\u8fde\u63a5<br \/>\n    return &#034;DIRECT&#034;;<br \/>\n} <\/p>\n<h5>2. Web\u4ee3\u7406\u81ea\u52a8\u53d1\u73b0\u534f\u8bae&#xff08;WPAD&#xff09;<\/h5>\n<p>http<\/p>\n<p># DHCP\u6216DNS\u914d\u7f6e<br \/>\noption wpad code 252 &#061; text;<br \/>\noption wpad &#034;http:\/\/config.company.com\/wpad.dat&#034;; <\/p>\n<h5>3. \u914d\u7f6e\u7ba1\u7406\u5de5\u5177<\/h5>\n<ul>\n<li>\n<p>\u7ec4\u7b56\u7565&#xff08;Windows&#xff09;<\/p>\n<\/li>\n<li>\n<p>\u79fb\u52a8\u8bbe\u5907\u7ba1\u7406&#xff08;MDM&#xff09;<\/p>\n<\/li>\n<li>\n<p>\u914d\u7f6e\u7ba1\u7406\u6570\u636e\u5e93&#xff08;CMDB&#xff09;<\/p>\n<\/li>\n<\/ul>\n<h4>15.3.7 \u5b89\u5168\u8003\u8651\u4e0e\u98ce\u9669<\/h4>\n<h5>\u6f5c\u5728\u653b\u51fb\u573a\u666f<\/h5>\n<p>python<\/p>\n<p># \u6076\u610f\u670d\u52a1\u5668\u53ef\u80fd\u5c1d\u8bd5\u91cd\u5b9a\u5411\u5230\u6076\u610f\u4ee3\u7406<br \/>\n&#064;app.route(&#039;\/malicious&#039;)<br \/>\ndef malicious_redirect():<br \/>\n    response &#061; make_response()<br \/>\n    response.status_code &#061; 305<br \/>\n    # \u91cd\u5b9a\u5411\u5230\u653b\u51fb\u8005\u63a7\u5236\u7684\u4ee3\u7406<br \/>\n    response.headers[&#039;Location&#039;] &#061; &#039;http:\/\/evil-proxy.com:8080&#039;<br \/>\n    return response <\/p>\n<h5>\u9632\u62a4\u63aa\u65bd<\/h5>\n<p>python<\/p>\n<p># \u5ba2\u6237\u7aef\u9632\u62a4\u793a\u4f8b<br \/>\nimport requests<br \/>\nfrom urllib.parse import urlparse<\/p>\n<p>class SafeHTTPClient:<br \/>\n    def __init__(self):<br \/>\n        self.allowed_proxies &#061; [<br \/>\n            &#039;proxy.company.com&#039;,<br \/>\n            &#039;backup-proxy.company.com&#039;<br \/>\n        ]<\/p>\n<p>    def request(self, method, url, **kwargs):<br \/>\n        response &#061; requests.request(method, url, **kwargs)<\/p>\n<p>        # \u68c0\u67e5305\u54cd\u5e94<br \/>\n        if response.status_code &#061;&#061; 305:<br \/>\n            proxy_url &#061; response.headers.get(&#039;Location&#039;)<\/p>\n<p>            if proxy_url:<br \/>\n                parsed &#061; urlparse(proxy_url)<br \/>\n                proxy_host &#061; parsed.hostname<\/p>\n<p>                # \u9a8c\u8bc1\u4ee3\u7406\u662f\u5426\u53ef\u4fe1<br \/>\n                if proxy_host in self.allowed_proxies:<br \/>\n                    # \u4f7f\u7528\u53ef\u4fe1\u4ee3\u7406\u91cd\u65b0\u8bf7\u6c42<br \/>\n                    proxies &#061; {<br \/>\n                        &#039;http&#039;: proxy_url,<br \/>\n                        &#039;https&#039;: proxy_url<br \/>\n                    }<br \/>\n                    return requests.request(method, url, proxies&#061;proxies, **kwargs)<br \/>\n                else:<br \/>\n                    raise SecurityError(f&#039;Untrusted proxy: {proxy_host}&#039;)<\/p>\n<p>        return response <\/p>\n<h4>15.3.8 \u5b9e\u9645\u5e94\u7528\u6848\u4f8b&#xff08;\u5386\u53f2&#xff09;<\/h4>\n<h5>\u6848\u4f8b1&#xff1a;\u4f01\u4e1a\u7f51\u7edc\u8bbf\u95ee\u63a7\u5236<\/h5>\n<p>text<\/p>\n<p># 2000\u5e74\u4ee3\u65e9\u671f\u7684\u4f01\u4e1a\u7f51\u7edc\u8bbe\u7f6e<br \/>\n# \u5185\u90e8\u670d\u52a1\u5668\u914d\u7f6e<br \/>\n&lt;VirtualHost *:80&gt;<br \/>\n    ServerName internal-app.company.com<br \/>\n    # \u53ea\u5141\u8bb8\u901a\u8fc7\u4f01\u4e1a\u4ee3\u7406\u8bbf\u95ee<br \/>\n    RewriteEngine On<br \/>\n    RewriteCond %{REMOTE_ADDR} !^10\\\\.10\\\\.1\\\\.100$  # \u4ee3\u7406\u670d\u52a1\u5668IP<br \/>\n    RewriteRule ^ &#8211; [R&#061;305]<br \/>\n    Header always set Location &#034;http:\/\/corporate-proxy:3128&#034;<br \/>\n&lt;\/VirtualHost&gt; <\/p>\n<h5>\u6848\u4f8b2&#xff1a;\u5b66\u672f\u673a\u6784\u8d44\u6e90\u63a7\u5236<\/h5>\n<p>text<\/p>\n<p># \u5927\u5b66\u56fe\u4e66\u9986\u6570\u636e\u5e93\u8bbf\u95ee<br \/>\n# \u53ea\u6709\u901a\u8fc7\u6821\u56ed\u4ee3\u7406\u624d\u80fd\u8bbf\u95ee\u4ed8\u8d39\u6570\u636e\u5e93<\/p>\n<p># Apache\u914d\u7f6e\u793a\u4f8b<br \/>\n&lt;Location &#034;\/research-databases&#034;&gt;<br \/>\n    # \u68c0\u67e5\u5ba2\u6237\u7aefIP<br \/>\n    SetEnvIf Remote_Addr &#034;^131\\\\.104\\\\.&#034; on_campus<br \/>\n    Order deny,allow<br \/>\n    Deny from all<br \/>\n    Allow from env&#061;on_campus<\/p>\n<p>    # \u4e0d\u5728\u6821\u56ed\u7f51\u7edc&#xff0c;\u9700\u8981\u4ee3\u7406<br \/>\n    ErrorDocument 305 &#034;Please use the campus proxy: http:\/\/library-proxy.university.edu:8080&#034;<br \/>\n&lt;\/Location&gt; <\/p>\n<h4>15.3.9 \u5ba2\u6237\u7aef\u5904\u7406<\/h4>\n<h5>\u73b0\u4ee3\u6d4f\u89c8\u5668\u884c\u4e3a<\/h5>\n<p>\u5927\u591a\u6570\u73b0\u4ee3\u6d4f\u89c8\u5668&#xff1a;<\/p>\n<li>\n<p>\u5ffd\u7565305\u54cd\u5e94&#xff1a;\u4e0d\u81ea\u52a8\u914d\u7f6e\u4ee3\u7406<\/p>\n<\/li>\n<li>\n<p>\u663e\u793a\u9519\u8bef\u9875\u9762&#xff1a;\u5c06305\u89c6\u4e3a\u9519\u8bef<\/p>\n<\/li>\n<li>\n<p>\u4e0d\u8ddf\u968fLocation\u5934&#xff1a;\u51fa\u4e8e\u5b89\u5168\u8003\u8651<\/p>\n<\/li>\n<h5>\u81ea\u5b9a\u4e49\u5ba2\u6237\u7aef\u5b9e\u73b0<\/h5>\n<p>java<\/p>\n<p>\/\/ Java HttpClient\u793a\u4f8b&#xff08;\u5c55\u793a\u5982\u4f55\u5904\u7406305&#xff09;<br \/>\nimport java.net.http.*;<br \/>\nimport java.net.URI;<\/p>\n<p>public class CustomHttpClient {<br \/>\n    private final HttpClient client;<br \/>\n    private final List&lt;String&gt; trustedProxies;<\/p>\n<p>    public CustomHttpClient() {<br \/>\n        this.client &#061; HttpClient.newBuilder()<br \/>\n            .followRedirects(HttpClient.Redirect.NEVER)  \/\/ \u4e0d\u81ea\u52a8\u91cd\u5b9a\u5411<br \/>\n            .build();<\/p>\n<p>        this.trustedProxies &#061; List.of(<br \/>\n            &#034;proxy.company.com&#034;,<br \/>\n            &#034;backup.company.com&#034;<br \/>\n        );<br \/>\n    }<\/p>\n<p>    public HttpResponse&lt;String&gt; sendRequest(HttpRequest request)<br \/>\n            throws Exception {<\/p>\n<p>        HttpResponse&lt;String&gt; response &#061; client.send(<br \/>\n            request, HttpResponse.BodyHandlers.ofString()<br \/>\n        );<\/p>\n<p>        \/\/ \u5904\u7406305\u54cd\u5e94<br \/>\n        if (response.statusCode() &#061;&#061; 305) {<br \/>\n            Optional&lt;String&gt; proxyLocation &#061; response.headers()<br \/>\n                .firstValue(&#034;Location&#034;);<\/p>\n<p>            if (proxyLocation.isPresent()) {<br \/>\n                URI proxyUri &#061; URI.create(proxyLocation.get());<br \/>\n                String proxyHost &#061; proxyUri.getHost();<\/p>\n<p>                \/\/ \u9a8c\u8bc1\u4ee3\u7406<br \/>\n                if (isTrustedProxy(proxyHost)) {<br \/>\n                    \/\/ \u901a\u8fc7\u4ee3\u7406\u91cd\u65b0\u53d1\u9001\u8bf7\u6c42<br \/>\n                    HttpClient proxyClient &#061; HttpClient.newBuilder()<br \/>\n                        .proxy(ProxySelector.of(<br \/>\n                            new InetSocketAddress(proxyHost, proxyUri.getPort())<br \/>\n                        ))<br \/>\n                        .build();<\/p>\n<p>                    return proxyClient.send(request,<br \/>\n                        HttpResponse.BodyHandlers.ofString());<br \/>\n                } else {<br \/>\n                    throw new SecurityException(<br \/>\n                        &#034;Untrusted proxy: &#034; &#043; proxyHost);<br \/>\n                }<br \/>\n            }<br \/>\n        }<\/p>\n<p>        return response;<br \/>\n    }<\/p>\n<p>    private boolean isTrustedProxy(String hostname) {<br \/>\n        return trustedProxies.contains(hostname);<br \/>\n    }<br \/>\n} <\/p>\n<h4>15.3.10 \u66ff\u4ee3305\u7684\u73b0\u4ee3\u65b9\u6848<\/h4>\n<h5>\u65b9\u68481&#xff1a;HTTP\u72b6\u6001\u7801307\u4e0e\u4ee3\u7406\u5934<\/h5>\n<p>http<\/p>\n<p># \u670d\u52a1\u5668\u54cd\u5e94<br \/>\nHTTP\/1.1 307 Temporary Redirect<br \/>\nLocation: https:\/\/api.company.com\/v2\/resource<br \/>\nProxy-Authenticate: Basic realm&#061;&#034;Corporate Proxy&#034;<br \/>\nX-Proxy-Required: true<br \/>\nX-Proxy-Server: proxy.company.com:3128 <\/p>\n<h5>\u65b9\u68482&#xff1a;\u4f7f\u7528\u81ea\u5b9a\u4e49\u5934\u5b57\u6bb5<\/h5>\n<p>http<\/p>\n<p># API\u54cd\u5e94<br \/>\nHTTP\/1.1 403 Forbidden<br \/>\nContent-Type: application\/json<\/p>\n<p>{<br \/>\n  &#034;error&#034;: &#034;proxy_required&#034;,<br \/>\n  &#034;message&#034;: &#034;Access through corporate proxy required&#034;,<br \/>\n  &#034;proxy_configuration&#034;: {<br \/>\n    &#034;http&#034;: &#034;http:\/\/proxy.company.com:3128&#034;,<br \/>\n    &#034;https&#034;: &#034;http:\/\/proxy.company.com:3128&#034;,<br \/>\n    &#034;no_proxy&#034;: &#034;localhost,127.0.0.1,.internal&#034;<br \/>\n  },<br \/>\n  &#034;documentation&#034;: &#034;https:\/\/wiki.company.com\/proxy-setup&#034;<br \/>\n} <\/p>\n<h5>\u65b9\u68483&#xff1a;\u914d\u7f6e\u53d1\u73b0\u670d\u52a1<\/h5>\n<p>python<\/p>\n<p># \u4ee3\u7406\u914d\u7f6e\u53d1\u73b0API<br \/>\n&#064;app.route(&#039;\/api\/proxy-config&#039;)<br \/>\ndef get_proxy_config():<br \/>\n    client_ip &#061; request.remote_addr<br \/>\n    network &#061; identify_network(client_ip)<\/p>\n<p>    if network &#061;&#061; &#039;corporate&#039;:<br \/>\n        return jsonify({<br \/>\n            &#039;proxy_required&#039;: False,<br \/>\n            &#039;direct_access&#039;: True<br \/>\n        })<br \/>\n    elif network &#061;&#061; &#039;partner&#039;:<br \/>\n        return jsonify({<br \/>\n            &#039;proxy_required&#039;: True,<br \/>\n            &#039;proxy_server&#039;: &#039;partner-gateway.company.com:8080&#039;,<br \/>\n            &#039;authentication&#039;: &#039;basic&#039;,<br \/>\n            &#039;credentials_required&#039;: True<br \/>\n        })<br \/>\n    else:<br \/>\n        return jsonify({<br \/>\n            &#039;proxy_required&#039;: True,<br \/>\n            &#039;proxy_server&#039;: &#039;external-gateway.company.com:3128&#039;,<br \/>\n            &#039;authentication&#039;: &#039;certificate&#039;,<br \/>\n            &#039;certificate_url&#039;: &#039;\/api\/client-certificate&#039;<br \/>\n        }) <\/p>\n<h4>15.3.11 \u6700\u4f73\u5b9e\u8df5&#xff08;\u5982\u679c\u5fc5\u987b\u4f7f\u7528&#xff09;<\/h4>\n<p>\u867d\u7136305\u5df2\u5e9f\u5f03&#xff0c;\u4f46\u5728\u67d0\u4e9b\u53d7\u9650\u73af\u5883\u4e2d\u53ef\u80fd\u4ecd\u9700\u8981&#xff1a;<\/p>\n<li>\n<p>\u4e25\u683c\u9a8c\u8bc1\u4ee3\u7406&#xff1a;<\/p>\n<\/li>\n<p>python<\/p>\n<p># \u670d\u52a1\u5668\u7aef\u9a8c\u8bc1<br \/>\nALLOWED_PROXIES &#061; {<br \/>\n    &#039;internal-proxy.company.com&#039;: &#039;192.168.1.100&#039;,<br \/>\n    &#039;dmz-proxy.company.com&#039;: &#039;10.0.0.50&#039;<br \/>\n}<\/p>\n<p>def require_proxy_access():<br \/>\n    proxy_host &#061; get_configured_proxy()<\/p>\n<p>    if proxy_host not in ALLOWED_PROXIES:<br \/>\n        raise ConfigurationError(f&#039;Invalid proxy: {proxy_host}&#039;)<\/p>\n<p>    response &#061; make_response()<br \/>\n    response.status_code &#061; 305<br \/>\n    response.headers[&#039;Location&#039;] &#061; f&#039;http:\/\/{proxy_host}:3128&#039;<br \/>\n    response.headers[&#039;X-Proxy-Version&#039;] &#061; &#039;1.0&#039;<br \/>\n    response.headers[&#039;X-Proxy-Auth-Type&#039;] &#061; &#039;Kerberos&#039;<br \/>\n    return response <\/p>\n<li>\n<p>\u63d0\u4f9b\u5907\u7528\u8bbf\u95ee\u65b9\u5f0f&#xff1a;<\/p>\n<\/li>\n<p>html<\/p>\n<p>&lt;!&#8211; \u5728305\u54cd\u5e94\u4e2d\u63d0\u4f9b\u5907\u9009\u65b9\u6848 &#8211;&gt;<br \/>\nHTTP\/1.1 305 Use Proxy<br \/>\nLocation: http:\/\/proxy.company.com:3128<br \/>\nContent-Type: text\/html<\/p>\n<p>&lt;html&gt;<br \/>\n&lt;head&gt;&lt;title&gt;Proxy Required&lt;\/title&gt;&lt;\/head&gt;<br \/>\n&lt;body&gt;<br \/>\n    &lt;h1&gt;Proxy Access Required&lt;\/h1&gt;<br \/>\n    &lt;p&gt;This resource requires access through the corporate proxy.&lt;\/p&gt;<\/p>\n<p>    &lt;h2&gt;Automatic Configuration&lt;\/h2&gt;<br \/>\n    &lt;p&gt;The system will automatically configure your proxy.&lt;\/p&gt;<\/p>\n<p>    &lt;h2&gt;Manual Configuration&lt;\/h2&gt;<br \/>\n    &lt;p&gt;If automatic configuration fails:&lt;\/p&gt;<br \/>\n    &lt;ol&gt;<br \/>\n        &lt;li&gt;Open network settings&lt;\/li&gt;<br \/>\n        &lt;li&gt;Configure proxy: &lt;code&gt;proxy.company.com:3128&lt;\/code&gt;&lt;\/li&gt;<br \/>\n        &lt;li&gt;Authentication: Use your corporate credentials&lt;\/li&gt;<br \/>\n    &lt;\/ol&gt;<\/p>\n<p>    &lt;h2&gt;Alternative Access&lt;\/h2&gt;<br \/>\n    &lt;p&gt;&lt;a href&#061;&#034;\/vpn-instructions&#034;&gt;VPN access instructions&lt;\/a&gt;&lt;\/p&gt;<br \/>\n    &lt;p&gt;&lt;a href&#061;&#034;\/api\/access-token&#034;&gt;Request API token for direct access&lt;\/a&gt;&lt;\/p&gt;<br \/>\n&lt;\/body&gt;<br \/>\n&lt;\/html&gt; <\/p>\n<hr \/>\n<h3>15.4 306 Switch Proxy&#xff08;\u5207\u6362\u4ee3\u7406&#xff09;<\/h3>\n<h4>15.4.1 \u5b9a\u4e49\u4e0e\u5386\u53f2<\/h4>\n<p>306 Switch Proxy\u00a0\u72b6\u6001\u7801\u662f\u4e00\u4e2a\u5386\u53f2\u4e0a\u5b58\u5728\u4f46\u4ece\u672a\u6b63\u5f0f\u6210\u4e3a\u6807\u51c6\u7684\u5b9e\u9a8c\u6027\u72b6\u6001\u7801\u3002\u5b83\u5728\u65e9\u671f\u7684HTTP\u8349\u6848\u4e2d\u77ed\u6682\u51fa\u73b0&#xff0c;\u4f46\u4ece\u672a\u88ab\u7eb3\u5165\u4efb\u4f55\u6b63\u5f0fRFC\u6807\u51c6\u3002<\/p>\n<h4>15.4.2 \u5386\u53f2\u80cc\u666f<\/h4>\n<ul>\n<li>\n<p>\u8d77\u6e90&#xff1a;\u5728HTTP\/1.1\u7684\u65e9\u671f\u8349\u6848\u4e2d\u51fa\u73b0<\/p>\n<\/li>\n<li>\n<p>\u76ee\u7684&#xff1a;\u6307\u793a\u5ba2\u6237\u7aef\u5207\u6362\u5230\u4e0d\u540c\u7684\u4ee3\u7406\u670d\u52a1\u5668<\/p>\n<\/li>\n<li>\n<p>\u72b6\u6001&#xff1a;\u4ece\u672a\u6b63\u5f0f\u6807\u51c6\u5316&#xff0c;\u5df2\u88ab\u5f03\u7528<\/p>\n<\/li>\n<li>\n<p>\u73b0\u72b6&#xff1a;\u4e0d\u5e94\u5728\u73b0\u4ee3\u5e94\u7528\u4e2d\u4f7f\u7528<\/p>\n<\/li>\n<\/ul>\n<h4>15.4.3 \u7406\u8bba\u4e0a\u7684\u8bed\u4e49<\/h4>\n<p>\u5982\u679c\u66fe\u7ecf\u6807\u51c6\u5316&#xff0c;306\u53ef\u80fd\u7528\u4e8e&#xff1a;<\/p>\n<li>\n<p>\u8d1f\u8f7d\u5747\u8861&#xff1a;\u5c06\u5ba2\u6237\u7aef\u91cd\u5b9a\u5411\u5230\u53e6\u4e00\u4e2a\u4ee3\u7406<\/p>\n<\/li>\n<li>\n<p>\u6545\u969c\u8f6c\u79fb&#xff1a;\u5f53\u524d\u4ee3\u7406\u4e0d\u53ef\u7528\u65f6\u5207\u6362\u5230\u5907\u7528\u4ee3\u7406<\/p>\n<\/li>\n<li>\n<p>\u5730\u7406\u4f4d\u7f6e\u4f18\u5316&#xff1a;\u91cd\u5b9a\u5411\u5230\u5730\u7406\u4e0a\u66f4\u8fd1\u7684\u4ee3\u7406<\/p>\n<\/li>\n<h4>15.4.4 \u4e0e\u73b0\u4ee3\u66ff\u4ee3\u65b9\u6848\u7684\u5bf9\u6bd4<\/h4>\n<table>\n<tr>\u529f\u80fd\u9700\u6c42\u7406\u8bba\u4e0a\u7684306\u65b9\u6848\u73b0\u4ee3\u66ff\u4ee3\u65b9\u6848<\/tr>\n<tbody>\n<tr>\n<td>\u8d1f\u8f7d\u5747\u8861<\/td>\n<td>306\u91cd\u5b9a\u5411<\/td>\n<td>DNS\u8f6e\u8be2\u3001Anycast\u3001\u8d1f\u8f7d\u5747\u8861\u5668<\/td>\n<\/tr>\n<tr>\n<td>\u6545\u969c\u8f6c\u79fb<\/td>\n<td>306\u5207\u6362\u5230\u5907\u7528\u4ee3\u7406<\/td>\n<td>\u5065\u5eb7\u68c0\u67e5\u3001\u81ea\u52a8\u6545\u969c\u8f6c\u79fb<\/td>\n<\/tr>\n<tr>\n<td>\u5730\u7406\u4f4d\u7f6e\u4f18\u5316<\/td>\n<td>306\u91cd\u5b9a\u5411\u5230\u6700\u8fd1\u4ee3\u7406<\/td>\n<td>CDN\u3001GeoDNS\u3001Anycast<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h4>15.4.5 \u73b0\u4ee3\u66ff\u4ee3\u5b9e\u73b0<\/h4>\n<h5>\u4f7f\u7528307\u91cd\u5b9a\u5411\u5b9e\u73b0\u4ee3\u7406\u5207\u6362<\/h5>\n<p>python<\/p>\n<p>from flask import Flask, request, jsonify<br \/>\nimport hashlib<\/p>\n<p>app &#061; Flask(__name__)<\/p>\n<p># \u4ee3\u7406\u670d\u52a1\u5668\u6c60<br \/>\nPROXY_POOL &#061; [<br \/>\n    {&#039;url&#039;: &#039;http:\/\/proxy-us-east-1.company.com:3128&#039;, &#039;region&#039;: &#039;us-east&#039;, &#039;load&#039;: 0},<br \/>\n    {&#039;url&#039;: &#039;http:\/\/proxy-us-west-1.company.com:3128&#039;, &#039;region&#039;: &#039;us-west&#039;, &#039;load&#039;: 0},<br \/>\n    {&#039;url&#039;: &#039;http:\/\/proxy-eu-west-1.company.com:3128&#039;, &#039;region&#039;: &#039;eu-west&#039;, &#039;load&#039;: 0},<br \/>\n    {&#039;url&#039;: &#039;http:\/\/proxy-ap-southeast-1.company.com:3128&#039;, &#039;region&#039;: &#039;ap-southeast&#039;, &#039;load&#039;: 0},<br \/>\n]<\/p>\n<p>def select_proxy(client_ip, requested_resource):<br \/>\n    &#034;&#034;&#034;\u57fa\u4e8e\u5730\u7406\u4f4d\u7f6e\u548c\u8d1f\u8f7d\u9009\u62e9\u4ee3\u7406&#034;&#034;&#034;<\/p>\n<p>    # 1. \u57fa\u4e8eIP\u786e\u5b9a\u533a\u57df<br \/>\n    client_region &#061; geolocate_ip(client_ip)<\/p>\n<p>    # 2. \u7b5b\u9009\u533a\u57df\u5339\u914d\u7684\u4ee3\u7406<br \/>\n    regional_proxies &#061; [p for p in PROXY_POOL if p[&#039;region&#039;] &#061;&#061; client_region]<\/p>\n<p>    if not regional_proxies:<br \/>\n        # \u56de\u9000\u5230\u5168\u5c40\u4ee3\u7406<br \/>\n        regional_proxies &#061; PROXY_POOL<\/p>\n<p>    # 3. \u9009\u62e9\u8d1f\u8f7d\u6700\u4f4e\u7684\u4ee3\u7406<br \/>\n    selected_proxy &#061; min(regional_proxies, key&#061;lambda x: x[&#039;load&#039;])<\/p>\n<p>    # 4. \u66f4\u65b0\u8d1f\u8f7d\u8ba1\u6570<br \/>\n    selected_proxy[&#039;load&#039;] &#043;&#061; 1<\/p>\n<p>    return selected_proxy[&#039;url&#039;]<\/p>\n<p>&#064;app.route(&#039;\/proxy-aware-resource&#039;)<br \/>\ndef get_resource():<br \/>\n    client_ip &#061; request.remote_addr<\/p>\n<p>    # \u68c0\u67e5\u662f\u5426\u5df2\u901a\u8fc7\u6b63\u786e\u4ee3\u7406\u8bbf\u95ee<br \/>\n    via_header &#061; request.headers.get(&#039;Via&#039;, &#039;&#039;)<br \/>\n    expected_proxy &#061; select_proxy(client_ip, request.path)<\/p>\n<p>    # \u63d0\u53d6\u5f53\u524d\u4f7f\u7528\u7684\u4ee3\u7406<br \/>\n    current_proxy &#061; None<br \/>\n    if via_header:<br \/>\n        # \u89e3\u6790Via\u5934&#xff0c;\u83b7\u53d6\u4ee3\u7406\u4fe1\u606f<br \/>\n        # \u683c\u5f0f: &#034;1.1 proxy1, 1.1 proxy2&#034;<br \/>\n        via_parts &#061; via_header.split(&#039;,&#039;)<br \/>\n        if via_parts:<br \/>\n            last_hop &#061; via_parts[-1].strip()<br \/>\n            # \u63d0\u53d6\u4ee3\u7406\u4e3b\u673a\u540d<br \/>\n            if &#039; &#039; in last_hop:<br \/>\n                current_proxy &#061; last_hop.split(&#039; &#039;)[1]<\/p>\n<p>    # \u5982\u679c\u672a\u901a\u8fc7\u4ee3\u7406&#xff0c;\u6216\u901a\u8fc7\u9519\u8bef\u4ee3\u7406\u8bbf\u95ee<br \/>\n    if not current_proxy or not is_correct_proxy(current_proxy, expected_proxy):<br \/>\n        # \u8fd4\u56de307\u91cd\u5b9a\u5411\u5230\u6b63\u786e\u4ee3\u7406<br \/>\n        response &#061; jsonify({<br \/>\n            &#039;error&#039;: &#039;proxy_mismatch&#039;,<br \/>\n            &#039;message&#039;: &#039;Please use the recommended proxy server&#039;,<br \/>\n            &#039;recommended_proxy&#039;: expected_proxy,<br \/>\n            &#039;proxy_configuration_help&#039;: &#039;https:\/\/docs.company.com\/proxy-setup&#039;<br \/>\n        })<br \/>\n        response.status_code &#061; 307<br \/>\n        response.headers[&#039;Location&#039;] &#061; expected_proxy &#043; request.full_path<br \/>\n        response.headers[&#039;X-Recommended-Proxy&#039;] &#061; expected_proxy<br \/>\n        return response<\/p>\n<p>    # \u901a\u8fc7\u6b63\u786e\u4ee3\u7406\u8bbf\u95ee&#xff0c;\u8fd4\u56de\u8d44\u6e90<br \/>\n    return jsonify({&#039;data&#039;: &#039;resource content here&#039;})<\/p>\n<p>def is_correct_proxy(current_proxy, expected_proxy):<br \/>\n    &#034;&#034;&#034;\u68c0\u67e5\u5f53\u524d\u4ee3\u7406\u662f\u5426\u6b63\u786e&#034;&#034;&#034;<br \/>\n    # \u7b80\u5355\u7684\u57df\u540d\u5339\u914d<br \/>\n    from urllib.parse import urlparse<br \/>\n    current_host &#061; urlparse(f&#039;http:\/\/{current_proxy}&#039;).hostname<br \/>\n    expected_host &#061; urlparse(expected_proxy).hostname<\/p>\n<p>    return current_host &#061;&#061; expected_host <\/p>\n<h4>15.4.6 \u4ee3\u7406\u81ea\u52a8\u53d1\u73b0\u4e0e\u5207\u6362\u7684\u73b0\u4ee3\u65b9\u6848<\/h4>\n<h5>\u65b9\u68481&#xff1a;\u52a8\u6001PAC\u6587\u4ef6<\/h5>\n<p>javascript<\/p>\n<p>\/\/ dynamic-proxy.pac &#8211; \u52a8\u6001\u4ee3\u7406\u914d\u7f6e<br \/>\nfunction FindProxyForURL(url, host) {<br \/>\n    \/\/ \u8c03\u7528\u914d\u7f6e\u670d\u52a1\u83b7\u53d6\u6700\u4f73\u4ee3\u7406<br \/>\n    var proxyConfig &#061; getProxyConfiguration(host);<\/p>\n<p>    if (proxyConfig.useProxy) {<br \/>\n        \/\/ \u8d1f\u8f7d\u5747\u8861&#xff1a;\u57fa\u4e8eURL\u54c8\u5e0c\u9009\u62e9\u4ee3\u7406<br \/>\n        var proxyServers &#061; [<br \/>\n            &#034;PROXY proxy1.company.com:3128&#034;,<br \/>\n            &#034;PROXY proxy2.company.com:3128&#034;,<br \/>\n            &#034;PROXY proxy3.company.com:3128&#034;<br \/>\n        ];<\/p>\n<p>        var hash &#061; md5(url);<br \/>\n        var index &#061; parseInt(hash.substr(0, 8), 16) % proxyServers.length;<\/p>\n<p>        return proxyServers[index];<br \/>\n    }<\/p>\n<p>    return &#034;DIRECT&#034;;<br \/>\n}<\/p>\n<p>\/\/ \u8f85\u52a9\u51fd\u6570&#xff1a;\u83b7\u53d6\u52a8\u6001\u914d\u7f6e<br \/>\nfunction getProxyConfiguration(host) {<br \/>\n    \/\/ \u8fd9\u91cc\u53ef\u4ee5\u8c03\u7528\u5185\u90e8API\u83b7\u53d6\u6700\u65b0\u914d\u7f6e<br \/>\n    \/\/ \u4f8b\u5982&#xff1a;fetch(&#039;https:\/\/config.company.com\/proxy-rules&#039;)<br \/>\n    return {<br \/>\n        useProxy: host.includes(&#034;.company.com&#034;),<br \/>\n        bypassLocal: true<br \/>\n    };<br \/>\n} <\/p>\n<h5>\u65b9\u68482&#xff1a;\u4ee3\u7406\u914d\u7f6eAPI<\/h5>\n<p>python<\/p>\n<p># \u4ee3\u7406\u914d\u7f6e\u670d\u52a1<br \/>\nfrom flask import Flask, request, jsonify<br \/>\nimport geoip2.database<\/p>\n<p>app &#061; Flask(__name__)<br \/>\ngeoip_reader &#061; geoip2.database.Reader(&#039;GeoLite2-City.mmdb&#039;)<\/p>\n<p>&#064;app.route(&#039;\/api\/proxy-config&#039;)<br \/>\ndef get_proxy_config():<br \/>\n    &#034;&#034;&#034;\u4e3a\u5ba2\u6237\u7aef\u63d0\u4f9b\u52a8\u6001\u4ee3\u7406\u914d\u7f6e&#034;&#034;&#034;<\/p>\n<p>    client_ip &#061; request.headers.get(&#039;X-Forwarded-For&#039;, request.remote_addr)<br \/>\n    user_agent &#061; request.headers.get(&#039;User-Agent&#039;, &#039;&#039;)<\/p>\n<p>    try:<br \/>\n        # \u83b7\u53d6\u5730\u7406\u4f4d\u7f6e<br \/>\n        geo_response &#061; geoip_reader.city(client_ip)<br \/>\n        country &#061; geo_response.country.iso_code<br \/>\n        city &#061; geo_response.city.name<\/p>\n<p>        # \u57fa\u4e8e\u5730\u7406\u4f4d\u7f6e\u9009\u62e9\u4ee3\u7406<br \/>\n        proxies &#061; {<br \/>\n            &#039;US&#039;: [&#039;us-proxy1.company.com&#039;, &#039;us-proxy2.company.com&#039;],<br \/>\n            &#039;GB&#039;: [&#039;uk-proxy1.company.com&#039;, &#039;uk-proxy2.company.com&#039;],<br \/>\n            &#039;JP&#039;: [&#039;jp-proxy1.company.com&#039;, &#039;jp-proxy2.company.com&#039;],<br \/>\n            &#039;AU&#039;: [&#039;au-proxy1.company.com&#039;, &#039;au-proxy2.company.com&#039;]<br \/>\n        }<\/p>\n<p>        # \u9009\u62e9\u4ee3\u7406<br \/>\n        proxy_pool &#061; proxies.get(country, proxies[&#039;US&#039;])<\/p>\n<p>        # \u7b80\u5355\u7684\u8d1f\u8f7d\u5747\u8861&#xff1a;\u57fa\u4e8eIP\u54c8\u5e0c<br \/>\n        import hashlib<br \/>\n        ip_hash &#061; int(hashlib.md5(client_ip.encode()).hexdigest()[:8], 16)<br \/>\n        selected_proxy &#061; proxy_pool[ip_hash % len(proxy_pool)]<\/p>\n<p>        # \u6784\u5efa\u914d\u7f6e<br \/>\n        config &#061; {<br \/>\n            &#039;proxy_server&#039;: f&#039;http:\/\/{selected_proxy}:3128&#039;,<br \/>\n            &#039;fallback_servers&#039;: [f&#039;http:\/\/{p}:3128&#039; for p in proxy_pool if p !&#061; selected_proxy],<br \/>\n            &#039;bypass_list&#039;: [<br \/>\n                &#039;localhost&#039;,<br \/>\n                &#039;127.0.0.1&#039;,<br \/>\n                &#039;*.local&#039;,<br \/>\n                &#039;*.internal&#039;<br \/>\n            ],<br \/>\n            &#039;config_version&#039;: &#039;2024.01.15&#039;,<br \/>\n            &#039;expires_in&#039;: 3600,  # 1\u5c0f\u65f6<br \/>\n            &#039;refresh_url&#039;: &#039;\/api\/proxy-config&#039;<br \/>\n        }<\/p>\n<p>        return jsonify(config)<\/p>\n<p>    except Exception as e:<br \/>\n        # \u51fa\u9519\u65f6\u8fd4\u56de\u9ed8\u8ba4\u914d\u7f6e<br \/>\n        return jsonify({<br \/>\n            &#039;proxy_server&#039;: &#039;http:\/\/global-proxy.company.com:3128&#039;,<br \/>\n            &#039;error&#039;: str(e),<br \/>\n            &#039;config_version&#039;: &#039;default&#039;<br \/>\n        }) <\/p>\n<h4>15.4.7 \u5ba2\u6237\u7aef\u4ee3\u7406\u7ba1\u7406\u793a\u4f8b<\/h4>\n<p>python<\/p>\n<p># \u667a\u80fdHTTP\u5ba2\u6237\u7aef&#xff0c;\u652f\u6301\u4ee3\u7406\u53d1\u73b0\u548c\u6545\u969c\u8f6c\u79fb<br \/>\nimport requests<br \/>\nfrom urllib.parse import urlparse<br \/>\nimport time<\/p>\n<p>class SmartHTTPClient:<br \/>\n    def __init__(self, config_service_url&#061;None):<br \/>\n        self.config_service_url &#061; config_service_url<br \/>\n        self.proxy_config &#061; None<br \/>\n        self.config_expiry &#061; 0<br \/>\n        self.session &#061; requests.Session()<\/p>\n<p>    def get_proxy_config(self, force_refresh&#061;False):<br \/>\n        &#034;&#034;&#034;\u83b7\u53d6\u6216\u5237\u65b0\u4ee3\u7406\u914d\u7f6e&#034;&#034;&#034;<br \/>\n        current_time &#061; time.time()<\/p>\n<p>        if (force_refresh or<br \/>\n            not self.proxy_config or<br \/>\n            current_time &gt; self.config_expiry):<\/p>\n<p>            if self.config_service_url:<br \/>\n                try:<br \/>\n                    response &#061; self.session.get(<br \/>\n                        self.config_service_url,<br \/>\n                        timeout&#061;5<br \/>\n                    )<br \/>\n                    if response.status_code &#061;&#061; 200:<br \/>\n                        self.proxy_config &#061; response.json()<br \/>\n                        self.config_expiry &#061; current_time &#043; self.proxy_config.get(&#039;expires_in&#039;, 300)<br \/>\n                except:<br \/>\n                    # \u914d\u7f6e\u670d\u52a1\u4e0d\u53ef\u7528&#xff0c;\u4f7f\u7528\u9ed8\u8ba4<br \/>\n                    self.proxy_config &#061; self.get_default_config()<br \/>\n            else:<br \/>\n                self.proxy_config &#061; self.get_default_config()<\/p>\n<p>        return self.proxy_config<\/p>\n<p>    def get_default_config(self):<br \/>\n        &#034;&#034;&#034;\u9ed8\u8ba4\u914d\u7f6e&#034;&#034;&#034;<br \/>\n        return {<br \/>\n            &#039;proxy_server&#039;: None,  # \u76f4\u8fde<br \/>\n            &#039;fallback_servers&#039;: [],<br \/>\n            &#039;bypass_list&#039;: [&#039;localhost&#039;, &#039;127.0.0.1&#039;],<br \/>\n            &#039;config_version&#039;: &#039;default&#039;<br \/>\n        }<\/p>\n<p>    def should_bypass_proxy(self, url):<br \/>\n        &#034;&#034;&#034;\u68c0\u67e5URL\u662f\u5426\u5e94\u8be5\u7ed5\u8fc7\u4ee3\u7406&#034;&#034;&#034;<br \/>\n        parsed &#061; urlparse(url)<br \/>\n        hostname &#061; parsed.hostname<\/p>\n<p>        if not hostname:<br \/>\n            return True<\/p>\n<p>        bypass_list &#061; self.proxy_config.get(&#039;bypass_list&#039;, [])<\/p>\n<p>        for pattern in bypass_list:<br \/>\n            if pattern.startswith(&#039;*.&#039;):<br \/>\n                # \u901a\u914d\u7b26\u5339\u914d&#xff1a;*.example.com<br \/>\n                domain &#061; pattern[2:]<br \/>\n                if hostname.endswith(&#039;.&#039; &#043; domain) or hostname &#061;&#061; domain:<br \/>\n                    return True<br \/>\n            elif pattern &#061;&#061; hostname:<br \/>\n                return True<\/p>\n<p>        return False<\/p>\n<p>    def request_with_proxy_failover(self, method, url, **kwargs):<br \/>\n        &#034;&#034;&#034;\u652f\u6301\u6545\u969c\u8f6c\u79fb\u7684\u8bf7\u6c42&#034;&#034;&#034;<\/p>\n<p>        # \u83b7\u53d6\u6700\u65b0\u914d\u7f6e<br \/>\n        config &#061; self.get_proxy_config()<\/p>\n<p>        # \u68c0\u67e5\u662f\u5426\u5e94\u8be5\u7ed5\u8fc7\u4ee3\u7406<br \/>\n        if self.should_bypass_proxy(url):<br \/>\n            return self.session.request(method, url, **kwargs)<\/p>\n<p>        # \u6784\u5efa\u4ee3\u7406\u5217\u8868&#xff08;\u4e3b\u4ee3\u7406 &#043; \u5907\u7528\u4ee3\u7406&#xff09;<br \/>\n        proxies_to_try &#061; []<\/p>\n<p>        main_proxy &#061; config.get(&#039;proxy_server&#039;)<br \/>\n        if main_proxy:<br \/>\n            proxies_to_try.append({<br \/>\n                &#039;http&#039;: main_proxy,<br \/>\n                &#039;https&#039;: main_proxy<br \/>\n            })<\/p>\n<p>        # \u6dfb\u52a0\u5907\u7528\u4ee3\u7406<br \/>\n        fallback_servers &#061; config.get(&#039;fallback_servers&#039;, [])<br \/>\n        for server in fallback_servers:<br \/>\n            proxies_to_try.append({<br \/>\n                &#039;http&#039;: server,<br \/>\n                &#039;https&#039;: server<br \/>\n            })<\/p>\n<p>        # \u5982\u679c\u6ca1\u6709\u914d\u7f6e\u4ee3\u7406&#xff0c;\u76f4\u8fde<br \/>\n        if not proxies_to_try:<br \/>\n            return self.session.request(method, url, **kwargs)<\/p>\n<p>        # \u5c1d\u8bd5\u6bcf\u4e2a\u4ee3\u7406&#xff0c;\u76f4\u5230\u6210\u529f<br \/>\n        last_exception &#061; None<\/p>\n<p>        for proxy_config in proxies_to_try:<br \/>\n            try:<br \/>\n                response &#061; self.session.request(<br \/>\n                    method, url,<br \/>\n                    proxies&#061;proxy_config,<br \/>\n                    timeout&#061;kwargs.get(&#039;timeout&#039;, 30),<br \/>\n                    **{k: v for k, v in kwargs.items() if k !&#061; &#039;timeout&#039;}<br \/>\n                )<br \/>\n                return response<br \/>\n            except requests.exceptions.RequestException as e:<br \/>\n                last_exception &#061; e<br \/>\n                continue<\/p>\n<p>        # \u6240\u6709\u4ee3\u7406\u90fd\u5931\u8d25&#xff0c;\u5c1d\u8bd5\u76f4\u8fde<br \/>\n        try:<br \/>\n            return self.session.request(method, url, **kwargs)<br \/>\n        except:<br \/>\n            # \u5982\u679c\u76f4\u8fde\u4e5f\u5931\u8d25&#xff0c;\u629b\u51fa\u6700\u540e\u4e00\u4e2a\u5f02\u5e38<br \/>\n            if last_exception:<br \/>\n                raise last_exception<br \/>\n            else:<br \/>\n                raise<\/p>\n<p># \u4f7f\u7528\u793a\u4f8b<br \/>\nclient &#061; SmartHTTPClient(&#039;https:\/\/config.company.com\/api\/proxy-config&#039;)<\/p>\n<p>try:<br \/>\n    response &#061; client.request_with_proxy_failover(<br \/>\n        &#039;GET&#039;,<br \/>\n        &#039;https:\/\/api.company.com\/data&#039;,<br \/>\n        headers&#061;{&#039;Authorization&#039;: &#039;Bearer token123&#039;}<br \/>\n    )<br \/>\n    print(f&#039;Success: {response.status_code}&#039;)<br \/>\nexcept Exception as e:<br \/>\n    print(f&#039;Request failed: {e}&#039;) <\/p>\n<hr \/>\n<h3>15.5 \u7efc\u5408\u6bd4\u8f83\u4e0e\u5e94\u7528\u573a\u666f<\/h3>\n<h4>15.5.1 \u72b6\u6001\u7801\u5bf9\u6bd4\u77e9\u9635<\/h4>\n<table>\n<tr>\u7279\u6027300 Multiple Choices303 See Other305 Use Proxy306 Switch Proxy<\/tr>\n<tbody>\n<tr>\n<td>\u72b6\u6001\u7801<\/td>\n<td>300<\/td>\n<td>303<\/td>\n<td>305<\/td>\n<td>306<\/td>\n<\/tr>\n<tr>\n<td>HTTP\u7248\u672c<\/td>\n<td>HTTP\/1.0&#043;<\/td>\n<td>HTTP\/1.1&#043;<\/td>\n<td>HTTP\/1.1<\/td>\n<td>\u5b9e\u9a8c\u6027&#xff0c;\u672a\u6807\u51c6\u5316<\/td>\n<\/tr>\n<tr>\n<td>\u5f53\u524d\u72b6\u6001<\/td>\n<td>\u6807\u51c6&#xff0c;\u4f46\u5c11\u7528<\/td>\n<td>\u6807\u51c6&#xff0c;\u5e38\u7528<\/td>\n<td>\u5df2\u5e9f\u5f03<\/td>\n<td>\u4ece\u672a\u6807\u51c6\u5316<\/td>\n<\/tr>\n<tr>\n<td>\u81ea\u52a8\u91cd\u5b9a\u5411<\/td>\n<td>\u901a\u5e38\u5426<\/td>\n<td>\u662f<\/td>\n<td>\u662f&#xff08;\u4f46\u6d4f\u89c8\u5668\u4e0d\u652f\u6301&#xff09;<\/td>\n<td>N\/A<\/td>\n<\/tr>\n<tr>\n<td>\u65b9\u6cd5\u53d8\u66f4<\/td>\n<td>N\/A<\/td>\n<td>\u5fc5\u987b\u6539\u4e3aGET<\/td>\n<td>\u4fdd\u6301\u539f\u65b9\u6cd5<\/td>\n<td>N\/A<\/td>\n<\/tr>\n<tr>\n<td>Location\u5934<\/td>\n<td>\u53ef\u9009&#xff08;\u9996\u9009\u8868\u793a&#xff09;<\/td>\n<td>\u5fc5\u9700<\/td>\n<td>\u5fc5\u9700&#xff08;\u4ee3\u7406URI&#xff09;<\/td>\n<td>\u7406\u8bba\u9700\u8981<\/td>\n<\/tr>\n<tr>\n<td>\u7f13\u5b58\u5efa\u8bae<\/td>\n<td>\u901a\u5e38\u4e0d\u7f13\u5b58<\/td>\n<td>\u901a\u5e38\u4e0d\u7f13\u5b58<\/td>\n<td>\u4e0d\u7f13\u5b58<\/td>\n<td>N\/A<\/td>\n<\/tr>\n<tr>\n<td>\u4e3b\u8981\u7528\u9014<\/td>\n<td>\u5185\u5bb9\u534f\u5546<\/td>\n<td>POST\u540e\u91cd\u5b9a\u5411<\/td>\n<td>\u5f3a\u5236\u4ee3\u7406\u8bbf\u95ee<\/td>\n<td>\u4ee3\u7406\u5207\u6362<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h4>15.5.2 \u5e94\u7528\u573a\u666f\u6307\u5357<\/h4>\n<h5>\u573a\u666f1&#xff1a;\u7528\u6237\u63d0\u4ea4\u8868\u5355\u540e\u663e\u793a\u7ed3\u679c\u9875<\/h5>\n<p>\u63a8\u8350&#xff1a;303 See Other<\/p>\n<p>python<\/p>\n<p># \u6700\u4f73\u5b9e\u8df5&#xff1a;POST\/Redirect\/GET\u6a21\u5f0f<br \/>\n&#064;app.route(&#039;\/submit-form&#039;, methods&#061;[&#039;POST&#039;])<br \/>\ndef submit_form():<br \/>\n    # \u5904\u7406\u8868\u5355\u6570\u636e<br \/>\n    result_id &#061; process_form_data(request.form)<\/p>\n<p>    # 303\u91cd\u5b9a\u5411\u5230\u7ed3\u679c\u9875<br \/>\n    return redirect(url_for(&#039;show_result&#039;, id&#061;result_id), code&#061;303)<\/p>\n<p>&#064;app.route(&#039;\/result\/&lt;id&gt;&#039;)<br \/>\ndef show_result(id):<br \/>\n    # \u663e\u793a\u7ed3\u679c&#xff0c;\u53ef\u5237\u65b0\u3001\u53ef\u6536\u85cf<br \/>\n    return render_template(&#039;result.html&#039;, result&#061;get_result(id)) <\/p>\n<h5>\u573a\u666f2&#xff1a;API\u63d0\u4f9b\u591a\u79cd\u6570\u636e\u683c\u5f0f<\/h5>\n<p>\u63a8\u8350&#xff1a;300 Multiple Choices<\/p>\n<p>python<\/p>\n<p>&#064;app.route(&#039;\/api\/data&#039;)<br \/>\ndef get_data():<br \/>\n    accept &#061; request.headers.get(&#039;Accept&#039;, &#039;&#039;)<\/p>\n<p>    if &#039;application\/json&#039; in accept:<br \/>\n        return jsonify({&#039;data&#039;: &#039;json&#039;})<br \/>\n    elif &#039;application\/xml&#039; in accept:<br \/>\n        return xml_response(&#039;&lt;data&gt;xml&lt;\/data&gt;&#039;)<br \/>\n    elif &#039;text\/csv&#039; in accept:<br \/>\n        return &#039;data,csv\\\\nvalue1,value2&#039;<br \/>\n    else:<br \/>\n        # \u65e0\u6cd5\u786e\u5b9a&#xff0c;\u8fd4\u56de\u9009\u62e9\u5217\u8868<br \/>\n        response &#061; jsonify({<br \/>\n            &#039;message&#039;: &#039;Multiple representations available&#039;,<br \/>\n            &#039;formats&#039;: [<br \/>\n                {&#039;type&#039;: &#039;application\/json&#039;, &#039;url&#039;: &#039;\/api\/data?format&#061;json&#039;},<br \/>\n                {&#039;type&#039;: &#039;application\/xml&#039;, &#039;url&#039;: &#039;\/api\/data?format&#061;xml&#039;},<br \/>\n                {&#039;type&#039;: &#039;text\/csv&#039;, &#039;url&#039;: &#039;\/api\/data?format&#061;csv&#039;}<br \/>\n            ]<br \/>\n        })<br \/>\n        response.status_code &#061; 300<br \/>\n        return response <\/p>\n<h5>\u573a\u666f3&#xff1a;\u4f01\u4e1a\u73af\u5883\u4e2d\u7684\u4ee3\u7406\u8bbf\u95ee<\/h5>\n<p>\u4e0d\u63a8\u8350&#xff1a;305 Use Proxy&#xff08;\u5df2\u5e9f\u5f03&#xff09; \u66ff\u4ee3\u65b9\u6848&#xff1a;<\/p>\n<p>python<\/p>\n<p># \u4f7f\u7528307\u91cd\u5b9a\u5411\u548c\u81ea\u5b9a\u4e49\u5934<br \/>\n&#064;app.route(&#039;\/internal-resource&#039;)<br \/>\ndef internal_resource():<br \/>\n    client_ip &#061; request.remote_addr<\/p>\n<p>    if not is_internal_ip(client_ip):<br \/>\n        response &#061; jsonify({<br \/>\n            &#039;error&#039;: &#039;proxy_required&#039;,<br \/>\n            &#039;message&#039;: &#039;This resource requires access through corporate proxy&#039;,<br \/>\n            &#039;proxy_config&#039;: {<br \/>\n                &#039;server&#039;: &#039;proxy.company.com:3128&#039;,<br \/>\n                &#039;auth_type&#039;: &#039;ntlm&#039;,<br \/>\n                &#039;config_url&#039;: &#039;https:\/\/it.company.com\/proxy-setup&#039;<br \/>\n            }<br \/>\n        })<br \/>\n        response.status_code &#061; 307<br \/>\n        response.headers[&#039;Location&#039;] &#061; &#039;https:\/\/gateway.company.com\/auth&#039;<br \/>\n        return response<\/p>\n<p>    return jsonify({&#039;data&#039;: &#039;internal resource&#039;}) <\/p>\n<h4>15.5.3 \u6027\u80fd\u4e0e\u7f13\u5b58\u8003\u8651<\/h4>\n<h5>\u7f13\u5b58\u7b56\u7565\u603b\u7ed3<\/h5>\n<table>\n<tr>\u72b6\u6001\u7801\u7f13\u5b58\u5efa\u8bae\u7f13\u5b58\u952e\u8003\u8651<\/tr>\n<tbody>\n<tr>\n<td>300<\/td>\n<td>Cache-Control: max-age&#061;3600<\/td>\n<td>\u5305\u542bVary: Accept, Accept-Language<\/td>\n<\/tr>\n<tr>\n<td>303<\/td>\n<td>Cache-Control: no-cache<\/td>\n<td>\u901a\u5e38\u4e0d\u7f13\u5b58<\/td>\n<\/tr>\n<tr>\n<td>305<\/td>\n<td>Cache-Control: no-store<\/td>\n<td>\u4e0d\u5e94\u7f13\u5b58<\/td>\n<\/tr>\n<tr>\n<td>306<\/td>\n<td>N\/A<\/td>\n<td>N\/A<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h5>\u6027\u80fd\u4f18\u5316\u5efa\u8bae<\/h5>\n<p>python<\/p>\n<p># 300\u54cd\u5e94\u7684\u7f13\u5b58\u4f18\u5316<br \/>\n&#064;app.route(&#039;\/multi-format-resource&#039;)<br \/>\ndef multi_format_resource():<br \/>\n    # \u57fa\u4e8eAccept\u5934\u751f\u6210\u4e0d\u540c\u7684\u7f13\u5b58\u952e<br \/>\n    accept_header &#061; request.headers.get(&#039;Accept&#039;, &#039;&#039;)<br \/>\n    cache_key &#061; f&#034;resource:{hash(accept_header)}&#034;<\/p>\n<p>    cached &#061; cache.get(cache_key)<br \/>\n    if cached and cached.get(&#039;status&#039;) &#061;&#061; 300:<br \/>\n        response &#061; make_response(cached[&#039;body&#039;])<br \/>\n        response.status_code &#061; 300<br \/>\n        for key, value in cached[&#039;headers&#039;].items():<br \/>\n            response.headers[key] &#061; value<br \/>\n        return response<\/p>\n<p>    # \u751f\u6210\u54cd\u5e94<br \/>\n    response_data &#061; generate_response()<\/p>\n<p>    # \u7f13\u5b58300\u54cd\u5e94&#xff08;\u77ed\u671f&#xff09;<br \/>\n    cache.set(cache_key, {<br \/>\n        &#039;body&#039;: response_data,<br \/>\n        &#039;headers&#039;: {&#039;Content-Type&#039;: &#039;application\/json&#039;},<br \/>\n        &#039;status&#039;: 300<br \/>\n    }, timeout&#061;300)  # 5\u5206\u949f<\/p>\n<p>    response &#061; make_response(response_data)<br \/>\n    response.status_code &#061; 300<br \/>\n    response.headers[&#039;Cache-Control&#039;] &#061; &#039;public, max-age&#061;300&#039;<br \/>\n    response.headers[&#039;Vary&#039;] &#061; &#039;Accept&#039;<br \/>\n    return response <\/p>\n<h4>15.5.4 \u5b89\u5168\u6700\u4f73\u5b9e\u8df5<\/h4>\n<h5>1. \u91cd\u5b9a\u5411\u9a8c\u8bc1<\/h5>\n<p>python<\/p>\n<p>def safe_redirect(target_url, code&#061;303):<br \/>\n    &#034;&#034;&#034;\u5b89\u5168\u7684\u91cd\u5b9a\u5411\u51fd\u6570&#034;&#034;&#034;<\/p>\n<p>    # \u9a8c\u8bc1\u76ee\u6807URL<br \/>\n    from urllib.parse import urlparse<\/p>\n<p>    parsed &#061; urlparse(target_url)<\/p>\n<p>    # \u68c0\u67e5\u534f\u8bae<br \/>\n    if parsed.scheme not in [&#039;http&#039;, &#039;https&#039;, &#039;&#039;]:<br \/>\n        raise ValueError(f&#039;Unsupported scheme: {parsed.scheme}&#039;)<\/p>\n<p>    # \u68c0\u67e5\u57df\u540d&#xff08;\u9632\u6b62\u5f00\u653e\u91cd\u5b9a\u5411&#xff09;<br \/>\n    allowed_domains &#061; [<br \/>\n        &#039;example.com&#039;,<br \/>\n        &#039;trusted-partner.com&#039;<br \/>\n    ]<\/p>\n<p>    if parsed.netloc and parsed.netloc not in allowed_domains:<br \/>\n        # \u91cd\u5b9a\u5411\u5230\u5b89\u5168\u9ed8\u8ba4\u9875<br \/>\n        target_url &#061; &#039;\/error\/invalid-redirect&#039;<\/p>\n<p>    # \u521b\u5efa\u54cd\u5e94<br \/>\n    from flask import redirect<br \/>\n    return redirect(target_url, code&#061;code) <\/p>\n<h5>2. \u5185\u5bb9\u534f\u5546\u5b89\u5168<\/h5>\n<p>python<\/p>\n<p>&#064;app.route(&#039;\/document&#039;)<br \/>\ndef get_document():<br \/>\n    # \u53ea\u5141\u8bb8\u7279\u5b9a\u7684\u5a92\u4f53\u7c7b\u578b<br \/>\n    accept &#061; request.headers.get(&#039;Accept&#039;, &#039;&#039;)<br \/>\n    allowed_types &#061; [<br \/>\n        &#039;text\/html&#039;,<br \/>\n        &#039;application\/json&#039;,<br \/>\n        &#039;application\/pdf&#039;<br \/>\n    ]<\/p>\n<p>    # \u68c0\u67e5\u8bf7\u6c42\u7684\u7c7b\u578b\u662f\u5426\u88ab\u5141\u8bb8<br \/>\n    requested_types &#061; [t.strip() for t in accept.split(&#039;,&#039;)]<br \/>\n    valid_types &#061; [t for t in requested_types if t in allowed_types]<\/p>\n<p>    if not valid_types:<br \/>\n        # \u8fd4\u56de300&#xff0c;\u53ea\u5217\u51fa\u5141\u8bb8\u7684\u7c7b\u578b<br \/>\n        response &#061; jsonify({<br \/>\n            &#039;message&#039;: &#039;Please specify a valid Accept header&#039;,<br \/>\n            &#039;allowed_formats&#039;: allowed_types,<br \/>\n            &#039;example_request&#039;: &#039;Accept: application\/json&#039;<br \/>\n        })<br \/>\n        response.status_code &#061; 300<br \/>\n        return response<\/p>\n<p>    # \u6b63\u5e38\u5904\u7406\u8bf7\u6c42<br \/>\n    return generate_document(valid_types[0]) <\/p>\n<h4>15.5.5 \u672a\u6765\u8d8b\u52bf\u4e0e\u5f03\u7528\u5efa\u8bae<\/h4>\n<h5>\u72b6\u6001\u7801\u7684\u4f7f\u7528\u5efa\u8bae<\/h5>\n<table>\n<tr>\u72b6\u6001\u7801\u672a\u6765\u5efa\u8bae\u66ff\u4ee3\u65b9\u6848<\/tr>\n<tbody>\n<tr>\n<td>300<\/td>\n<td>\u8c28\u614e\u4f7f\u7528&#xff0c;\u8003\u8651\u670d\u52a1\u5668\u7aef\u534f\u5546<\/td>\n<td>\u4f7f\u7528Vary\u5934&#043;200\u72b6\u6001\u7801<\/td>\n<\/tr>\n<tr>\n<td>303<\/td>\n<td>\u63a8\u8350\u4f7f\u7528&#xff0c;PRG\u6a21\u5f0f\u6807\u51c6<\/td>\n<td>\u7ee7\u7eed\u4f7f\u7528<\/td>\n<\/tr>\n<tr>\n<td>305<\/td>\n<td>\u5b8c\u5168\u907f\u514d<\/td>\n<td>\u4ee3\u7406\u81ea\u52a8\u914d\u7f6e(PAC\/WPAD)<\/td>\n<\/tr>\n<tr>\n<td>306<\/td>\n<td>\u4e0d\u5e94\u4f7f\u7528<\/td>\n<td>\u4ece\u672a\u6807\u51c6\u5316&#xff0c;\u4e0d\u4f7f\u7528<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h5>\u73b0\u4ee3Web\u5f00\u53d1\u4e2d\u7684\u6700\u4f73\u5b9e\u8df5<\/h5>\n<p>python<\/p>\n<p># \u73b0\u4ee3API\u8bbe\u8ba1\u793a\u4f8b&#xff0c;\u907f\u514d\u8fc7\u5ea6\u4f7f\u7528300<br \/>\n&#064;app.route(&#039;\/api\/v2\/resource&#039;)<br \/>\ndef api_resource_v2():<br \/>\n    # \u4f7f\u7528\u5185\u5bb9\u534f\u5546&#xff0c;\u800c\u975e300\u54cd\u5e94<br \/>\n    accept &#061; request.headers.get(&#039;Accept&#039;, &#039;application\/json&#039;)<\/p>\n<p>    # \u4f18\u5148\u5904\u7406JSON&#xff08;API\u6807\u51c6&#xff09;<br \/>\n    if &#039;application\/json&#039; in accept or &#039;*\/*&#039; in accept:<br \/>\n        return jsonify({&#039;data&#039;: &#039;json response&#039;})<\/p>\n<p>    # \u652f\u6301\u5176\u4ed6\u683c\u5f0f&#xff0c;\u4f46\u901a\u8fc7\u4e0d\u540c\u7aef\u70b9<br \/>\n    elif &#039;application\/xml&#039; in accept:<br \/>\n        # \u91cd\u5b9a\u5411\u5230\u4e13\u95e8\u7aef\u70b9<br \/>\n        return redirect(&#039;\/api\/v2\/resource.xml&#039;, code&#061;307)<\/p>\n<p>    else:<br \/>\n        # \u8fd4\u56de406 Not Acceptable&#xff0c;\u800c\u975e300<br \/>\n        response &#061; jsonify({<br \/>\n            &#039;error&#039;: &#039;not_acceptable&#039;,<br \/>\n            &#039;message&#039;: &#039;Requested format not supported&#039;,<br \/>\n            &#039;supported_formats&#039;: [&#039;application\/json&#039;, &#039;application\/xml&#039;],<br \/>\n            &#039;default_format&#039;: &#039;application\/json&#039;<br \/>\n        })<br \/>\n        response.status_code &#061; 406<br \/>\n        return response <\/p>\n<h4>15.5.6 \u603b\u7ed3\u4e0e\u5173\u952e\u8981\u70b9<\/h4>\n<li>\n<p>300 Multiple Choices&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u7528\u4e8e\u5185\u5bb9\u534f\u5546\u5931\u8d25\u65f6\u63d0\u4f9b\u9009\u62e9<\/p>\n<\/li>\n<li>\n<p>\u5b9e\u8df5\u4e2d\u8f83\u5c11\u4f7f\u7528&#xff0c;\u901a\u5e38\u7531\u670d\u52a1\u5668\u81ea\u52a8\u9009\u62e9<\/p>\n<\/li>\n<li>\n<p>\u53ef\u4ee5\u63d0\u4f9b\u66f4\u597d\u7684\u7528\u6237\u4f53\u9a8c&#xff0c;\u4f46\u589e\u52a0\u4e86\u590d\u6742\u6027<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p>303 See Other&#xff1a;<\/p>\n<ul>\n<li>\n<p>POST\/Redirect\/GET\u6a21\u5f0f\u7684\u5173\u952e<\/p>\n<\/li>\n<li>\n<p>\u9632\u6b62\u8868\u5355\u91cd\u590d\u63d0\u4ea4<\/p>\n<\/li>\n<li>\n<p>\u73b0\u4ee3Web\u5f00\u53d1\u4e2d\u7684\u6700\u4f73\u5b9e\u8df5<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p>305 Use Proxy&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u5df2\u5e9f\u5f03&#xff0c;\u4e0d\u5e94\u4f7f\u7528<\/p>\n<\/li>\n<li>\n<p>\u5b89\u5168\u98ce\u9669\u5927&#xff0c;\u6d4f\u89c8\u5668\u4e0d\u652f\u6301<\/p>\n<\/li>\n<li>\n<p>\u4f7f\u7528\u73b0\u4ee3\u4ee3\u7406\u914d\u7f6e\u65b9\u6848\u66ff\u4ee3<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p>306 Switch Proxy&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u4ece\u672a\u6b63\u5f0f\u6807\u51c6\u5316<\/p>\n<\/li>\n<li>\n<p>\u5386\u53f2\u9057\u7559&#xff0c;\u4e0d\u5e94\u4f7f\u7528<\/p>\n<\/li>\n<li>\n<p>\u4ee3\u7406\u7ba1\u7406\u901a\u8fc7\u5176\u4ed6\u673a\u5236\u5b9e\u73b0<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<h5>\u51b3\u7b56\u6d41\u7a0b\u56fe<\/h5>\n<p>text<\/p>\n<p>\u5f00\u59cb<br \/>\n\u251c\u2500 \u9700\u8981\u5185\u5bb9\u534f\u5546&#xff1f;<br \/>\n\u2502  \u251c\u2500 \u80fd\u81ea\u52a8\u9009\u62e9\u6700\u4f73\u8868\u793a&#xff1f; \u2192 \u8fd4\u56de200<br \/>\n\u2502  \u2514\u2500 \u9700\u8981\u7528\u6237\u9009\u62e9&#xff1f; \u2192 \u8fd4\u56de300<br \/>\n\u251c\u2500 \u8868\u5355\u63d0\u4ea4\u540e\u91cd\u5b9a\u5411&#xff1f;<br \/>\n\u2502  \u2514\u2500 \u662f \u2192 \u8fd4\u56de303<br \/>\n\u251c\u2500 \u9700\u8981\u5f3a\u5236\u4ee3\u7406\u8bbf\u95ee&#xff1f;<br \/>\n\u2502  \u251c\u2500 \u662f \u2192 \u907f\u514d305&#xff0c;\u4f7f\u7528\u4ee3\u7406\u914d\u7f6eAPI<br \/>\n\u2502  \u2514\u2500 \u5426 \u2192 \u7ee7\u7eed<br \/>\n\u2514\u2500 \u5176\u4ed6\u91cd\u5b9a\u5411\u9700\u6c42&#xff1f;<br \/>\n   \u251c\u2500 \u6c38\u4e45\u91cd\u5b9a\u5411 \u2192 301\/308<br \/>\n   \u251c\u2500 \u4e34\u65f6\u91cd\u5b9a\u5411&#xff0c;\u4fdd\u6301\u65b9\u6cd5 \u2192 307<br \/>\n   \u2514\u2500 \u4e34\u65f6\u91cd\u5b9a\u5411&#xff0c;\u53ef\u6539\u65b9\u6cd5 \u2192 302 <\/p>\n<p>\u901a\u8fc7\u672c\u7ae0\u7684\u8be6\u7ec6\u5206\u6790&#xff0c;\u6211\u4eec\u53ef\u4ee5\u770b\u5230\u867d\u7136HTTP\u5b9a\u4e49\u4e86\u591a\u4e2a3xx\u72b6\u6001\u7801&#xff0c;\u4f46\u5728\u5b9e\u9645\u5f00\u53d1\u4e2d&#xff0c;303\u662f\u6700\u5e38\u7528\u4e14\u6700\u6709\u4ef7\u503c\u7684&#xff0c;300\u5728\u7279\u5b9a\u573a\u666f\u4e0b\u6709\u7528&#xff0c;\u800c305\u548c306\u5e94\u907f\u514d\u4f7f\u7528\u3002\u7406\u89e3\u6bcf\u4e2a\u72b6\u6001\u7801\u7684\u8bed\u4e49\u548c\u9002\u7528\u573a\u666f&#xff0c;\u6709\u52a9\u4e8e\u6784\u5efa\u66f4\u5065\u58ee\u3001\u66f4\u5b89\u5168\u7684Web\u5e94\u7528\u3002<\/p>\n<hr \/>\n<h3>\u603b\u7ed3<\/h3>\n<p>HTTP\u91cd\u5b9a\u5411\u72b6\u6001\u78013xx\u662fWeb\u67b6\u6784\u4e2d\u7684\u91cd\u8981\u7ec4\u6210\u90e8\u5206&#xff0c;\u6bcf\u79cd\u72b6\u6001\u7801\u90fd\u6709\u5176\u7279\u5b9a\u7684\u8bed\u4e49\u548c\u7528\u9014&#xff1a;<\/p>\n<ul>\n<li>\n<p>301 Moved Permanently&#xff1a;\u6c38\u4e45\u91cd\u5b9a\u5411&#xff0c;\u9002\u5408\u7f51\u7ad9\u91cd\u6784\u3001\u57df\u540d\u8fc1\u79fb\u7b49\u6c38\u4e45\u6027\u53d8\u66f4<\/p>\n<\/li>\n<li>\n<p>302 Found&#xff1a;\u4e34\u65f6\u91cd\u5b9a\u5411&#xff0c;\u4f20\u7edf\u4f46\u65b9\u6cd5\u4fdd\u6301\u6a21\u7cca&#xff0c;\u9002\u5408A\/B\u6d4b\u8bd5\u3001\u7ef4\u62a4\u9875\u9762\u7b49\u4e34\u65f6\u573a\u666f<\/p>\n<\/li>\n<li>\n<p>303 See Other&#xff1a;\u660e\u786e\u8f6c\u4e3aGET\u7684\u91cd\u5b9a\u5411&#xff0c;\u9002\u5408POST\u540e\u663e\u793a\u7ed3\u679c\u9875<\/p>\n<\/li>\n<li>\n<p>304 Not Modified&#xff1a;\u7f13\u5b58\u9a8c\u8bc1\u673a\u5236&#xff0c;\u4f18\u5316\u6027\u80fd&#xff0c;\u51cf\u5c11\u4e0d\u5fc5\u8981\u7684\u6570\u636e\u4f20\u8f93<\/p>\n<\/li>\n<li>\n<p>307 Temporary Redirect&#xff1a;\u4e34\u65f6\u91cd\u5b9a\u5411\u4e14\u4fdd\u6301\u65b9\u6cd5&#xff0c;\u89e3\u51b3302\u7684\u6a21\u7cca\u6027&#xff0c;\u9002\u5408API\u4e34\u65f6\u8fc1\u79fb<\/p>\n<\/li>\n<li>\n<p>308 Permanent Redirect&#xff1a;\u6c38\u4e45\u91cd\u5b9a\u5411\u4e14\u4fdd\u6301\u65b9\u6cd5&#xff0c;\u89e3\u51b3301\u7684\u6a21\u7cca\u6027&#xff0c;\u9002\u5408API\u7248\u672c\u8fc1\u79fb<\/p>\n<\/li>\n<\/ul>\n<p>\u6838\u5fc3\u5efa\u8bae&#xff1a;<\/p>\n<li>\n<p>\u660e\u786e\u610f\u56fe&#xff1a;\u6839\u636e\u8d44\u6e90\u79fb\u52a8\u7684\u6c38\u4e45\u6027\u548c\u65b9\u6cd5\u4fdd\u6301\u9700\u6c42\u9009\u62e9\u5408\u9002\u7684\u72b6\u6001\u7801<\/p>\n<\/li>\n<li>\n<p>\u65b9\u6cd5\u5b89\u5168&#xff1a;\u5bf9API\u548c\u8868\u5355\u63d0\u4ea4\u4f7f\u7528307\/308\u786e\u4fdd\u65b9\u6cd5\u4fdd\u6301<\/p>\n<\/li>\n<li>\n<p>\u6027\u80fd\u4f18\u5316&#xff1a;\u5c3d\u91cf\u51cf\u5c11\u91cd\u5b9a\u5411\u94fe\u957f\u5ea6&#xff0c;\u5408\u7406\u8bbe\u7f6e\u7f13\u5b58\u5934<\/p>\n<\/li>\n<li>\n<p>\u5b89\u5168\u8003\u8651&#xff1a;\u9a8c\u8bc1\u91cd\u5b9a\u5411\u76ee\u6807&#xff0c;\u9632\u6b62\u5f00\u653e\u91cd\u5b9a\u5411\u6f0f\u6d1e<\/p>\n<\/li>\n<li>\n<p>\u76d1\u63a7\u7ef4\u62a4&#xff1a;\u5efa\u7acb\u76d1\u63a7\u544a\u8b66&#xff0c;\u5b9a\u671f\u5ba1\u8ba1\u91cd\u5b9a\u5411\u89c4\u5219<\/p>\n<\/li>\n","protected":false},"excerpt":{"rendered":"<p>\u7b2c10\u7ae0&#xff1a;\u91cd\u5b9a\u5411\u673a\u5236\u539f\u7406<br \/>\n10.1 \u91cd\u5b9a\u5411\u7684\u57fa\u672c\u6982\u5ff5<br \/>\n10.1.1 \u91cd\u5b9a\u5411\u7684\u5b9a\u4e49\u4e0e\u672c\u8d28<br \/>\nHTTP\u91cd\u5b9a\u5411\u662f\u4e00\u79cd\u5ba2\u6237\u7aef-\u670d\u52a1\u5668\u901a\u4fe1\u673a\u5236&#xff0c;\u5f53\u8bf7\u6c42\u7684\u8d44\u6e90\u4f4d\u7f6e\u53d1\u751f\u53d8\u5316\u6216\u9700\u8981\u4ece\u4e0d\u540c\u4f4d\u7f6e\u83b7\u53d6\u65f6&#xff0c;\u670d\u52a1\u5668\u6307\u793a\u5ba2\u6237\u7aef\u5411\u65b0\u5730\u5740\u91cd\u65b0\u53d1\u8d77\u8bf7\u6c42\u3002\u4ece\u672c\u8d28\u4e0a\u8bb2&#xff0c;\u91cd\u5b9a\u5411\u4e0d\u662f\u8d44\u6e90\u672c\u8eab&#xff0c;\u800c\u662f\u5173\u4e8e\u8d44\u6e90\u4f4d\u7f6e\u7684\\&#8221;\u5143\u4fe1\u606f\\&#8221;\u3002<br \/>\n\u6280\u672f\u5b9a\u4e49&#xff1a; \u534f\u8bae\u5c42\u9762&#xff1a;HTTP\/1.1\u89c4\u8303RFC 7231\u5b9a\u4e493xx\u72b6\u6001\u7801\u4e3a\\&#8221;\u91cd\u5b9a\u5411\u72b6\u6001\u7801\\&#8221;<\/p>\n","protected":false},"author":2,"featured_media":79129,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[151,43,191],"topic":[],"class_list":["post-79135","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-server","tag-http","tag-43","tag-191"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v20.3 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>HTTP \u72b6\u6001\u7801\uff1a\u5ba2\u6237\u7aef\u4e0e\u670d\u52a1\u5668\u7684\u901a\u4fe1\u8bed\u8a00\u2014\u2014\u7b2c\u4e09\u90e8\u5206\uff1a\u91cd\u5b9a\u5411\u7c7b\u72b6\u6001\u7801\uff083xx\uff09\u5168\u9762\u5256\u6790 - \u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.wsisp.com\/helps\/79135.html\" \/>\n<meta property=\"og:locale\" content=\"zh_CN\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"HTTP \u72b6\u6001\u7801\uff1a\u5ba2\u6237\u7aef\u4e0e\u670d\u52a1\u5668\u7684\u901a\u4fe1\u8bed\u8a00\u2014\u2014\u7b2c\u4e09\u90e8\u5206\uff1a\u91cd\u5b9a\u5411\u7c7b\u72b6\u6001\u7801\uff083xx\uff09\u5168\u9762\u5256\u6790 - \u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3\" \/>\n<meta property=\"og:description\" content=\"\u7b2c10\u7ae0&#xff1a;\u91cd\u5b9a\u5411\u673a\u5236\u539f\u7406 10.1 \u91cd\u5b9a\u5411\u7684\u57fa\u672c\u6982\u5ff5 10.1.1 \u91cd\u5b9a\u5411\u7684\u5b9a\u4e49\u4e0e\u672c\u8d28 HTTP\u91cd\u5b9a\u5411\u662f\u4e00\u79cd\u5ba2\u6237\u7aef-\u670d\u52a1\u5668\u901a\u4fe1\u673a\u5236&#xff0c;\u5f53\u8bf7\u6c42\u7684\u8d44\u6e90\u4f4d\u7f6e\u53d1\u751f\u53d8\u5316\u6216\u9700\u8981\u4ece\u4e0d\u540c\u4f4d\u7f6e\u83b7\u53d6\u65f6&#xff0c;\u670d\u52a1\u5668\u6307\u793a\u5ba2\u6237\u7aef\u5411\u65b0\u5730\u5740\u91cd\u65b0\u53d1\u8d77\u8bf7\u6c42\u3002\u4ece\u672c\u8d28\u4e0a\u8bb2&#xff0c;\u91cd\u5b9a\u5411\u4e0d\u662f\u8d44\u6e90\u672c\u8eab&#xff0c;\u800c\u662f\u5173\u4e8e\u8d44\u6e90\u4f4d\u7f6e\u7684&quot;\u5143\u4fe1\u606f&quot;\u3002 \u6280\u672f\u5b9a\u4e49&#xff1a; \u534f\u8bae\u5c42\u9762&#xff1a;HTTP\/1.1\u89c4\u8303RFC 7231\u5b9a\u4e493xx\u72b6\u6001\u7801\u4e3a&quot;\u91cd\u5b9a\u5411\u72b6\u6001\u7801&quot;\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.wsisp.com\/helps\/79135.html\" \/>\n<meta property=\"og:site_name\" content=\"\u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3\" \/>\n<meta property=\"article:published_time\" content=\"2026-02-28T23:36:43+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.wsisp.com\/helps\/wp-content\/uploads\/2026\/03\/20260228233637-69a37c054762a.png\" \/>\n<meta name=\"author\" content=\"admin\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"\u4f5c\u8005\" \/>\n\t<meta name=\"twitter:data1\" content=\"admin\" \/>\n\t<meta name=\"twitter:label2\" content=\"\u9884\u8ba1\u9605\u8bfb\u65f6\u95f4\" \/>\n\t<meta name=\"twitter:data2\" content=\"89 \u5206\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.wsisp.com\/helps\/79135.html\",\"url\":\"https:\/\/www.wsisp.com\/helps\/79135.html\",\"name\":\"HTTP \u72b6\u6001\u7801\uff1a\u5ba2\u6237\u7aef\u4e0e\u670d\u52a1\u5668\u7684\u901a\u4fe1\u8bed\u8a00\u2014\u2014\u7b2c\u4e09\u90e8\u5206\uff1a\u91cd\u5b9a\u5411\u7c7b\u72b6\u6001\u7801\uff083xx\uff09\u5168\u9762\u5256\u6790 - \u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3\",\"isPartOf\":{\"@id\":\"https:\/\/www.wsisp.com\/helps\/#website\"},\"datePublished\":\"2026-02-28T23:36:43+00:00\",\"dateModified\":\"2026-02-28T23:36:43+00:00\",\"author\":{\"@id\":\"https:\/\/www.wsisp.com\/helps\/#\/schema\/person\/358e386c577a3ab51c4493330a20ad41\"},\"breadcrumb\":{\"@id\":\"https:\/\/www.wsisp.com\/helps\/79135.html#breadcrumb\"},\"inLanguage\":\"zh-Hans\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.wsisp.com\/helps\/79135.html\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.wsisp.com\/helps\/79135.html#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"\u9996\u9875\",\"item\":\"https:\/\/www.wsisp.com\/helps\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"HTTP \u72b6\u6001\u7801\uff1a\u5ba2\u6237\u7aef\u4e0e\u670d\u52a1\u5668\u7684\u901a\u4fe1\u8bed\u8a00\u2014\u2014\u7b2c\u4e09\u90e8\u5206\uff1a\u91cd\u5b9a\u5411\u7c7b\u72b6\u6001\u7801\uff083xx\uff09\u5168\u9762\u5256\u6790\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.wsisp.com\/helps\/#website\",\"url\":\"https:\/\/www.wsisp.com\/helps\/\",\"name\":\"\u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3\",\"description\":\"\u9999\u6e2f\u670d\u52a1\u5668_\u9999\u6e2f\u4e91\u670d\u52a1\u5668\u8d44\u8baf_\u670d\u52a1\u5668\u5e2e\u52a9\u6587\u6863_\u670d\u52a1\u5668\u6559\u7a0b\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.wsisp.com\/helps\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"zh-Hans\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.wsisp.com\/helps\/#\/schema\/person\/358e386c577a3ab51c4493330a20ad41\",\"name\":\"admin\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"zh-Hans\",\"@id\":\"https:\/\/www.wsisp.com\/helps\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/gravatar.wp-china-yes.net\/avatar\/?s=96&d=mystery\",\"contentUrl\":\"https:\/\/gravatar.wp-china-yes.net\/avatar\/?s=96&d=mystery\",\"caption\":\"admin\"},\"sameAs\":[\"http:\/\/wp.wsisp.com\"],\"url\":\"https:\/\/www.wsisp.com\/helps\/author\/admin\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"HTTP \u72b6\u6001\u7801\uff1a\u5ba2\u6237\u7aef\u4e0e\u670d\u52a1\u5668\u7684\u901a\u4fe1\u8bed\u8a00\u2014\u2014\u7b2c\u4e09\u90e8\u5206\uff1a\u91cd\u5b9a\u5411\u7c7b\u72b6\u6001\u7801\uff083xx\uff09\u5168\u9762\u5256\u6790 - \u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.wsisp.com\/helps\/79135.html","og_locale":"zh_CN","og_type":"article","og_title":"HTTP \u72b6\u6001\u7801\uff1a\u5ba2\u6237\u7aef\u4e0e\u670d\u52a1\u5668\u7684\u901a\u4fe1\u8bed\u8a00\u2014\u2014\u7b2c\u4e09\u90e8\u5206\uff1a\u91cd\u5b9a\u5411\u7c7b\u72b6\u6001\u7801\uff083xx\uff09\u5168\u9762\u5256\u6790 - \u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3","og_description":"\u7b2c10\u7ae0&#xff1a;\u91cd\u5b9a\u5411\u673a\u5236\u539f\u7406 10.1 \u91cd\u5b9a\u5411\u7684\u57fa\u672c\u6982\u5ff5 10.1.1 \u91cd\u5b9a\u5411\u7684\u5b9a\u4e49\u4e0e\u672c\u8d28 HTTP\u91cd\u5b9a\u5411\u662f\u4e00\u79cd\u5ba2\u6237\u7aef-\u670d\u52a1\u5668\u901a\u4fe1\u673a\u5236&#xff0c;\u5f53\u8bf7\u6c42\u7684\u8d44\u6e90\u4f4d\u7f6e\u53d1\u751f\u53d8\u5316\u6216\u9700\u8981\u4ece\u4e0d\u540c\u4f4d\u7f6e\u83b7\u53d6\u65f6&#xff0c;\u670d\u52a1\u5668\u6307\u793a\u5ba2\u6237\u7aef\u5411\u65b0\u5730\u5740\u91cd\u65b0\u53d1\u8d77\u8bf7\u6c42\u3002\u4ece\u672c\u8d28\u4e0a\u8bb2&#xff0c;\u91cd\u5b9a\u5411\u4e0d\u662f\u8d44\u6e90\u672c\u8eab&#xff0c;\u800c\u662f\u5173\u4e8e\u8d44\u6e90\u4f4d\u7f6e\u7684\"\u5143\u4fe1\u606f\"\u3002 \u6280\u672f\u5b9a\u4e49&#xff1a; \u534f\u8bae\u5c42\u9762&#xff1a;HTTP\/1.1\u89c4\u8303RFC 7231\u5b9a\u4e493xx\u72b6\u6001\u7801\u4e3a\"\u91cd\u5b9a\u5411\u72b6\u6001\u7801\"","og_url":"https:\/\/www.wsisp.com\/helps\/79135.html","og_site_name":"\u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3","article_published_time":"2026-02-28T23:36:43+00:00","og_image":[{"url":"https:\/\/www.wsisp.com\/helps\/wp-content\/uploads\/2026\/03\/20260228233637-69a37c054762a.png"}],"author":"admin","twitter_card":"summary_large_image","twitter_misc":{"\u4f5c\u8005":"admin","\u9884\u8ba1\u9605\u8bfb\u65f6\u95f4":"89 \u5206"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.wsisp.com\/helps\/79135.html","url":"https:\/\/www.wsisp.com\/helps\/79135.html","name":"HTTP \u72b6\u6001\u7801\uff1a\u5ba2\u6237\u7aef\u4e0e\u670d\u52a1\u5668\u7684\u901a\u4fe1\u8bed\u8a00\u2014\u2014\u7b2c\u4e09\u90e8\u5206\uff1a\u91cd\u5b9a\u5411\u7c7b\u72b6\u6001\u7801\uff083xx\uff09\u5168\u9762\u5256\u6790 - \u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3","isPartOf":{"@id":"https:\/\/www.wsisp.com\/helps\/#website"},"datePublished":"2026-02-28T23:36:43+00:00","dateModified":"2026-02-28T23:36:43+00:00","author":{"@id":"https:\/\/www.wsisp.com\/helps\/#\/schema\/person\/358e386c577a3ab51c4493330a20ad41"},"breadcrumb":{"@id":"https:\/\/www.wsisp.com\/helps\/79135.html#breadcrumb"},"inLanguage":"zh-Hans","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.wsisp.com\/helps\/79135.html"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.wsisp.com\/helps\/79135.html#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"\u9996\u9875","item":"https:\/\/www.wsisp.com\/helps"},{"@type":"ListItem","position":2,"name":"HTTP \u72b6\u6001\u7801\uff1a\u5ba2\u6237\u7aef\u4e0e\u670d\u52a1\u5668\u7684\u901a\u4fe1\u8bed\u8a00\u2014\u2014\u7b2c\u4e09\u90e8\u5206\uff1a\u91cd\u5b9a\u5411\u7c7b\u72b6\u6001\u7801\uff083xx\uff09\u5168\u9762\u5256\u6790"}]},{"@type":"WebSite","@id":"https:\/\/www.wsisp.com\/helps\/#website","url":"https:\/\/www.wsisp.com\/helps\/","name":"\u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3","description":"\u9999\u6e2f\u670d\u52a1\u5668_\u9999\u6e2f\u4e91\u670d\u52a1\u5668\u8d44\u8baf_\u670d\u52a1\u5668\u5e2e\u52a9\u6587\u6863_\u670d\u52a1\u5668\u6559\u7a0b","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.wsisp.com\/helps\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"zh-Hans"},{"@type":"Person","@id":"https:\/\/www.wsisp.com\/helps\/#\/schema\/person\/358e386c577a3ab51c4493330a20ad41","name":"admin","image":{"@type":"ImageObject","inLanguage":"zh-Hans","@id":"https:\/\/www.wsisp.com\/helps\/#\/schema\/person\/image\/","url":"https:\/\/gravatar.wp-china-yes.net\/avatar\/?s=96&d=mystery","contentUrl":"https:\/\/gravatar.wp-china-yes.net\/avatar\/?s=96&d=mystery","caption":"admin"},"sameAs":["http:\/\/wp.wsisp.com"],"url":"https:\/\/www.wsisp.com\/helps\/author\/admin"}]}},"_links":{"self":[{"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/posts\/79135","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/comments?post=79135"}],"version-history":[{"count":0,"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/posts\/79135\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/media\/79129"}],"wp:attachment":[{"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/media?parent=79135"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/categories?post=79135"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/tags?post=79135"},{"taxonomy":"topic","embeddable":true,"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/topic?post=79135"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}