Cómo construir un acortador de URL sin servidor usando AWS Lambda y S3

Uso de gráficos de SAP Scenes Pack

A lo largo de esta publicación, crearemos un acortador de URL sin servidor utilizando Amazon Web Services (AWS) Lambda y S3. Si bien no necesita experiencia previa con AWS, supongo que está familiarizado con ES6 JavaScript y Node.js.

Irónicamente, las URL que se generarán a partir de nuestro acortador de URL a menudo serán más largas que las URL a las que redirigen, esto se debe a que estamos utilizando la dirección de sitio web predeterminada del cubo S3. Hacia el final de la publicación, analizaré cómo puede agregar un dominio personalizado para evitar esta limitación.

Ver la demo

Ver el código en Github

Es relativamente fácil comenzar con AWS y, sin embargo, definitivamente se percibe una complejidad. El número de servicios disponibles puede ser desalentador para elegir, ya que muchos de ellos se superponen en la funcionalidad. La consola de administración de AWS lenta y poco intuitiva no ayuda, ni la documentación en línea con texto pesado. Pero a lo largo de esta publicación, espero demostrar que la mejor manera de adoptar los servicios de AWS es utilizar un enfoque incremental y puede comenzar utilizando solo un puñado de servicios.

Utilizaremos Serverless Framework para interactuar con AWS, por lo que no será necesario iniciar sesión en la consola de administración de AWS. Serverless Framework proporciona una abstracción sobre AWS y ayuda a proporcionar una estructura de proyecto y valores predeterminados de configuración razonables. Si desea obtener más información antes de comenzar, debe leer sus documentos.

Arquitectura

Antes de saltar a cualquier desarrollo, veamos primero los servicios de AWS que usaremos para construir nuestro acortador de URL.

Para alojar nuestro sitio web, utilizaremos el servicio de almacenamiento de archivos de Amazon S3. Configuraremos nuestro depósito S3, que puede considerarse como una carpeta de nivel superior, para servir un sitio web estático. El sitio web consistirá en contenido estático y scripts del lado del cliente. No hay capacidad para ejecutar código del lado del servidor (como PHP, Ruby o Java, por ejemplo), pero eso está bien para nuestro caso de uso.

También usaremos una característica poco conocida de S3 que le permite configurar el reenvío de objetos dentro de los depósitos de S3 simplemente agregando un valor de Ubicación de redireccionamiento de sitio web a los metadatos del objeto. Establecer esto en una URL hará que los navegadores se redirijan a través de una respuesta HTTP 301 y el encabezado de ubicación.

La URL de un objeto S3 se compone de la dirección del depósito S3 seguida del nombre del objeto.

http: // [nombre del cubo] .s3-website-eu-west-1.amazonaws.com / [object-name]

El siguiente es un ejemplo del formato de un objeto de depósito S3 para la región eu-west-1.

http://serverless-url-shortener.s3-website-eu-west-1.amazonaws.com/6GpLcdl

Este nombre de objeto "6GpLcdl" al final de la URL en el ejemplo anterior se convierte en el código corto para nuestras URL acortadas. Al usar esta funcionalidad, obtenemos la redirección de URL nativa, así como las capacidades de almacenamiento. No requerimos una base de datos para almacenar los detalles de qué código abreviado apunta a qué URL, ya que esta información se almacenará con el objeto en sí.

Crearemos una función Lambda para guardar estos objetos S3 con los metadatos apropiados en nuestro depósito S3.

Alternativamente, podría usar el lado del cliente AWS SDK en el navegador para guardar los objetos. Pero es mejor extraer esta funcionalidad en un servicio separado. Proporciona la ventaja de no tener que preocuparse por exponer las credenciales de seguridad y es más ampliable en el futuro. Asignaremos la función Lambda a un punto final en API Gateway para que sea accesible a través de una llamada API.

Empezando

Dirígete a los documentos de Serverless Framework y revisa su guía de inicio rápido. Como parte del proceso de configuración, deberá instalar la AWS CLI y configurar sus credenciales de AWS.

Comience creando un archivo package.json en la raíz del proyecto.

