attribute javascript node.js url-routing hapijs

javascript - attribute - title css



Hapi anidado enrutamiento (2)

Lo que estás buscando es algo similar a Express''s Router . De hecho, Express hace un buen trabajo al enterrar la utilidad de esta función, por lo que volveré a publicar un ejemplo aquí:

// routes/users.js: // Note we are not specifying the ''/users'' portion of the path here... const router = express.Router(); // index route router.get(''/'', (req, res) => {... }); // item route router.get(''/:id'', (req, res) => { ... }); // create route router.post(''/'', (req,res) => { ... }); // update route router.put(''/:id'', (req,res) => { ... }); // Note also you should be using router.param to consolidate lookup logic: router.param(''id'', (req, res, next) => { const id = req.params.id; User.findById(id).then( user => { if ( ! user ) return next(Boom.notFound(`User [${id}] does not exist`)); req.user = user; next(); }).catch(next); }); module.exports = router;

Luego, en su aplicación.js o rutas principales / index.js donde ensambla sus rutas:

const userRoutes = require(''./routes/users'') // now we say to mount those routes at /users! Yay DRY! server.use(''/users'', userRoutes)

Realmente me decepciona encontrar esta publicación SO sin otras respuestas, así que asumiré que no hay nada fuera de la caja (o incluso un módulo de terceros) para lograr esto. Imagino que podría no ser demasiado difícil crear un módulo simple que use una composición funcional para eliminar la duplicación. Debido a que cada una de esas definiciones de ruta hapi es solo un objeto, parece que podría hacer una envoltura similar como la siguiente (sin probar):

