@drawohara¡me ❤️ esto! << haz clic 🐛 🫖 🧚
/detección-móvil-detrás-de-un-cdn
publicado el: 2014-07-23

En un proyecto reciente, nos asignaron la tarea de construir la versión móvil de un sitio web existente. El sitio web existente realmente no era un buen candidato para un diseño responsivo, por lo que decidimos crear una versión completamente nueva del sitio para dispositivos móviles.

![55478517703598506559_z.jpg](assets/b.jpeg)
_(foto por Johan Larsson)

Al principio estábamos pensando en usar el patrón m.example.com, donde los sitios son dominios completamente separados. Sin embargo, dado que no administramos la infraestructura de este sitio, queríamos optar por un enfoque que requiriera la menor cantidad posible de cambios en la infraestructura. Soportar otro dominio en los mismos servidores no es muy difícil, pero es solo otra tarea que es fácil de estropear y no es necesaria en nuestro caso.

También he publicado esta técnica para que cualquiera pueda usarla fácilmente en una aplicación de rails a través de un motor de rails:

Intro a CloudFront

Una gran ventaja de este sitio web en particular es que utiliza CloudFront. CloudFront es una red de entrega de contenido (también conocida como CDN), donde las páginas web estáticas se alojan en 52 centros de datos en todo el mundo. Esto permite que la página se sirva al navegador lo más rápido posible porque, sin importar dónde estés en el mundo, no estarás lejos de uno de los nodos de CloudFront, por lo que tomará menos tiempo para que el sitio web se entregue a tu dispositivo. CloudFront es parte de la oferta de AWS de Amazon y, además de garantizar la velocidad, garantizan el tiempo de actividad. (A diferencia de S3, donde solo se garantiza el tiempo de actividad.)

Cuando usas CloudFront, le dices dónde está tu servidor web para que CloudFront pueda obtener la última versión de las páginas web que está sirviendo para ti. CloudFront también solo le pide a tu servidor web nuevas versiones de tus páginas web cada pocos minutos, o el intervalo de tiempo que configures en tu panel de CloudFront. Normalmente, es en algún lugar alrededor de 24 horas. Esto permite que CloudFront se centre en la disponibilidad y la velocidad, al mismo tiempo que reduce considerablemente la carga en tu servidor web. (También lleva tiempo para que CloudFront tome una nueva versión de una página y actualice los 52 centros de datos.)

Sin embargo, esto introduce el problema de rastrear el estado. Por ejemplo, si tu sitio es enteramente servido por CloudFront, ¿cómo soportas que los usuarios inicien sesión en tu sitio? Si soy un usuario llamado Bob y la página en /mi/cuenta dice “Hola Bob!”, CloudFront pensará que todos los que soliciten la página en /mi/cuenta deben ver la versión de la página que dice “Hola Bob!”. Los usuarios (excepto tal vez las personas llamadas Bob…) estarán muy confundidos. No solo eso, sino ¿qué pasa si un usuario actualiza algún valor en su cuenta y espera que ese cambio se muestre al instante? CloudFront no obtiene una nueva versión de la página en cada solicitud (eso más o menos derrotaría el propósito de CloudFront), espera cada pocos minutos para obtener una nueva versión. CloudFront ha abordado varias de estas preguntas con nuevas funciones, pero debes ser consciente de estas implicaciones al usar CloudFront.

En nuestro caso, el sitio en el que estamos trabajando no tiene el concepto de "usuarios" y nadie puede iniciar sesión en el sistema. Sin embargo, aún queremos que CloudFront muestre diferentes versiones del sitio dependiendo del dispositivo desde el que se solicita el sitio (móvil vs. escritorio).

CloudFront puede detectar el dispositivo para ti y luego enviar un encabezado especial a tu servidor web para decir "Dame la versión de la página para un teléfono", sin embargo, queríamos controlar el método de detección del dispositivo (respondiendo a la pregunta - ¿es esto un teléfono o un escritorio?) y queríamos la capacidad de que los usuarios finales y los desarrolladores anulen el dispositivo elegido para ellos. Por ejemplo, los usuarios finales en una tablet pueden querer ver la versión de escritorio incluso si inicialmente les mostramos la versión móvil. Nuestros desarrolladores también encontrarán que es una molestia cambiar su dispositivo cada vez que quieran cambiar entre las versiones al construir el sitio.