{
  "name": "serverless-url-shortener",
  "guiones": {},
  "dependencias": {}
}

Sabemos que necesitaremos usar AWS SDK, así que adelante e instálelo desde NPM ahora ingresando el siguiente comando.

npm install aws-sdk --save

Ahora cree un archivo config.json también en la raíz del proyecto. Utilizaremos esto para almacenar opciones de usuario personalizables en formato JSON.

Agregue las siguientes teclas con valores apropiados para su configuración.

  • CUBO: el nombre que desea usar para su cubo S3. Esto formará parte de la URL corta si elige no agregar un dominio personalizado. Tiene que ser exclusivo de la región en la que está implementando, así que no elija algo demasiado genérico. Pero no se preocupe, si su nombre de depósito elegido ya está en uso, se le advertirá a través de la CLI sin servidor en el momento de la implementación.
  • REGIÓN: la región de AWS en la que desea implementar. Es mejor elegir la región más cercana a sus usuarios por razones de rendimiento. Si solo sigue el tutorial, usaré eu-west-1.
  • ETAPA: el escenario para desplegar. Por lo general, tendría un entorno provisional que replica la misma configuración que su entorno de producción. Esto le permite probar versiones de software de una manera no destructiva. Como se trata de un tutorial, me desplegaré en la etapa de desarrollo.

Su archivo config.json debería ser similar al siguiente una vez que esté completo.

{
  "BUCKET": "tu-nombre-cubo",
  "REGIÓN": "eu-west-1",
  "ETAPA": "dev",
}

Luego, cree otro archivo en la raíz del proyecto, serverless.yml. Esto mantendrá nuestra configuración de Framework sin servidor formateada en el lenguaje de marcado YAML.

Dentro de este archivo comenzaremos definiendo nuestro entorno. Observe cómo podemos hacer referencia a las variables almacenadas anteriormente en config.json.

servicio: serverless-url-shortener

proveedor:
  nombre: aws
  tiempo de ejecución: nodejs6.10
  etapa: $ {archivo (config.json): ETAPA}
  región: $ {archivo (config.json): REGIÓN}
  iamRoleStatements:
    - Efecto: Permitir
      Acción:
        - s3: PutObject
      Recurso: "arn: aws: s3 ::: $ {file (config.json): BUCKET} / *"

La sección iamRoleStatements se refiere a la Gestión de Identidad y Acceso que se utiliza para configurar los permisos de Lambda. Aquí le damos al Lambda acceso de escritura a nuestro bucket S3.

Para guardar objetos necesitamos permiso para ejecutar la acción s3: PutObject. Aquí se pueden agregar otros permisos si su proyecto los requiere. Consulte los documentos de S3 para otras acciones disponibles.

El valor del recurso se establece en el nombre del recurso de Amazon del cubo S3, que se utiliza para identificar de forma exclusiva un recurso de AWS en particular. El formato de este identificador depende del servicio de AWS al que se hace referencia, pero generalmente tienen el siguiente formato.

arn: partición: servicio: región: id-cuenta: recurso

Debajo del proveedor, agregue nuestra configuración de funciones.

funciones:
  almacenar:
    manejador: api.handle
    eventos:
      - http:
          camino: /
          método: publicar
          cors: verdadero

Aquí definimos la configuración de la API y asignamos nuestro Lambda a un evento HTTP POST en la URL base de la API. Un controlador con el valor api.handle se refiere a una función llamada handle que se exporta desde api.js (no necesitamos la extensión de archivo js porque anteriormente en serverless.yml configuramos el tiempo de ejecución en nodejs6.10).

Lambda se basa en eventos y, por lo tanto, las funciones solo se ejecutan en función de desencadenantes predefinidos. Aquí hemos definido un evento HTTP, pero también podría haber sido un evento desencadenado por una tabla DynamoDB o una cola SQS.

A continuación, en serverless.yml definimos los recursos de AWS que se instanciarán para nosotros en la implementación mediante CloudFormation. Vale la pena mencionar que no necesariamente tiene que configurar los recursos de esta manera, también puede crearlos usando la consola de administración de AWS. Si se proporcionan los permisos de acceso correctos, no importa cómo se creen los recursos. Pero al definir los servicios requeridos en serverless.yml, está definiendo su "infraestructura como código" y obtiene una serie de beneficios al hacerlo.