function mountRoutes(pathPrefix, server, routes) { // for the sake of argument assume routes is an array and each item is // what you''d normally pass to hapi''s `server.route routes.forEach( route => { const path = `${pathPrefix}{route.path}`; server.route(Object.assign(routes, {path})); }); }

EDITAR En su caso, ya que tiene varias capas de anidamiento, una característica similar a router.param de Express también sería extremadamente útil. No estoy muy familiarizado con hapi, así que no sé si ya tiene esta capacidad.

EDITAR # 2 Para responder de manera más directa a la pregunta original, aquí hay un hapi-route-builder tiene un método setRootPath() que le permite lograr algo muy similar al permitirle especificar la parte base de la ruta una vez.

Supongamos que quiero tener puntos finales REST que se parecen aproximadamente a esto:

/projects/ /projects/project_id /projects/project_id/items/ /projects/project_id/items/item_id

CRUD en cada uno si tiene sentido. Por ejemplo, / projects POST crea un nuevo proyecto, GET recupera todos los proyectos. / projects / project_id GET obtiene solo ese proyecto.

Los elementos son específicos del proyecto, así que los puse bajo project_id, que es un proyecto en particular.

¿Hay alguna forma de crear este tipo de rutas anidadas?

Ahora mismo tengo algo como esto:

server.route({ method: ''GET'', path: ''/projects'', handler: getAllProjects }); server.route({ method: ''GET'', path: ''/projects/{project_id}'', handler: getOneProject }); server.route({ method: ''GET'', path: ''/projects/{project_id}/items/{item_id}'', handler: getOneItemForProject }); server.route({ method: ''GET'', path: ''/projects/{project_id}/items'', handler: getAllItemsForProject })

Pero estoy buscando una manera de anidar las rutas de los artículos en las rutas de los proyectos y la capacidad de pasar el proyecto aún más.

¿Alguna recomendación?


Si bien no existe el concepto de "subarrendamiento" (que yo sepa) en el propio hapi, los conceptos básicos son fáciles de implementar.

En primer lugar, hapi ofrece variables comodín en las rutas, al usarlas, básicamente creas una ruta para todos para una ruta determinada. Por ejemplo:

server.route({ method: ''GET'', path: ''/projects/{project*}'', handler: (request, reply) => { reply(''in /projects, re-dispatch '' + request.params.project); } });

Existen algunas reglas para estas rutas comodín, la más importante es que solo puede estar en el último segmento, lo que tiene sentido si lo considera un "catch-all".

En el ejemplo anterior, el parámetro {project*} estará disponible como request.params.project y contendrá el resto de la ruta llamada, por ejemplo, GET /projects/some/awesome/thing establecerá request.params.project en some/awesome/project .

El siguiente paso es manejar esta "ruta secundaria" (su pregunta real), que es principalmente una cuestión de gusto y cómo le gustaría trabajar. Su pregunta parece implicar que no desea crear una lista interminable de cosas bastante similares, pero al mismo tiempo poder tener rutas de proyectos muy específicas.

Una forma sería dividir el parámetro request.params.project en trozos y buscar carpetas con nombres coincidentes, que podrían contener lógica para seguir manejando la solicitud.

Exploremos este concepto asumiendo una estructura de carpetas (relativa al archivo que contiene la ruta, por ejemplo, index.js ) que se puede usar fácilmente para incluir los controladores para las rutas específicas.

const fs = require(''fs''); // require the built-in fs (filesystem) module server.route({ method: ''GET'', path: ''/projects/{project*}'', handler: (request, reply) => { const segment = ''project'' in request.params ? request.params.project.split(''/'') : []; const name = segment.length ? segment.shift() : null; if (!name) { // given the samples in the question, this should provide a list of all projects, // which would be easily be done with fs.readdir or glob. return reply(''getAllProjects''); } let projectHandler = [__dirname, ''projects'', name, ''index.js''].join(''/''); fs.stat(projectHandler, (error, stat) => { if (error) { return reply(''Not found'').code(404); } if (!stat.isFile()) { return reply(projectHandler + '' is not a file..'').code(500); } const module = require(projectHandler); module(segment, request, reply); }); } });

Un mecanismo como este le permitiría tener cada proyecto como un módulo de nodo en su aplicación y hacer que su código resuelva el módulo apropiado para usar para manejar la ruta en tiempo de ejecución.

Ni siquiera tiene que especificar esto para cada método de solicitud, ya que simplemente puede hacer que las rutas manejen múltiples métodos utilizando el method: [''GET'', ''POST'', ''PUT'', ''DELETE''] lugar del method: ''GET'' .

Sin embargo, no trata completamente con la declaración repetitiva de cómo manejar rutas, ya que necesitará una configuración de módulo bastante similar para cada proyecto.

En la forma en que el ejemplo anterior incluye e invoca "manejadores de sub-ruta", una implementación de ejemplo sería:

// <app>/projects/<projectname>/index.js module.exports = (segments, request, reply) => { // segments contains the remainder of the called project path // e.g. /projects/some/awesome/project // would become [''some'', ''awesome'', ''project''] inside the hapi route itself // which in turn removes the first part (the project: ''some''), which is were we are now // <app>/projects/some/index.js // leaving the remainder to be [''awesome'', ''project''] // request and reply are the very same ones the hapi route has received const action = segments.length ? segments.shift() : null; const item = segments.length ? segments.shift() : null; // if an action was specified, handle it. if (action) { // if an item was specified, handle it. if (item) { return reply(''getOneItemForProject:'' + item); } // if action is ''items'', the reply will become: getAllItemsForProject // given the example, the reply becomes: getAllAwesomeForProject return reply(''getAll'' + action[0].toUpperCase() + action.substring(1) + ''ForProject''); } // no specific action, so reply with the entire project reply(''getOneProject''); };

Creo que esto ilustra cómo se pueden manejar los proyectos individuales dentro de su aplicación en el tiempo de ejecución, aunque plantea un par de preocupaciones con las que querrá tratar cuando construya la arquitectura de su aplicación:

  • Si el módulo de manejo del proyecto realmente es muy similar, debe crear una biblioteca que use para evitar copiar el mismo módulo una y otra vez, ya que eso facilita el mantenimiento (que, según lo reconozco, era el objetivo final de tener sub-enrutamiento)
  • Si puede averiguar qué módulos usar en tiempo de ejecución, también debería poder resolver esto cuando se inicie el proceso del servidor.

Crear una biblioteca para evitar el código repetitivo es algo que debe hacer (aprender a hacer) desde el principio, ya que esto facilita el mantenimiento y su futuro se lo agradecerá.

Descubrir qué módulos estarán disponibles para manejar los diversos proyectos que tiene al inicio de la aplicación evitará que cada solicitud tenga que aplicar la misma lógica una y otra vez. Hapi puede ser capaz de almacenar esto en caché para usted, en cuyo caso realmente no importa, pero si el almacenamiento en caché no es una opción, es mejor que utilice rutas menos dinámicas (lo cual, creo que es la razón principal por la que no se ofrece). por hapi por defecto).

Puede atravesar la carpeta de proyectos buscando todos los <project>/index.js al inicio de la aplicación y registrar una ruta más específica utilizando glob como este:

const glob = require(''glob''); glob(''projects/*'', (error, projects) => { projects.forEach((project) => { const name = project.replace(''projects/'', ''''); const module = require(project); server.route({ method: ''GET'', path: ''/projects/'' + name + ''/{remainder*}'', handler: (request, reply) => { const segment = ''remainder'' in request.params ? request.params.remainder.split(''/'') : []; module(segment, request, reply); } }); }); });

Esto reemplaza de manera efectiva la lógica anterior de buscar el módulo en cada solicitud y cambiar a un enrutamiento (un poco más eficiente) ya que está considerando exactamente qué proyectos estará sirviendo al mismo tiempo que deja el manejo real a cada módulo de proyecto que proporcione. (No olvide implementar la ruta /projects , ya que ahora esto debe hacerse de manera explícita)