Cas d'utilisation de fonction Edge
Les cas d'utilisation suivants sont fournis uniquement à titre d'exemples et ne sont pas destinés à une duplication exacte dans votre environnement.
Soyez prudent lorsque vous testez l'un des codes suivants, car il pourrait entraîner une interruption du service.
Test A/B
Vous pouvez créer une fonction Edge CIS pour contrôler les tests A/B.
addEventListener('fetch', event => {
event.respondWith(fetchAndApply(event.request))
})
async function fetchAndApply(request) {
const name = 'experiment-0'
let group // 'control' or 'test', set below
let isNew = false // is the group newly-assigned?
// Determine which group this request is in.
const cookie = request.headers.get('Cookie')
if (cookie && cookie.includes(`${name}=control`)) {
group = 'control'
} else if (cookie && cookie.includes(`${name}=test`)) {
group = 'test'
} else {
// 50/50 Split
group = Math.random() < 0.5 ? 'control' : 'test'
isNew = true
}
// We'll prefix the request path with the experiment name. This way,
// the origin server merely has to have two copies of the site under
// top-level directories named "control" and "test".
let url = new URL(request.url)
// Note that `url.pathname` always begins with a `/`, so we don't
// need to explicitly add one after `${group}`.
url.pathname = `/${group}${url.pathname}`
const modifiedRequest = new Request(url, {
method: request.method,
headers: request.headers
})
const response = await fetch(modifiedRequest)
if (isNew) {
// The experiment was newly-assigned, so add a Set-Cookie header
// to the response.
const newHeaders = new Headers(response.headers)
newHeaders.append('Set-Cookie', `${name}=${group}; path=/`)
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers: newHeaders
})
} else {
// Return response unmodified.
return response
}
}
Ajout d'un en-tête de réponse
Pour modifier les en-têtes de réponse, faites d'abord une copie de la réponse afin de la rendre modifiable. Vous pouvez ensuite utiliser l'interface des en-têtes pour ajouter, modifier ou supprimer des en-têtes.
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
/**
* Set the `x-my-header` header
* @param {Request} request
*/
async function handleRequest(request) {
let response = await fetch(request);
// Make the headers mutable by re-constructing the Response.
response = new Response(response.body, response);
response.headers.set('x-my-header', 'custom value');
return response;
}
Agrégation de plusieurs demandes
Cet exemple effectue plusieurs demandes auprès de différents points d'extrémité de l'API, regroupe les réponses et les renvoie sous la forme d'une réponse unique.
addEventListener('fetch', event => {
event.respondWith(fetchAndApply(event.request))
})
/**
* Make multiple requests,
* aggregate the responses and
* send it back as a single response
*/
async function fetchAndApply(request) {
const init = {
method: 'GET',
headers: {'Authorization': 'XXXXXX'}
}
const [btcResp, ethResp, ltcResp] = await Promise.all([
fetch('https://api.coinbase.com/v2/prices/BTC-USD/spot', init),
fetch('https://api.coinbase.com/v2/prices/ETH-USD/spot', init),
fetch('https://api.coinbase.com/v2/prices/LTC-USD/spot', init)
])
const btc = await btcResp.json()
const eth = await ethResp.json()
const ltc = await ltcResp.json()
let combined = {}
combined['btc'] = btc['data'].amount
combined['ltc'] = ltc['data'].amount
combined['eth'] = eth['data'].amount
const responseInit = {
headers: {'Content-Type': 'application/json'}
}
return new Response(JSON.stringify(combined), responseInit)
}
Routage conditionnel
Le moyen le plus simple de fournir un contenu différent en fonction de l'appareil utilisé est de réécrire le site URL de la requête en fonction de la condition qui vous intéresse. Voir les exemples suivants.
Type de dispositif
addEventListener('fetch', event => {
event.respondWith(fetchAndApply(event.request))
})
async function fetchAndApply(request) {
let uaSuffix = ''
const ua = request.headers.get('user-agent')
if (ua.match(/iphone/i) || ua.match(/ipod/i)) {
uaSuffix = '/mobile'
} else if (ua.match(/ipad/i)) {
uaSuffix = '/tablet'
}
return fetch(request.url + uaSuffix, request)
}
En-têtes personnalisés
addEventListener('fetch', event => {
event.respondWith(fetchAndApply(event.request))
})
async function fetchAndApply(request) {
let suffix = ''
//Assuming that the client is sending a custom header
const cryptoCurrency = request.headers.get('X-Crypto-Currency')
if (cryptoCurrency === 'BTC') {
suffix = '/btc'
} else if (cryptoCurrency === 'XRP') {
suffix = '/xrp'
} else if (cryptoCurrency === 'ETH') {
suffix = '/eth'
}
return fetch(request.url + suffix, request)
}
Protection des liens dynamiques
Vous pouvez utiliser des fonctions Edge CIS pour protéger vos liens dynamiques sur vos propriétés Web.
addEventListener('fetch', event => {
event.respondWith(fetchAndApply(event.request))
})
/**
* If the browser is requesting an image and
* the referer does not match your host
* we redirect the request to your page
*/
async function fetchAndApply(request) {
// Fetch the response.
let response = await fetch(request)
// If it's an image, engage hotlink protection based on the
// Referer header.
let referer = request.headers.get('Referer')
let contentType = response.headers.get('Content-Type') || ''
if (referer && contentType.startsWith('image/')) {
// It's an image and there's a Referer. Verify that the
// hostnames match.
if (new URL(referer).hostname !==
new URL(request.url).hostname) {
// Hosts don't match. This is a hotlink. Redirect the
// user to our homepage.
return new Response('', {
status: 302,
headers: {
'Location': '/'
}
})
}
}
// Everything is fine, return the response normally.
return response
}
Réponses sans origine
Vous pouvez renvoyer les réponses directement à partir du bord (Edge). Il n'est pas nécessaire d'envoyer une demande à votre origine.
Ignorer les demandes HTTP POST et PUT
Ignorez les demandes HTTP POST et PUT. Ce fragment de code permet à toutes les autres demandes de traverser à l'origine.
addEventListener('fetch', event => {
event.respondWith(fetchAndApply(event.request))
})
async function fetchAndApply(request) {
if (request.method === 'POST' || request.method === 'PUT') {
return new Response('Sorry, this page is not available.',
{ status: 403, statusText: 'Forbidden' })
}
return fetch(request)
}
Refuser un moteur de balayage ou un moteur d'exploration
Protégez votre origine des moteurs de balayage ou d'exploration indésirables. Dans ce cas, si l'agent utilisateur est un "annoying_robot", la fonction Edge renvoie la réponse au lieu d'envoyer la demande à l'origine.
addEventListener('fetch', event => {
event.respondWith(fetchAndApply(event.request))
})
async function fetchAndApply(request) {
if (request.headers.get('user-agent').includes('annoying_robot')) {
return new Response('Sorry, this page is not available.',
{ status: 403, statusText: 'Forbidden' })
}
return fetch(request)
}
Empêcher une adresse IP spécifique de se connecter
Ajoutez des adresses IP à la liste rouge. Ce bout de code empêche une IP spécifique (dans ce cas 225.0.0.1) de se connecter à l'origine.
addEventListener('fetch', event => {
event.respondWith(fetchAndApply(event.request))
})
async function fetchAndApply(request) {
if (request.headers.get('cf-connecting-ip') === '225.0.0.1') {
return new Response('Sorry, this page is not available.',
{ status: 403, statusText: 'Forbidden' })
}
return fetch(request)
}
Demandes POST
Lecture du contenu d'une demande POST HTTP :
addEventListener('fetch', event => {
event.respondWith(fetchAndApply(event.request))
})
/**
* Making a curl request that looks like
* curl -X POST --data 'key=world' example.com
* or
* curl -X POST --form 'key=world' example.com
*/
async function fetchAndApply(request) {
try {
const postData = await request.formData();
return new Response(`hello ${postData.get('key')}`)
} catch (err) {
return new Response('could not unbundle post data')
}
}
Création d'une requête POST HTTP à partir d'une fonction Edge :
addEventListener('fetch', event => {
event.respondWith(fetchAndApply(event.request))
})
/**
* Create a POST request with body 'key=world'
* Here, we are assuming that example.com acknowledges the POST request with body key=world
*/
async function fetchAndApply(request) {
let content = 'key=world'
let headers = {
'Content-Type': 'application/x-www-form-urlencoded'
}
const init = {
method: 'POST',
headers: headers,
body: content
}
const response = await fetch('https://example.com', init)
console.log('Got response', response)
return response
}
Demandes signées
Une méthode d'authentification d'URL commune appelée signature de requête peut être implémentée dans une fonction Edge à l'aide de l'API Crypto Web.
Dans l'exemple présenté ici, CIS authentifie le chemin d'accès à URL ainsi que l'horodatage qui l'accompagne en utilisant un code d'authentification des messages basé sur le hachage (HMAC) avec un algorithme de condensé SHA-256. Pour extraire une ressource authentifiée, l'agent utilisateur doit fournir le chemin d'accès, l'horodatage d'expiration et le code HMAC corrects à l'aide des paramètres de requête. Si l'un de ces trois paramètres est falsifié, la demande échoue.
L'authenticité de l'horodatage d'expiration est couverte par le HMAC, ce qui signifie que vous pouvez être sûr que l'horodatage fourni par l'utilisateur est correct si le HMAC l'est, et lorsque l' URL e expire. Vous pouvez également déterminer si un certificat de conformité ( URL ) en leur possession est expiré.
Vérification des demandes signées
Cet exemple vérifie le HMAC pour toute requête URL dont le nom de chemin commence par /verify/.
Pour faciliter le débogage, cette fonction Edge renvoie un message 403 si le URL ou le HMAC n'est pas valide, ou si le URL a expiré. Vous souhaiterez peut-être renvoyer le 404 dans une implémentation réelle.
addEventListener('fetch', event => {
event.respondWith(verifyAndFetch(event.request))
})
async function verifyAndFetch(request) {
const url = new URL(request.url)
// If the path doesn't begin with our protected prefix, just pass the request
// through.
if (!url.pathname.startsWith("/verify/")) {
return fetch(request)
}
// Make sure we have the minimum necessary query parameters.
if (!url.searchParams.has("mac") || !url.searchParams.has("expiry")) {
return new Response("Missing query parameter", { status: 403 })
}
// We'll need some super-secret data to use as a symmetric key.
const encoder = new TextEncoder()
const secretKeyData = encoder.encode("my secret symmetric key")
const key = await crypto.subtle.importKey(
"raw", secretKeyData,
{ name: "HMAC", hash: "SHA-256" },
false, [ "verify" ]
)
// Extract the query parameters we need and run the HMAC algorithm on the
// parts of the request we're authenticating: the path and the expiration
// timestamp.
const expiry = Number(url.searchParams.get("expiry"))
const dataToAuthenticate = url.pathname + expiry
// The received MAC is Base64-encoded, so we have to go to some trouble to
// get it into a buffer type that crypto.subtle.verify() can read.
const receivedMacBase64 = url.searchParams.get("mac")
const receivedMac = byteStringToUint8Array(atob(receivedMacBase64))
// Use crypto.subtle.verify() to guard against timing attacks. Since HMACs use
// symmetric keys, we could implement this by calling crypto.subtle.sign() and
// then doing a string comparison -- this is insecure, as string comparisons
// bail out on the first mismatch, which leaks information to potential
// attackers.
const verified = await crypto.subtle.verify(
"HMAC", key,
receivedMac,
encoder.encode(dataToAuthenticate)
)
if (!verified) {
const body = "Invalid MAC"
return new Response(body, { status: 403 })
}
if (Date.now() > expiry) {
const body = `URL expired at ${new Date(expiry)}`
return new Response(body, { status: 403 })
}
// We've verified the MAC and expiration time; we're good to pass the request
// through.
return fetch(request)
}
// Convert a ByteString (a string whose code units are all in the range
// [0, 255]), to a Uint8Array. If you pass in a string with code units larger
// than 255, their values overflow!
function byteStringToUint8Array(byteString) {
const ui = new Uint8Array(byteString.length)
for (let i = 0; i < byteString.length; ++i) {
ui[i] = byteString.charCodeAt(i)
}
return ui
}
Génération de demandes signées
En règle générale, les demandes signées vous sont remises d'une manière extrabudgétaire, par exemple par courrier électronique, ou vous en générez une vous-même si vous disposez de la clé symétrique. Vous pouvez également générer les demandes signées dans une fonction Edge.
Pour toute demande URL qui commence par /generate/, CIS remplace /generate/ par /verify/, signe le chemin résultant avec son horodatage, et renvoie la version complète et signée de URL dans le corps de
la réponse.
addEventListener('fetch', event => {
const url = new URL(event.request.url)
const prefix = "/generate/"
if (url.pathname.startsWith(prefix)) {
// Replace the "/generate/" path prefix with "/verify/", which we
// use in the first example to recognize authenticated paths.
url.pathname = `/verify/${url.pathname.slice(prefix.length)}`
event.respondWith(generateSignedUrl(url))
} else {
event.respondWith(fetch(event.request))
}
})
async function generateSignedUrl(url) {
// We'll need some super-secret data to use as a symmetric key.
const encoder = new TextEncoder()
const secretKeyData = encoder.encode("my secret symmetric key")
const key = await crypto.subtle.importKey(
"raw", secretKeyData,
{ name: "HMAC", hash: "SHA-256" },
false, [ "sign" ]
)
// Signed requests expire after one minute. Note that you could choose
// expiration durations dynamically, depending on, e.g. the path or a query
// parameter.
const expirationMs = 60000
const expiry = Date.now() + expirationMs
const dataToAuthenticate = url.pathname + expiry
const mac = await crypto.subtle.sign(
"HMAC", key,
encoder.encode(dataToAuthenticate)
)
// `mac` is an ArrayBuffer, so we need to jump through a couple hoops to get
// it into a ByteString, then a Base64-encoded string.
const base64Mac = btoa(String.fromCharCode(...new Uint8Array(mac)))
url.searchParams.set("mac", base64Mac)
url.searchParams.set("expiry", expiry)
return new Response(url)
}
Réponses en continu
Un script de fonction Edge n'a pas besoin de préparer l'ensemble de son corps de réponse avant d'envoyer une réponse à event.respondWith(). En utilisant un TransformStream,, vous pouvez diffuser un corps de réponse après avoir envoyé
l'en-tête de la réponse (par exemple, la ligne d'état et les en-têtes d' HTTP ). Cette rationalisation aide l' CIS, à minimiser le temps de réponse du visiteur et la quantité de mise en mémoire tampon qui doit être effectuée dans le script
de la fonction Edge.
Réduire la mise en mémoire tampon est particulièrement important si vous devez traiter ou transformer des corps de réponse supérieurs à la limite de mémoire de la fonction Edge. Dans ces cas, la diffusion en continu est la seule stratégie de mise en oeuvre réalisable.
Le service CIS Edge Function est déjà diffusé par défaut dans la mesure du possible. Ces API ne sont nécessaires que si vous souhaitez modifier le corps de la réponse d'une manière ou d'une autre, tout en conservant le comportement de diffusion en flux. Si votre script de fonction Edge renvoie les réponses de sous-demande au client textuellement, sans lire leur corps, la gestion du corps est déjà optimale.
Passe-système en continu
Commencez avec l'exemple de passe-système minimal suivant.
addEventListener("fetch", event => {
event.respondWith(fetchAndStream(event.request))
})
async function fetchAndStream(request) {
// Fetch from origin server.
let response = await fetch(request)
// Create an identity TransformStream (a.k.a. a pipe).
// The readable side becomes our new response body.
let { readable, writable } = new TransformStream()
// Start pumping the body. NOTE: No await!
streamBody(response.body, writable)
// ... and deliver our Response while that's running.
return new Response(readable, response)
}
async function streamBody(readable, writable) {
let reader = readable.getReader()
let writer = writable.getWriter()
while (true) {
const { done, value } = await reader.read()
if (done) break
// Optionally transform value's bytes here.
await writer.write(value)
}
await writer.close()
}
D'importants détails doivent être pris en compte :
- Bien que
streamBody()soit une fonction asynchrone, vous ne voulez pas appelerawaitsur cette fonction afin qu'elle ne bloque pas la progression de l'appel à la fonctionfetchAndStream(). La fonction continue de s'exécuter de manière asynchrone pendant la période au cours de laquelle elle a une opérationreader.read()ouwriter.write()en attente. - Contre-pression:
awaitopération de lecture avant d'appeler l'opération d'écriture. De même,awaitl'opération d'écriture avant d'appeler l'opération de lecture suivante. Ce masque propage la contre-pression à l'origine. - Achèvement : appeler
writer.close()à la fin, ce qui signale à l'exécution de la fonction Edge que vous avez fini d'écrire ce corps de réponse. Après avoir été appelé,streamBody()se termine - si ce comportement n'est pas souhaitable, transmettez la promesse renvoyée àFetchEvent.waitUntil(). Si votre script n'appelle jamaiswriter.close(), le corps apparaît tronqué au moment de l'exécution, mais il peut continuer de fonctionner comme prévu.
Agrégation et diffusion de plusieurs demandes
Ce cas d'utilisation est similaire à la recette de l'agrégation de plusieurs requêtes, mais cette fois, vous commencez à écrire la réponse dès que vous avez vérifié que chaque sous-requête a abouti - il n'est pas nécessaire d'attendre les corps de réponse.
addEventListener('fetch', event => {
event.respondWith(fetchAndApply(event.request))
})
/**
* Make multiple requests,
* aggregate the responses and
* stream it back as a single response.
*/
async function fetchAndApply(request) {
const requestInit = {
headers: { "Authorization": "XXXXXX" }
}
const fetches = [
"https://api.coinbase.com/v2/prices/BTC-USD/spot",
"https://api.coinbase.com/v2/prices/ETH-USD/spot",
"https://api.coinbase.com/v2/prices/LTC-USD/spot"
].map(url => fetch(url, requestInit))
// Wait for each fetch() to complete.
let responses = await Promise.all(fetches)
// Make sure every subrequest succeeded.
if (!responses.every(r => r.ok)) {
return new Response(null, { status: 502 })
}
// Create a pipe and stream the response bodies out
// as a JSON array.
let { readable, writable } = new TransformStream()
streamJsonBodies(responses.map(r => r.body), writable)
return new Response(readable)
}
async function streamJsonBodies(bodies, writable) {
// We're presuming these bodies are JSON, so we
// concatenate them into a JSON array. Since we're
// streaming, we can't use JSON.stringify(), but must
// instead manually write an initial '[' before the
// bodies, interpolate ',' between them, and write a
// terminal ']' after them.
let writer = writable.getWriter()
let encoder = new TextEncoder()
await writer.write(encoder.encode("[\n"))
for (let i = 0; i < bodies.length; ++i) {
if (i > 0) {
await writer.write(encoder.encode(",\n"))
}
writer.releaseLock()
await bodies[i].pipeTo(writable, { preventClose: true })
writer = writable.getWriter()
}
await writer.write(encoder.encode("]"))
await writer.close()
}
Le moteur d'exécution s'attend à recevoir TypedArrays du côté lisible du TransformStream. Par conséquent, vous ne transmettez jamais une chaîne à writer.write(), uniquement Uint 8Arrays. Si vous devez écrire une chaîne, utilisez
un TextEncoder.
Equilibreur de charge personnalisé avec fonctions Edge
L'équilibrage de charge vous aide à maintenir l'évolutivité et la fiabilité des sites web que vous hébergez. Vous pouvez utiliser les fonctions Edge pour créer des répartiteurs de charge personnalisés, conçus pour répondre à vos besoins spécifiques.
const US_HOSTS = [
"0.us.example.com",
"1.us.example.com",
"2.us.example.com"
];
const IN_HOSTS = [
"0.in.example.com",
"1.in.example.com",
"2.in.example.com"
];
var COUNTRIES_MAP = {
IN: IN_HOSTS,
PK: IN_HOSTS,
BD: IN_HOSTS,
SL: IN_HOSTS,
NL: IN_HOSTS
}
addEventListener('fetch', event => {
var url = new URL(event.request.url);
var countryCode = event.request.headers.get('CF-IPCountry');
var hostnames = US_HOSTS;
if (COUNTRIES_MAP[countryCode]) {
hostnames = COUNTRIES_MAP[countryCode];
}
// Randomly pick the next host
var primary = hostnames[getRandomInt(hostnames.length)];
var primaryUrl = new URL(event.request.url);
primaryUrl.hostname = hostnames[primary];
// Fallback if there is no response within timeout
var timeoutId = setTimeout(function() {
var backup;
do {
// Naive solution to pick a backup host
backup = getRandomInt(hostnames.length);
} while(backup === primary);
var backupUrl = new URL(event.request.url);
backupUrl.hostname = hostnames[backup];
event.respondWith(fetch(backupUrl));
}, 2000 /* 2 seconds */);
fetch(primaryUrl)
.then(function(response) {
clearTimeout(timeoutId);
event.respondWith(response);
});
});
function getRandomInt(max) {
return Math.floor(Math.random() * max);
}
Mise en cache à l'aide de la fonction d'extraction
Déterminez comment mettre en cache une ressource en définissant des durées de vie (TTL), des clés de mémoire cache personnalisées et des en-têtes de cache dans une demande d'extraction.
async function handleRequest(request) {
const url = new URL(request.url)
// Only use the path for the cache key, removing query strings
// and always store using HTTPS, for example, https://www.example.com/file-uri-here
const someCustomKey = `https://${url.hostname}${url.pathname}`
let response = await fetch(request, {
cf: {
// Always cache this fetch regardless of content type
// for a max of 5 seconds before revalidating the resource
cacheTtl: 5,
cacheEverything: true,
//Enterprise only feature, see Cache API for other plans
cacheKey: someCustomKey,
},
})
// Reconstruct the Response object to make its headers mutable.
response = new Response(response.body, response)
//Set cache control headers to cache on browser for 25 minutes
response.headers.set("Cache-Control", "max-age=1500")
return response
}
addEventListener("fetch", event => {
return event.respondWith(handleRequest(event.request))
})
Mise en cache des ressources HTML
// Force CIS to cache an asset
fetch(event.request, { cf: { cacheEverything: true } })
La définition du paramètre "Cache Everything" comme niveau de cache remplace le niveau de mise en cache par défaut de l'actif. Pour la durée de vie (TTL), CIS utilise toujours les en-têtes définis par l'origine.
Clés de mémoire cache personnalisées
Cette fonction ne peut être utilisée que par des clients d'entreprise.
La clé de cache d'une demande est ce qui détermine si deux demandes sont "identiques" à des fins de mise en cache. Si une demande possède la même clé de mémoire cache qu'une demande précédente, la même réponse mise en cache peut être transmise pour les deux.
// Set cache key for this request to "some-string".
fetch(event.request, { cf: { cacheKey: "some-string" } })
CIS calcule la clé de cache d'une requête en se basant sur l'adresse URL, mais il se peut que vous souhaitiez que des URL différentes soient traitées comme si elles étaient identiques à des fins de mise en cache. Par exemple, si le contenu de votre site web est hébergé à la fois sur Amazon S3 et Google Cloud Storage (vous avez le même contenu aux deux endroits), et que vous utilisez ensuite une fonction d'arête pour équilibrer aléatoirement les deux. Cependant, vous ne voulez pas mettre en cache deux copies de votre contenu. Vous pouvez utiliser des clés de cache personnalisées pour mettre en cache la demande originale URL plutôt que la sous-demande URL.
addEventListener("fetch", (event) => {
let url = new URL(event.request.url)
if (Math.random() < 0.5) {
url.hostname = "example.s3.amazonaws.com"
}
else {
url.hostname = "example.storage.googleapis.com"
}
let request = new Request(url, event.request)
event.respondWith(
fetch(request, {
cf: { cacheKey: event.request.url },
})
)
})
N'oubliez pas que les fonctions de bordure qui opèrent pour le compte de différentes zones ne peuvent pas affecter le cache de l'autre. Vous ne pouvez remplacer les clés de cache que lorsque vous effectuez des requêtes dans votre propre zone
(dans l'exemple précédent, event.request.url était la clé stockée), ou des requêtes vers des hôtes qui ne se trouvent pas sur CIS. Lorsque vous faites une requête vers une autre zone d' CIS s (par exemple, une zone qui appartient
à un autre client d' CIS ), cette zone contrôle entièrement la façon dont son propre contenu est mis en cache dans CIS; vous ne pouvez pas la remplacer.
Remplacement en fonction du code de réponse d'origine
Cette fonction ne peut être utilisée que par des clients d'entreprise.
// Force response to be cached for 86400 seconds for 200 status
// codes, 1 second for 404, and do not cache 500 errors.
fetch(request, {
cf: { cacheTtlByStatus: { "200-299": 86400, 404: 1, "500-599": 0 } },
})
Cette option est une version de la fonction cacheTtl qui choisit une durée de vie (TTL) en fonction du code d'état de la réponse et ne définit pas automatiquement cacheEverything: true. Si la réponse à cette demande
possède un code d'état concordant, CIS effectue la mise en cache pendant la durée indiquée et remplace les directives de cache envoyées par l'origine.
Interprétation de la durée de vie (TTL)
Les valeurs TTL suivantes sont interprétées par CIS.
- Valeurs positives : Indiquez, en secondes, pendant combien de tempsCIS doit mettre en cache l'actif.
0: L'actif est mis en cache mais arrive à expiration immédiatement (revalidation à partir de l'origine à chaque fois).-1ou toute autre valeur négative : Demande à CIS de ne pas effectuer de mise en cache.
API de cache
Mise en cache à l'aide de l'API de cache CIS. Cet exemple permet également de mettre en cache des demandes POST.
const someOtherHostname = "my.herokuapp.com"
async function handleRequest(event) {
const request = event.request
const cacheUrl = new URL(request.url)
// Hostname for a different zone
cacheUrl.hostname = someOtherHostname
const cacheKey = new Request(cacheUrl.toString(), request)
const cache = caches.default
// Get this request from this zone's cache
let response = await cache.match(cacheKey)
if (!response) {
//If not in cache, get it from origin
response = await fetch(request)
// Must use Response constructor to inherit all of response's fields
response = new Response(response.body, response)
// Cache API respects Cache-Control headers. Setting max-age to 10
// will limit the response to be in cache for 10 seconds max
response.headers.append("Cache-Control", "max-age=10")
// Store the fetched response as cacheKey
// Use waitUntil so computational expensive tasks don"t delay the response
event.waitUntil(cache.put(cacheKey, response.clone()))
}
return response
}
async function sha256(message) {
// encode as UTF-8
const msgBuffer = new TextEncoder().encode(message)
// hash the message
const hashBuffer = await crypto.subtle.digest("SHA-256", msgBuffer)
// convert ArrayBuffer to Array
const hashArray = Array.from(new Uint8Array(hashBuffer))
// convert bytes to hex string
const hashHex = hashArray.map(b => ("00" + b.toString(16)).slice(-2)).join("")
return hashHex
}
async function handlePostRequest(event) {
const request = event.request
const body = await request.clone().text()
const hash = await sha256(body)
const cacheUrl = new URL(request.url)
// Store the URL in cache by prepending the body's hash
cacheUrl.pathname = "/posts" + cacheUrl.pathname + hash
// Convert to a GET to be able to cache
const cacheKey = new Request(cacheUrl.toString(), {
headers: request.headers,
method: "GET",
})
const cache = caches.default
//Find the cache key in the cache
let response = await cache.match(cacheKey)
// Otherwise, fetch response to POST request from origin
if (!response) {
response = await fetch(request)
event.waitUntil(cache.put(cacheKey, response.clone()))
}
return response
}
addEventListener("fetch", event => {
try {
const request = event.request
if (request.method.toUpperCase() === "POST")
return event.respondWith(handlePostRequest(event))
return event.respondWith(handleRequest(event))
} catch (e) {
return event.respondWith(new Response("Error thrown " + e.message))
}
})