Node express: следует ли всегда вызывать next() в обработчике get или post?

До сих пор я определял обработчики get и post с помощью только (req, res) в качестве аргументов, предполагая, что я помещаю эти обработчики в цепочку промежуточного программного обеспечения и слежу за тем, чтобы я обрабатывал любые ответы и правильная обработка ошибок в этих обработчиках... поэтому не имеет значения, что я не ссылаюсь на next.

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

Например, в экспресс путеводитель по маршрутизации следующий простой пример:

app.get('/example/b', function (req, res, next) {
    console.log('the response will be sent by the next function ...')
    next()
}, function (req, res) {
    res.send('Hello from B!')
})

Конечно, я понимаю, что это очень простой пример, иллюстрирующий, что обработчики могут быть привязаны и не предназначены для обеспечения полной структуры для обработчика get, но было бы лучше определить и использовать next даже во втором обработчике следующим образом:

app.get('/example/b', function (req, res, next) {
    console.log('the response will be sent by the next function ...')
    next()
}, function (req, res, next) {
    res.send('Hello from B!')
    next()
})

Или на практике принято предполагать, что функция обработчика, которая отправляет ответ клиенту, должна не вызывать next()... т.е. предполагается, что цепочка будет заканчиваться на обработчик, который фактически отправляет ответ?

Или нет установленной практики в этом вопросе?

Мне даже интересно, может ли быть распространено не отправлять какой-либо ответ в обработчике get, а отложить его до выделенного обработчика ответа, следующего за... под которым я подразумеваю обработчик ответа OK, а не обработчик ответа об ошибках (для которого, как представляется, обычно принято определять окончательный обработчик ошибок и вызвать next(err)). Таким образом, в случае отсутствия ошибок вы вызываете next() и в следующем промежуточном программном обеспечении вы будете использовать res.status(200).send(req.mydata), где req.mydata добавляется в ваш обработчик get.

Ответ 1

Нет. Вы должны вызывать только next(), если вы хотите, чтобы что-то еще обработало запрос. Обычно это говорит о том, что ваш маршрут может соответствовать этому запросу, но вы хотите поступать так, как будто этого не произошло. Например, у вас могут быть два обработчика для одного и того же маршрута:

app.get('/test', (req, res, next) => {
  if (something) {
    return next();
  }
  // handle request one way (1)
});

app.get('/test', (req, res) => {
  // handle request other way (2)
});

Всегда вызывается первый обработчик соответствия, поэтому для запроса GET /test будет вызываться первый обработчик, но он может выбрать передать элемент управления второму обработчику, как если бы первый не соответствовал запросу.

Обратите внимание, что если второй обработчик не собирается передавать запрос следующему обработчику, он даже не имеет next в своих аргументах.

Если второго обработчика не было, тогда стандартный обработчик 404 будет использоваться, если первый из них называется next().

Если вы передадите аргумент next(), тогда будет вызвано промежуточное программное обеспечение обработки ошибок.

Ответ 2

Мое правило состоит в том, чтобы обрабатывать ответ в обработчике, если вы собираетесь дать код ответа 20x (Успех) и в централизованной обработке ошибок, если нет. На практике это выглядит примерно так:

// ./routes/things.js

const express = require('express');
const Thing = require('../models/thing');

const Router = express.Router();

// note, the handlers might get pulled out into a controllers file, if they're getting more complex. 
router.param('thingId', (req, res, next, id) => {
  Thing.findById(id, (e, thing) => {
    if (e) return next(e);
    // let say we have defined a NotFoundError that has 'statusCode' property which equals 404
    if (!bot) return next(new NotFoundError(`Thing ${id} not found`));
    req.thing = thing;
    return next();
  });
});

router.get('/', (req, res, next) => {
  // possibly pull in some sort, limit, and filter stuff
  Thing.find({}, (e, things) => {
    if (e) return next(e);
    res.send(things);
  });
});

router.route('/:thingId')
  .get((req, res) => { 
     // if you get here, you've already got a thing from the param fn
     return res.send(req.thing);
  })
  .put((req, res, next) => {
    const { name, description } = req.body; // pull whitelist of changes from body
    let thing = req.thing;
    thing = Object.assign(thing, { name, description }); // copy new stuff into the old thing
    thing.save((e) => {
      if (e) return next(e);
      return res.send(thing); // return updated thing
    });
  });

Сохранение каждого логического фрагмента в собственном файле может уменьшить повторение

// ./routes/index.js then mounts the subrouters to the main router
const thingsRoute = require('./things');
const express = require('express');

const router = express.Router(); 

/* .... other routes **/
router.use('/things', thingsRoute);

Обработка ошибок затем централизована и может монтироваться либо в собственном файле, либо прямо в приложении:

// in ./index.js (main app entry point)
const express = require('express');

// this will require by default ./routes/index.js
const routes = require('./routes'); 
const app = express();
const log = require('./log');// I prefer debug.js to console.log, and ./log.js is my default config file for it

/* ... other app setup stuff */

app.use(routes);

// you can mount several of these, passing next(e) if you don't handle the error and want the next error handler to do so.
app.use((err, req, res, next) => {
  // you can tune log verbosity, this is just an example
  if (err.statusCode === 404) {
    return res.status(404).send(err.message);
  }
  log.error(err.message);
  log.verbose(err.stack); // don't do stack traces unless log levels are set to verbose
  return res.status(500).send(err.message);

});