Implementera villkorlig GET genom att skicka stabila validatorer med varje dokument, kontrollera klientens sparade validatorer och returnera antingen det aktuella dokumentet eller en 304 Not Modified utan innehåll. Den här Mac-guiden visar processen med en syntetisk textsida.
Uppgiften är att se till att upprepade förfrågningar efter ett offentligt dokument bara överför dess innehåll när det behövs. Du kör en liten lokal Python-server, testar den från Terminal och simulerar en dokumentuppdatering. Exemplet körs lokalt; publicering är en separat uppgift.
Hur ska servern välja mellan 200 och 304?
En första förfrågan får svaret 200 OK, dokumentets innehåll och dess validatorer. En senare villkorlig förfrågan skickar tillbaka de sparade validatorerna. Om dokumentet är oförändrat returnerar servern 304 Not Modified, så att klienten kan återanvända det lagrade innehållet. Ändrat innehåll får ett nytt 200-svar. Villkorliga HTTP-förfrågningar.
För ett befintligt dokument som annars skulle returnera 200, följ den här beslutsordningen. If-None-Match har företräde framför If-Modified-Since; de är inte två oberoende tester. RFC 9110, fält för villkorliga förfrågningar.
| Förfrågansvillkor | Svar |
|---|---|
| Inga villkorliga headers | 200 med dokumentet |
If-None-Match matchar den aktuella taggen |
304, utan innehåll |
If-None-Match finns men matchar inte |
200, även om ett angivet datum antyder att inget har ändrats |
Endast en giltig If-Modified-Since anges och dokumentet inte är nyare |
304, utan innehåll |
Endast en giltig If-Modified-Since anges och dokumentet är nyare |
200 med dokumentet |
Utvärdera villkoren efter att du har valt dokumentet och fastställt dess normala svar. Ett dokument som saknas ska fortfarande ge det normala svaret för en saknad resurs. Ett 304 får inte innehålla innehåll eller trailers. Det måste ha tillämpliga cachemetadata, inklusive fälten Date, ETag, Vary, Cache-Control, Expires och Content-Location som skulle ha följt med svaret 200. RFC 9110, utvärdering av förvillkor och krav för 304.
Välj validatorer som beskriver det publicerade dokumentet
En ETag är en citerad versionsidentifierare, till exempel "doc-v1". Hur den skapas är en implementeringsfråga: ett revisions-ID eller en innehållshash kan fungera. Håll den oförändrad för samma representation och ändra den när representationen ändras. Exemplet använder två uttryckliga revisioner med fast innehåll. ETag-headern.
En stark tagg identifierar en specifik representation; en svag tagg har prefixet W/ och kan beskriva semantiskt likvärdigt innehåll vars byte skiljer sig åt. Villkorliga GET-jämförelser med If-None-Match använder svag jämförelse, så W/"doc-v1" matchar "doc-v1". Förfrågningar kan också innehålla en lista med taggar eller det fristående jokertecknet *. RFC 9110, If-None-Match.
Last-Modified anger när dokumentet ändrades. Använd en konsekvent fastställd ändringstid som tar hänsyn till relevanta dokumentkomponenter, i stället för tidpunkten för varje förfrågan. Tiden får inte vara senare än svaret Date. RFC 9110, generering av Last-Modified.
Exemplet tilldelar de två syntetiska revisionerna avsiktligt illustrativa publiceringstider: klockan 12 GMT den 1 respektive 2 januari 2024. Datumen är exempeldata. Använd underhållna publiceringsmetadata i ett verkligt publiceringsflöde. Last-Modified är mindre exakt än en ETag när det gäller att upptäcka innehållsskillnader och fungerar som ett användbart reservalternativ. Last-Modified-headern.
Välj en cachepolicy separat
Validatorer svarar på frågan om en lagrad kopia fortfarande stämmer. Färskhetspolicyn avgör när en cache behöver fråga servern. I den här guiden skickas Cache-Control: no-cache, vilket tillåter lagring men kräver att svaret valideras innan det återanvänds. Det gör beteendet vid upprepade förfrågningar lätt att se. no-store förbjuder lagring, medan max-age tillåter återanvändning så länge svaret är färskt. RFC 9111, svarsdirektiv för cachelagring.
För ett offentligt dokument där det går bra att en uppdatering blir synlig med en kort fördröjning kan en policy som max-age=60 passa. Det är ett illustrativt redaktionellt val: välj intervallet utifrån dokumentets uppdateringsbehov. Ett färskt cachat svar kan återanvändas utan kontakt med servern, så varje återbesök behöver inte resultera i en 304. RFC 9111, färskhet.
Kör ett komplett lokalt exempel på din Mac
Öppna Terminal och kontrollera förutsättningarna med följande kommandon. Använd Python 3.9 eller senare eftersom programmet använder str.removeprefix. Om python3 inte är tillgängligt installerar du Python 3 innan du fortsätter. Inga Python-paket från tredje part behövs.
python3 --version
curl --version
Skapa en ny arbetsmapp och spara följande kompletta program som server.py med en vanlig textredigerare för kod. Programmet hanterar bara /page.txt, implementerar GET och HEAD och väljer en dokumentrevision utifrån sitt kommandoradsargument. Pythons BaseHTTPRequestHandler dirigerar förfrågningar till motsvarande metoder och tillhandahåller hjälpfunktioner för att skriva svar. Dokumentation för Python HTTP-servern.
import argparse
import re
from datetime import datetime, timezone
from email.utils import format_datetime, parsedate_to_datetime
from http.server import BaseHTTPRequestHandler, HTTPServer
parser = argparse.ArgumentParser()
parser.add_argument("version", type=int, choices=(1, 2))
args = parser.parse_args()
BODY = {
1: b"Paper Shapes\nA circle and a square.\n",
2: b"Paper Shapes\nA circle, a square, and a triangle.\n",
}[args.version]
ETAG = '"doc-v%d"' % args.version
MODIFIED = datetime(2024, 1, args.version, 12, tzinfo=timezone.utc)
LAST_MODIFIED = format_datetime(MODIFIED, usegmt=True)
TOKEN = r'(?:W/)?"[\x21\x23-\x7e\x80-\xff]*"'
TAG_LIST = re.compile(rf'[ \t]*(?:{TOKEN})?(?:[ \t]*,[ \t]*(?:{TOKEN})?)*[ \t]*')
def tag_matches(value):
if value.strip() == "*":
return True
if not TAG_LIST.fullmatch(value):
return False
return any(tag.removeprefix("W/") == ETAG
for tag in re.findall(TOKEN, value))
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
self.serve_document()
def do_HEAD(self):
self.serve_document()
def serve_document(self):
if self.path != "/page.txt":
self.send_error(404)
return
not_modified = False
tags = self.headers.get_all("If-None-Match")
if tags is not None:
not_modified = tag_matches(",".join(tags))
else:
dates = self.headers.get_all("If-Modified-Since", [])
if len(dates) == 1:
try:
since = parsedate_to_datetime(dates[0])
if since.tzinfo is None:
since = since.replace(tzinfo=timezone.utc)
not_modified = MODIFIED <= since
except (TypeError, ValueError, OverflowError):
pass
self.send_response(304 if not_modified else 200)
self.send_header("ETag", ETAG)
self.send_header("Cache-Control", "no-cache")
if not not_modified:
self.send_header("Last-Modified", LAST_MODIFIED)
self.send_header("Content-Type", "text/plain; charset=utf-8")
self.send_header("Content-Length", str(len(BODY)))
self.end_headers()
if not not_modified and self.command == "GET":
self.wfile.write(BODY)
if __name__ == "__main__":
HTTPServer(("127.0.0.1", 8000), Handler).serve_forever()
Det här är en lokal demonstration av de två namngivna validatorerna, inte en komplett HTTP-serverstack. Datumkonverteringen använder Pythons tillåtande e-postdatumtolkare; en driftsatt implementation bör använda HTTP-specifik datumvalidering och hantering av villkorliga förfrågningar. Python dokumenterar vilka indata tolkaren godtar och vilka undantag den kan ge. Pythons verktyg för e-postdatum.
Gå i Terminal till mappen som innehåller server.py och starta revision 1. Låt det fönstret vara öppet medan servern körs. Om port 8000 redan används väljer du en annan ledig port i programmet och använder samma port i alla test-URL:er.
python3 server.py 1
Testa oförändrat innehåll, ändrat innehåll och företrädesordningen
Öppna ett annat Terminal-fönster. Följande förfrågningar visar svarshuvuden och eventuellt returnerat innehåll. Alternativet -i inkluderar headers, -H anger en förfrågningsheader och --max-time begränsar överföringstiden. curl-manualen.
Fastställ det första svaret
curl --max-time 5 -sS -i http://127.0.0.1:8000/page.txt
Förvänta dig 200 OK, den syntetiska sidtexten, ETag: "doc-v1" och Last-Modified: Mon, 01 Jan 2024 12:00:00 GMT. Anteckna de värden som faktiskt returneras när du följer den här proceduren för ett annat dokument; validatorer hör till det specifika svaret.
Validera på nytt med varje validator
curl --max-time 5 -sS -i \
-H 'If-None-Match: "doc-v1"' \
http://127.0.0.1:8000/page.txt
curl --max-time 5 -sS -i \
-H 'If-Modified-Since: Mon, 01 Jan 2024 12:00:00 GMT' \
http://127.0.0.1:8000/page.txt
Båda förfrågningarna ska returnera 304 Not Modified utan sidtexten. Svaret behåller ETag och Cache-Control; Pythons svarshjälpfunktion lägger till Date. Om du upprepar den första förfrågan med W/"doc-v1" ska den också returnera 304. Exemplet tolkar citerade taggar i stället för att dela upp dem blint vid kommatecken, eftersom en tagg kan innehålla ett kommatecken.
Visa att ETag har företräde
curl --max-time 5 -sS -i \
-H 'If-None-Match: "older-revision"' \
-H 'If-Modified-Since: Wed, 03 Jan 2024 12:00:00 GMT' \
http://127.0.0.1:8000/page.txt
Förvänta dig 200 OK med dokumentet. Taggen matchar inte, så det senare datumet får inte utlösa en 304. Den här avsiktligt motstridiga förfrågan avslöjar en implementation som felaktigt kombinerar de två testerna.
Simulera publicering av nästa revision
Tryck på Control-C i serverfönstret och starta sedan revision 2. Behåll samma förfrågnings-URL.
python3 server.py 2
Upprepa de två tidigare valideringsförfrågningarna med revision 1:s validatorer. Båda bör nu få svaret 200, den uppdaterade meningen som nämner en triangel, ETag: "doc-v2" och ändringsdatumet den 2 januari. Om du upprepar en förfrågan med If-None-Match: "doc-v2" ska den returnera 304.
Resultaten ger dig en godkännandesekvens: första nedladdningen, validering av oförändrat innehåll, motstridiga validatorer, ändrat dokument och därefter validering av den nya, oförändrade revisionen. Exemplet testades med 24 lokala kontroller för båda revisionerna, inklusive svaga taggar, tagglistor, jokerteckenmatchning, HEAD och saknade sökvägar.
Varför förfrågningar från webbläsare och Google kan se annorlunda ut
Webbläsare hanterar normalt HTTP-validatorer genom sin cache. WHATWG Fetch-algoritmen lägger till sparade validatorer när svaret valideras på nytt och uppdaterar, efter en lyckad 304, den lagrade response-headern och returnerar det lagrade svaret. Därför kan JavaScript i applikationen få ett användbart 200-svar och dokumentinnehåll även om nätverksvalideringen fick svaret 304. Fetch-standarden, hämtning från HTTP-nätverk eller cache.
Börja med de uttryckliga curl-förfrågningarna ovan när du felsöker serverbeteende. Om du manuellt anger villkorliga headers i JavaScript i webbläsaren ändras testet: Fetch växlar en förfrågan med standardcachning som innehåller sådana headers till no-store. Låt webbläsaren hantera sin cache när du testar vanlig validering. Fetch-standarden, förfrågans cacheläge.
Googles crawlinfrastruktur stöder båda validatorparen och rekommenderar ETag eftersom de undviker problem med datumformat. Dokumentationen säger också att infrastrukturen inte stöder andra HTTP-cachelagringsdirektiv. Välj webbläsarens cachepolicy separat från dina förväntningar på hur Google crawlar på nytt. Översikt över Googles crawlers och hämtare.
Google skickar inte villkorliga headers vid varje crawl-försök. Googles sökdokumentation förklarar att en 304 tillåter återanvändning av tidigare crawlat innehåll, medan nyare innehåll kan få en 200 med den uppdaterade innehållstexten. Se till att vanliga ovillkorliga förfrågningar fungerar; en crawlerförfrågan utan validatorer är inget bevis på att din implementation misslyckades. Felsök crawl-fel i Google Sök.
Innan du använder det här mönstret för ett publicerat dokument upprepar du godkännandesekvensen mot dokumentets faktiska URL. Kontrollera att oförändrade revisioner behåller sina validatorer, att redigeringar uppdaterar dem och att ett 304 inte innehåller dokumentets innehåll. Målet är att dokument kan återanvändas korrekt samtidigt som ändrat innehåll förblir tillgängligt.
Källor
- RFC 9110: HTTP-semantik — IETF / RFC Editor.
- RFC 9111: HTTP-cachelagring — IETF / RFC Editor.
- Fetch-standarden — WHATWG.
- Villkorliga HTTP-förfrågningar — MDN Web Docs.
- ETag-headern — MDN Web Docs.
- Last-Modified-headern — MDN Web Docs.
- http.server — HTTP-servrar — Python Software Foundation.
- email.utils: Diverse verktyg — Python Software Foundation.
- curl — Så använder du curl — curl-projektet.
- Översikt över Googles crawlers och hämtare (user agents) — Google.
- Felsök crawl-fel i Google Sök — Google Search Central.