“La infraestructura como código es el enfoque para definir la infraestructura informática y de red a través del código fuente que luego puede tratarse como cualquier sistema de software. Dicho código puede mantenerse en el control de la fuente para permitir la auditabilidad y los ReproducibleBuilds, sujeto a las prácticas de prueba y la disciplina completa de la Entrega Continua ".
- Martin Fowler

Continúe y agregue la configuración de recursos.

recursos:
  Recursos:
    ServerlessRedirectS3Bucket:
      Tipo: AWS :: S3 :: Cubo
      Propiedades:
        BucketName: $ {file (config.json): BUCKET}
        AccessControl: PublicRead
        Configuración del sitio web:
          IndexDocument: index.html
    ServerlessRedirectS3BucketPolicy:
      Tipo: AWS :: S3 :: BucketPolicy
      Propiedades:
        Bucket: $ {file (config.json): BUCKET}
        Documento de política:
          Declaración:
          - Acción:
            - s3: GetObject
            Efecto: Permitir
            Recurso:
            - arn: aws: s3 ::: $ {file (config.json): BUCKET} / *
            Director de escuela: "*"

Solicitamos un recurso de depósito S3 configurado para usar el alojamiento de sitios estáticos con index.html como documento raíz. Los cubos S3 por una buena razón son privados de forma predeterminada, por lo que debemos crear una política de cubos S3 que permita el acceso público a ellos. Sin esta política, los visitantes del sitio web mostrarían un mensaje de error no autenticado.

Construyendo la API

Nuestra función Lambda es responsable de cuatro tareas.

  1. Agarrando la URL para acortar el envío del formulario del usuario.
  2. Generando un shortcode único para la URL.
  3. Guardar el objeto de redireccionamiento apropiado en S3.
  4. Devolver la ruta del objeto al cliente.

Crea el controlador

Cree un nuevo archivo llamado api.js y exporte una función de flecha llamada handle que tome tres argumentos: evento, contexto y devolución de llamada. AWS los proporcionará cuando se invoque el controlador. Este archivo es un script Node.js y para exportar la función de flecha necesita agregarlo a module.exports.

module.exports.handle = (evento, contexto, devolución de llamada) => {
}

Este controlador se invocará cuando se realice una solicitud HTTP POST a nuestro punto final. Para devolver una respuesta API, debemos usar la función de devolución de llamada proporcionada que se proporciona como el tercer argumento de la función de flecha. Es una devolución de llamada de primer error que toma dos argumentos. Si la solicitud se completa con éxito, nulo debe pasarse como primer argumento. El objeto de respuesta pasado como el segundo argumento determina el tipo de respuesta que se devolverá al usuario. Generar una respuesta es tan simple como proporcionar un código de estado y un cuerpo como se muestra en el siguiente ejemplo.

respuesta constante = {
  statusCode: 201,
  cuerpo: JSON.stringify ({"shortUrl": "http://example.com"})
}
devolución de llamada (nulo, respuesta)

El objeto de contexto pasado como segundo argumento al controlador contiene información de tiempo de ejecución a la que no necesitamos acceso para este tutorial. Sin embargo, necesitamos hacer uso del evento pasado como primer argumento ya que contiene el envío del formulario con la URL para acortar.

Analiza la solicitud

A continuación se muestra un ejemplo de un evento API Gateway que se pasará a nuestro controlador cuando un usuario envíe un formulario. A medida que construimos nuestro acortador de URL como una aplicación de una sola página, enviaremos el formulario usando JavaScript y, por lo tanto, el tipo de contenido será application / json en lugar de application / x-www-form-urlencoded.