Nuestra Solución

Entonces, este es el método que se nos ocurrió para tener tanto una versión de escritorio como móvil de un sitio en el mismo dominio y servido por CloudFront. Una nota requerida es que CloudFront normalmente deshabilita todas las cookies para tu dominio, pero puedes configurarlo para permitir las cookies que nombres. En nuestro caso, le dijimos a CloudFront que permitiera una cookie llamada 'dispositivo'. También ten en cuenta que estamos en el contexto de una aplicación de rails 4, pero esta arquitectura se puede replicar en cualquier stack. El punto principal aquí es que el cliente necesita averiguar qué dispositivo está usando el usuario, permitirle anularlo y mantener al servidor informado del tipo de dispositivo para que el servidor pueda servir las páginas correctas (esto se hace a través de cookies).

En cada página necesitamos hacer algo como esto (fragmento de app/views/layout/application.html.erb)

<%= javascript_include_tag "mobile_detection" %>

<script>

  if (!window.location.href.match(/get_device|set_device/)){
    console.log('logging the window.location.href = ' + window.location.href);
    localStorage.setItem('referrer_href', window.location.href);
  }

  var device_cookie  = cookie.get('device'),
      cookiesEnabled = cookie.enabled();

  if (cookiesEnabled && device_cookie == undefined) {
    window.location = '/get_device.html'
  }

</script>

El código anterior simplemente recuerda en qué página aterrizaste (generalmente la página de inicio, pero queremos que el cliente pueda compartir enlaces y que funcione en móvil también), y luego te redirige a la página /get_device para detectar tu dispositivo si no lo hemos hecho aún. Ten en cuenta el uso de localStorage, lo que significa que esto solo funcionará en IE8+ y no funcionará en Opera Mini.

También ten en cuenta que el fragmento anterior se coloca en el <head> del diseño para que se ejecute lo antes posible y antes de cualquier script que pueda tener la versión de escritorio. En este caso, la versión de escritorio usa mucho javascript para cargar mucho y mucho contenido, por lo que realmente no queríamos que todo eso sucediera en un teléfono antes de que el sitio reconociera que el usuario debería estar viendo la versión móvil del sitio.

En cada página móvil necesitamos hacer esto (de `app/views/layout/application.mobile.erb):

    <script>
      var device_cookie  = cookie.get('device'),
          cookiesEnabled = cookie.enabled();
 
      if (cookiesEnabled && device_cookie == 'desktop') {
        window.location.reload(true);
      }
    </script>

Esto se debe a que los dispositivos móviles almacenan en caché las páginas en gran medida (lo cual tiene sentido) por lo que si el usuario ha cambiado el dispositivo en el que se encuentra, debemos asegurarnos de enviar esa cookie actualizada al servidor aunque la url no haya cambiado (y por lo tanto la caché piensa que es la misma página que debe servirse). El true en la llamada a la función reload le dice al navegador que ignore lo que hay en la caché.

Ten en cuenta que hacemos exactamente lo mismo en cada página de escritorio también, excepto que verificamos si el usuario ha cambiado su dispositivo a 'móvil'.

Detección de Dispositivo en /get_device (app/views/home/get_device.html)

  <script>
 
    function isMobile(){
      var MOBILE_USER_AGENTS = 'palm|blackberry|nokia|phone|midp|mobi|symbian|chtml|ericsson|minimo|' +
                            'audiovox|motorola|samsung|telit|upg1|windows ce|ucweb|astel|plucker|' +
                            'x320|x240|j2me|sgh|portable|sprint|docomo|kddi|softbank|android|mmp|' +
                            'pdxgw|netfront|xiino|vodafone|portalmmm|sagem|mot-|sie-|ipod|up\\.b|' +
                            'webos|amoi|novarra|cdm|alcatel|pocket|ipad|iphone|mobileexplorer|' +
                        'mobile|zune',
          mobile_regex      = new RegExp(MOBILE_USER_AGENTS, "i");
 
      return navigator.userAgent.match(mobile_regex);
    }
 
    // estableciendo la cookie del dispositivo en