Skip to content

16. Ejemplo [nuxt-13]: verificación de la navegación de [nuxt-12]

En este ejemplo, nos enfocamos en la navegación de [nuxt-12]. No lo hicimos en [nuxt-12] porque la verificación de la navegación habría complicado aún más un ejemplo que ya era complejo.

Objetivo: queremos que el usuario solo pueda realizar acciones autorizadas:

  • si la sesión jSON no se ha iniciado, entonces solo se permite la sesión URL y [/];
  • si la sesión jSON se ha iniciado, pero el usuario no está autenticado, entonces solo se permiten las sesiones URL y [/authentification];
  • si la sesión jSON se ha iniciado y el usuario está autenticado, entonces solo se autorizan las sesiones URL y [/get-admindata, /fin-session];
  • cuando el destino del enrutamiento actual no esté autorizado, se procederá a una redirección hacia una URL autorizada;

El ejemplo [nuxt-13] se obtiene inicialmente al copiar el ejemplo [nuxt-12]:

Image

Es en la carpeta de enrutamiento de [middleware] donde se realizarán las modificaciones.

16.1. Enrutamiento de la aplicación [nuxt]

El enrutamiento de la aplicación está configurado de la siguiente manera en el archivo [nuxt.config]:


// enrutador
  router: {
    // raíz de los URL de la aplicación
    base: '/nuxt-13/',
    // middleware de enrutamiento
    middleware: ['routing']
},
  • línea 6: el enrutamiento de la aplicación está controlado por el archivo [middleware/routing];

El archivo [middleware/routing] es el siguiente:


/* eslint-disable no-console */

// importamos el middleware del servidor y del cliente
import serverRouting from './server/routing'
import clientRouting from './client/routing'

export default function(context) {
  // ¿Quién ejecuta este código?
  console.log('[middleware], process.server', process.server, ', process.client=', process.client)
  if (process.server) {
    // enrutamiento del servidor
    serverRouting(context)
  } else {
    // enrutamiento del cliente
    clientRouting(context)
  }
}
  • líneas 10-16: se tratan de manera diferente las rutas del cliente y del servidor [nuxt]. Este es un punto en el que difieren considerablemente;
  • línea 4: el enrutamiento del servidor se implementa mediante el script [middleware/server/routing];
  • línea 5: el enrutamiento del cliente se implementa mediante el script [middleware/client/routing];

16.2. Enrutamiento del cliente [nuxt]

El enrutamiento del cliente [nuxt] sigue siendo el mismo que en [nuxt-12]:


/* eslint-desactivar no-console */
export default function(context) {
  // ¿Quién ejecuta este código?
  console.log('[middleware client], process.server', process.server, ', process.client=', process.client)
  // gestión de la cookie de sesión PHP en el navegador
  // la cookie de sesión PHP del navegador debe ser idéntica a la que se encuentra en la sesión de Nuxt
  // la acción [fin-session] recibe una nueva cookie PHP (tanto del servidor como del cliente Nuxt)
  // si es el servidor quien la recibe, el cliente debe transmitirla al navegador
  // para sus propias comunicaciones con el servidor PHP
  //: aquí se trata de un enrutamiento del cliente

  // se recupera la cookie de sesión PHP
  const phpSessionCookie = context.store.state.phpSessionCookie
  if (phpSessionCookie) {
    // si existe, se asigna la cookie de sesión PHP al navegador
    document.cookie = phpSessionCookie
  }

  ...
}

Para evitar que el cliente acceda a rutas no autorizadas, simplemente le ofreceremos en el menú de navegación del cliente únicamente las rutas autorizadas. El componente [components/navigation] queda así:


<template>
  <!-- menú de Bootstrap con tres opciones -->
  <b-nav vertical>
    <b-nav-item v-if="$store.state.jsonSessionStarted && !$store.state.userAuthenticated" to="/authentification" exact exact-active-class="active">
      Authentification
    </b-nav-item>
    <b-nav-item
      v-if="$store.state.jsonSessionStarted && $store.state.userAuthenticated && !$store.state.adminData"
      to="/get-admindata"
      exact
      exact-active-class="active"
    >
      Requête AdminData
    </b-nav-item>
    <b-nav-item v-if="$store.state.jsonSessionStarted && $store.state.userAuthenticated" to="/fin-session" exact exact-active-class="active">
      Fin session impôt
    </b-nav-item>
  </b-nav>
