Sending 405 from express.js when there is a route match but no HTTP method match

后端 未结 6 1120
孤城傲影
孤城傲影 2020-12-30 02:33

I\'m looking for a clean way to have my express app return 405 Method Not Allowed if a client sends a request that matches a mapped url route but does not match the mapped H

相关标签:
6条回答
  • 2020-12-30 02:40

    Method 1: Use .route() and .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);

    This works because requests are passed to the handlers in the order they are attached to the route (the request "waterfall"). The .get() and .put() handlers will catch GET and PUT requests, and the rest will fall through to the .all() handler.

    Method 2: Middleware

    Create middleware which checks for allowed methods, and returns a 405 error if the method is not whitelisted. This approach is nice because it allows you to see and set the allowed methods for each route along with the route itself.

    Here's the methods.js middleware:

    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;

    You would then use the methods middleware in your routes like this:

    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);

    0 讨论(0)
  • 2020-12-30 02:56

    I fixed it like this :

    /*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 */
    
    0 讨论(0)
  • 2020-12-30 02:59

    I have been doing it this way:

    Say if you have GET and POST method handlers for /. You can wrap the path with app.route or router.route and assign the handlers accordingly.

        app.route("/").get((req, res) => {
                /* DO SOMETHING*/
        }).post((req, res) => {
                /* DO SOMETHING*/
        }).all((req, res) => {
                res.status(405).send();
        });
    
    • http://expressjs.com/en/4x/api.html#app.route
    • http://expressjs.com/en/4x/api.html#router.route

    A request will get matched to the route and filtered through the handlers. If a handler is present, it will get handled as usual. Else, it will reach the all handler that will set the status code to 405 and ending the request.

    0 讨论(0)
  • 2020-12-30 03:02

    Kinda old question but here is what i did. I just put this after all my routes but before my 400 handler

    // 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();
    });
    
    0 讨论(0)
  • 2020-12-30 03:03

    Due to ambiguity, there really is no other way. Personally, I would do something like this:

    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)
    }
    

    Either way, you have to check the routes twice.

    0 讨论(0)
  • 2020-12-30 03:04

    Here is an approach that I have used successfully with multiple Django applications and now with Node and Express. It is also follows RFC 2616 (HTTP/1.1) that says the following about HTTP 405:

    The response MUST include an Allow header containing a list of valid methods for the requested resource.

    So, the key point is to route the requests to the same handler without regard to methods.

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

    The next point is to validate the method in the handler function 'comments'. Note that the handler is responsible for handling all the methods. In Django's world this is the only way to go because the framework forces you to separate the routing of the URLs from the actual action about to be performed against the resource the URL represents.

    In the handler you could check the method like this...

    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');
        }
    }
    

    ...but as you can expect the code will quickly become repetitious and not nice to read especially when you have many handler functions and many different sets of allowed methods.

    Therefore I prepared a shortcut function named restful for the job. Define the function wherever you want. I personally would place it in helpers.js under the same directory where the handler functions are implemented.

    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);
        }
    }
    

    With restful it is now quite painless to handle 405 responses automatically and having proper Allow header being set. Just give a function for each method you allow and restful does the rest.

    So lets modify the previous example:

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

    Why the name restful? In RESTful web it is quite essential for the API to obey the conventions like responsing with HTTP 405 to the request having non-supported method. Many of those conventions could be integrated to restful when needed. Therefore the name is restful and not something like auto405 or http405handler.

    Hope this helps. Any thoughts?

    0 讨论(0)
提交回复
热议问题