From: Justin Wind Date: Fri, 28 Mar 2025 20:56:33 +0000 (-0700) Subject: send Allow header on 405 Method Not Allowed responses X-Git-Url: https://git.squeep.com/?a=commitdiff_plain;h=08ef79bf99363bea37dc06c2ed5e4a25842e2de5;p=squeep-api-dingus send Allow header on 405 Method Not Allowed responses --- diff --git a/CHANGELOG.md b/CHANGELOG.md index cf32abe..a3b048b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ Releases and notable changes to this project are documented here. ## [Unreleased] - support naming route paths, and rendering those named paths with parameter substitution +- send Allow header on 405 Method Not Allowed responses ## [v2.1.3] - 2025-03-28 diff --git a/lib/dingus.js b/lib/dingus.js index 2c521db..31c3a10 100644 --- a/lib/dingus.js +++ b/lib/dingus.js @@ -359,6 +359,7 @@ class Dingus { ({ handler, handlerArgs } = this._determineHeadHandler(req, res, ctx, pathPart)); } else { handler = this.handlerMethodNotAllowed.bind(this); + res.setHeader(Enum.Header.Allow, e.methods.join(', ')); } } else { this.logger.error(_scope, 'unexpected error', { error: e }); @@ -386,6 +387,7 @@ class Dingus { } catch (e) { if (e instanceof RouterNoMethodError) { handler = this.handlerMethodNotAllowed.bind(this); + res.setHeader(Enum.Header.Allow, e.methods.join(', ')); } else { this.logger.error(_scope, 'unexpected error', { error: e }); handler = this.handlerInternalServerError.bind(this); @@ -808,7 +810,7 @@ class Dingus { * @param {string} newPath url to redirect to * @param {number=} statusCode status code to use for redirect, default 307 */ - async handlerRedirect(req, res, ctx, newPath, statusCode = 307) { + async handlerRedirect(req, res, ctx, newPath, statusCode = Enum.HTTPStatusCode.TemporaryRedirect) { this.setResponseType(this.responseTypes, req, res, ctx); res.setHeader(Enum.Header.Location, newPath); res.statusCode = statusCode; diff --git a/lib/enum.js b/lib/enum.js index 5f63ea6..77d2c02 100644 --- a/lib/enum.js +++ b/lib/enum.js @@ -202,6 +202,7 @@ const ErrorResponseProxy = new Proxy(ErrorResponse, { const Header = { Accept: 'Accept', AcceptEncoding: 'Accept-Encoding', + Allow: 'Allow', CacheControl: 'Cache-Control', ContentEncoding: 'Content-Encoding', ContentLength: 'Content-Length', diff --git a/lib/errors.js b/lib/errors.js index 942ecc0..0aec633 100644 --- a/lib/errors.js +++ b/lib/errors.js @@ -33,6 +33,10 @@ class RouterNoPathError extends RouterError { } class RouterNoMethodError extends RouterError { + constructor(methods, ...args) { + super(...args); + this.methods = methods; + } } module.exports = { diff --git a/lib/router/index.js b/lib/router/index.js index af17a4d..0e2a4e3 100644 --- a/lib/router/index.js +++ b/lib/router/index.js @@ -290,7 +290,7 @@ class Router { if ('*' in matchedPath[kPathMethods]) { return matchedPath[kPathMethods]['*']; } - throw new RouterNoMethodError(); + throw new RouterNoMethodError(Object.keys(matchedPath[kPathMethods])); } ctx.unmatchedPath = pathParts; throw new RouterNoPathError(); diff --git a/test/lib/dingus.js b/test/lib/dingus.js index 8023436..e68878a 100644 --- a/test/lib/dingus.js +++ b/test/lib/dingus.js @@ -513,6 +513,7 @@ describe('Dingus', function () { assert(!stubHandler.called); assert(dingus.handlerMethodNotAllowed.called); assert(!dingus.handlerNotFound.called); + assert(res.setHeader.called); }); it('does not lookup nonexistent path', async function () { req.url = '/foo/bar';