</template>
  • línea 4: la opción [Authentification] solo se ofrece si la sesión jSON se ha iniciado, pero el usuario no está autenticado. Si la sesión jSON no se ha iniciado o si el usuario ya está autenticado, entonces la opción no se ofrece;
  • líneas 7-11: la opción [Requête AdminData] solo está disponible si la sesión jSON se ha iniciado, el usuario está autenticado y aún no se ha recuperado el dato [AdminData]. Si no se cumple alguna de estas tres condiciones (la sesión jSON no se ha iniciado, el usuario no está autenticado o ya se han recuperado los datos [AdminData]), la opción no está disponible;
  • línea 15: la opción [Fin session impôt] está disponible tan pronto como se inicie la sesión jSON y el usuario esté autenticado; de lo contrario, no está disponible;

16.3. Enrutamiento del servidor [nuxt]

El enrutamiento del servidor suele ser más complejo que el del cliente, ya que el usuario puede escribir cualquier URL en la barra de direcciones de su navegador. Podemos dejarlo así (después de todo, no se supone que el usuario haga eso) o intentar controlar la situación. Eso es lo que haremos aquí, a modo de ejemplo, ya que en el caso de la aplicación [nuxt-12], podemos prescindir perfectamente de ello, ya que el servidor de cálculo de impuestos está bien protegido contra estas URL introducidas manualmente y sabe enviar los mensajes de error adecuados. Ya lo vimos en [next-12], donde no había ningún control de enrutamiento.

El enrutamiento de un servidor [nuxt] es muy diferente al de un cliente [nuxt] en cuanto al concepto de redirección:

  • cuando un servidor [nuxt] es redirigido, envía una orden de redirección al navegador del cliente con el destino de la redirección. El navegador entonces realiza una nueva solicitud al servidor [nuxt] pidiéndole el destino que se le ha transmitido. Todo ocurre como si el usuario hubiera escrito manualmente la dirección del destino de la redirección: toda la aplicación se reinicia y, por lo tanto, todo su ciclo de vida (plugins del servidor, tienda, enrutamiento del servidor, páginas);
  • cuando se redirige a un cliente [nuxt], no ocurre nada de eso. Solo hay un simple cambio de página, el mismo que se habría obtenido si el usuario hubiera hecho clic en un enlace que llevara al destino de la redirección. El ciclo de vida es entonces diferente (enrutamiento del cliente, visualización del destino de la ruta);

Por esta razón, es preferible separar el enrutamiento del cliente del enrutamiento del servidor, aunque ambos códigos puedan parecer similares.

El script de enrutamiento del servidor [middleware/server/routing] será el siguiente:


/* eslint-disable no-console */
export default function(context) {
  // ¿Quién ejecuta este código?
  console.log('[middleware server], process.server', process.server, ', process.client=', process.client)

  // se recupera cierta información del almacén [nuxt]
  const store = context.store
  // ¿De dónde venimos?
  const from = store.state.from || 'nowhere'
  ...
}
  • en el enrutamiento del cliente, la función de enrutamiento recibe el contexto [context] con la propiedad [context.from], que es la ruta de la página de la que se proviene. La ruta a la que se va se obtiene mediante [context.route];
  • en el enrutamiento del servidor, la función de enrutamiento recibe el contexto [context] sin la propiedad [context.from]. El enrutamiento del servidor solo interviene cuando se solicita manualmente un URL al servidor [nuxt]. Sabemos que, en ese momento, toda la aplicación [nuxt] se reinicia. Es como si se empezara de cero y, por lo tanto, no existe el concepto de «página anterior»;
  • gracias a la sesión [nuxt] sabemos que el servidor puede recuperar esta sesión y, por lo tanto, no empezar de cero. Por lo tanto, es en esta sesión [nuxt] y, más específicamente, en el almacén de esta sesión donde guardaremos el nombre de la última página mostrada por el navegador del cliente antes de que se solicite una URL al servidor [nuxt];
  • líneas 7-9: recuperamos el nombre de la última página mostrada por el navegador del cliente. Al iniciar la aplicación, esta información [from] no existe en el almacén. A continuación, se asigna el nombre [nowhere] a la variable [from];

