Skip to content

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

En este ejemplo, nos centramos en la navegación de [nuxt-12]. No lo hemos hecho en [nuxt-12] porque la comprobación de la navegación habría complicado aún más un ejemplo que ya de por sí es complejo.

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

  • si la sesión jSON no se ha iniciado, solo se autoriza la sesión URL y [/];
  • si la sesión jSON se ha iniciado pero el usuario no está autenticado, solo se autorizan 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 copiando el ejemplo [nuxt-12]:

Image

Las modificaciones se realizarán en la carpeta de enrutamiento [middleware].

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 se controla mediante 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: el enrutamiento del cliente y del servidor [nuxt] se trata de forma diferente. 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-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í nos encontramos en 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 de la siguiente manera:


<template>
  <!-- menú 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 el usuario ya está autenticado, 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 han recuperado los datos [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 en cuanto se ha iniciado la sesión jSON y el usuario está autenticado; de lo contrario, no lo está;

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í (al fin y al cabo, no se supone que el usuario haga eso) o intentar controlar la situación. Eso es lo que vamos a hacer 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 lo que respecta 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. A continuación, el navegador realiza una nueva solicitud al servidor [nuxt] pidiéndole el destino que se le ha transmitido. Todo ocurre como si el usuario hubiera introducido manualmente la URL del destino de la redirección: toda la aplicación se reinicia y, por lo tanto, todo su ciclo de vida (complementos del servidor, almacén, enrutamiento del servidor, páginas);
  • cuando se redirige a un cliente [nuxt], no ocurre nada de eso. Se produce un simple cambio de página, el mismo que se habría obtenido si el usuario hubiera hecho clic en un enlace que condujera al destino de la redirección. El ciclo de vida es entonces diferente (enrutamiento del cliente, visualización del destino de la ruta);

Por este motivo, 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 viene. 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 desde 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 desde cero. Por lo tanto, es en esta sesión [nuxt] y, más concretamente, en el almacén de esta sesión donde almacenaremos 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í nos encontramos en 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 introduce 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 almacén en la sesión [nuxt]
  const session = context.app.$session()
  session.value.store = context.store.state
  session.save(context)
}
  • se añaden las líneas 19-24;
  • línea 20: se introduce 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 se viene. Por otra parte, veremos que, en el enrutamiento del servidor [nuxt], este necesita saber si el enrutamiento actual procede de una redirección previa del servidor [nuxt]. En este caso no es así, por lo que establecemos la propiedad [serverRedirection] en [false];
  • líneas 22-24: el estado del almacén se guarda en la sesión [nuxt] (línea 23) y, a continuación, la sesión [nuxt] se guarda en una cookie (línea 24), que 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 finalizada
  let done = false

  // ¿Estamos ya en una redirección del servidor [nuxt]?
  if (store.state.serverRedirection) {
    // no hay que hacer nada
    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 finalizada
    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 ha obtenido [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 finalizado
    done = true
  }

  // se han realizado todas las comprobaciones ---------------------
  // ¿redirección?
  if (redirection) {
    // se anota 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 anota 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 comprueba 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 ponerse en duda mediante un nuevo enrutamiento;
  • líneas 23-27: se comprueba si el enrutamiento en curso corresponde a una recarga de la página. Si es así, se deja 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 trata el caso en el que se ha iniciado la sesión jSON, 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 vuelve al punto anterior;
  • líneas 56-62: se trata el caso en el que se ha obtenido [adminData]. En este caso, solo hay un destino posible para el enrutamiento: la página [fin-session]. Si no era esta la que se había solicitado, se rechaza el enrutamiento y se vuelve al punto anterior;
  • líneas 64-72: si se ha producido una redirección, se anota en el almacén del servidor [nuxt]: [serverRedirection: true]. Cabe señalar que no se asigna ningún valor a la propiedad [from] del almacén. El motivo es que se va a producir 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, también se anota en el almacén del servidor [nuxt]: [serverRedirection: false]. Por otra parte, el enrutamiento en curso mostrará la página [to], que para la siguiente solicitud (cliente o 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], que a su vez se guarda en una cookie;
  • líneas 77-80: si [redirection] no está vacío, se solicita al navegador que se redirija. De lo contrario (aunque aquí no se vea), 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 ha dicho, no realizar ningún enrutamiento. El elegido anteriormente tiene la ventaja de mantener siempre la aplicación en un estado estable, independientemente de la página URL solicitada por el usuario.

Se puede mejorar un aspecto cuando la página que se carga finalmente es la página original. Hay dos casos:

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

En ambos casos, la página original se volverá a ejecutar 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 se hace nada, la función [asyncData] se ejecutará de nuevo. 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 operaciones 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 la espera
      context.app.$eventBus().$emit('loading', true)
      // sin errores
      context.app.$eventBus().$emit('errorLoading', false)
    }
    try {
      // se realiza la autenticación en el servidor
...
  • líneas 6-9: si la página la ejecuta 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 con todas las páginas:

Página [index]:


// datos asíncronos
  async asyncData(context) {
    // registro
    console.log('[index asyncData started]')
    // no se repiten las operaciones 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 operaciones 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 la espera
      context.app.$eventBus().$emit('loading', true)
      // sin errores
      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 operaciones 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 errores
      context.app.$eventBus().$emit('errorLoading', false)
    }
    try {
   

16.4. Exécution

Para ejecutar este ejemplo, hay que asegurarse, antes de la ejecución, de eliminar la cookie de sesión [nuxt] y la cookie PHP del navegador en el que se ejecuta el cliente [nuxt], con el fin de partir de 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á pensada para utilizarse de esta manera. Una vez que el enrutador del servidor [nuxt] ha servido la página [index], se podrían redirigir las siguientes llamadas realizadas al servidor a una página de error.

En el caso concreto de nuestro ejemplo [nuxt-13], el enrutamiento del servidor [nuxt] era innecesario. El enrutamiento por defecto (en realidad, la ausencia de enrutamiento) del ejemplo [nuxt-12] era más que suficiente.