{
   recurso:'/',
   camino:'/',
   httpMethod: 'POST',
   encabezados: {
      Aceptar:'*/*',
      'Accept-Encoding': 'gzip, deflate',
      'cache-control': 'no-cache',
      'CloudFront-Fordered-Proto': 'https',
      'CloudFront-Is-Desktop-Viewer': 'verdadero',
      'CloudFront-Is-Mobile-Viewer': 'falso',
      'CloudFront-Is-SmartTV-Viewer': 'falso',
      'CloudFront-Is-Tablet-Viewer': 'falso',
      'CloudFront-Viewer-Country': 'GB',
      'content-type': 'application / json',
      Anfitrión:'',
      'Agente de usuario':'',
      'X-Amz-Cf-Id': '',
      'X-Amzn-Trace-Id': '',
      'X-Forward-For': '',
      'Puerto reenviado X': '443',
      'X-Fordered-Proto': 'https'
   },
   queryStringParameters: null,
   pathParameters: {},
   stageVariables: nulo,
   requestContext: {
      ruta: '/ dev',
      ID de la cuenta:'',
      resourceId: '',
      etapa: 'dev',
      requestId: '',
      identidad:{
         cognitoIdentityPoolId: nulo,
         accountId: null,
         cognitoIdentityId: nulo,
         llamador: nulo,
         Clave API:'',
         IP de origen:'',
         accessKey: nulo,
         cognitoAuthenticationType: nulo,
         cognitoAuthenticationProvider: nulo,
         userArn: nulo,
         agente de usuario:'',
         usuario: nulo
      },
      resourcePath: '/',
      httpMethod: 'POST',
      apiId: ''
   },
   cuerpo: '{"url": "http://ejemplo.com"}',
   isBase64Encoded: false
}

Solo necesitamos el envío del formulario del evento, que podemos obtener mirando el cuerpo de la solicitud. El cuerpo de la solicitud se almacena como un objeto JavaScript en cadena que podemos tomar dentro de nuestro controlador usando JSON.parse (). Aprovechando la evaluación de cortocircuito de JavaScript, podemos establecer un valor predeterminado de una cadena vacía para los casos en que no se ha enviado una URL como parte del envío del formulario. Esto nos permite tratar instancias donde falta la URL y donde la URL es una cadena vacía por igual.

module.exports.handle = (evento, contexto, devolución de llamada) => {
  let longUrl = JSON.parse (event.body) .url || ''
}

Validar la URL

Agreguemos una validación básica para verificar que la URL proporcionada sea legítima. Hay múltiples enfoques que podrían adoptarse para lograr esto. Pero para el propósito de este tutorial lo mantendremos simple y usaremos el módulo URL Node.js incorporado. Desarrollaremos nuestra validación para devolver una promesa resuelta en una URL válida y devolver una promesa rechazada en una URL no válida. Las promesas en JavaScript pueden encadenarse secuencialmente para que la resolución de una promesa pase al controlador de éxito de la siguiente. Utilizaremos este atributo de promesas para estructurar nuestro controlador. Escribamos la función de validación usando promesas.

const url = require ('url')
función validate (longUrl) {
  if (longUrl === '') {
    return Promise.reject ({
      statusCode: 400,
      mensaje: 'Se requiere URL'
    })
  }
let parsedUrl = url.parse (longUrl)
  if (parsedUrl.protocol === null || parsedUrl.host === null) {
    return Promise.reject ({
      statusCode: 400,
      mensaje: 'URL no es válida'
    })
  }
return Promise.resolve (longUrl)
}

En nuestra función de validación, primero verificamos que la URL no esté establecida en una cadena vacía. Si es así, devolvemos una promesa rechazada. Observe cómo el valor rechazado es un objeto que contiene un código de estado y un mensaje. Usaremos esto más adelante para crear una respuesta API adecuada. Llamar a parse en el módulo de URL Node.js devuelve un objeto URL con información que podría extraerse de la URL que se pasó como un argumento de cadena. Como parte de nuestra validación de URL básica, simplemente verificamos si se puede extraer un protocolo (por ejemplo, "http") y un host (como "ejemplo.com"). Si alguno de estos valores es nulo en el objeto URL devuelto, asumimos que la URL no es válida. Si la URL es válida, la devolvemos como parte de una promesa resuelta.

Devolviendo una respuesta