Para que el servidor [nuxt] pueda recuperar del almacén el nombre de la última página mostrada por el navegador del cliente, es necesario que el cliente [nuxt] también guarde esta información en el almacén. Por lo tanto, el script de enrutamiento del cliente [nuxt] se completa de la siguiente manera:


/* eslint-disable no-console */
export default function(context) {
  // ¿Quién ejecuta este código?
  console.log('[middleware client], process.server', process.server, ', process.client=', process.client)
  // Gestión de la cookie de sesión PHP en el navegador
  // La cookie de sesión PHP del navegador debe ser idéntica a la que se encuentra en la sesión de Nuxt
  // la acción [fin-session] recibe una nueva cookie PHP (tanto el servidor como el cliente Nuxt)
  // si es el servidor quien la recibe, el cliente debe transmitirla al navegador
  // para sus propias comunicaciones con el servidor PHP
  //: aquí se trata de un enrutamiento del cliente

  // se recupera la cookie de sesión PHP
  const phpSessionCookie = context.store.state.phpSessionCookie
  if (phpSessionCookie) {
    // si existe, se asigna la cookie de sesión PHP al navegador
    document.cookie = phpSessionCookie
  }

  // se guarda en la sesión el nombre de la página a la que se va; no hay redirección del servidor
  context.store.commit('replace', { serverRedirection: false, from: context.route.name })
  // se guarda el store en la sesión [nuxt]
  const session = context.app.$session()
  session.value.store = context.store.state
  session.save(context)
}
  • se agregan las líneas 19 a 24;
  • línea 20: se guarda en el almacén el nombre de la página [context.route.name] que se va a mostrar y que, por lo tanto, en el siguiente enrutamiento será la página de la que venimos. Además, veremos que en el enrutamiento del servidor [nuxt], este necesita saber si el enrutamiento actual proviene de una redirección previa del servidor [nuxt]. En este caso no es así, por lo que establecemos la propiedad de [serverRedirection] a [false];
  • líneas 22-24: el estado del almacén se guarda en la sesión [nuxt] (línea 23) y luego la sesión [nuxt] se guarda en una cookie (línea 24), la cual a su vez se guardará en el navegador del cliente [nuxt];

Volvamos al script de enrutamiento del servidor [nuxt]:


/* eslint-disable no-console */
export default function(context) {
  // ¿Quién ejecuta este código?
  console.log('[middleware server], process.server', process.server, ', process.client=', process.client)

  // se recupera cierta información del almacén [nuxt]
  const store = context.store
  // ¿De dónde venimos?
  const from = store.state.from || 'nowhere'
  // ¿A dónde vamos?
  const to = context.route.name
  // posible redirección
  let redirection = ''
  // Gestión del enrutamiento completada
  let done = false

  // ¿Ya estamos en una redirección del servidor [nuxt]?
  if (store.state.serverRedirection) {
    // no hay nada que hacer
    done = true
  }

  // ¿Se trata de una recarga de la página?
  if (to === from) {
    // No hay nada que hacer
    done = true
  }
  
  // control de la navegación del servidor [nuxt]
  // se toma como referencia la navegación del cliente en el componente [navigation]

  // caso en el que la sesión PHP no se ha iniciado
  if (!done && !store.state.jsonSessionStarted && to !== 'index') {
    // redirección
    redirection = 'index'
    // tarea completada
    done = true
  }

  // en caso de que el usuario no esté autenticado
  if (!done && store.state.jsonSessionStarted && !store.state.userAuthenticated && to !== 'authentification') {
    // redirección
    redirection = from
    // tarea completada
    done = true
  }

  // caso en el que el usuario se ha autenticado
  if (!done && store.state.jsonSessionStarted && store.state.userAuthenticated && to !== 'get-admindata' && to !== 'fin-session') {
    // se permanece en la misma página
    redirection = from
    // tarea completada
    done = true
  }

  // caso en el que se obtuvo [adminData]
  if (!done && store.state.jsonSessionStarted && store.state.userAuthenticated && store.state.adminData && to !== 'fin-session') {
    // se permanece en la misma página
    redirection = from
    // trabajo terminado
    done = true
  }

  // se han realizado todas las verificaciones ---------------------
  // ¿redirección?
  if (redirection) {
    // se registra la redirección en el almacén
    store.commit('replace', { serverRedirection: true })
  } else {
    // sin redirección
    store.commit('replace', { serverRedirection: false, from: to })
  }
  // se guarda el almacén en la sesión [nuxt]
  const session = context.app.$session()
  session.value.store = store.state
  session.save(context)
  // se realiza la posible redirección
  if (redirection) {
    context.redirect({ name: redirection })
  }
}
  • líneas 6-9: se recupera el valor de [from] del almacén del servidor [nuxt];
  • línea 11: se registra el destino del enrutamiento actual;
  • línea 13: el enrutamiento puede dar lugar a una redirección del navegador del cliente. [redirection] será el destino de esta redirección;
  • línea 15: [done] a [true] indica que el enrutamiento ha finalizado;
  • líneas 17-21: primero se verifica si el enrutamiento actual proviene de una solicitud de redirección enviada al navegador del cliente. Esta información se almacena en la propiedad [serverRedirection] del almacén. Si esta propiedad es verdadera, significa que el servidor [nuxt] envió una redirección al navegador del cliente durante la solicitud anterior al servidor [nuxt]. En este caso, no hay que realizar ningún enrutamiento. Durante la solicitud anterior, el enrutador del servidor [nuxt] decidió que el navegador del cliente debía ser redirigido. Esta decisión no debe ser cuestionada por un nuevo enrutamiento;
  • líneas 23-27: se verifica si el enrutamiento actual corresponde a una recarga de página. Si es así, se permite que se realice;
  • A partir de la línea 29, se retoman las reglas aplicadas en el componente [navigation] del cliente [nuxt] (véase el párrafo anterior);
  • líneas 32-38: se trata el caso en el que la sesión jSON no se ha iniciado y el destino del enrutamiento no es la página [index]. En este caso, se redirige el navegador del cliente a la página [index];
  • líneas 40-46: se trata el caso en el que la sesión jSON se ha iniciado, el usuario no está autenticado y el destino del enrutamiento actual no es la página [authentification]. En este caso, se rechaza el enrutamiento y se permanece donde se estaba;
  • líneas 48-54: se procesa el caso en el que la sesión jSON se ha iniciado, el usuario está autenticado y el destino del enrutamiento actual no es ni la página [get-admindata] ni la página [fin-session], que son entonces los únicos destinos posibles. En este caso, se rechaza el enrutamiento solicitado y se regresa al punto anterior;
  • líneas 56-62: se procesa el caso en el que se obtuvo [adminData]. En este caso, solo hay un destino posible para el enrutamiento: la página [fin-session]. Si no era esa la que se solicitó, se rechaza el enrutamiento y se regresa al punto anterior;
  • líneas 64-72: si hubo redirección, se registra en el almacén del servidor [nuxt]: [serverRedirection: true]. Cabe señalar que no se le asigna ningún valor a la propiedad [from] del almacén. La razón es que habrá una redirección del navegador del cliente y hemos visto que, en este caso, no hay enrutamiento (líneas 17-20) y la propiedad [from] del almacén no se utiliza;
  • líneas 66-69: si no hay redirección, entonces también se registra en el almacén del servidor [nuxt]: [serverRedirection: false]. Además, el enrutamiento actual mostrará la página [to], que para la siguiente solicitud (ya sea del cliente o del servidor [nuxt]) se convertirá en la página anterior. Por eso se escribe [from: to];
  • líneas 73-76: se guarda el store en la sesión [nuxt], la cual a su vez se guarda en una cookie;
  • líneas 77-80: si [redirection] no está vacío, se le pide al navegador que se redirija. De lo contrario (no se ve aquí), el ciclo de vida del servidor [nuxt] continuará: la página [to] será procesada por el servidor [nuxt] y enviada al navegador del cliente [nuxt] junto con la cookie de sesión [nuxt];

El enrutamiento elegido aquí para el servidor [nuxt] es arbitrario. Se podría haber elegido otro o, como se mencionó anteriormente, no haberlo hecho en absoluto. El elegido anteriormente tiene la ventaja de mantener siempre la aplicación en un estado estable, independientemente de la página URL que solicite el usuario.

