Ответ 1
Пока нет понятия "подпрограммирование" (что я знаю) в самом хапи, основы достаточно легки для реализации.
Во-первых, hapi предлагает подстановочные переменные в путях, используя их, вы в основном создаете маршрут для всего заданного пути. Например:
server.route({
method: 'GET',
path: '/projects/{project*}',
handler: (request, reply) => {
reply('in /projects, re-dispatch ' + request.params.project);
}
});
Есть несколько правил для этих шаблонов подстановочных знаков, наиболее важным из которых является то, что он может быть только в последнем сегменте, что имеет смысл, если вы думаете об этом как о "catch-all".
В приведенном выше примере параметр {project*}
будет доступен как request.params.project
и будет содержать оставшуюся часть вызываемого пути, например. GET /projects/some/awesome/thing
установит request.params.project
в some/awesome/project
.
Следующий шаг - обработать этот "подпуть" (ваш реальный вопрос), который в основном зависит от вкуса и того, как вы хотели бы работать. Ваш вопрос, похоже, подразумевает, что вы не хотите создавать бесконечный повторяющийся список почти подобных вещей, но в то же время иметь очень конкретные маршруты проекта.
Один из способов заключается в разделении параметра request.params.project
на куски и поиск папок с соответствующими именами, которые могут содержать логику для дальнейшей обработки запроса.
Давайте рассмотрим это понятие, предположив структуру папок (относительно файла, содержащего маршрут, например 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);
});
}
});
Механизм, подобный этому, позволит вам иметь каждый проект в качестве модуля node в вашем приложении, а ваш код вычисляет соответствующий модуль, который будет использоваться для обработки пути во время выполнения.
Вам даже не нужно указывать это для каждого метода запросов, так как вы можете просто маршрутизировать несколько методов, используя method: ['GET', 'POST', 'PUT', 'DELETE']
вместо method: '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');
};
Я думаю, это иллюстрирует, как отдельные проекты могут обрабатываться внутри вашего приложения во время выполнения, хотя это вызывает несколько проблем, которые вы хотите решить при построении архитектуры приложений:
- Если модуль обработки проекта действительно очень похож, вы должны создайте библиотеку, которую вы используете для предотвращения копирования одного и того же модуля снова и снова, поскольку это облегчает обслуживание (что, я recon, была конечной целью иметь под-маршрутизацию)
- если вы можете выяснить, какие модули использовать во время выполнения, вы должны также сможете понять это, когда начинается процесс сервера.
Создание библиотеки для предотвращения повторяющегося кода - это то, что вам нужно сделать (научиться делать) на раннем этапе, поскольку это облегчит обслуживание, и ваше будущее будет благодарным.
Выяснив, какие модули будут доступны для обработки различных проектов, которые у вас есть в начале приложения, будет сохраняться каждый запрос от необходимости повторять одну и ту же логику снова и снова. Хапи может кэшировать это для вас, и в этом случае это не имеет особого значения, но если кэширование не является вариантом, вам может быть лучше использовать менее динамические пути (что, я считаю, является основной причиной, по которой это не предлагается по умолчанию hapi).
Вы можете перемещать папку проектов в поисках всех <project>/index.js
в начале приложения и регистрировать более конкретный маршрут, используя glob
вот так:
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);
}
});
});
});
Это эффективно заменяет приведенную выше логику поиска модуля на каждый запрос и переключается на (немного) более эффективную маршрутизацию, поскольку вы высоко цените hapi, какие проекты вы будете обслуживать, оставив при этом фактическую обработку для каждого модуля проекта Вы предоставляете.
(Не забудьте реализовать маршрут /projects
, так как это теперь нужно сделать явно)