-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
module/chi: add go-chi engine support (#6)
add go-chi engine support
- Loading branch information
Showing
12 changed files
with
523 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
# Mir.Chi | ||
Mir.Chi module provide mir.Engine implement backend by [Chi](https://github.com/go-chi/chi). | ||
|
||
### Usage | ||
```go | ||
package main | ||
|
||
import( | ||
"github.com/alimy/mir" | ||
"github.com/go-chi/chi" | ||
"log" | ||
"net/http" | ||
|
||
mirE "github.com/alimy/mir/module/chi" | ||
) | ||
|
||
type site struct { | ||
count uint32 | ||
|
||
Group mir.Group `mir:"v1"` | ||
index mir.Get `mir:"/index/"` | ||
articles mir.Get `mir:"/articles/{category}/{id:[0-9]+}#GetArticles"` | ||
} | ||
|
||
// Index handler of the index field that in site struct, the struct tag indicate | ||
// this handler will register to path "/index/" and method is http.MethodGet. | ||
func (h *site) Index(rw http.ResponseWriter, r *http.Request) { | ||
h.count++ | ||
rw.Write([]byte("Index")) | ||
} | ||
|
||
// GetArticles handler of articles indicator that contains Host/Path/Queries/Handler info. | ||
// Path info is the second or first(if no host info) segment start with '/'(eg: /articles/{category}/{id:[0-9]+}/ | ||
// Handler info is forth info start with '#' that indicate real handler method name(eg: GetArticles). | ||
// if no handler info will use field name capital first char as default handler name(eg: if articles had | ||
// no #GetArticles then the handler name will is Articles) | ||
func (h *site) GetArticles(rw http.ResponseWriter, r *http.Request) { | ||
rw.Write([]byte("GetArticles")) | ||
} | ||
|
||
func main() { | ||
// Create a new mux router instance | ||
r := chi.NewRouter() | ||
|
||
// Instance a mir engine to register handler for mux router by mir | ||
mirE.Register(r, &site{}) | ||
|
||
// Bind to a port and pass our router in | ||
log.Fatal(http.ListenAndServe(":8013", r)) | ||
} | ||
|
||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
// Copyright 2019 Michael Li <alimy@gility.net>. All rights reserved. | ||
// Use of this source code is governed by Apache License 2.0 that | ||
// can be found in the LICENSE file. | ||
|
||
package chi | ||
|
||
import ( | ||
"github.com/alimy/mir" | ||
"github.com/go-chi/chi" | ||
) | ||
|
||
// Mir return mir.Engine interface implements instance. | ||
// Used to register routes to Chi router with struct tag string's information. | ||
func Mir(r chi.Router) mir.Engine { | ||
return &mirEngine{engine: r} | ||
} | ||
|
||
// Register use entries's info to register handler to Chi router. | ||
func Register(r chi.Router, entries ...interface{}) error { | ||
mirE := Mir(r) | ||
return mir.Register(mirE, entries...) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
// Copyright 2019 Michael Li <alimy@gility.net>. All rights reserved. | ||
// Use of this source code is governed by Apache License 2.0 that | ||
// can be found in the LICENSE file. | ||
|
||
package chi_test | ||
|
||
import ( | ||
"testing" | ||
|
||
. "github.com/onsi/ginkgo" | ||
. "github.com/onsi/gomega" | ||
) | ||
|
||
func TestMux(t *testing.T) { | ||
RegisterFailHandler(Fail) | ||
RunSpecs(t, "Chi Suite") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
// Copyright 2019 Michael Li <alimy@gility.net>. All rights reserved. | ||
// Use of this source code is governed by Apache License 2.0 that | ||
// can be found in the LICENSE file. | ||
|
||
package chi_test | ||
|
||
import ( | ||
"bytes" | ||
"github.com/alimy/mir" | ||
"github.com/go-chi/chi" | ||
"net/http/httptest" | ||
|
||
. "github.com/alimy/mir/module/chi" | ||
. "github.com/onsi/ginkgo" | ||
. "github.com/onsi/gomega" | ||
) | ||
|
||
var _ = Describe("Core", func() { | ||
var ( | ||
router chi.Router | ||
w *httptest.ResponseRecorder | ||
err error | ||
) | ||
|
||
JustBeforeEach(func() { | ||
w = httptest.NewRecorder() | ||
}) | ||
|
||
Context("check Mir function", func() { | ||
BeforeEach(func() { | ||
router = chi.NewRouter() | ||
mirE := Mir(router) | ||
err = mir.Register(mirE, &entry{Chain: mirChain()}) | ||
}) | ||
|
||
It("no error", func() { | ||
Expect(err).Should(BeNil()) | ||
}) | ||
|
||
It("no nil", func() { | ||
Expect(router).ShouldNot(BeNil()) | ||
}) | ||
|
||
It("handle add", func() { | ||
body := bytes.NewReader([]byte("hello")) | ||
r := httptest.NewRequest(mir.MethodPost, "/v1/add/10086/", body) | ||
router.ServeHTTP(w, r) | ||
|
||
Expect(w.Code).To(Equal(200)) | ||
Expect(w.Body.String()).To(Equal("Add:10086:hello")) | ||
}) | ||
|
||
It("handler index", func() { | ||
r := httptest.NewRequest(mir.MethodGet, "/v1/index/", nil) | ||
router.ServeHTTP(w, r) | ||
|
||
Expect(w.Code).To(Equal(200)) | ||
Expect(w.Body.String()).To(Equal("Index")) | ||
}) | ||
|
||
It("handle articles", func() { | ||
r := httptest.NewRequest(mir.MethodGet, "/v1/articles/golang/10086", nil) | ||
router.ServeHTTP(w, r) | ||
|
||
Expect(w.Code).To(Equal(200)) | ||
Expect(w.Body.String()).To(Equal("GetArticles:golang:10086")) | ||
}) | ||
}) | ||
|
||
Context("check Register function", func() { | ||
BeforeEach(func() { | ||
router = chi.NewRouter() | ||
err = Register(router, &entry{Group: "/v2", Chain: mirChain()}) | ||
}) | ||
|
||
It("no error", func() { | ||
Expect(err).Should(BeNil()) | ||
}) | ||
|
||
It("no nil", func() { | ||
Expect(router).ShouldNot(BeNil()) | ||
}) | ||
|
||
It("handle add", func() { | ||
body := bytes.NewReader([]byte("hello")) | ||
r := httptest.NewRequest(mir.MethodPost, "/v2/add/10086/", body) | ||
router.ServeHTTP(w, r) | ||
|
||
Expect(w.Code).To(Equal(200)) | ||
Expect(w.Body.String()).To(Equal("Add:10086:hello")) | ||
}) | ||
|
||
It("handler index", func() { | ||
r := httptest.NewRequest(mir.MethodGet, "/v2/index/", nil) | ||
router.ServeHTTP(w, r) | ||
|
||
Expect(w.Code).To(Equal(200)) | ||
Expect(w.Body.String()).To(Equal("Index")) | ||
}) | ||
|
||
It("handle articles", func() { | ||
r := httptest.NewRequest(mir.MethodGet, "/v2/articles/golang/10086", nil) | ||
router.ServeHTTP(w, r) | ||
|
||
Expect(w.Code).To(Equal(200)) | ||
Expect(w.Body.String()).To(Equal("GetArticles:golang:10086")) | ||
}) | ||
}) | ||
|
||
Context("check Register entries", func() { | ||
BeforeEach(func() { | ||
router = chi.NewRouter() | ||
err = Register(router, &entry{}, &entry{Group: "v2", Chain: mirChain()}) | ||
}) | ||
|
||
It("no error", func() { | ||
Expect(err).Should(BeNil()) | ||
}) | ||
|
||
It("no nil", func() { | ||
Expect(router).ShouldNot(BeNil()) | ||
}) | ||
|
||
It("handle v1 add", func() { | ||
body := bytes.NewReader([]byte("hello")) | ||
r := httptest.NewRequest(mir.MethodPost, "/v1/add/10086/", body) | ||
router.ServeHTTP(w, r) | ||
|
||
Expect(w.Code).To(Equal(200)) | ||
Expect(w.Body.String()).To(Equal("Add:10086:hello")) | ||
}) | ||
|
||
It("handler v1 index", func() { | ||
r := httptest.NewRequest(mir.MethodGet, "/v1/index/", nil) | ||
router.ServeHTTP(w, r) | ||
|
||
Expect(w.Code).To(Equal(200)) | ||
Expect(w.Body.String()).To(Equal("Index")) | ||
}) | ||
|
||
It("handle v1 articles", func() { | ||
r := httptest.NewRequest(mir.MethodGet, "/v1/articles/golang/10086", nil) | ||
router.ServeHTTP(w, r) | ||
|
||
Expect(w.Code).To(Equal(200)) | ||
Expect(w.Body.String()).To(Equal("GetArticles:golang:10086")) | ||
}) | ||
|
||
It("handle v2 add", func() { | ||
body := bytes.NewReader([]byte("hello")) | ||
r := httptest.NewRequest(mir.MethodPost, "/v2/add/10086/", body) | ||
router.ServeHTTP(w, r) | ||
|
||
Expect(w.Code).To(Equal(200)) | ||
Expect(w.Body.String()).To(Equal("Add:10086:hello")) | ||
}) | ||
|
||
It("handler v2 index", func() { | ||
r := httptest.NewRequest(mir.MethodGet, "/v2/index/", nil) | ||
router.ServeHTTP(w, r) | ||
|
||
Expect(w.Code).To(Equal(200)) | ||
Expect(w.Body.String()).To(Equal("Index")) | ||
}) | ||
|
||
It("handle v2 articles", func() { | ||
r := httptest.NewRequest(mir.MethodGet, "/v2/articles/golang/10086", nil) | ||
router.ServeHTTP(w, r) | ||
|
||
Expect(w.Code).To(Equal(200)) | ||
Expect(w.Body.String()).To(Equal("GetArticles:golang:10086")) | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
// Copyright 2019 Michael Li <alimy@gility.net>. All rights reserved. | ||
// Use of this source code is governed by Apache License 2.0 that | ||
// can be found in the LICENSE file. | ||
|
||
package chi | ||
|
||
import ( | ||
"fmt" | ||
"github.com/alimy/mir" | ||
"github.com/go-chi/chi" | ||
"net/http" | ||
"strings" | ||
) | ||
|
||
var _ mir.Engine = &mirEngine{} | ||
|
||
// mirEngine used to implements mir.Engine interface | ||
type mirEngine struct { | ||
engine chi.Router | ||
} | ||
|
||
// Register register entries to chi engine | ||
func (e *mirEngine) Register(entries []*mir.TagMir) error { | ||
for _, entry := range entries { | ||
var router chi.Router | ||
if entry.Group == "" || entry.Group == "/" { | ||
router = e.engine | ||
} else { | ||
pathPrefix := entry.Group | ||
if !strings.HasPrefix(entry.Group, "/") { | ||
pathPrefix = "/" + entry.Group | ||
} | ||
router = chi.NewRouter() | ||
e.engine.Mount(pathPrefix, router) | ||
} | ||
if err := handlerChainTo(router, entry.Chain); err != nil { | ||
return err | ||
} | ||
// Notice just return if catch a error or continue next entry register | ||
if err := registerWith(router, entry.Fields); err != nil { | ||
return err | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
// registerWith register fields to give router | ||
func registerWith(router chi.Router, fields []*mir.TagField) error { | ||
for _, field := range fields { | ||
if handlerFunc, ok := field.Handler.(func(http.ResponseWriter, *http.Request)); ok { | ||
if field.Method == mir.MethodAny { | ||
router.Connect(field.Path, handlerFunc) | ||
router.Delete(field.Path, handlerFunc) | ||
router.Get(field.Path, handlerFunc) | ||
router.Head(field.Path, handlerFunc) | ||
router.Options(field.Path, handlerFunc) | ||
router.Patch(field.Path, handlerFunc) | ||
router.Post(field.Path, handlerFunc) | ||
router.Put(field.Path, handlerFunc) | ||
router.Trace(field.Path, handlerFunc) | ||
} else { | ||
router.MethodFunc(field.Method, field.Path, handlerFunc) | ||
} | ||
} else { | ||
return fmt.Errorf("handler not func(http.ResponseWriter, *http.Request) function") | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
// handlerChainTo setup handlers to router that grouped | ||
func handlerChainTo(router chi.Router, chain mir.Chain) error { | ||
// just return if empty chain | ||
if chain == nil { | ||
return nil | ||
} | ||
if middlewares, ok := chain.(chi.Middlewares); ok { | ||
router.Use(middlewares...) | ||
} else { | ||
return fmt.Errorf("chain type not chi.Middlewares") | ||
} | ||
return nil | ||
} |
Oops, something went wrong.