Hapi嵌套路由 [英] Hapi nested routing

查看:83
本文介绍了Hapi嵌套路由的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我想拥有看起来像这样的REST端点:

Suppose I want to have REST endpoints which look roughly like this:

/projects/
/projects/project_id 

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

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

CRUD on each if makes sense. For example, /projects POST creates a new project, GET fetches all projects. /projects/project_id GET fetches just that one project.

项目是特定于项目的,因此我将其放在特定项目project_id下.

Items are project specific so I put them under project_id, which is a particular project.

有什么方法可以创建这种嵌套路由吗?

Are there any way of creating this kind of nested routes?

现在我有这样的东西:

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

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

But I am looking for a way to nest items routes into projects routes and for ability to pass project further.

有什么建议吗?

推荐答案

虽然hapi本身没有我知道"子路由"的概念,但基本原理很容易实现.

While there is no concept of "subrouting" (that I know of) in hapi itself, the basics are easy enough to implement.

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

First off, hapi offers wildcard variables in paths, using these you basically create a catch-all route for a given path. For example:

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

这些通配符路径有一些规则,最重要的是它只能出现在最后一个段中,如果您将其视为包罗万象",这是有道理的.

There are some rules to these wildcard paths, the most important one being it can only be in the last segment, which makes sense if you think of it as a "catch-all".

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

In the example above, the {project*} parameter will be available as request.params.project and will contain the remainder of the called path, e.g. GET /projects/some/awesome/thing will set request.params.project to some/awesome/project.

下一步是处理这个子路径"(您的实际问题),这主要取决于口味和工作方式. 您的问题似乎暗示您不想创建一个无休止的重复列表,其中包含很多非常相似的内容,但同时又希望能够具有非常特定的项目路线.

The next step is to handle this "sub-path" (your actual question), which is mostly a matter of taste and how you would like to work. Your question seems to imply you don't want to create an endless repeating list of pretty much similar things but at the same time to be able to have very specific project routes.

一种方法是将request.params.project参数分成多个块,并查找具有匹配名称的文件夹,其中可能包含用于进一步处理请求的逻辑.

One way would be to split up the request.params.project parameter into chunks and look for folders with matching names, which could contain logic to handle the request further.

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

Let's explore this concept by assuming a folder structure (relative to the file containing the route, e.g. index.js) which can be easily be used to include the handlers for the specific routes.

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

通过这种机制,您可以将每个项目作为应用程序中的节点模块,并让您的代码找出在运行时用于处理路径的适当模块.

A mechanism like this would allow you have each project as a node module in your application and have your code figure out the appropriate module to use to handle the path at runtime.

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

You don't even have to specify this for every request method, as you can simply have routes handle multiple methods by using method: ['GET', 'POST', 'PUT', 'DELETE'] instead of method: 'GET'.

但是,它并不能完全处理关于如何处理路线的重复声明,因为您将需要为每个项目建立一个非常相似的模块设置.

It does not, however, entirely deal with the repetitive declaration of how to handle routes, as you will need a rather similar module setup for every project.

以上面的示例包含和调用"sub-route-handlers"的方式,示例实现为:

In the way the example above includes and invokes "sub-route-handlers", a sample implementation would be:

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

我认为这说明了如何在运行时在您的应用程序中处理各个项目,尽管它确实引起了您在构建应用程序体系结构时要处理的两个问题:

I think this illustrates how individual projects can be handled within you application at runtime, though it does raise a couple of concerns you will want to deal with when building your application architecture:

  • 如果项目处理模块确实非常相似,则应 创建一个用于防止复制相同模块的库 一遍又一遍,因为这使维护更容易(我 侦查,这是进行子路由的最终目标)
  • 如果您可以确定在运行时使用哪些模块,则应该 服务器进程启动时也可以弄清楚这一点.
  • if the project handling module really are very similar, you should create a library which you use to prevent copying the same module over and over again, as that makes maintenance easier (which, I recon, was the ultimate goal of having sub-routing)
  • if you can figure out which modules to use at runtime, you should also be able to figure this out when the server process starts.

创建一个库来防止重复代码是您应该尽早做的事情(学习做),因为这会使维护变得更容易,并且您将来的自我将感激不尽.

Creating a library to prevent repetitive code is something you should do (learn to do) early on, as this make maintenance easier and your future self will be thankful.

弄清楚哪些模块可用于处理您在应用程序启动时拥有的各种项目,这将避免每次请求都必须一遍又一遍地应用相同的逻辑. Hapi可能可以为您缓存此内容,在这种情况下,它并不重要,但是如果不选择缓存,则最好使用较少的动态路径(这是我不提供的主要原因,我认为这是主要原因)默认为hapi).

Figuring out which modules will be available to handle the various projects you have at the start of the application will save every request from having to apply the same logic over and over again. Hapi may be able to cache this for you, in which case it doesn't really matter, but if caching is not an option you might be better off using less dynamic paths (which - I believe - is the primary reason this is not offered by hapi by default).

您可以在应用程序的开头遍历project文件夹以查找所有<project>/index.js,并使用

You can traverse the projects folder looking for all <project>/index.js at the start of the application and register a more specific route using glob like this:

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

这有效地代替了上面在每个请求上查找模块并切换到(略微)高效路由的逻辑,因为您确切地知道将要服务的项目,同时仍将实际处理留给每个项目模块你提供. (别忘了实现/projects路由,因为现在需要明确地完成此操作)

This effectively replaces the above logic of looking up module on every request and switch to a (slightly) more efficient routing as you are talling hapi exactly which projects you'll be serving while still leaving the actual handling to every project-module you provide. (Don't forget to implement the /projects route, as this now needs to be done explicitly)

这篇关于Hapi嵌套路由的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