Funciones de ejemplo de Lambda@Edge - Amazon CloudFront

Funciones de ejemplo de Lambda@Edge

Consulte las secciones siguientes para ver ejemplos de cómo usar funciones de Lambda con Amazon CloudFront.

nota

Si elige el tiempo de ejecución Node.js 18 o una versión posterior para la función Lambda@Edge, se creará automáticamente un archivo index.mjs. Para usar los siguientes ejemplos de código, cambie el nombre del archivo index.mjs a index.js.

Ejemplos generales

En los ejemplos de esta sección se muestran algunas formas habituales de usar Lambda@Edge en CloudFront.

Ejemplo: prueba A/B

Puede utilizar el siguiente ejemplo para probar dos versiones diferentes de una imagen sin crear redirecciones ni cambiar la dirección URL. En este ejemplo se leen las cookies de la solicitud del lector y se modifica la URL de la solicitud en consecuencia. Si el espectador no envía una cookie con uno de los valores esperados, el ejemplo asigna aleatoriamente al espectador una de las URL.

Node.js
'use strict'; exports.handler = (event, context, callback) => { const request = event.Records[0].cf.request; const headers = request.headers; if (request.uri !== '/experiment-pixel.jpg') { // do not process if this is not an A-B test request callback(null, request); return; } const cookieExperimentA = 'X-Experiment-Name=A'; const cookieExperimentB = 'X-Experiment-Name=B'; const pathExperimentA = '/experiment-group/control-pixel.jpg'; const pathExperimentB = '/experiment-group/treatment-pixel.jpg'; /* * Lambda at the Edge headers are array objects. * * Client may send multiple Cookie headers, i.e.: * > GET /viewerRes/test HTTP/1.1 * > User-Agent: curl/7.18.1 (x86_64-unknown-linux-gnu) libcurl/7.18.1 OpenSSL/1.0.1u zlib/1.2.3 * > Cookie: First=1; Second=2 * > Cookie: ClientCode=abc * > Host: example.com * * You can access the first Cookie header at headers["cookie"][0].value * and the second at headers["cookie"][1].value. * * Header values are not parsed. In the example above, * headers["cookie"][0].value is equal to "First=1; Second=2" */ let experimentUri; if (headers.cookie) { for (let i = 0; i < headers.cookie.length; i++) { if (headers.cookie[i].value.indexOf(cookieExperimentA) >= 0) { console.log('Experiment A cookie found'); experimentUri = pathExperimentA; break; } else if (headers.cookie[i].value.indexOf(cookieExperimentB) >= 0) { console.log('Experiment B cookie found'); experimentUri = pathExperimentB; break; } } } if (!experimentUri) { console.log('Experiment cookie has not been found. Throwing dice...'); if (Math.random() < 0.75) { experimentUri = pathExperimentA; } else { experimentUri = pathExperimentB; } } request.uri = experimentUri; console.log(`Request uri set to "${request.uri}"`); callback(null, request); };
Python
import json import random def lambda_handler(event, context): request = event['Records'][0]['cf']['request'] headers = request['headers'] if request['uri'] != '/experiment-pixel.jpg': # Not an A/B Test return request cookieExperimentA, cookieExperimentB = 'X-Experiment-Name=A', 'X-Experiment-Name=B' pathExperimentA, pathExperimentB = '/experiment-group/control-pixel.jpg', '/experiment-group/treatment-pixel.jpg' ''' Lambda at the Edge headers are array objects. Client may send multiple cookie headers. For example: > GET /viewerRes/test HTTP/1.1 > User-Agent: curl/7.18.1 (x86_64-unknown-linux-gnu) libcurl/7.18.1 OpenSSL/1.0.1u zlib/1.2.3 > Cookie: First=1; Second=2 > Cookie: ClientCode=abc > Host: example.com You can access the first Cookie header at headers["cookie"][0].value and the second at headers["cookie"][1].value. Header values are not parsed. In the example above, headers["cookie"][0].value is equal to "First=1; Second=2" ''' experimentUri = "" for cookie in headers.get('cookie', []): if cookieExperimentA in cookie['value']: print("Experiment A cookie found") experimentUri = pathExperimentA break elif cookieExperimentB in cookie['value']: print("Experiment B cookie found") experimentUri = pathExperimentB break if not experimentUri: print("Experiment cookie has not been found. Throwing dice...") if random.random() < 0.75: experimentUri = pathExperimentA else: experimentUri = pathExperimentB request['uri'] = experimentUri print(f"Request uri set to {experimentUri}") return request

Ejemplo: Sobrescritura de un encabezado de respuesta

En el ejemplo siguiente, se muestra cómo cambiar el valor de un encabezado de respuesta según el valor de otro encabezado.

Node.js
'use strict'; exports.handler = (event, context, callback) => { const response = event.Records[0].cf.response; const headers = response.headers; const headerNameSrc = 'X-Amz-Meta-Last-Modified'; const headerNameDst = 'Last-Modified'; if (headers[headerNameSrc.toLowerCase()]) { headers[headerNameDst.toLowerCase()] = [ headers[headerNameSrc.toLowerCase()][0], ]; console.log(`Response header "${headerNameDst}" was set to ` + `"${headers[headerNameDst.toLowerCase()][0].value}"`); } callback(null, response); };
Python
import json def lambda_handler(event, context): response = event["Records"][0]["cf"]["response"] headers = response["headers"] headerNameSrc = "X-Amz-Meta-Last-Modified" headerNameDst = "Last-Modified" if headers.get(headerNameSrc.lower(), None): headers[headerNameDst.lower()] = [headers[headerNameSrc.lower()][0]] print(f"Response header {headerNameDst.lower()} was set to {headers[headerNameSrc.lower()][0]}") return response

Generación de respuestas: ejemplos

En los ejemplos de esta sección se muestra cómo puede usar Lambda@Edge para generar respuestas.

Ejemplo: Envío de contenido estático (respuesta generada)

En el siguiente ejemplo se muestra cómo utilizar una función de Lambda para enviar contenido de sitio web estático, lo que reduce la carga en el servidor de origen y la latencia total.

nota

Puede generar respuestas HTTP para los eventos de solicitud del espectador y al origen. Para obtener más información, consulte Generación de respuestas HTTP en los desencadenadores de solicitud.

También puede sustituir o quitar el cuerpo de la respuesta HTTP en eventos de respuesta de origen. Para obtener más información, consulte Actualización de respuestas HTTP en desencadenadores de respuesta de origen.

Node.js
'use strict'; const content = ` <\!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Simple Lambda@Edge Static Content Response</title> </head> <body> <p>Hello from Lambda@Edge!</p> </body> </html> `; exports.handler = (event, context, callback) => { /* * Generate HTTP OK response using 200 status code with HTML body. */ const response = { status: '200', statusDescription: 'OK', headers: { 'cache-control': [{ key: 'Cache-Control', value: 'max-age=100' }], 'content-type': [{ key: 'Content-Type', value: 'text/html' }] }, body: content, }; callback(null, response); };
Python
import json CONTENT = """ <\!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Simple Lambda@Edge Static Content Response</title> </head> <body> <p>Hello from Lambda@Edge!</p> </body> </html> """ def lambda_handler(event, context): # Generate HTTP OK response using 200 status code with HTML body. response = { 'status': '200', 'statusDescription': 'OK', 'headers': { 'cache-control': [ { 'key': 'Cache-Control', 'value': 'max-age=100' } ], "content-type": [ { 'key': 'Content-Type', 'value': 'text/html' } ] }, 'body': CONTENT } return response

Ejemplo: Generación de un redireccionamiento HTTP (respuesta generada)

En el siguiente ejemplo se muestra cómo generar una redirección HTTP.

nota

Puede generar respuestas HTTP para los eventos de solicitud del espectador y al origen. Para obtener más información, consulte Generación de respuestas HTTP en los desencadenadores de solicitud.

Node.js
'use strict'; exports.handler = (event, context, callback) => { /* * Generate HTTP redirect response with 302 status code and Location header. */ const response = { status: '302', statusDescription: 'Found', headers: { location: [{ key: 'Location', value: 'https://docs.aws.amazon.com/lambda/latest/dg/lambda-edge.html', }], }, }; callback(null, response); };
Python
def lambda_handler(event, context): # Generate HTTP redirect response with 302 status code and Location header. response = { 'status': '302', 'statusDescription': 'Found', 'headers': { 'location': [{ 'key': 'Location', 'value': 'https://docs.aws.amazon.com/lambda/latest/dg/lambda-edge.html' }] } } return response

Cadenas de consulta: ejemplos

En los ejemplos de esta sección se incluyen formas de usar Lambda@Edge con cadenas de consulta.

Ejemplo: Adición de un encabezado en función de un parámetro de la cadena de consulta

El siguiente ejemplo muestra cómo obtener el par clave-valor de un parámetro de la cadena de consulta y, a continuación, añadir un encabezado en función de dichos valores.

Node.js
'use strict'; const querystring = require('querystring'); exports.handler = (event, context, callback) => { const request = event.Records[0].cf.request; /* When a request contains a query string key-value pair but the origin server * expects the value in a header, you can use this Lambda function to * convert the key-value pair to a header. Here's what the function does: * 1. Parses the query string and gets the key-value pair. * 2. Adds a header to the request using the key-value pair that the function got in step 1. */ /* Parse request querystring to get javascript object */ const params = querystring.parse(request.querystring); /* Move auth param from querystring to headers */ const headerName = 'Auth-Header'; request.headers[headerName.toLowerCase()] = [{ key: headerName, value: params.auth }]; delete params.auth; /* Update request querystring */ request.querystring = querystring.stringify(params); callback(null, request); };
Python
from urllib.parse import parse_qs, urlencode def lambda_handler(event, context): request = event['Records'][0]['cf']['request'] ''' When a request contains a query string key-value pair but the origin server expects the value in a header, you can use this Lambda function to convert the key-value pair to a header. Here's what the function does: 1. Parses the query string and gets the key-value pair. 2. Adds a header to the request using the key-value pair that the function got in step 1. ''' # Parse request querystring to get dictionary/json params = {k : v[0] for k, v in parse_qs(request['querystring']).items()} # Move auth param from querystring to headers headerName = 'Auth-Header' request['headers'][headerName.lower()] = [{'key': headerName, 'value': params['auth']}] del params['auth'] # Update request querystring request['querystring'] = urlencode(params) return request

Ejemplo: Normalización de parámetros de cadenas de consulta para mejorar la tasa de aciertos de caché

El siguiente ejemplo muestra cómo mejorar la tasa de acceso a la caché haciendo los siguientes cambios en las cadenas de consulta antes de que CloudFront reenvíe las solicitudes a su origen:

  • Alfabetizar los pares de clave-valor por el nombre del parámetro.

  • Cambiar a minúsculas el modelo de mayúsculas y minúsculas de los pares de clave-valor.

Para obtener más información, consulte Almacenamiento en caché de contenido en función de parámetros de cadenas de consulta.

Node.js
'use strict'; const querystring = require('querystring'); exports.handler = (event, context, callback) => { const request = event.Records[0].cf.request; /* When you configure a distribution to forward query strings to the origin and * to cache based on an allowlist of query string parameters, we recommend * the following to improve the cache-hit ratio: * - Always list parameters in the same order. * - Use the same case for parameter names and values. * * This function normalizes query strings so that parameter names and values * are lowercase and parameter names are in alphabetical order. * * For more information, see: * https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/QueryStringParameters.html */ console.log('Query String: ', request.querystring); /* Parse request query string to get javascript object */ const params = querystring.parse(request.querystring.toLowerCase()); const sortedParams = {}; /* Sort param keys */ Object.keys(params).sort().forEach(key => { sortedParams[key] = params[key]; }); /* Update request querystring with normalized */ request.querystring = querystring.stringify(sortedParams); callback(null, request); };
Python
from urllib.parse import parse_qs, urlencode def lambda_handler(event, context): request = event['Records'][0]['cf']['request'] ''' When you configure a distribution to forward query strings to the origin and to cache based on an allowlist of query string parameters, we recommend the following to improve the cache-hit ratio: Always list parameters in the same order. - Use the same case for parameter names and values. This function normalizes query strings so that parameter names and values are lowercase and parameter names are in alphabetical order. For more information, see: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/QueryStringParameters.html ''' print("Query string: ", request["querystring"]) # Parse request query string to get js object params = {k : v[0] for k, v in parse_qs(request['querystring'].lower()).items()} # Sort param keys sortedParams = sorted(params.items(), key=lambda x: x[0]) # Update request querystring with normalized request['querystring'] = urlencode(sortedParams) return request

Ejemplo: Redireccionamiento de los usuarios no autenticados a una página de inicio de sesión

El siguiente ejemplo muestra cómo redirigir a los usuarios una página de inicio de sesión si no ha introducido sus credenciales.

Node.js
'use strict'; function parseCookies(headers) { const parsedCookie = {}; if (headers.cookie) { headers.cookie[0].value.split(';').forEach((cookie) => { if (cookie) { const parts = cookie.split('='); parsedCookie[parts[0].trim()] = parts[1].trim(); } }); } return parsedCookie; } exports.handler = (event, context, callback) => { const request = event.Records[0].cf.request; const headers = request.headers; /* Check for session-id in request cookie in viewer-request event, * if session-id is absent, redirect the user to sign in page with original * request sent as redirect_url in query params. */ /* Check for session-id in cookie, if present then proceed with request */ const parsedCookies = parseCookies(headers); if (parsedCookies && parsedCookies['session-id']) { callback(null, request); return; } /* URI encode the original request to be sent as redirect_url in query params */ const encodedRedirectUrl = encodeURIComponent(`https://${headers.host[0].value}${request.uri}?${request.querystring}`); const response = { status: '302', statusDescription: 'Found', headers: { location: [{ key: 'Location', value: `https://www.example.com/signin?redirect_url=${encodedRedirectUrl}`, }], }, }; callback(null, response); };
Python
import urllib def parseCookies(headers): parsedCookie = {} if headers.get('cookie'): for cookie in headers['cookie'][0]['value'].split(';'): if cookie: parts = cookie.split('=') parsedCookie[parts[0].strip()] = parts[1].strip() return parsedCookie def lambda_handler(event, context): request = event['Records'][0]['cf']['request'] headers = request['headers'] ''' Check for session-id in request cookie in viewer-request event, if session-id is absent, redirect the user to sign in page with original request sent as redirect_url in query params. ''' # Check for session-id in cookie, if present, then proceed with request parsedCookies = parseCookies(headers) if parsedCookies and parsedCookies['session-id']: return request # URI encode the original request to be sent as redirect_url in query params redirectUrl = "https://%s%s?%s" % (headers['host'][0]['value'], request['uri'], request['querystring']) encodedRedirectUrl = urllib.parse.quote_plus(redirectUrl.encode('utf-8')) response = { 'status': '302', 'statusDescription': 'Found', 'headers': { 'location': [{ 'key': 'Location', 'value': 'https://www.example.com/signin?redirect_url=%s' % encodedRedirectUrl }] } } return response

Personalización de contenido por encabezados de tipo de dispositivo o país: ejemplos

En los ejemplos de esta sección se muestra cómo puede usar Lambda@Edge para personalizar el comportamiento en función de la ubicación o el tipo de dispositivo que usa el espectador.

Ejemplo: Redireccionamiento de solicitudes de espectadores a una URL específica de un país

El siguiente ejemplo muestra cómo generar una respuesta de redireccionamiento HTTP con una URL específica del país y devolver la respuesta al espectador. Esto resulta útil cuando se quiere proporcionar respuestas específicas del país. Por ejemplo:

  • Si tiene subdominios específicos de un país, como us.ejemplo.com y tw.ejemplo.com, puede generar una respuesta de redireccionamiento cuando un espectador solicite ejemplo.com.

  • Si está haciendo streaming de video, pero no tiene derechos para transmitir el contenido en un país determinado, puede redirigir a los usuarios de dicho país a una página en la que se explica por qué no pueden ver el video.

Tenga en cuenta lo siguiente:

  • Debe configurar la distribución para almacenar en la caché en función del encabezado CloudFront-Viewer-Country. Para obtener más información, consulte Caché en función de encabezados de solicitud seleccionados.

  • CloudFront agrega el encabezado CloudFront-Viewer-Country después del evento de solicitud del lector. Para utilizar este ejemplo, debe crear un activador para el evento de solicitud al origen.

Node.js
'use strict'; /* This is an origin request function */ exports.handler = (event, context, callback) => { const request = event.Records[0].cf.request; const headers = request.headers; /* * Based on the value of the CloudFront-Viewer-Country header, generate an * HTTP status code 302 (Redirect) response, and return a country-specific * URL in the Location header. * NOTE: 1. You must configure your distribution to cache based on the * CloudFront-Viewer-Country header. For more information, see * https://docs.aws.amazon.com/console/cloudfront/cache-on-selected-headers * 2. CloudFront adds the CloudFront-Viewer-Country header after the viewer * request event. To use this example, you must create a trigger for the * origin request event. */ let url = 'https://example.com/'; if (headers['cloudfront-viewer-country']) { const countryCode = headers['cloudfront-viewer-country'][0].value; if (countryCode === 'TW') { url = 'https://tw.example.com/'; } else if (countryCode === 'US') { url = 'https://us.example.com/'; } } const response = { status: '302', statusDescription: 'Found', headers: { location: [{ key: 'Location', value: url, }], }, }; callback(null, response); };
Python
# This is an origin request function def lambda_handler(event, context): request = event['Records'][0]['cf']['request'] headers = request['headers'] ''' Based on the value of the CloudFront-Viewer-Country header, generate an HTTP status code 302 (Redirect) response, and return a country-specific URL in the Location header. NOTE: 1. You must configure your distribution to cache based on the CloudFront-Viewer-Country header. For more information, see https://docs.aws.amazon.com/console/cloudfront/cache-on-selected-headers 2. CloudFront adds the CloudFront-Viewer-Country header after the viewer request event. To use this example, you must create a trigger for the origin request event. ''' url = 'https://example.com/' viewerCountry = headers.get('cloudfront-viewer-country') if viewerCountry: countryCode = viewerCountry[0]['value'] if countryCode == 'TW': url = 'https://tw.example.com/' elif countryCode == 'US': url = 'https://us.example.com/' response = { 'status': '302', 'statusDescription': 'Found', 'headers': { 'location': [{ 'key': 'Location', 'value': url }] } } return response

Ejemplo: Envío de distintas versiones de un objeto en función del dispositivo

El siguiente ejemplo muestra cómo ofrecer distintas versiones de un objeto en función del tipo de dispositivo que el usuario está utilizando; por ejemplo, un dispositivo móvil o una tablet. Tenga en cuenta lo siguiente:

  • Debe configurar la distribución para almacenar en la caché en función de los encabezados CloudFront-Is-*-Viewer. Para obtener más información, consulte Caché en función de encabezados de solicitud seleccionados.

  • CloudFront agrega los encabezados CloudFront-Is-*-Viewer después del evento de solicitud del lector. Para utilizar este ejemplo, debe crear un activador para el evento de solicitud al origen.

Node.js
'use strict'; /* This is an origin request function */ exports.handler = (event, context, callback) => { const request = event.Records[0].cf.request; const headers = request.headers; /* * Serve different versions of an object based on the device type. * NOTE: 1. You must configure your distribution to cache based on the * CloudFront-Is-*-Viewer headers. For more information, see * the following documentation: * https://docs.aws.amazon.com/console/cloudfront/cache-on-selected-headers * https://docs.aws.amazon.com/console/cloudfront/cache-on-device-type * 2. CloudFront adds the CloudFront-Is-*-Viewer headers after the viewer * request event. To use this example, you must create a trigger for the * origin request event. */ const desktopPath = '/desktop'; const mobilePath = '/mobile'; const tabletPath = '/tablet'; const smarttvPath = '/smarttv'; if (headers['cloudfront-is-desktop-viewer'] && headers['cloudfront-is-desktop-viewer'][0].value === 'true') { request.uri = desktopPath + request.uri; } else if (headers['cloudfront-is-mobile-viewer'] && headers['cloudfront-is-mobile-viewer'][0].value === 'true') { request.uri = mobilePath + request.uri; } else if (headers['cloudfront-is-tablet-viewer'] && headers['cloudfront-is-tablet-viewer'][0].value === 'true') { request.uri = tabletPath + request.uri; } else if (headers['cloudfront-is-smarttv-viewer'] && headers['cloudfront-is-smarttv-viewer'][0].value === 'true') { request.uri = smarttvPath + request.uri; } console.log(`Request uri set to "${request.uri}"`); callback(null, request); };
Python
# This is an origin request function def lambda_handler(event, context): request = event['Records'][0]['cf']['request'] headers = request['headers'] ''' Serve different versions of an object based on the device type. NOTE: 1. You must configure your distribution to cache based on the CloudFront-Is-*-Viewer headers. For more information, see the following documentation: https://docs.aws.amazon.com/console/cloudfront/cache-on-selected-headers https://docs.aws.amazon.com/console/cloudfront/cache-on-device-type 2. CloudFront adds the CloudFront-Is-*-Viewer headers after the viewer request event. To use this example, you must create a trigger for the origin request event. ''' desktopPath = '/desktop'; mobilePath = '/mobile'; tabletPath = '/tablet'; smarttvPath = '/smarttv'; if 'cloudfront-is-desktop-viewer' in headers and headers['cloudfront-is-desktop-viewer'][0]['value'] == 'true': request['uri'] = desktopPath + request['uri'] elif 'cloudfront-is-mobile-viewer' in headers and headers['cloudfront-is-mobile-viewer'][0]['value'] == 'true': request['uri'] = mobilePath + request['uri'] elif 'cloudfront-is-tablet-viewer' in headers and headers['cloudfront-is-tablet-viewer'][0]['value'] == 'true': request['uri'] = tabletPath + request['uri'] elif 'cloudfront-is-smarttv-viewer' in headers and headers['cloudfront-is-smarttv-viewer'][0]['value'] == 'true': request['uri'] = smarttvPath + request['uri'] print("Request uri set to %s" % request['uri']) return request

Selección de origen dinámico basada en contenido: ejemplos

En los ejemplos de esta sección se muestra cómo puede usar Lambda@Edge para el direccionamiento a diferentes orígenes en función de la información de la solicitud.

Ejemplo: Uso de un desencadenador de solicitud de origen para cambiar desde un origen personalizado a un origen de Amazon S3

Esta función demuestra cómo utilizar un desencadenador de solicitud al origen para cambiar desde un origen personalizado a un origen de Amazon S3 desde el que recuperar el contenido, en función de las propiedades de la solicitud.

Node.js
'use strict'; const querystring = require('querystring'); exports.handler = (event, context, callback) => { const request = event.Records[0].cf.request; /** * Reads query string to check if S3 origin should be used, and * if true, sets S3 origin properties. */ const params = querystring.parse(request.querystring); if (params['useS3Origin']) { if (params['useS3Origin'] === 'true') { const s3DomainName = 'my-bucket.s3.amazonaws.com'; /* Set S3 origin fields */ request.origin = { s3: { domainName: s3DomainName, region: '', authMethod: 'none', path: '', customHeaders: {} } }; request.headers['host'] = [{ key: 'host', value: s3DomainName}]; } } callback(null, request); };
Python
from urllib.parse import parse_qs def lambda_handler(event, context): request = event['Records'][0]['cf']['request'] ''' Reads query string to check if S3 origin should be used, and if true, sets S3 origin properties ''' params = {k: v[0] for k, v in parse_qs(request['querystring']).items()} if params.get('useS3Origin') == 'true': s3DomainName = 'my-bucket.s3.amazonaws.com' # Set S3 origin fields request['origin'] = { 's3': { 'domainName': s3DomainName, 'region': '', 'authMethod': 'none', 'path': '', 'customHeaders': {} } } request['headers']['host'] = [{'key': 'host', 'value': s3DomainName}] return request

Ejemplo: Uso de un desencadenador de solicitud de origen para cambiar la región de origen de Amazon S3

Esta función demuestra cómo utilizar un desencadenador de solicitud al origen para cambiar el origen de Amazon S3 desde el que se recupera el contenido, en función de las propiedades de la solicitud.

En este ejemplo, utilizamos el valor del encabezado CloudFront-Viewer-Country para actualizar el nombre de dominio del bucket de S3 por un bucket de una región que está más cerca del lector. Esto puede resultar útil de varias maneras:

  • Reduce las latencias cuando la región especificada está más cerca del país del lector.

  • Proporciona soberanía de los datos, al asegurarse de que los datos se distribuyen desde un origen que está en el país del que provino la solicitud.

Para utilizar este ejemplo, debe hacer lo siguiente:

  • Configure la distribución para almacenar en la caché en función del encabezado CloudFront-Viewer-Country. Para obtener más información, consulte Caché en función de encabezados de solicitud seleccionados.

  • Crear un disparador para esta función en el evento de solicitud al origen. CloudFront agrega el encabezado CloudFront-Viewer-Country después del evento de solicitud del lector; por lo tanto, para utilizar este ejemplo, debe asegurarse de que la función ejecuta una solicitud de origen.

Node.js
'use strict'; exports.handler = (event, context, callback) => { const request = event.Records[0].cf.request; /** * This blueprint demonstrates how an origin-request trigger can be used to * change the origin from which the content is fetched, based on request properties. * In this example, we use the value of the CloudFront-Viewer-Country header * to update the S3 bucket domain name to a bucket in a Region that is closer to * the viewer. * * This can be useful in several ways: * 1) Reduces latencies when the Region specified is nearer to the viewer's * country. * 2) Provides data sovereignty by making sure that data is served from an * origin that's in the same country that the request came from. * * NOTE: 1. You must configure your distribution to cache based on the * CloudFront-Viewer-Country header. For more information, see * https://docs.aws.amazon.com/console/cloudfront/cache-on-selected-headers * 2. CloudFront adds the CloudFront-Viewer-Country header after the viewer * request event. To use this example, you must create a trigger for the * origin request event. */ const countryToRegion = { 'DE': 'eu-central-1', 'IE': 'eu-west-1', 'GB': 'eu-west-2', 'FR': 'eu-west-3', 'JP': 'ap-northeast-1', 'IN': 'ap-south-1' }; if (request.headers['cloudfront-viewer-country']) { const countryCode = request.headers['cloudfront-viewer-country'][0].value; const region = countryToRegion[countryCode]; /** * If the viewer's country is not in the list you specify, the request * goes to the default S3 bucket you've configured. */ if (region) { /** * If you've set up OAI, the bucket policy in the destination bucket * should allow the OAI GetObject operation, as configured by default * for an S3 origin with OAI. Another requirement with OAI is to provide * the Region so it can be used for the SIGV4 signature. Otherwise, the * Region is not required. */ request.origin.s3.region = region; const domainName = `my-bucket-in-${region}.s3.amazonaws.com`; request.origin.s3.domainName = domainName; request.headers['host'] = [{ key: 'host', value: domainName }]; } } callback(null, request); };
Python
def lambda_handler(event, context): request = event['Records'][0]['cf']['request'] ''' This blueprint demonstrates how an origin-request trigger can be used to change the origin from which the content is fetched, based on request properties. In this example, we use the value of the CloudFront-Viewer-Country header to update the S3 bucket domain name to a bucket in a Region that is closer to the viewer. This can be useful in several ways: 1) Reduces latencies when the Region specified is nearer to the viewer's country. 2) Provides data sovereignty by making sure that data is served from an origin that's in the same country that the request came from. NOTE: 1. You must configure your distribution to cache based on the CloudFront-Viewer-Country header. For more information, see https://docs.aws.amazon.com/console/cloudfront/cache-on-selected-headers 2. CloudFront adds the CloudFront-Viewer-Country header after the viewer request event. To use this example, you must create a trigger for the origin request event. ''' countryToRegion = { 'DE': 'eu-central-1', 'IE': 'eu-west-1', 'GB': 'eu-west-2', 'FR': 'eu-west-3', 'JP': 'ap-northeast-1', 'IN': 'ap-south-1' } viewerCountry = request['headers'].get('cloudfront-viewer-country') if viewerCountry: countryCode = viewerCountry[0]['value'] region = countryToRegion.get(countryCode) # If the viewer's country in not in the list you specify, the request # goes to the default S3 bucket you've configured if region: ''' If you've set up OAI, the bucket policy in the destination bucket should allow the OAI GetObject operation, as configured by default for an S3 origin with OAI. Another requirement with OAI is to provide the Region so it can be used for the SIGV4 signature. Otherwise, the Region is not required. ''' request['origin']['s3']['region'] = region domainName = 'my-bucket-in-%s.s3.amazonaws.com' % region request['origin']['s3']['domainName'] = domainName request['headers']['host'] = [{'key': 'host', 'value': domainName}] return request

Ejemplo: Uso de un desencadenador de solicitud de origen para cambiar desde un origen de Amazon S3 a un origen personalizado

Esta función demuestra cómo utilizar un disparador de solicitud al origen para cambiar el origen personalizado desde el que se recupera el contenido, en función de las propiedades de la solicitud.

Node.js
'use strict'; const querystring = require('querystring'); exports.handler = (event, context, callback) => { const request = event.Records[0].cf.request; /** * Reads query string to check if custom origin should be used, and * if true, sets custom origin properties. */ const params = querystring.parse(request.querystring); if (params['useCustomOrigin']) { if (params['useCustomOrigin'] === 'true') { /* Set custom origin fields*/ request.origin = { custom: { domainName: 'www.example.com', port: 443, protocol: 'https', path: '', sslProtocols: ['TLSv1', 'TLSv1.1'], readTimeout: 5, keepaliveTimeout: 5, customHeaders: {} } }; request.headers['host'] = [{ key: 'host', value: 'www.example.com'}]; } } callback(null, request); };
Python
from urllib.parse import parse_qs def lambda_handler(event, context): request = event['Records'][0]['cf']['request'] # Reads query string to check if custom origin should be used, and # if true, sets custom origin properties params = {k: v[0] for k, v in parse_qs(request['querystring']).items()} if params.get('useCustomOrigin') == 'true': # Set custom origin fields request['origin'] = { 'custom': { 'domainName': 'www.example.com', 'port': 443, 'protocol': 'https', 'path': '', 'sslProtocols': ['TLSv1', 'TLSv1.1'], 'readTimeout': 5, 'keepaliveTimeout': 5, 'customHeaders': {} } } request['headers']['host'] = [{'key': 'host', 'value': 'www.example.com'}] return request

Ejemplo: Uso de un desencadenador de solicitud al origen para transferir gradualmente el tráfico desde un bucket de Amazon S3 a otro

Esta función demuestra cómo transferir gradualmente el tráfico desde un bucket de Amazon S3 a otro de forma controlada.

Node.js
'use strict'; function getRandomInt(min, max) { /* Random number is inclusive of min and max*/ return Math.floor(Math.random() * (max - min + 1)) + min; } exports.handler = (event, context, callback) => { const request = event.Records[0].cf.request; const BLUE_TRAFFIC_PERCENTAGE = 80; /** * This Lambda function demonstrates how to gradually transfer traffic from * one S3 bucket to another in a controlled way. * We define a variable BLUE_TRAFFIC_PERCENTAGE which can take values from * 1 to 100. If the generated randomNumber less than or equal to BLUE_TRAFFIC_PERCENTAGE, traffic * is re-directed to blue-bucket. If not, the default bucket that we've configured * is used. */ const randomNumber = getRandomInt(1, 100); if (randomNumber <= BLUE_TRAFFIC_PERCENTAGE) { const domainName = 'blue-bucket.s3.amazonaws.com'; request.origin.s3.domainName = domainName; request.headers['host'] = [{ key: 'host', value: domainName}]; } callback(null, request); };
Python
import math import random def getRandomInt(min, max): # Random number is inclusive of min and max return math.floor(random.random() * (max - min + 1)) + min def lambda_handler(event, context): request = event['Records'][0]['cf']['request'] BLUE_TRAFFIC_PERCENTAGE = 80 ''' This Lambda function demonstrates how to gradually transfer traffic from one S3 bucket to another in a controlled way. We define a variable BLUE_TRAFFIC_PERCENTAGE which can take values from 1 to 100. If the generated randomNumber less than or equal to BLUE_TRAFFIC_PERCENTAGE, traffic is re-directed to blue-bucket. If not, the default bucket that we've configured is used. ''' randomNumber = getRandomInt(1, 100) if randomNumber <= BLUE_TRAFFIC_PERCENTAGE: domainName = 'blue-bucket.s3.amazonaws.com' request['origin']['s3']['domainName'] = domainName request['headers']['host'] = [{'key': 'host', 'value': domainName}] return request

Ejemplo: Uso de un desencadenador de solicitud de origen para cambiar el nombre del dominio de origen en función del encabezado de país

Esta función demuestra cómo cambiar el nombre del dominio de origen en función del encabezado CloudFront-Viewer-Country, de forma que el contenido se distribuya desde un origen más cercano al país del lector.

La implementación de esta funcionalidad para su distribución puede tener ventajas como las siguientes:

  • Reducir las latencias cuando la región especificada está más cerca del país del lector

  • Proporcionar soberanía de los datos, al asegurarse de que los datos se distribuyen desde un origen que está en el país del que provino la solicitud

Tenga en cuenta que para habilitar esta funcionalidad, debe configurar su distribución para almacenar en la caché en función del encabezado CloudFront-Viewer-Country. Para obtener más información, consulte Caché en función de encabezados de solicitud seleccionados.

Node.js
'use strict'; exports.handler = (event, context, callback) => { const request = event.Records[0].cf.request; if (request.headers['cloudfront-viewer-country']) { const countryCode = request.headers['cloudfront-viewer-country'][0].value; if (countryCode === 'GB' || countryCode === 'DE' || countryCode === 'IE' ) { const domainName = 'eu.example.com'; request.origin.custom.domainName = domainName; request.headers['host'] = [{key: 'host', value: domainName}]; } } callback(null, request); };
Python
def lambda_handler(event, context): request = event['Records'][0]['cf']['request'] viewerCountry = request['headers'].get('cloudfront-viewer-country') if viewerCountry: countryCode = viewerCountry[0]['value'] if countryCode == 'GB' or countryCode == 'DE' or countryCode == 'IE': domainName = 'eu.example.com' request['origin']['custom']['domainName'] = domainName request['headers']['host'] = [{'key': 'host', 'value': domainName}] return request

Actualización de estados de error: ejemplos

En los ejemplos de esta sección se proporciona orientación acerca de cómo puede usar Lambda@Edge para cambiar el estado de error que se devuelve a los usuarios.

Ejemplo: Uso de un desencadenador de respuesta de origen para actualizar el código de estado de error a 200

Esta función demuestra cómo actualizar el estado de la respuesta a 200 y generar un cuerpo con contenido estático para devolverlo al espectador en la siguiente situación:

  • La función se desencadena en una respuesta del origen.

  • El estado de la respuesta del servidor de origen es un código de estado de error (4xx o 5xx).

Node.js
'use strict'; exports.handler = (event, context, callback) => { const response = event.Records[0].cf.response; /** * This function updates the response status to 200 and generates static * body content to return to the viewer in the following scenario: * 1. The function is triggered in an origin response * 2. The response status from the origin server is an error status code (4xx or 5xx) */ if (response.status >= 400 && response.status <= 599) { response.status = 200; response.statusDescription = 'OK'; response.body = 'Body generation example'; } callback(null, response); };
Python
def lambda_handler(event, context): response = event['Records'][0]['cf']['response'] ''' This function updates the response status to 200 and generates static body content to return to the viewer in the following scenario: 1. The function is triggered in an origin response 2. The response status from the origin server is an error status code (4xx or 5xx) ''' if int(response['status']) >= 400 and int(response['status']) <= 599: response['status'] = 200 response['statusDescription'] = 'OK' response['body'] = 'Body generation example' return response

Ejemplo: Uso de un desencadenador de respuesta de origen para actualizar el código de estado de error a 302

Esta función demuestra cómo actualizar el código de estado HTTP a 302 para la redirección a otra ruta (comportamiento de la caché) en la que se ha configurado un origen diferente. Tenga en cuenta lo siguiente:

  • La función se desencadena en una respuesta del origen.

  • El estado de la respuesta del servidor de origen es un código de estado de error (4xx o 5xx).

Node.js
'use strict'; exports.handler = (event, context, callback) => { const response = event.Records[0].cf.response; const request = event.Records[0].cf.request; /** * This function updates the HTTP status code in the response to 302, to redirect to another * path (cache behavior) that has a different origin configured. Note the following: * 1. The function is triggered in an origin response * 2. The response status from the origin server is an error status code (4xx or 5xx) */ if (response.status >= 400 && response.status <= 599) { const redirect_path = `/plan-b/path?${request.querystring}`; response.status = 302; response.statusDescription = 'Found'; /* Drop the body, as it is not required for redirects */ response.body = ''; response.headers['location'] = [{ key: 'Location', value: redirect_path }]; } callback(null, response); };
Python
def lambda_handler(event, context): response = event['Records'][0]['cf']['response'] request = event['Records'][0]['cf']['request'] ''' This function updates the HTTP status code in the response to 302, to redirect to another path (cache behavior) that has a different origin configured. Note the following: 1. The function is triggered in an origin response 2. The response status from the origin server is an error status code (4xx or 5xx) ''' if int(response['status']) >= 400 and int(response['status']) <= 599: redirect_path = '/plan-b/path?%s' % request['querystring'] response['status'] = 302 response['statusDescription'] = 'Found' # Drop the body as it is not required for redirects response['body'] = '' response['headers']['location'] = [{'key': 'Location', 'value': redirect_path}] return response

Acceso al cuerpo de la solicitud: ejemplos

En los ejemplos de esta sección se muestra cómo puede usar Lambda@Edge para trabajar con las solicitudes POST.

nota

Para utilizar estos ejemplos, debe habilitar la opción incluir cuerpo en la asociación de funciones Lambda de la distribución. No está habilitada de forma predeterminada.

  • Para habilitar esta configuración en la consola de CloudFront, seleccione la casilla de verificación Incluir cuerpo en la Asociación de funciones Lambda.

  • Para habilitar esta configuración en la API de CloudFront o con AWS CloudFormation, establezca el campo IncludeBody en true en LambdaFunctionAssociation.

Ejemplo: Uso de un desencadenador de solicitud para leer un formulario HTML

Esta función ilustra cómo puede procesar el cuerpo de una solicitud POST generada por un formulario HTML (formulario web), como por ejemplo un formulario tipo "póngase en contacto con nosotros". Por ejemplo, es posible que tenga un formulario HTML como el siguiente:

<html> <form action="https://example.com" method="post"> Param 1: <input type="text" name="name1"><br> Param 2: <input type="text" name="name2"><br> input type="submit" value="Submit"> </form> </html>

Para la función de ejemplo que se indica a continuación, la función se debe desencadenar en una solicitud al origen o del lector de CloudFront.

Node.js
'use strict'; const querystring = require('querystring'); /** * This function demonstrates how you can read the body of a POST request * generated by an HTML form (web form). The function is triggered in a * CloudFront viewer request or origin request event type. */ exports.handler = (event, context, callback) => { const request = event.Records[0].cf.request; if (request.method === 'POST') { /* HTTP body is always passed as base64-encoded string. Decode it. */ const body = Buffer.from(request.body.data, 'base64').toString(); /* HTML forms send the data in query string format. Parse it. */ const params = querystring.parse(body); /* For demonstration purposes, we only log the form fields here. * You can put your custom logic here. For example, you can store the * fields in a database, such as Amazon DynamoDB, and generate a response * right from your Lambda@Edge function. */ for (let param in params) { console.log(`For "${param}" user submitted "${params[param]}".\n`); } } return callback(null, request); };
Python
import base64 from urllib.parse import parse_qs ''' Say there is a POST request body generated by an HTML such as: <html> <form action="https://example.com" method="post"> Param 1: <input type="text" name="name1"><br> Param 2: <input type="text" name="name2"><br> input type="submit" value="Submit"> </form> </html> ''' ''' This function demonstrates how you can read the body of a POST request generated by an HTML form (web form). The function is triggered in a CloudFront viewer request or origin request event type. ''' def lambda_handler(event, context): request = event['Records'][0]['cf']['request'] if request['method'] == 'POST': # HTTP body is always passed as base64-encoded string. Decode it body = base64.b64decode(request['body']['data']) # HTML forms send the data in query string format. Parse it params = {k: v[0] for k, v in parse_qs(body).items()} ''' For demonstration purposes, we only log the form fields here. You can put your custom logic here. For example, you can store the fields in a database, such as Amazon DynamoDB, and generate a response right from your Lambda@Edge function. ''' for key, value in params.items(): print("For %s use submitted %s" % (key, value)) return request

Ejemplo: Uso de un desencadenador de solicitud para modificar un formulario HTML

Esta función ilustra cómo puede modificar el cuerpo de una solicitud POST generada por un formulario HTML (formulario web). La función se activa en una solicitud al origen o del lector de CloudFront.

Node.js
'use strict'; const querystring = require('querystring'); exports.handler = (event, context, callback) => { var request = event.Records[0].cf.request; if (request.method === 'POST') { /* Request body is being replaced. To do this, update the following /* three fields: * 1) body.action to 'replace' * 2) body.encoding to the encoding of the new data. * * Set to one of the following values: * * text - denotes that the generated body is in text format. * Lambda@Edge will propagate this as is. * base64 - denotes that the generated body is base64 encoded. * Lambda@Edge will base64 decode the data before sending * it to the origin. * 3) body.data to the new body. */ request.body.action = 'replace'; request.body.encoding = 'text'; request.body.data = getUpdatedBody(request); } callback(null, request); }; function getUpdatedBody(request) { /* HTTP body is always passed as base64-encoded string. Decode it. */ const body = Buffer.from(request.body.data, 'base64').toString(); /* HTML forms send data in query string format. Parse it. */ const params = querystring.parse(body); /* For demonstration purposes, we're adding one more param. * * You can put your custom logic here. For example, you can truncate long * bodies from malicious requests. */ params['new-param-name'] = 'new-param-value'; return querystring.stringify(params); }
Python
import base64 from urllib.parse import parse_qs, urlencode def lambda_handler(event, context): request = event['Records'][0]['cf']['request'] if request['method'] == 'POST': ''' Request body is being replaced. To do this, update the following three fields: 1) body.action to 'replace' 2) body.encoding to the encoding of the new data. Set to one of the following values: text - denotes that the generated body is in text format. Lambda@Edge will propagate this as is. base64 - denotes that the generated body is base64 encoded. Lambda@Edge will base64 decode the data before sending it to the origin. 3) body.data to the new body. ''' request['body']['action'] = 'replace' request['body']['encoding'] = 'text' request['body']['data'] = getUpdatedBody(request) return request def getUpdatedBody(request): # HTTP body is always passed as base64-encoded string. Decode it body = base64.b64decode(request['body']['data']) # HTML forms send data in query string format. Parse it params = {k: v[0] for k, v in parse_qs(body).items()} # For demonstration purposes, we're adding one more param # You can put your custom logic here. For example, you can truncate long # bodies from malicious requests params['new-param-name'] = 'new-param-value' return urlencode(params)