Отправка 405 из express.js при совпадении маршрута, но не соответствие метода HTTP

Я ищу чистый способ возврата моего экспресс-приложения. 405 Метод не разрешен, если клиент отправляет запрос, соответствующий сопоставленному URL-адресу, но не соответствует сопоставленному методу HTTP.

Моя текущая реализация состоит в том, чтобы иметь обработчик по умолчанию "catch-all", который пытается сопоставить URL-адрес с маршрутами регистрации, игнорируя метод HTTP. Если есть совпадение, то мы знаем, что возвращаем значение 405, в противном случае мы даем выразить свое поведение по умолчанию 404.

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

Ответ 1

Вот такой подход, который я успешно использовал с несколькими приложениями Django и теперь с Node и Express. Также следует RFC 2616 (HTTP/1.1), в котором говорится о HTTP 405:

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

Итак, ключевым моментом является маршрутизировать запросы к одному и тому же обработчику без учета методов.

app.all('/page/:id', page.page);
app.all('/page/:id/comments', page.comments);
app.all('/page/:id/attachments', page.attachments);
...

Следующий момент - проверить метод в функции обработчика. Обратите внимание, что обработчик отвечает за обработку всех методов. В мире Django это единственный путь, потому что структура заставляет вас отделять маршрутизацию URL-адресов от фактического действия, которое необходимо выполнить против ресурса, который представляет URL.

В обработчике вы могли проверить метод, подобный этому...

exports.comments = function (req, res) {
    if (req.route.method === 'get') {
        res.send(200, 'Hello universe.');
    } else {
        res.set('Allow', 'GET');
        res.send(405, 'Method Not Allowed');
    }
}

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

Поэтому я подготовил функцию ярлыка, названную restful для задания. Определите функцию везде, где вы хотите. Я лично поместил бы его в helpers.js в тот же каталог, где реализованы функции обработчика.

var restful = function (req, res, handlers) {
    // 
    // This shortcut function responses with HTTP 405
    // to the requests having a method that does not
    // have corresponding request handler. For example
    // if a resource allows only GET and POST requests
    // then PUT, DELETE, etc requests will be responsed
    // with the 405. HTTP 405 is required to have Allow
    // header set to a list of allowed methods so in
    // this case the response has "Allow: GET, POST" in
    // its headers [1].
    // 
    // Example usage
    //     
    //     A handler that allows only GET requests and returns
    //     
    //     exports.myrestfulhandler = function (req, res) {
    //         restful(req, res, {
    //             get: function (req, res) {
    //                 res.send(200, 'Hello restful world.');
    //             }
    //         });
    //     }
    // 
    // References
    //     
    //     [1] RFC-2616, 10.4.6 405 Method Not Allowed
    //     https://tools.ietf.org/html/rfc2616#page-66
    //     
    //     [2] Express.js request method
    //     http://expressjs.com/api.html#req.route
    //
    var method = req.route.method; // [2]
    if (!(method in handlers)) {
        res.set('Allow', Object.keys(handlers).join(', ').toUpperCase());
        res.send(405);
    } else {
        handlers[method](req, res);
    }
}

С успокоительным теперь совершенно безболезненно обрабатывать 405 ответов автоматически и иметь правильный разрешающий заголовок. Просто дайте функцию для каждого метода, который вы разрешаете, и успокаиваете остальные.

Таким образом, можно изменить предыдущий пример:

exports.comments = function (req, res) {
    restful(req, res, {
        get: function (req, res) {
            res.send(200, 'Hello restful universe.');
        }
    });
}

Почему имя успокаивается? В RESTful веб-интерфейсом очень важно, чтобы API выполнял соглашения, подобные тому, как отвечать на HTTP 405 на запрос, имеющий не поддерживаемый метод. Многие из этих конвенций могут быть интегрированы, чтобы успокоиться, когда это необходимо. Поэтому имя является спокойным, а не что-то вроде auto405 или http405handler.

Надеюсь, это поможет. Любые мысли?

Ответ 2

Из-за двусмысленности нет другого пути. Лично я бы сделал что-то вроде этого:

var route = '/page/:id/comments'
app.get(route, getComments)
app.all(route, send405)

function send405(req, res, next) {
  var err = new Error()
  err.status = 405
  next(err)
}

В любом случае вам нужно дважды проверять маршруты.

Ответ 3

Дорогой вопрос, но вот что я сделал. Я просто положил это после всех своих маршрутов, но до моего 400-х обработчика

// Handle 405 errors
app.use(function(req, res, next) {
  var flag = false;
  for (var i = 0; i < req.route.stack.length; i++) {
    if (req.method == req.route.stack[i].method) {
      flag = true;
    }
  }
  if (!flag) {
    err = new Error('Method Not Allowed')
    err.status = 405;
    return next(err)
  }

  next();
});

Ответ 4

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

Здесь промежуточное ПО:

const methods = (methods = ['GET']) => (req, res, next) => {
  if (methods.includes(req.method)) {
    return next();
  }
  res.error(405, `The ${req.method} method for the "${req.originalUrl}" route is not supported.`);
};

module.exports = methods;

Ответ 5

Я делаю это так:

Скажите, если у вас есть обработчики методов GET и POST для /. Вы можете обернуть путь с помощью app.route или router.route и соответственно назначить обработчики.

    app.route("/").get((req, res) => {
            /* DO SOMETHING*/
    }).post((req, res) => {
            /* DO SOMETHING*/
    }).all((req, res) => {
            res.status(405).send();
    });

Запрос будет согласован с маршрутом и пропущен через обработчики. Если обработчик присутствует, он будет обрабатываться, как обычно. В противном случае он достигнет обработчика all, который установит код состояния 405 и завершит запрос.

Ответ 6

Я зафиксировал его так:

/*paths here*/

router.get('/blah/path1', blah.do_something );
router.post('/blah/path2', blah.do_something_else );

/* if we get here we haven't already gone off down another path */

router.all('/*', (req,res) => { res.status(405), 
   res.json({'status':405,
             'message':req.method + ' not allowed on this route'}) 
});

/* simples */