Se puede mejorar un punto cuando la página que se carga al final es la página original. Hay dos casos:

  • el usuario ha provocado una recarga de la página (to===from);
  • hay redirecciones hacia la página original (redirección===from);

En ambos casos, la página original se ejecutará nuevamente con su llamada asíncrona al servidor de cálculo de impuestos. Veamos un ejemplo. Si, una vez autenticado, el usuario recarga la página (F5). En este caso, en el enrutamiento anterior, tenemos: [to]=[from]=[authentification]. No hay redirección. La página [to=authentification] será ejecutada por el servidor [nuxt]. Si no hacemos nada, la función [asyncData] se ejecutará nuevamente. Esto es innecesario, ya que la autenticación ya se ha realizado.

Podemos mejorar esto modificando ligeramente la página [authentification]:


// datos asíncronos
  async asyncData(context) {
    // registro
    console.log('[authentification asyncData started]')
    // no se repiten las acciones si la página ya se ha solicitado
    if (process.server && context.store.state.userAuthenticated) {
      console.log('[authentification asyncData canceled]')
      return { result: '[succès]' }
    }
     // cliente [nuxt]
    if (process.client) {
      // inicio de espera
      context.app.$eventBus().$emit('loading', true)
      // sin errores
      context.app.$eventBus().$emit('errorLoading', false)
    }
    try {
      // se realiza la autenticación con el servidor
...
  • líneas 6-9: si la página es ejecutada por el servidor [nuxt] y se detecta en el almacén que la autenticación ya se ha realizado, entonces se devuelve directamente el resultado deseado (línea 8);

Se hace lo mismo para todas las páginas:

Página [index]:


// datos asíncronos
  async asyncData(context) {
    // registro
    console.log('[index asyncData started]')
    // no se repiten las acciones si la página ya se ha solicitado
    if (process.server && context.store.state.jsonSessionStarted) {
      console.log('[index asyncData canceled]')
      return { result: '[succès]' }
    }
    try {
...

Página [get-admindata]


// datos asíncronos
  async asyncData(context) {
    // registro
    console.log('[get-admindata asyncData started]')
    // no se repiten las acciones si la página ya se ha solicitado
    if (process.server && context.store.state.adminData) {
      console.log('[get-admindata asyncData canceled]')
      return { result: context.store.state.adminData }
    }
    // cliente
    if (process.client) {
      // inicio de espera
      context.app.$eventBus().$emit('loading', true)
      // sin error
      context.app.$eventBus().$emit('errorLoading', false)
    }
    try {
   ...  

Página [fin-session]


// datos asíncronos
  async asyncData(context) {
    // registro
    console.log('[fin-session asyncData started]')
    // no se repiten las acciones si la página ya se ha solicitado
    if (process.server && context.store.state.jsonSessionStarted && !context.store.state.userAuthenticated) {
      console.log('[fin-session asyncData canceled]')
      return { result: "[succès]. La session jSON reste initialisée mais vous n'êtes plus authentifié(e)." }
    }
    // caso del cliente [nuxt]
    if (process.client) {
      // inicio de la espera
      context.app.$eventBus().$emit('loading', true)
      // sin error
      context.app.$eventBus().$emit('errorLoading', false)
    }
    try {
   

16.4. Exécution

Para ejecutar este ejemplo, es necesario asegurarse, antes de la ejecución, de eliminar la cookie de sesión [nuxt] y la cookie PHP del navegador que ejecuta el cliente [nuxt], a fin de comenzar desde una situación limpia. A continuación se muestra un ejemplo con el navegador Chrome:

Image

16.5. Conclusion

El enrutamiento del servidor [nuxt] es complejo, ya que hay que prever todas las URL que el usuario pueda escribir manualmente. Es un caso de libro. Una aplicación [nuxt] no está diseñada para usarse de esta manera. Una vez que el enrutador del servidor [nuxt] ha servido la página [index], se podrían redirigir las siguientes llamadas al servidor hacia una página de error.

En el caso específico de nuestro ejemplo [nuxt-13], el enrutamiento del servidor [nuxt] era innecesario. El enrutamiento predeterminado (en realidad, la ausencia de enrutamiento) en el ejemplo [nuxt-12] funcionaba perfectamente.