diff --git a/HISTORY.md b/HISTORY.md
index 49e33e00..6c988cab 100644
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -2,6 +2,39 @@
**How to upgrade**: remove your `$GOPATH/src/github.com/kataras` folder, open your command-line and execute this command: `go get -u github.com/kataras/iris/iris`.
+
+## 4.5.1 -> 4.5.2
+
+- **Feature request**: I never though that it will be easier for users to catch 405 instead of simple 404, I though that will make your life harder, but it's requested by the Community [here](https://github.com/kataras/iris/issues/469), so I did my duty. Enable firing Status Method Not Allowed (405) with a simple configuration field: `iris.Config.FireMethodNotAllowed=true` or `iris.Set(iris.OptionFireMethodNotAllowed(true))` or `app := iris.New(iris.Configuration{FireMethodNotAllowed:true})`, a test example:
+
+```go
+
+func TestMuxFireMethodNotAllowed(t *testing.T) {
+
+ iris.Config.FireMethodNotAllowed = true // enable catching 405 errors
+
+ h := func(ctx *iris.Context) {
+ ctx.Write("%s", ctx.MethodString())
+ }
+
+ Iris.OnError(iris.StatusMethodNotAllowed, func(ctx *iris.Context) {
+ ctx.Write("Hello from my custom 405 page")
+ })
+
+ iris.Get("/mypath", h)
+ iris.Put("/mypath", h)
+
+ e := iris.Tester(t)
+
+ e.GET("/mypath").Expect().Status(StatusOK).Body().Equal("GET")
+ e.PUT("/mypath").Expect().Status(StatusOK).Body().Equal("PUT")
+ // this should fail with 405 and catch by the custom http error
+
+ e.POST("/mypath").Expect().Status(StatusMethodNotAllowed).Body().Equal("Hello from my custom 405 page")
+ iris.Close()
+}
+```
+
## 4.5.0 -> 4.5.1
- **NEW**: `PreBuild` plugin type, raises before `.Build`. Used by third-party plugins to register any runtime routes or make any changes to the iris main configuration, example of this usage is the [OAuth/OAuth2 Plugin](https://github.com/iris-contrib/plugin/tree/master/oauth).
diff --git a/README.md b/README.md
index 7bcacc37..f9678011 100644
--- a/README.md
+++ b/README.md
@@ -19,7 +19,7 @@
-
+
@@ -871,7 +871,7 @@ I recommend writing your API tests using this new library, [httpexpect](https://
Versioning
------------
-Current: **v4.5.1**
+Current: **v4.5.2**
> Iris is an active project
@@ -907,7 +907,7 @@ This project is licensed under the [MIT License](LICENSE), Copyright (c) 2016 Ge
[Travis]: http://travis-ci.org/kataras/iris
[License Widget]: https://img.shields.io/badge/license-MIT%20%20License%20-E91E63.svg?style=flat-square
[License]: https://github.com/kataras/iris/blob/master/LICENSE
-[Release Widget]: https://img.shields.io/badge/release-4.5.1%20-blue.svg?style=flat-square
+[Release Widget]: https://img.shields.io/badge/release-4.5.2%20-blue.svg?style=flat-square
[Release]: https://github.com/kataras/iris/releases
[Chat Widget]: https://img.shields.io/badge/community-chat%20-00BCD4.svg?style=flat-square
[Chat]: https://kataras.rocket.chat/channel/iris
diff --git a/configuration.go b/configuration.go
index c644f445..e4dd9d29 100644
--- a/configuration.go
+++ b/configuration.go
@@ -1,15 +1,16 @@
package iris
import (
- "github.com/imdario/mergo"
- "github.com/kataras/go-options"
- "github.com/kataras/go-sessions"
- "github.com/valyala/fasthttp"
"io"
"net/url"
"os"
"strconv"
"time"
+
+ "github.com/imdario/mergo"
+ "github.com/kataras/go-options"
+ "github.com/kataras/go-sessions"
+ "github.com/valyala/fasthttp"
)
type (
@@ -160,6 +161,10 @@ type Configuration struct {
// Default is false
DisablePathEscape bool
+ // FireMethodNotAllowed if it's true router checks for StatusMethodNotAllowed(405) and fires the 405 error instead of 404
+ // Default is false
+ FireMethodNotAllowed bool
+
// DisableBanner outputs the iris banner at startup
//
// Default is false
@@ -381,6 +386,14 @@ var (
}
}
+ // FireMethodNotAllowed if it's true router checks for StatusMethodNotAllowed(405) and fires the 405 error instead of 404
+ // Default is false
+ OptionFireMethodNotAllowed = func(val bool) OptionSet {
+ return func(c *Configuration) {
+ c.FireMethodNotAllowed = val
+ }
+ }
+
// OptionDisableBanner outputs the iris banner at startup
//
// Default is false
@@ -523,6 +536,7 @@ func DefaultConfiguration() Configuration {
CheckForUpdatesSync: false,
DisablePathCorrection: DefaultDisablePathCorrection,
DisablePathEscape: DefaultDisablePathEscape,
+ FireMethodNotAllowed: false,
DisableBanner: false,
LoggerOut: DefaultLoggerOut,
LoggerPreffix: DefaultLoggerPreffix,
diff --git a/context.go b/context.go
index f7f3c76b..5b12c6af 100644
--- a/context.go
+++ b/context.go
@@ -147,16 +147,31 @@ func (ctx *Context) GetHandlerName() string {
/* Request */
// Param returns the string representation of the key's path named parameter's value
+//
+// Return value should be never stored directly, instead store it to a local variable,
+// for example
+// instead of: context.Session().Set("name", ctx.Param("user"))
+// do this: username:= ctx.Param("user");ctx.Session().Set("name", username)
func (ctx *Context) Param(key string) string {
return ctx.Params.Get(key)
}
// ParamInt returns the int representation of the key's path named parameter's value
+//
+// Return value should be never stored directly, instead store it to a local variable,
+// for example
+// instead of: context.Session().Set("age", ctx.Param("age"))
+// do this: age:= ctx.Param("age");ctx.Session().Set("age", age)
func (ctx *Context) ParamInt(key string) (int, error) {
return strconv.Atoi(ctx.Param(key))
}
// ParamInt64 returns the int64 representation of the key's path named parameter's value
+//
+// Return value should be never stored directly, instead store it to a local variable,
+// for example
+// instead of: context.Session().Set("ms", ctx.ParamInt64("ms"))
+// do this: ms:= ctx.ParamInt64("ms");ctx.Session().Set("ms", ms)
func (ctx *Context) ParamInt64(key string) (int64, error) {
return strconv.ParseInt(ctx.Param(key), 10, 64)
}
diff --git a/http.go b/http.go
index a99e9b56..2ca8b645 100644
--- a/http.go
+++ b/http.go
@@ -949,18 +949,22 @@ type (
// if false then the /something it's not the same as /something/
// defaults to true
correctPath bool
- mu sync.Mutex
+ // if enabled then the router checks and fires an error for 405 http status method not allowed too if no method compatible method was found
+ // by default is false
+ fireMethodNotAllowed bool
+ mu sync.Mutex
}
)
func newServeMux(logger *log.Logger) *serveMux {
mux := &serveMux{
- lookups: make([]*route, 0),
- errorHandlers: make(map[int]Handler, 0),
- hostname: DefaultServerHostname, // these are changing when the server is up
- escapePath: !DefaultDisablePathEscape,
- correctPath: !DefaultDisablePathCorrection,
- logger: logger,
+ lookups: make([]*route, 0),
+ errorHandlers: make(map[int]Handler, 0),
+ hostname: DefaultServerHostname, // these are changing when the server is up
+ escapePath: !DefaultDisablePathEscape,
+ correctPath: !DefaultDisablePathCorrection,
+ fireMethodNotAllowed: false,
+ logger: logger,
}
return mux
@@ -978,6 +982,10 @@ func (mux *serveMux) setCorrectPath(b bool) {
mux.correctPath = b
}
+func (mux *serveMux) setFireMethodNotAllowed(b bool) {
+ mux.fireMethodNotAllowed = b
+}
+
// registerError registers a handler to a http status
func (mux *serveMux) registerError(statusCode int, handler Handler) {
mux.mu.Lock()
@@ -1183,6 +1191,17 @@ func (mux *serveMux) BuildHandler() HandlerFunc {
// not found
break
}
+ // https://github.com/kataras/iris/issues/469
+ if mux.fireMethodNotAllowed {
+ for i := range mux.garden {
+ tree := mux.garden[i]
+ if !methodEqual(context.Method(), tree.method) {
+ continue
+ }
+ }
+ mux.fireError(StatusMethodNotAllowed, context)
+ return
+ }
mux.fireError(StatusNotFound, context)
}
}
diff --git a/http_test.go b/http_test.go
index 9f801898..1127b1fb 100644
--- a/http_test.go
+++ b/http_test.go
@@ -665,3 +665,27 @@ func TestMuxCustomHandler(t *testing.T) {
expectedData4.DynamicPathParameter = param4
e.GET("/custom_handler_2/" + param4).Expect().Status(StatusOK).JSON().Equal(expectedData4)
}
+
+func TestMuxFireMethodNotAllowed(t *testing.T) {
+ initDefault()
+ Default.Config.FireMethodNotAllowed = true
+ h := func(ctx *Context) {
+ ctx.Write("%s", ctx.MethodString())
+ }
+
+ Default.OnError(StatusMethodNotAllowed, func(ctx *Context) {
+ ctx.Write("Hello from my custom 405 page")
+ })
+
+ Get("/mypath", h)
+ Put("/mypath", h)
+
+ e := Tester(t)
+
+ e.GET("/mypath").Expect().Status(StatusOK).Body().Equal("GET")
+ e.PUT("/mypath").Expect().Status(StatusOK).Body().Equal("PUT")
+ // this should fail with 405 and catch by the custom http error
+
+ e.POST("/mypath").Expect().Status(StatusMethodNotAllowed).Body().Equal("Hello from my custom 405 page")
+ Close()
+}
diff --git a/iris.go b/iris.go
index cb47d355..28c98338 100644
--- a/iris.go
+++ b/iris.go
@@ -79,7 +79,7 @@ import (
const (
// Version is the current version of the Iris web framework
- Version = "4.5.1"
+ Version = "4.5.2"
banner = ` _____ _
|_ _| (_)
@@ -353,6 +353,7 @@ func (s *Framework) Build() {
// prepare the mux runtime fields again, for any case
s.mux.setCorrectPath(!s.Config.DisablePathCorrection)
s.mux.setEscapePath(!s.Config.DisablePathEscape)
+ s.mux.setFireMethodNotAllowed(s.Config.FireMethodNotAllowed)
// prepare the server's handler, we do that check because iris supports
// custom routers (you can take the routes registed by iris using iris.Lookups function)