Después de obtener la URL de la solicitud, llamamos validar y para cada paso adicional del controlador que se requiera, devolveremos una nueva promesa en el controlador de éxito de la promesa anterior. El controlador de éxito final es responsable de devolver una respuesta API a través del argumento de devolución de llamada del identificador. Se invocará tanto para respuestas API de error generadas a partir de promesas rechazadas como para respuestas API exitosas.

module.exports.handle = (evento, contexto, devolución de llamada) => {
  let longUrl = JSON.parse (event.body) .url || ''
  validar (longUrl)
    .then (función (ruta) {
      let response = buildResponse (200, 'éxito', ruta)
      return Promise.resolve (respuesta)
    })
    .catch (función (err) {
      let response = buildResponse (err.statusCode, err.message)
      return Promise.resolve (respuesta)
    })
    .then (función (respuesta) {
      devolución de llamada (nulo, respuesta)
    })
}
función buildResponse (statusCode, message, path = false) {
  dejar cuerpo = {mensaje}
  if (ruta) cuerpo ['ruta'] = ruta
  
  regreso {
    encabezados: {
      'Acceso-Control-Permitir-Origen': '*'
    },
    statusCode: statusCode,
    cuerpo: JSON.stringify (cuerpo)
  }
}

Generar un shortcode URL

La API debe ser capaz de generar códigos cortos de URL únicos, que se representan como nombres de archivo en el depósito S3. Como un shortcode es solo un nombre de archivo, existe un gran grado de flexibilidad en su composición. Para nuestro shortcode usaremos una cadena alfanumérica de 7 dígitos que consta de mayúsculas y minúsculas, esto se traduce en 62 combinaciones posibles para cada carácter. Utilizaremos la recursividad para construir el código corto seleccionando un carácter a la vez hasta que se hayan seleccionado siete.

función generatePath (ruta = '') {
  let characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
  let position = Math.floor (Math.random () * characters.length)
  let character = characters.charAt (posición)
if (path.length === 7) {
  vía de retorno
}
return generatePath (ruta + carácter)
}

Si bien la posibilidad de generar aleatoriamente el mismo shortcode es escasa (en realidad existe una probabilidad de 0.0000000000000000000000008063365516 de que dos shortcodes sean iguales), debemos verificar si el shortcode generado ya está en uso, lo que podemos hacer con el SDK de AWS. Hay un método headObject en el servicio S3 que carga los metadatos de un objeto. Podemos usar esto para probar si ya existe un objeto con el mismo nombre que cuando no se encuentra una promesa con el código NotFound es rechazado. Esta promesa rechazada indica que el shortcode es gratuito y puede usarse. Llamar a headObject es más eficaz que probar si el objeto existe a través de getObject, que carga todo el objeto.

const AWS = require ('aws-sdk')
const S3 = nuevo AWS.S3 ()
función isPathFree (ruta) {
  return S3.headObject (buildRedirect (ruta)). promise ()
    .then (() => Promise.resolve (false))
    .catch (función (err) {
      if (err.code == 'NotFound') {
        return Promise.resolve (verdadero)
      } más {
        return Promise.reject (err)
      }
    })
}
función buildRedirect (ruta, longUrl = false) {
  dejar redireccionar = {
    'Bucket': config.BUCKET,
    'Clave': ruta
  }
if (longUrl) {
    redirect ['WebsiteRedirectLocation'] = longUrl
  }
volver a redireccionar
}

Podemos usar isPathFree para encontrar recursivamente una ruta de objeto única.

función getPath () {
  return new Promise (función (resolver, rechazar) {
    let ruta = generatePath ()
    isPathFree (ruta)
      .then (function (isFree) {
        el retorno es gratis? resolve (ruta): resolve (getPath ())
      })
  })
}

Aprovechando la capacidad de encadenar promesas, devolvemos una nueva invocación de getPath si isPathFree devuelve falso.

Para guardar un objeto después de encontrar un shortcode único, solo necesitamos llamar al método putObject en el servicio AWS SDK S3. Vamos a concluir esto en una función que resuelve el shortcode si la llamada al método putObject fue exitosa y devuelve un objeto de error para construir una respuesta API si no fue así.

