当前位置: 首页 > 知识库问答 >
问题:

快乐的嵌套路由

宓诚
2023-03-14

假设我希望RESTendpoint大致如下:

/projects/
/projects/project_id 

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

每个if上的CRUD都有意义。例如,/projects POST创建一个新项目,GET获取所有项目/projects/project_id GET仅获取该一个项目。

项目是特定于项目的,所以我将它们放在project_id下,这是一个特定的项目。

有没有办法创建这种嵌套路由?

现在我有这样的东西:

  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
  })

但是我正在寻找一种方法将项目路线嵌套到项目路线中,并能够进一步传递项目。

有什么建议吗?

共有3个答案

宫晟
2023-03-14

关于这样一个基本需求的信息并不多。目前,我正在做以下工作

步骤1:在插件中包含路由,如下所示:

// server.js
const server = Hapi.server({ ... })
await server.register(require('./routes/projects'), { routes: { prefix: '/projects' } })

步骤2:在该插件的范围内注册一个ext

// routes/projects/index.js
module.exports = {

    name: 'projects',

    async register(server) {

        server.route({
            method: 'get',
            path: '/', // note: you don't need to prefix with `projects`
            async handler(request, h) {
                return [ ... ]
            }
        })

        server.route({
            method: 'get',
            path: '/{projectId}', // note: you don't need to prefix with `projects`
            async handler(request, h) {
                return { ... }
            }
        })

        server.ext({
            // https://hapijs.com/api#request-lifecycle
            type: 'onPostAuth',
            options: {
                // `sandbox: plugin` will scope this ext to this plugin
                sandbox: 'plugin'
            },
            async method (request, h) {
                // here you can do things as `pre` steps to all routes, for example:
                // verify that the project `id` exists
                if(request.params.projectId) {
                    const project = await getProjectById(request.params.projectId)
                    if(!project) {
                        throw Boom.notFound()
                    }
                    // Now request.params.project can be available to all sub routes
                    request.params.project = project
                }
                return h.continue
            }
        })

    }

}

这已经接近我已经能够重新创建快速路由器功能。

江浩慨
2023-03-14

您正在寻找的是类似于Express路由器的东西。事实上,Express在掩埋此功能的有用性方面做得很好,所以我将在这里重新发布一个示例:

// 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;

然后在您的html" target="_blank">应用程序中.js或主要路线/索引.js您组装路线的位置:

const userRoutes = require('./routes/users')

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

事实上,我很失望地发现这篇文章没有其他回复,所以我假设没有现成的东西(甚至第三方模块!)为了实现这一点。我想,创建一个简单的模块,使用函数组合来消除重复可能不会太困难。由于这些hapi路由定义中的每一个都只是一个对象,因此您似乎可以制作类似的包装器,如下所示(未测试):

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}));
  });
}

编辑 在你的情况下,因为你有多个嵌套层,类似于Express的路由器.param的功能也将是非常有帮助的。我不是很熟悉happy,所以我不知道它是否已经具有这种能力。

编辑#2为了更直接地回答原来的问题,这里有一个hapi-route-builder有一个< code>setRootPath()方法,通过让您一次指定路径的基本部分,您可以实现非常类似的事情。

金昌胤
2023-03-14

虽然在hapi本身中没有“子程序”的概念(据我所知),但其基本原理很容易实现。

首先,hapi在路径中提供通配符变量,使用这些变量,您基本上可以为给定路径创建一个全面的路由。例如:

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

对于这些通配符路径有一些规则,最重要的是它只能在最后一段中,如果您将其视为一个“全包”,这是有意义的。

在上面的示例中,< code>{project*}参数将作为< code > request . params . project 提供,并将包含被调用路径的剩余部分,例如< code > GET/projects/some/awesome/thing 会将< code > request . params . project 设置为< code > some/awesome/project 。

下一步是处理这个“子路径”(你的实际问题),这主要是一个品味和你想如何工作的问题。你的问题似乎暗示你不想创建一个无休止的重复列表,上面有几乎相似的事情,但同时又能有非常具体的项目路线。

一种方法是拆分<code>request.params。将参数投影到块中,并查找具有匹配名称的文件夹,其中可能包含进一步处理请求的逻辑。

让我们通过假设一个文件夹结构来探索这个概念(相对于包含路由的文件,例如index.js),它可以很容易地用于包含特定路由的处理程序。

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);
        });
    }
});

