20

如果客户端发送的请求与映射的 url 路由匹配但与映射的 HTTP 方法不匹配,我正在寻找一种干净的方法让我的快速应用程序返回 405 Method Not Allowed。

我当前的实现是有一个默认的“catch-all”处理程序,它尝试将 url 与注册路由匹配,忽略 HTTP 方法。如果匹配,那么我们知道返回 405,否则我们让 express 执行其默认的 404 行为。

我希望有一种更好的方法,它不涉及将所有路由匹配运行两次(一次通过快递,一次通过我的处理程序)。

4

6 回答 6

24

这是我已成功用于多个 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);
...

下一点是验证处理函数“comments”中的方法。请注意,处理程序负责处理所有方法。在 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://www.rfc-editor.org/rfc/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);
    }
}

有了restful,现在自动处理 405 响应并设置适当的 Allow 标头变得非常轻松。只需为您允许的每种方法提供一个功能,其余的由restful完成。

所以让我们修改前面的例子:

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

为什么叫宁静?在RESTful Web 中,API 必须遵守约定,例如使用 HTTP 405 响应具有不受支持的方法的请求。其中许多约定可以在需要时集成到restful。因此这个名字是宁静的,而不是像auto405http405handler这样的名字。

希望这可以帮助。有什么想法吗?

于 2013-04-02T00:54:13.487 回答
11

方法一:使用.route().all()

// Your route handlers
const handlers = require(`./handlers.js`);

// The 405 handler
const methodNotAllowed = (req, res, next) => res.status(405).send();

router
.route(`/products`)
.get(handlers.getProduct)
.put(handlers.addProduct)
.all(methodNotAllowed);

这是可行的,因为请求按照它们附加到路由的顺序传递给处理程序(请求“瀑布”)。.get()和处理程序将.put()捕获 GET 和 PUT 请求,其余的将落入.all()处理程序。

方法二:中间件

创建检查允许方法的中间件,如果方法未列入白名单,则返回 405 错误。这种方法很好,因为它允许您查看和设置每条路线的允许方法以及路线本身。

这是methods.js中间件:

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;

然后,您将在您的路线中使用methods中间件,如下所示:

const handlers = require(`./handlers.js`); // route handlers
const methods = require(`./methods.js`);   // methods middleware

// allows only GET or PUT requests
router.all(`/products`, methods([`GET`, `PUT`]), handlers.products);

// defaults to allowing GET requests only
router.all(`/products`, methods(), handlers.products);

于 2016-12-02T03:07:04.163 回答
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)
}

无论哪种方式,您都必须检查路线两次。

于 2013-02-14T21:29:07.463 回答
1

有点老问题,但这是我所做的。我只是把它放在我所有的路线之后,但在我的 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();
});
于 2015-01-06T20:41:00.453 回答
1

我一直这样做:

假设你有 GET 和 POST 方法处理程序/app.route您可以使用or包装路径router.route并相应地分配处理程序。

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

请求将与路由匹配并通过处理程序过滤。如果存在处理程序,它将照常处理。否则,它将到达all将状态代码设置为 405 并结束请求的处理程序。

于 2017-08-15T17:27:01.840 回答
0

我这样修复它:

/*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 */
于 2017-09-04T13:12:46.647 回答