función saveRedirect (redireccionar) {
  return S3.putObject (redirigir) .promise ()
    .then (() => Promise.resolve (redirigir ['Clave']))
    .catch (() => Promise.reject ({
      statusCode: 500,
      mensaje: 'Error al guardar la redirección'
  })
}

Utilizando las funciones anteriores, podemos agregar dos nuevos controladores de éxito prometedor para finalizar nuestro punto final API. Necesitamos devolver getPath del primer controlador de éxito de promesa que resolverá un código abreviado de URL único. Al devolver saveRedirect con un objeto de redireccionamiento creado utilizando este código corto único en el segundo controlador de éxito, se guardará el objeto en el depósito S3. La ruta de este objeto se puede devolver al cliente como parte de una respuesta API. Nuestro controlador ahora debería estar completo.

module.exports.handle = (evento, contexto, devolución de llamada) => {
  let longUrl = JSON.parse (event.body) .url || ''
  validar (longUrl)
    .then (function () {
      return getPath ()
    })
    .then (función (ruta) {
      let redirect = buildRedirect (ruta, longUrl)
      return saveRedirect (redireccionar)
    })
    .then (función (ruta) {
      let response = buildResponse (200, 'éxito', ruta)
      return Promise.resolve (respuesta)
    })
    .catch (función (err) {
      let response = buildResponse (err.statusCode, err.message)
      return Promise.resolve (respuesta)
    })
    .then (función (respuesta) {
      devolución de llamada (nulo, respuesta)
    })
}

Implementar la API

Ejecute la implementación sin servidor en su terminal para implementar la API en AWS. Esto configurará nuestro depósito S3 y devolverá la URL del punto final. Tenga a mano la URL del punto final, ya que la necesitaremos más adelante.

Sin servidor: servicio de embalaje ...
Sin servidor: excluyendo dependencias de desarrollo ...
Sin servidor: cargando el archivo CloudFormation a S3 ...
Sin servidor: cargando artefactos ...
Sin servidor: cargando el archivo .zip del servicio a S3 (5.44 MB) ...
Sin servidor: Validando plantilla ...
Sin servidor: Actualizando la pila ...
Sin servidor: comprobando el progreso de la actualización de la pila ...
..............
Sin servidor: actualización de la pila finalizada ...
Servicio de información
servicio: serverless-url-shortener
etapa: dev
región: eu-west-1
pila: serverless-url-shortener-dev
claves de la API:
  Ninguna
puntos finales:
  POST - https://t2fgbcl26h.execute-api.eu-west-1.amazonaws.com/dev/
funciones:
  tienda: serverless-url-shortener-dev-store
Sin servidor: Eliminando versiones antiguas de servicio ...

Crear la interfaz

Para ayudar con el diseño de la interfaz, utilizaremos el marco PaperCSS. También tomaremos jQuery para simplificar el trabajo con el DOM y hacer consultas AJAX. Vale la pena señalar que para un entorno de producción es probable que desee obtener dos dependencias más ligeras, pero como esto es solo un tutorial, creo que es aceptable.

Cree una carpeta estática para que tengamos un lugar donde almacenar nuestro código de interfaz.

Descargar las dependencias

Guarde una copia de paper.min.css y jquery-3.2.1.min.js en nuestra carpeta estática recién creada, estas son versiones minimizadas del marco PaperCSS y la biblioteca jQuery respectivamente.

Agrega el HTML

Cree un nuevo archivo llamado index.html dentro de la carpeta estática y agregue el HTML requerido. Necesitamos un formulario con una entrada de URL y un botón para enviar el formulario. También necesitamos un lugar para colocar el resultado de cualquier llamada a la API, que para una llamada API exitosa sería la URL acortada y para una llamada API fallida, este sería el mensaje de error.




  
  
   Acortador de URL sin servidor 
  



  
    
      

Acortador de URL sin servidor

      
        
                              
        
                      

                   Ver este proyecto en Github                

    
  

Aunque no se muestra en el bloque de código anterior por brevedad, asegúrese de establecer la acción del formulario en el punto final de la API que se mostró cuando ejecutó la implementación sin servidor. Si ya no tiene acceso a la salida de su terminal desde esa implementación, puede encontrar la URL del punto final a través del comando de información sin servidor.

Hacer solicitudes de API

Antes de escribir el JavaScript para realizar solicitudes a nuestra API, primero carguemos jQuery agregando una etiqueta de script justo antes de y haciendo referencia al archivo minificado que descargamos anteriormente.

Ahora agregue otro par de etiquetas de script debajo y dentro, creemos una función que se pueda usar para mostrar un mensaje al usuario usando el mensaje div en nuestra plantilla que está configurado para mostrar: ninguno de forma predeterminada al cargar la página. Para mostrar un mensaje, simplemente podemos configurar el texto dentro de este div usando text () y alternar la pantalla usando show ().

Escribamos otra función para ir dentro del mismo conjunto de etiquetas de script que usarán jQuery para realizar solicitudes a nuestra API.

función shortenLink (apiUrl, longUrl) {
  $ .ajax (apiUrl, {
    tipo: 'POST',
    datos: JSON.stringify ({url: longUrl})})
    .done (función (respuesta JSON) {
      protocolo var = window.location.protocol + '//'
      var host = window.location.host + '/'
      var shortUrl = protocolo + host + respuestaJSON.path
      addMessage (shortUrl)
    })
    .fail (función (datos) {
      if (data.status === 400) {
        addMessage (data.responseJSON.message)
      } más {
        addMessage ('ocurrió un error inesperado')
      }
    })
}

Esta función crea una solicitud POST y establece el cuerpo de la solicitud en un objeto JSON que contiene la URL para acortar. Si la solicitud se completó con éxito y se devolvió un código de estado HTTP 2XX, toma el código corto de la clave de ruta en la respuesta y construye una URL corta totalmente calificada para presentar al usuario usando la función addMessage creada previamente. Si la solicitud no tuvo éxito, se muestra un mensaje de error.

Finalmente, podemos conectar esto a nuestro formulario agregando un controlador de envío. Obtenemos la URL de punto final de API del atributo de acción de formulario y obtenemos la URL para acortar desde la entrada del formulario de URL.

$ ('formulario'). submit (function (event) {
  event.preventDefault ()
  agregar mensaje('...')
  shortenLink (event.target.action, event.target.url.value)
})

Implementar el sitio web

Para la implementación del sitio web, utilizaremos el comando de sincronización de AWS CLI para cargar el contenido de la carpeta estática en nuestro bucket de S3. Ejecute aws s3 sync static s3: // [bucket] en su terminal, reemplazando [bucket] con su nombre de bucket elegido en config.json. Después de que esto se complete, debería poder dirigirse a su dirección de depósito S3 en un navegador para ver el acortador de URL en acción. Las URL públicas para los cubos S3 toman la siguiente forma.

http: // [bucket] .s3-website- [region] .amazonaws.com

Entonces, después de agregar el nombre del depósito y la región, su dirección de acortador de URL debería ser similar a la siguiente.

http://serverless-url-shortener.s3-website-eu-west-1.amazonaws.com

Para agregar un dominio personalizado a su bucket, debe seguir una de las instrucciones en este artículo de soporte de AWS. Para la opción más fácil, debe establecer el nombre del depósito en el subdominio www de su dominio (por ejemplo, www.example.com). Si luego agrega un registro CNAME en su configuración de DNS para el subdominio www y lo configura en su dirección de depósito S3, el sitio web debería ser accesible a través de su dominio. Asegúrese de eliminar también todos los registros A existentes y tenga en cuenta que esto no configurará una redirección de su dominio raíz al subdominio www. Hay un par de formas en que esto podría resolverse, que se describen en el artículo de AWS.

Envolver

Espero que hayas encontrado útil este tutorial. La realidad es que AWS es increíblemente flexible y en este artículo revisamos solo una forma en que puede crear un acortador de URL usando Lambda y S3. Pero hay una variedad de otras formas en que el mismo proceso también podría haberse logrado.

Si le pareció interesante, puede disfrutar de uno de mis artículos anteriores en el que creé un servicio de reenvío de formularios con AWS Lambda.