像这样的机制将允许您将每个项目作为应用程序中的一个节点模块,并让您的代码在运行时找出适当的模块来处理路径。

您甚至不必为每个请求方法指定它,因为您可以使用方法简单地让路由处理多个方法:['GET','POST','PUT','DELETE']而不是方法:'GET'

然而,它并不完全处理如何处理路线的重复声明,因为每个项目都需要类似的模块设置。

上面的示例包括并调用“子路由处理程序”的方式,示例实现将是:

//  <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');
};

我认为这说明了如何在运行时在应用程序中处理单个项目,尽管它确实提出了一些在构建应用程序架构时需要处理的问题:

  • 如果项目处理模块真的非常相似,您应该创建一个库,用于防止重复复制同一模块,因为这使维护更容易(我再次确认,这是子路由的最终目标)。
  • 如果您能够确定在运行时使用哪些模块,那么您也应该能够在服务器进程启动时确定

创建一个库来防止重复的代码是你应该尽早做(学会做)的事情,因为这会使维护更容易,你未来的自己会很感激。

在应用程序开始时,确定哪些模块可用于处理您的各种项目,可以避免每次请求都必须反复应用相同的逻辑。Hapi可能能够为您缓存这些,在这种情况下,这并不重要,但如果缓存不是一个选项,您最好使用较少的动态路径(我认为这是Hapi默认不提供的主要原因)。

您可以遍历项目文件夹,查找所有

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);
            }
        });
    });
});

这有效地取代了上述在每个请求上查找模块的逻辑,并且切换到(稍微)更有效的路由,因为您准确地记录了您将服务的项目,同时仍然将实际处理留给您提供的每个项目模块。(不要忘记实现< code>/projects路径,因为现在需要显式地完成这项工作)

 类似资料:
  • 1. 前言 本小节我们介绍如何嵌套使用 VueRouter。嵌套路由在日常的开发中非常常见,如何定义和使用嵌套路由是本节的重点。同学们在学完本节课程之后需要自己多尝试配置路由。 2. 配置嵌套路由 实际项目中的应用界面,通常由多层嵌套的组件组合而成。同样地,URL 中各段动态路径也按某种结构对应嵌套的各层组件,例如: /article/vue /a

  • 如果我将路由折叠起来,这样看起来就像: 工作很好。我嵌套的原因是因为我将在“dashboard”下有多个子项,并且希望它们都在URL中以为前缀。

  • 我的问题是关于在为API目的构建URL时嵌套资源的优势。请考虑以下两种访问员工资源的备选方案: 现在考虑开发一个通用库以从API访问REST资源的任务。如果所有路由都是平坦的,这样的REST包装库只需要知道要访问的资源的名称: 但是,如果我们要支持嵌套路由,这个包装器将需要知道关于嵌套了哪些模型以及在哪个其他资源下的额外信息,以便知道如何构建引用这样一个模型的URL。考虑到并非所有模型都嵌套在同一

  • 问题内容: 我正在React-Router中设置一些嵌套路由(我正在使用v0.11.6),但是每当我尝试访问其中一个嵌套路由时,都会触发父路由。 我的路线如下所示: 如果我将路线折叠起来,它看起来像: 它工作正常。之所以要嵌套,是因为我将在“仪表盘”下有多个子代,并希望它们在URL中都带有前缀。 问题答案: 配置与路由无关(尽管有名称),而是与路径驱动的布局有关。 因此,使用此配置: 就是说要嵌入

  • 我在想这样的事情: 前台有一个不同的布局和风格与管理区域。所以在frontpage中的路由是home、about等等,其中一个应该是子路由。 /home应该呈现到Frontpage组件中,而/admin/home应该呈现在后端组件中。 最终解决方案: 这是我现在使用的最终解决方案。这个例子也像传统的404页面一样有一个全局错误组件。

  • 问题内容: 我一直在尝试搜索包含嵌套字段的文档。我创建了这样的嵌套映射: 我的搜索如下所示: 但是我收到此错误消息: 编辑 我仍然收到此错误。我正在通过Java执行此操作,因此这是我要创建的文档: 像这样创建索引: 我认为我可能对文档编制了错误的索引。 问题答案: TLDR:输入您的嵌套类型。 假设我们有一个普通类型,并且嵌套了另一个类型: 该行是分配给的嵌套查询所必需的,如下所示: 该行似乎仅在