Welcome to the Treehouse Community
Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.
Looking to learn something new?
Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.
Start your free trialLuis Marsano
21,425 PointsOn unimplemented methods, isn't response 405 Method Not Allowed more appropriate?
According to HTTP/1.1 specs
The 405 (Method Not Allowed) status code indicates that the method received in the request-line is known by the origin server but not supported by the target resource. The origin server MUST generate an Allow header field in a 405 response containing a list of the target resource's currently supported methods.
Yet in those situations, our app returns 404 Not Found.
What would be the best way to implement 405 responses? In our app, I tried this approach:
-
in
routes.js
, definefunction notAllowedHandler(req, res, next) { const methods = Object.keys(req.route.methods) .filter((value) => value !== '_all') .map((value) => value.toUpperCase()) , err = new Error('Method Not Allowed') err.status = 405 res.set('Allow', methods.join(', ')) return next(err) }
-
for each URL, add this as the last handler for
all
methodsrouter .route(url) .METHOD1(callback1) ⋮ .METHODn(callbackn) .all(notAllowedHandler)
Is there a better way?
2 Answers
jobbol
Full Stack JavaScript Techdegree Student 17,885 PointsYes there is a better way. Express is a series of middleware functions that get called one after the other and loop. Middleware functions in Express look like this.
app.use(bodyParser.json());
app.use(methodOverride());
app.use(logErrors);
app.use(clientErrorHandler);
app.use(errorHandler);
Order matters here as they get loaded into memory in the same order you call them. So errors must be done last. Middleware error functions use the similar req, res, next
parameters but start with an err
.
All middleware functions must end with a call to the next middleware function via next(err)
on the stack, or it must already be finished with the response. Otherwise Express gets stuck and the loop is broken.
If at any time during this loop next
is called with a value, Express will jump to those error handling middleware functions. It knows them because it looks for them with the four parameters.
Putting this all together you have
app.use(error405);
function Error405(err, req, res, next) {
if (err === 405 && req.xhr) {
return res.status(405).send('"Method not allowed."');
}
next(err);
}
In order for this to work, you need to pass 405 to the next function from inside a route via your routes.js.
next(405);
Express will see sort this as an error, jump to the error middleware. This one needs to be on the top of the stack. It checks for the 405 number and sends the custom message. Any other errors will fall through the stack and be passed onto the next handler.
Still need help? Post your code and I'll help hack it together.
Luis Marsano
21,425 PointsNo worries. Thanks for the link: I'll check that out.
We can both agree my approach is not elegant, since it means repeating the same code at the end of each URL's route.
The only reason I do that is to scrape the method names defined for the route (the keys of the req.route.methods object other than _all
), so I can report these as "the target resource's currently supported methods".
To do the same from middleware, we'd need to look up the defined methods for the requested URL's route(s). Though I think you understand, I should save others confusion by clarifying the goal
look up defined methods for the requested URL's route(s) from middleware
Luis Marsano
21,425 PointsLuis Marsano
21,425 PointsThanks for the thoughtful answer. This would be fine except for some minor issues and the stipulation in the HTTP spec, namely
We need the response to list methods the resource at the URL (target resource) does support. Though the code I wrote does that, I don't see where yours does.
FWIW, the app already implements custom error handlers in
app.js
asand passing the status code through
err.status
. Any suggestions?jobbol
Full Stack JavaScript Techdegree Student 17,885 Pointsjobbol
Full Stack JavaScript Techdegree Student 17,885 PointsAt first I did not understand your question, but - ohh - it's finally clicking. I didn't realize what that long line of code did. The whole thing sorta self documents itself then it says what's not allowed. I also see now that you already had the whole idea about the middleware stack down. I feel bad for writing that long response as well since it seems trivial in comparison to what you have.
So for this part,
Attaching that to the end of each router.
I think I found a project which addresses that same exact problem.
This is beyond my knowledge of Express, as routers are new to me. I'll get to it, as I'm working on some back-end API stuff. But hopefully this will help you.