diff --git a/context.go b/context.go index 5063b498..e9735d28 100644 --- a/context.go +++ b/context.go @@ -960,7 +960,18 @@ func (c *Context) NegotiateFormat(offered ...string) string { } for _, accepted := range c.Accepted { for _, offert := range offered { - if accepted == offert { + // According to RFC 2616 and RFC 2396, non-ASCII characters are not allowed in headers, + // therefore we can just iterate over the string without casting it into []rune + i := 0 + for ; i < len(accepted); i++ { + if accepted[i] == '*' || offert[i] == '*' { + return offert + } + if accepted[i] != offert[i] { + break + } + } + if i == len(accepted) { return offert } } diff --git a/context_test.go b/context_test.go index 5d28bde6..0da5fbe6 100644 --- a/context_test.go +++ b/context_test.go @@ -1171,17 +1171,41 @@ func TestContextNegotiationFormat(t *testing.T) { func TestContextNegotiationFormatWithAccept(t *testing.T) { c, _ := CreateTestContext(httptest.NewRecorder()) c.Request, _ = http.NewRequest("POST", "/", nil) - c.Request.Header.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8") + c.Request.Header.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9;q=0.8") assert.Equal(t, MIMEXML, c.NegotiateFormat(MIMEJSON, MIMEXML)) assert.Equal(t, MIMEHTML, c.NegotiateFormat(MIMEXML, MIMEHTML)) assert.Empty(t, c.NegotiateFormat(MIMEJSON)) } +func TestContextNegotiationFormatWithWildcardAccept(t *testing.T) { + c, _ := CreateTestContext(httptest.NewRecorder()) + c.Request, _ = http.NewRequest("POST", "/", nil) + c.Request.Header.Add("Accept", "*/*") + + assert.Equal(t, c.NegotiateFormat("*/*"), "*/*") + assert.Equal(t, c.NegotiateFormat("text/*"), "text/*") + assert.Equal(t, c.NegotiateFormat("application/*"), "application/*") + assert.Equal(t, c.NegotiateFormat(MIMEJSON), MIMEJSON) + assert.Equal(t, c.NegotiateFormat(MIMEXML), MIMEXML) + assert.Equal(t, c.NegotiateFormat(MIMEHTML), MIMEHTML) + + c, _ = CreateTestContext(httptest.NewRecorder()) + c.Request, _ = http.NewRequest("POST", "/", nil) + c.Request.Header.Add("Accept", "text/*") + + assert.Equal(t, c.NegotiateFormat("*/*"), "*/*") + assert.Equal(t, c.NegotiateFormat("text/*"), "text/*") + assert.Equal(t, c.NegotiateFormat("application/*"), "") + assert.Equal(t, c.NegotiateFormat(MIMEJSON), "") + assert.Equal(t, c.NegotiateFormat(MIMEXML), "") + assert.Equal(t, c.NegotiateFormat(MIMEHTML), MIMEHTML) +} + func TestContextNegotiationFormatCustom(t *testing.T) { c, _ := CreateTestContext(httptest.NewRecorder()) c.Request, _ = http.NewRequest("POST", "/", nil) - c.Request.Header.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8") + c.Request.Header.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9;q=0.8") c.Accepted = nil c.SetAccepted(MIMEJSON, MIMEXML) diff --git a/docs/how-to-build-an-effective-middleware.md b/docs/how-to-build-an-effective-middleware.md deleted file mode 100644 index 568d5720..00000000 --- a/docs/how-to-build-an-effective-middleware.md +++ /dev/null @@ -1,137 +0,0 @@ -# How to build one effective middleware? - -## Consitituent part - -The middleware has two parts: - - - part one is what is executed once, when you initialize your middleware. That's where you set up all the global objects, logicals etc. Everything that happens one per application lifetime. - - - part two is what executes on every request. For example, a database middleware you simply inject your "global" database object into the context. Once it's inside the context, you can retrieve it from within other middlewares and your handler function. - -```go -func funcName(params string) gin.HandlerFunc { - // <--- - // This is part one - // ---> - // The follow code is an example - if err := check(params); err != nil { - panic(err) - } - - return func(c *gin.Context) { - // <--- - // This is part two - // ---> - // The follow code is an example - c.Set("TestVar", params) - c.Next() - } -} -``` - -## Execution process - -Firstly, we have the follow example code: - -```go -func main() { - router := gin.Default() - - router.Use(globalMiddleware()) - - router.GET("/rest/n/api/*some", mid1(), mid2(), handler) - - router.Run() -} - -func globalMiddleware() gin.HandlerFunc { - fmt.Println("globalMiddleware...1") - - return func(c *gin.Context) { - fmt.Println("globalMiddleware...2") - c.Next() - fmt.Println("globalMiddleware...3") - } -} - -func handler(c *gin.Context) { - fmt.Println("exec handler.") -} - -func mid1() gin.HandlerFunc { - fmt.Println("mid1...1") - - return func(c *gin.Context) { - - fmt.Println("mid1...2") - c.Next() - fmt.Println("mid1...3") - } -} - -func mid2() gin.HandlerFunc { - fmt.Println("mid2...1") - - return func(c *gin.Context) { - fmt.Println("mid2...2") - c.Next() - fmt.Println("mid2...3") - } -} -``` - -According to [Consitituent part](#consitituent-part) said, when we run the gin process, **part one** will execute firstly and will print the follow information: - -```go -globalMiddleware...1 -mid1...1 -mid2...1 -``` - -And init order are: - -```go -globalMiddleware...1 - | - v -mid1...1 - | - v -mid2...1 -``` - -When we curl one request `curl -v localhost:8080/rest/n/api/some`, **part two** will execute their middleware and output the following information: - -```go -globalMiddleware...2 -mid1...2 -mid2...2 -exec handler. -mid2...3 -mid1...3 -globalMiddleware...3 -``` - -In other words, run order are: - -```go -globalMiddleware...2 - | - v -mid1...2 - | - v -mid2...2 - | - v -exec handler. - | - v -mid2...3 - | - v -mid1...3 - | - v -globalMiddleware...3 -```