it-roy-ru.com

Лучший способ сделать динамическую маршрутизацию с Express.js (node.js)

Я пытаюсь создать простую CMS с помощью express.js, который динамически создает маршруты . Он получает JSON из базы данных, которая выглядит следующим образом:

pagesfromdb = {
    home = {
        paths = ['/','/home','/koti'],
        render = 'home.ejs',
        fi_FI = {html='<h1>Hei maailma!</h1>'},
        en_US = {html='<h1>Hello World!</h1>'}
    },
    about = {
        paths = ['/about','/tietoja'],
        render = 'general.ejs',
        fi_FI = {html='Tietoja'},
        en_US = {html='About Us'}
    }
}

и перебирает объекты, создавая маршруты следующим образом:

Object.keys(pagesfromdb).forEach(function(key) {
    var page = pagesfromdb[key];
    app.get(page.global.paths,function(req, res){
         res.render(page.render, page[language]);
    });
});

Теперь все работает нормально. Но проблема в том, что каждый раз, когда пользователь изменяет содержимое и пути, необходимо перезапускать все приложение узла .. Я не нашел никаких вызовов API для удаления маршрутов.

Есть ли способ безопасно удалить старые маршруты, установленные с app.get? Должен ли я даже сделать это?

Есть ли лучший способ сделать этот вид маршрутизации? Мне действительно нравится этот метод, так как он позволяет мне использовать встроенную функцию, быстр и поддерживает регулярные выражения.

Я попытался удалить весь app.routes с app.routes = nul, но он ничего не сделал, старые маршруты все еще были на месте.

Одна вещь, которая действительно удаляла их, была 

delete app._router.map.get;
app._router.map.get = [];

Но удаляет ли это их на самом деле, и безопасно ли их использовать, чтобы я не угонял огромное количество оперативной памяти, если маршрутизатор продолжает заполняться?

14
Toni Lähdekorpi

Как сказала @supermova в комментариях, можно обновить Express на лету. Другая архитектура, на которую стоит обратить внимание, - это архитектура, аналогичная классическим CMS (например, Wordpress). В других CMS все запросы переходят к одному и тому же «обратному вызову» и на на каждый запрос вы ищите в базе данных, какую страницу обслуживать для этого URL.

app.get('/*', function (req, res) {
   db.findPage({ slug: req.url}, function (err, pageData) {
       res.render('page-template', {
           pageContent: pageData.content,
           pageTitle: pageData.title
       });
   });
});

В результате этого метода наблюдается значительное снижение скорости, но, в конце концов, я думаю, что это более разумно. Если скорость является огромной проблемой, вы можете настроить систему кеширования (например, Varnish), но с подходом изменения маршрутов Express на лету возникнут головные боли. Например, что если вам нужно масштабировать до двух веб-серверов? Как вы синхронизируете их, если сервер A получает запрос «создать страницу» и поэтому знает, как обновить свои маршруты, но как насчет сервера B? С каждым обращением к базе данных вы сможете лучше масштабировать по горизонтали.

14
Max

Я был бы очень осторожен, пытаясь создать или уничтожить маршруты во время выполнения. Хотя вы можете изменить структуры данных самостоятельно, я не верю, что они задокументированы как API, поэтому вы рискуете сломать их, если/когда вы обновите Express.

Это также может служить ограничением памяти, если вы создаете новый маршрут для каждого объекта базы данных, потому что набор страниц может увеличиваться до тех, кто знает, насколько они велики.

Посмотрите на это так ... вам не нужны новые маршруты; Вы хотите добавить URL к существующим маршрутам. Если вы добавляете маршрут, это означает, что вы хотите, чтобы какой-то URL отображался на какую-то отдельную функцию. Но вы сопоставляете каждый URL одной и той же функции. По сути, вы только добавляете маршруты по побочным эффектам. Что вас волнует, так это то, что какой-то динамический набор URL-адресов соответствует определенной функции.

Можно ли вместо этого использовать подстановочный знак для сопоставления шаблона URL с определенной функцией, например

app.get('/pages/*', function(req, res) {
    var page = pagesfromdb[key];
    if (page != null) {
        res.render(page.render, page.language)
    }
    else {
        res.send(404);
    }
});

И управляйте отображением pagesfromdb вне экспресс-слоя. Вы можете использовать прямой доступ к базе данных, кэш + доступ к базе данных или обновление по таймеру, что бы вам ни подходило.

1
Brandon

Добавление маршрутов может быть сделано на лету, как уже упоминалось. Удаление может тоже, учитывая эту функцию:

function deleteRoute(url) {
  for (var i = app.routes.get.length - 1; i >= 0; i--) {
    if (app.routes.get[i].path === "/" + url) {
      app.routes.get.splice(i, 1);
    }
  }
}

(украдено из При удалении специфического сопоставленного маршрута в Node.js во время выполнения удаляется статическое сопоставление? )

Подобные обновления маршрутов делают ваше приложение «отслеживающим состояние» и, возможно, приведут к проблемам, если вам потребуется балансировка нагрузки.

0
Albin