๊ณต์ง: ์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ์๋์ ์ฃผ์๋ก ๋ง์ด ์๋ ค์ ธ ์์ต๋๋ค.
github.com/codegangsta/negroni
-- Github๊ฐ ์๋์ผ๋ก ์ด ์ ์ฅ์์ ๋ํ ์์ฒญ์ ๋ฆฌ๋ค์ด๋ ํธ ์ํฌ ๊ฒ์ด์ง๋ง, ํ์คํ ์ฌ์ฉ์ ์ํด ์ฐธ์กฐ๋ฅผ ์ด๊ณณ์ผ๋ก ๋ณ๊ฒฝํ๋ ๊ฒ์ ์ถ์ฒ๋๋ฆฝ๋๋ค.
Negroni๋ Go์์ ์น ๋ฏธ๋ค์จ์ด๋ก์ ์์ฐ์ค๋ฌ์ด ์ ๊ทผ์ ์ถ๊ตฌํฉ๋๋ค. ์ด๊ฒ์ ์๊ณ , ๊ฑฐ์ฌ๋ฆฌ์ง ์์ผ๋ฉฐ net/http
ํธ๋ค๋ฌ์ ์ฌ์ฉ์ ์งํฅํ๋ ๊ฒ์ ์๋ฏธํฉ๋๋ค.
๋ง์ฝ ๋น์ ์ด Martini์ ๊ธฐ๋ณธ์ ์ธ ์ปจ์ ์ ์ํ์ง๋ง, ์ด๊ฒ์ด ๋๋ฌด ๋ง์ ๊ธฐ๋ฅ์ ํฌํจํ๋ค๊ณ ๋๊ปด์ก๋ค๋ฉด Negroni๊ฐ ์ต์ ์ ์ ํ์ผ ๊ฒ์ ๋๋ค.
Go ์ค์น์ GOPATH๋ฅผ ์ธํ
ํ๋ ์์
์ ์๋ฃํ ๋ค, ๋น์ ์ ์ฒซ .go
ํ์ผ์ ์์ฑํ์ธ์.
์ฐ๋ฆฌ๋ ์ด๋ฅผ server.go
๋ผ๊ณ ๋ถ๋ฅผ ๊ฒ์
๋๋ค.
package main
import (
"fmt"
"net/http"
"github.com/urfave/negroni"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
fmt.Fprintf(w, "Welcome to the home page!")
})
n := negroni.Classic() // ๊ธฐ๋ณธ ๋ฏธ๋ค์จ์ด๋ค์ ํฌํจํฉ๋๋ค
n.UseHandler(mux)
http.ListenAndServe(":3000", n)
}
๊ทธ๋ฆฌ๊ณ Negroni ํจํค์ง๋ฅผ ์ค์นํฉ๋๋ค (๊ณต์ง: go 1.1 ์ด์์ด ์๊ตฌ๋ฉ๋๋ค) :
go get github.com/urfave/negroni
์๋ฒ๋ฅผ ์คํ์ํต๋๋ค:
go run server.go
์ด์ localhost:3000
์์ ๋์ํ๋ Go net/http
์น์๋ฒ๋ฅผ ๊ฐ์ง๊ฒ ๋์์ต๋๋ค.
Debian์ ์ฌ์ฉ์ค์ด์๋ผ๋ฉด, negroni
๋ a package์์๋ ์ฌ์ฉ์ด ๊ฐ๋ฅํฉ๋๋ค. apt install golang-github-urfave-negroni-dev
๋ฅผ ํตํด์ ์ค์น๊ฐ ๊ฐ๋ฅํฉ๋๋ค. (๊ธ์ ์์ฑํ ๋น์, ์ด๋ sid
์ ์ฅ์ ์์ ์์ต๋๋ค.)
Negroni๋ ํ๋ ์์ํฌ๊ฐ ์๋๋๋ค. ์ด๋ net/http
๋ฅผ ์ง์ ์ ์ผ๋ก ์ด์ฉํ ์ ์๋๋ก ๋์์ธ๋ ๋ฏธ๋ค์จ์ด ์ค์ฌ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์
๋๋ค.
Negroni๋ BYOR (Bring your own Router, ๋์ ๋ผ์ฐํฐ๋ฅผ ์ฌ์ฉํ๊ธฐ)๋ฅผ ์งํฅํฉ๋๋ค. Go ์ปค๋ฎค๋ํฐ์๋ ์ด๋ฏธ ์ข์ http ๋ผ์ฐํฐ๋ค์ด ์กด์ฌํ๊ธฐ ๋๋ฌธ์ Negroni๋ ๊ทธ๋ค๊ณผ ์ ์ด์ฐ๋ฌ์ง ์ ์๋๋ก net/http
๋ฅผ ์ ์ ์ผ๋ก ์ง์ํ๊ณ ์์ต๋๋ค.
์๋ฅผ ๋ค์ด, ์๋๋ Gorilla Mux๋ฅผ ์ฌ์ฉํ ์์ ๋๋ค:
router := mux.NewRouter()
router.HandleFunc("/", HomeHandler)
n := negroni.New(Middleware1, Middleware2)
// Use() ํจ์๋ฅผ ์ฌ์ฉํด์ ๋ฏธ๋ค์จ์ด๋ฅผ ์ฌ์ฉํ ์๋ ์์ต๋๋ค.
n.Use(Middleware3)
// ๋ผ์ฐํฐ์ ๊ฒฝ์ฐ ๋ง์ง๋ง์ ์ต๋๋ค.
n.UseHandler(router)
http.ListenAndServe(":3001", n)
negroni.Classic()
์ ๋๋ถ๋ถ์ ์ดํ๋ฆฌ์ผ์ด์
์์ ์ ์ฉํ๊ฒ ์ฌ์ฉ๋๋ ๊ธฐ๋ณธ์ ์ธ ๋ฏธ๋ค์จ์ด๋ค์ ์ ๊ณตํฉ๋๋ค.
negroni.Recovery
- Panic ๋ณต๊ตฌ์ฉ ๋ฏธ๋ค์จ์ดnegroni.Logger
- Request/Response ๋ก๊น ๋ฏธ๋ค์จ์ดnegroni.Static
- "public" ๋๋ ํฐ๋ฆฌ ์๋์ ์ ์ ํ์ผ ์ ๊ณต(serving)์ ์ํ ๋ฏธ๋ค์จ์ด
์ด๋ Negroni์ ์ ์ฉํ ๊ธฐ๋ฅ๋ค์ ์ฌ์ฉํ๊ธฐ ์์ํ๋๋ฐ ํฐ ๋์์ด ๋๋๋ก ๋ง๋ค์ด์ค ๊ฒ์ ๋๋ค.
Negroni๋ ์๋ฐฉํฅ ๋ฏธ๋ค์จ์ด ํ๋ฆ์ ์ ๊ณตํฉ๋๋ค. ์ด๋ negroni.Handler
์ธํฐํ์ด์ค๋ฅผ ํตํด ๊ตฌํํฉ๋๋ค.
type Handler interface {
ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)
}
๋ฏธ๋ค์จ์ด๊ฐ ResponseWriter
์ ์์ง ๋ฌด์ธ๊ฐ ์ฐ์ง ์์๋ค๋ฉด, ์ด๋ ๋ค์ ๋ฏธ๋ค์จ์ด์ ์ฐ๊ฒฐ๋์ด์๋ http.HandleFunc
๋ฅผ ํธ์ถํด์ผ ํฉ๋๋ค. ์ด๋ ์ ์ฉํ๊ฒ ์ฌ์ฉ๋ ์ ์์ต๋๋ค:
func MyMiddleware(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
// next() ์ฒ๋ฆฌ ์ด์ ์์
์ํ
next(rw, r)
// next() ์ฒ๋ฆฌ ์ดํ ์์
์ํ
}
์ดํ Use
ํจ์๋ฅผ ํตํด ํธ๋ค๋ฌ ์ฒด์ธ(handler chain)์ ๋งคํ ์ํฌ ์ ์์ต๋๋ค:
n := negroni.New()
n.Use(negroni.HandlerFunc(MyMiddleware))
๋ํ, ๊ธฐ์กด์ http.Handler
๋ค๊ณผ๋ ๋งคํ์ํฌ ์ ์์ต๋๋ค:
n := negroni.New()
mux := http.NewServeMux()
// ์ฌ๊ธฐ์ ๋ผ์ฐํธ๋ค์ ๋งคํํ์ธ์
n.UseHandler(mux)
http.ListenAndServe(":3000", n)
Negroni๋ With
๋ผ๊ณ ๋ถ๋ฆฌ๋ ํธ๋ฆฌํ ํจ์๋ฅผ ๊ฐ์ง๊ณ ์์ต๋๋ค. With
๋ ํ ๊ฐ ํน์ ๊ทธ ์ด์์ Handler
์ธ์คํด์ค๋ค์ ๋ฐ์ ๊ธฐ์กด ๋ฆฌ์๋ฒ์ ํธ๋ค๋ฌ๋ค๊ณผ ์๋ก์ด ํธ๋ค๋ฌ๋ค์ด ์กฐํฉ๋ ์๋ก์ด Negroni
๊ฐ์ฒด๋ฅผ ๋ฆฌํดํฉ๋๋ค.
// ์ฌ์ฌ์ฉ์ ์ํ๋ ๋ฏธ๋ค์จ์ด๋ค
common := negroni.New()
common.Use(MyMiddleware1)
common.Use(MyMiddleware2)
// `specific`์ `common`์ ํธ๋ค๋ฌ๋ค๊ณผ ์๋ก ์ ๋ฌ๋ ํธ๋ค๋ฌ๋ค์ด ์กฐํฉ๋ ์๋ก์ด `negroni` ๊ฐ์ฒด
specific := common.With(
SpecificMiddleware1,
SpecificMiddleware2
)
Negroni๋ Run
์ด๋ผ๊ณ ๋ถ๋ฆฌ๋ ํธ๋ฆฌํ ํจ์๋ฅผ ๊ฐ์ง๊ณ ์์ต๋๋ค. Run
์ http.ListenAndServe
์ ๊ฐ์ด ์ฃผ์ ์คํธ๋ง ๊ฐ(addr string)์ ๋๊ฒจ๋ฐ์ต๋๋ค.
package main
import (
"github.com/urfave/negroni"
)
func main() {
n := negroni.Classic()
n.Run(":8080")
}
๋ง์ฝ ์ฃผ์ ๊ฐ์ด ์ ๊ณต๋์ง ์๋๋ค๋ฉด, PORT
ํ๊ฒฝ ๋ณ์๊ฐ ๋์ ์ฌ์ฉ๋ฉ๋๋ค. PORT
ํ๊ฒฝ ๋ณ์ ๋ํ ์ ์๋์ด์์ง ์๋ค๋ฉด, ๊ธฐ๋ณธ ์ฃผ์(default address)๊ฐ ์ฌ์ฉ๋ฉ๋๋ค. ์ ์ฒด ์ค๋ช
์ ๋ณด์๋ ค๋ฉด Run์ ์ฐธ๊ณ ํ์ธ์.
์ผ๋ฐ์ ์ผ๋ก๋, ์ข ๋ ์ ์ฐํ ์ฌ์ฉ์ ์ํด์ net/http
๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ negroni
๊ฐ์ฒด๋ฅผ ํธ๋ค๋ฌ๋ก์ ๋๊ธฐ๋ ๊ฒ์ ์ ํธํ ๊ฒ์
๋๋ค. ์๋ฅผ ๋ค๋ฉด:
package main
import (
"fmt"
"log"
"net/http"
"time"
"github.com/urfave/negroni"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
fmt.Fprintf(w, "Welcome to the home page!")
})
n := negroni.Classic() // ๊ธฐ๋ณธ ๋ฏธ๋ค์จ์ด๋ค์ ํฌํจํฉ๋๋ค
n.UseHandler(mux)
s := &http.Server{
Addr: ":8080",
Handler: n,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20,
}
log.Fatal(s.ListenAndServe())
}
๋ง์ฝ ๋น์ ์ด ๋ผ์ฐํธ๋ค์ ๋ผ์ฐํธ ๊ทธ๋ฃน์ ๊ฐ์ง๊ณ ์๋ค๋ฉด ๊ทธ๋ฐ๋ฐ ๊ทธ๊ฒ์ ์คํ๋์ด์ผํ๋ ๋ฏธ๋ค์จ์ด๋ฅผ ํ์๋กํ๋ค.
ํน์ ๋ผ์ฐํธ ๊ทธ๋ฃน๋ง์ด ์ฌ์ฉํ๋ ๋ฏธ๋ค์จ์ด๊ฐ ์๋ค๋ฉด, ๊ฐ๋จํ๊ฒ Negroni ์ธ์คํด์ค๋ฅผ ์๋กญ๊ฒ ์์ฑํ์ฌ ๋ผ์ฐํธ ํธ๋ค๋ฌ(route handler)๋ก์ ์ฌ์ฉํ๋ฉด ๋๋ค.
router := mux.NewRouter()
adminRoutes := mux.NewRouter()
// admin routes๋ฅผ ์ฌ๊ธฐ์ ์ถ๊ฐํ์ธ์
// ๊ด๋ฆฌ์ ๋ฏธ๋ค์จ์ด๋ค์ ์ํ ์๋ก์ด negroni ์ธ์คํด์ค๋ฅผ ์์ฑํฉ๋๋ค
router.PathPrefix("/admin").Handler(negroni.New(
Middleware1,
Middleware2,
negroni.Wrap(adminRoutes),
))
Gorilla Mux๋ฅผ ์ฌ์ฉํ๊ณ ์๋ค๋ฉด, ์๋๋ ์๋ธ ๋ผ์ฐํฐ(subrouter)๋ฅผ ์ฌ์ฉํ๋ ์์ ์ ๋๋ค:
router := mux.NewRouter()
subRouter := mux.NewRouter().PathPrefix("/subpath").Subrouter().StrictSlash(true)
subRouter.HandleFunc("/", someSubpathHandler) // "/subpath/"
subRouter.HandleFunc("/:id", someSubpathHandler) // "/subpath/:id"
// "/subpath" ๋ subRouter์ ๋ฉ์ธ ๋ผ์ฐํฐ(main router)์ ์ฐ๊ฒฐ ๋ณด์ฅ์ ์ํด ๋ฐ๋์ ํ์ํฉ๋๋ค
router.PathPrefix("/subpath").Handler(negroni.New(
Middleware1,
Middleware2,
negroni.Wrap(subRouter),
))
With()
๋ ๋ผ์ฐํฐ ๊ฐ ๊ณต์ ์ ๋ฐ์ํ๋ ๋ฏธ๋ค์จ์ด ์ค๋ณต์ ๋ฐฉ์งํ๊ธฐ ์ํด์ ์ฌ์ฉ๋ ์ ์์ต๋๋ค.
router := mux.NewRouter()
apiRoutes := mux.NewRouter()
// api ๋ผ์ฐํธ๋ค์ ์ฌ๊ธฐ์ ์ถ๊ฐํ์ธ์
webRoutes := mux.NewRouter()
// web ๋ผ์ฐํธ๋ค์ ์ฌ๊ธฐ์ ์ถ๊ฐํ์ธ์
// ๋ผ์ฐํฐ๊ฐ ๊ณต์ ๋ common ๋ฏธ๋ค์จ์ด๋ฅผ ์์ฑํฉ๋๋ค
common := negroni.New(
Middleware1,
Middleware2,
)
// common ๋ฏธ๋ค์จ์ด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก
// api ๋ฏธ๋ค์จ์ด๋ฅผ ์ํ ์๋ก์ด negroni ๊ฐ์ฒด๋ฅผ ์์ฑํฉ๋๋ค
router.PathPrefix("/api").Handler(common.With(
APIMiddleware1,
negroni.Wrap(apiRoutes),
))
// common ๋ฏธ๋ค์จ์ด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก
// web ๋ฏธ๋ค์จ์ด๋ฅผ ์ํ ์๋ก์ด negroni ๊ฐ์ฒด๋ฅผ ์์ฑํฉ๋๋ค
router.PathPrefix("/web").Handler(common.With(
WebMiddleware1,
negroni.Wrap(webRoutes),
))
์ด ๋ฏธ๋ค์จ์ด๋ ํ์ผ๋ค์ ํ์ผ ์์คํ
(filesystem)์ผ๋ก ์ ๊ณตํ๋ ์ญํ ์ ์ํํฉ๋๋ค. ํ์ผ์ด ์กด์ฌํ์ง ์์ ๊ฒฝ์ฐ, ์์ฒญ์ ๋ค์ ๋ฏธ๋ค์จ์ด๋ก ๋๊น๋๋ค. ์กด์ฌํ์ง ์๋ ํ์ผ์ ๋ํด 404 File Not Found
๋ฅผ ์ ์ ์๊ฒ ๋ฐํํ๊ธธ ์ํ๋ ๊ฒฝ์ฐ http.FileServer๋ฅผ ํธ๋ค๋ฌ๋ก ์ฌ์ฉํ๋ ๊ฒ์ ์ดํด๋ณด์์ผ ํฉ๋๋ค.
์์ :
package main
import (
"fmt"
"net/http"
"github.com/urfave/negroni"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
fmt.Fprintf(w, "Welcome to the home page!")
})
// "๋ฏธ๋ค์จ์ด(middleware)"์ ์ญํ ๋ณด๋ค๋ "์๋ฒ์ ๊ฐ์(server-like)" ์ญํ ์ ์ํํ๊ธฐ๋ฅผ ์ํ ๋
// http.FileServer๋ฅผ ์ฌ์ฉํ ์์
// mux.Handle("/public", http.FileServer(http.Dir("/home/public")))
n := negroni.New()
n.Use(negroni.NewStatic(http.Dir("/tmp")))
n.UseHandler(mux)
http.ListenAndServe(":3002", n)
}
์ ์ฝ๋๋ /tmp
๋๋ ํฐ๋ฆฌ๋ก๋ถํฐ ํ์ผ์ ์ ๊ณตํ ๊ฒ์
๋๋ค. ๊ทธ๋ฌ๋ ํ์ผ ์์คํ
์์ ํ์ผ์ ๋ํ ์์ฒญ์ด ์ผ์นํ์ง ์๋ ๊ฒฝ์ฐ ํ๋ก์๋ ๋ค์ ํธ๋ค๋ฌ๋ฅผ ํธ์ถํ ๊ฒ์
๋๋ค.
์ด ๋ฏธ๋ค์จ์ด๋ panic
๋ค์ ๊ฐ์งํ๊ณ 500
์๋ต ์ฝ๋(response code)๋ฅผ ๋ฐํํ๋ ์ญํ ์ ์ํํฉ๋๋ค. ๋ค๋ฅธ ๋ฏธ๋ค์จ์ด๊ฐ ์๋ต ์ฝ๋ ๋๋ ๋ฐ๋(body)๋ฅผ ์ธ ๊ฒฝ์ฐ, ํด๋ผ์ด์ธํธ๋ ์ด๋ฏธ HTTP ์๋ต ์ฝ๋๋ฅผ ๋ฐ์๊ธฐ ๋๋ฌธ์ ์ด ๋ฏธ๋ค์จ์ด๊ฐ ์ ์ ํ ์์ ์ 500
์ฝ๋๋ฅผ ๋ณด๋ด๋ ๊ฒ์ ์คํจํ ๊ฒ์
๋๋ค. ์ถ๊ฐ์ ์ผ๋ก PanicHandlerFunc
๋ Sentry ๋๋ Aribrake์ ๊ฐ์ ์๋ฌ ๋ณด๊ณ ์๋น์ค์ 500
์ฝ๋๋ฅผ ๋ฐํํ๋๋ก ๋ถ์ ์ ์์ต๋๋ค.
์์ :
package main
import (
"net/http"
"github.com/urfave/negroni"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
panic("oh no")
})
n := negroni.New()
n.Use(negroni.NewRecovery())
n.UseHandler(mux)
http.ListenAndServe(":3003", n)
}
์ ์ฝ๋๋ ๊ฐ ์์ฒญ์ ๋ํด 500 Internal Server Error
๋ฐํํ ๊ฒ์
๋๋ค. PrintStack
๊ฐ์ด true
(๊ธฐ๋ณธ ๊ฐ)๋ก ์ค์ ๋์ด์๋ค๋ฉด ์์ฒญ์(requester)์๊ฒ ์คํ ํธ๋ ์ด์ค(stack trace) ๊ฐ์ ์ถ๋ ฅํ๋ ๊ฒ์ฒ๋ผ ๋ก๊น
๋ํ ์งํํฉ๋๋ค.
PanicHandlerFunc
๋ฅผ ์ฌ์ฉํ ์์ :
package main
import (
"net/http"
"github.com/urfave/negroni"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
panic("oh no")
})
n := negroni.New()
recovery := negroni.NewRecovery()
recovery.PanicHandlerFunc = reportToSentry
n.Use(recovery)
n.UseHandler(mux)
http.ListenAndServe(":3003", n)
}
func reportToSentry(info *negroni.PanicInformation) {
// Sentry์๊ฒ ์๋ฌ๋ฅผ ๋ณด๊ณ ํ๋ ์ฝ๋๋ฅผ ์์ฑํ์ธ์
}
๋ฏธ๋ค์จ์ด๋ STDOUT
์ ๊ธฐ๋ณธ์ผ๋ก ๊ฐ๋ค์ ์ถ๋ ฅํฉ๋๋ค. ํ์ง๋ง SetFormatter()
ํจ์๋ฅผ ์ด์ฉํด์ ์ถ๋ ฅ ํ๋ก์ธ์ค๋ฅผ ์ปค์คํฐ๋ง์ด์ง ํ ์ ์์ต๋๋ค. ๋น์ฐํ HTMLPanicFormatter
๋ฅผ ์ฌ์ฉํด์ ๊น๋ํ HTML๋ก๋ ์๋ฌ ์ํฉ์ ๋ณด์ฌ์ค ์ ์์ต๋๋ค.
package main
import (
"net/http"
"github.com/urfave/negroni"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
panic("oh no")
})
n := negroni.New()
recovery := negroni.NewRecovery()
recovery.Formatter = &negroni.HTMLPanicFormatter{}
n.Use(recovery)
n.UseHandler(mux)
http.ListenAndServe(":3003", n)
}
์ด ๋ฏธ๋ค์จ์ด๋ ์๋ฒ์ ๋ค์ด์ค๋ ์์ฒญ๊ณผ ์๋ต๋ค์ ๊ธฐ๋กํ๋ ์ญํ ์ ์ํํฉ๋๋ค.
์์ :
package main
import (
"fmt"
"net/http"
"github.com/urfave/negroni"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
fmt.Fprintf(w, "Welcome to the home page!")
})
n := negroni.New()
n.Use(negroni.NewLogger())
n.UseHandler(mux)
http.ListenAndServe(":3004", n)
}
์ ์ฝ๋๋ ๊ฐ ์์ฒญ์ ๋ํด ์๋์ ๊ฐ์ด ์ถ๋ ฅํ ๊ฒ์ ๋๋ค.
[negroni] 2017-10-04T14:56:25+02:00 | 200 | 378ยตs | localhost:3004 | GET /
๋ฌผ๋ก , SetFormat
ํจ์๋ฅผ ์ด์ฉํด ์ฌ์ฉ์๋ง์ ๋ก๊ทธ ํฌ๋งท(log format) ๋ํ ์ ์ํ ์ ์์ต๋๋ค. ๋ก๊ทธ ํฌ๋งท์ LoggerEntry
๊ตฌ์กฐ์ฒด ๋ด๋ถ์ ํ๋๋ค๋ก ๊ตฌ์ฑ๋ ํ
ํ๋ฆฟ ๋ฌธ์์ด์
๋๋ค.
l.SetFormat("[{{.Status}} {{.Duration}}] - {{.Request.UserAgent}}")
์ ๊ตฌ์กฐ๋ ์ด์ ๊ฐ์ด ์ถ๋ ฅ๋ ๊ฒ์
๋๋ค - [200 18.263ยตs] - Go-User-Agent/1.1
์๋๋ ํ์ฌ(2018.10.10) Negroni์ ํธํ๋๋ ๋ฏธ๋ค์จ์ด๋ค์ ๋๋ค. ๋น์ ์ ๋ฏธ๋ค์จ์ด๊ฐ ๋งํฌ๋๊ธฐ๋ฅผ ์ํ๋ค๋ฉด ์์ ๋กญ๊ฒ PR์ ๋ณด๋ด์ฃผ์ธ์.
Middleware | Author | Description |
---|---|---|
authz | Yang Luo | ACL, RBAC, ABAC Authorization middlware based on Casbin |
binding | Matt Holt | Data binding from HTTP requests into structs |
cloudwatch | Colin Steele | AWS cloudwatch metrics middleware |
cors | Olivier Poitrey | Cross Origin Resource Sharing (CORS) support |
csp | Awake Networks | Content Security Policy (CSP) support |
delay | Jeff Martinez | Add delays/latency to endpoints. Useful when testing effects of high latency |
New Relic Go Agent | Yadvendar Champawat | Official New Relic Go Agent (currently in beta) |
gorelic | Jingwen Owen Ou | New Relic agent for Go runtime |
Graceful | Tyler Bunnell | Graceful HTTP Shutdown |
gzip | phyber | GZIP response compression |
JWT Middleware | Auth0 | Middleware checks for a JWT on the Authorization header on incoming requests and decodes it |
JWT Middleware | Marcelo Fuentes | JWT middleware for golang |
logrus | Dan Buch | Logrus-based logger |
oauth2 | David Bochenski | oAuth2 middleware |
onthefly | Alexander Rรธdseth | Generate TinySVG, HTML and CSS on the fly |
permissions2 | Alexander Rรธdseth | Cookies, users and permissions |
prometheus | Rene Zbinden | Easily create metrics endpoint for the prometheus instrumentation tool |
prometheus | Xabier Larrakoetxea | Prometheus metrics with multiple options that follow standards and try to be measured in a efficent way |
render | Cory Jacobsen | Render JSON, XML and HTML templates |
RestGate | Prasanga Siripala | Secure authentication for REST API endpoints |
secure | Cory Jacobsen | Middleware that implements a few quick security wins |
sessions | David Bochenski | Session Management |
stats | Florent Messa | Store information about your web application (response time, etc.) |
VanGoH | Taylor Wrobel | Configurable AWS-Style HMAC authentication middleware |
xrequestid | Andrea Franz | Middleware that assigns a random X-Request-Id header to each request |
mgo session | Joel James | Middleware that handles creating and closing mgo sessions per request |
digits | Bilal Amarni | Middleware that handles Twitter Digits authentication |
stats | Chirag Gupta | Middleware that manages qps and latency stats for your endpoints and asynchronously flushes them to influx db |
Chaos | Marc Falzon | Middleware for injecting chaotic behavior into application in a programmatic way |
Alexander Rรธdseth๋ Negroni ๋ฏธ๋ค์จ์ด ํธ๋ค๋ฌ๋ฅผ ์์ฑํ๊ธฐ ์ํ ๋ผ๋์ธ mooseware๋ฅผ ๋ง๋ค์์ต๋๋ค.
Prasanga Siripala๋ ์น ๊ธฐ๋ฐ์ Go/Negroni ํ๋ก์ ํธ๋ค์ ์ํ ํจ์จ์ ์ธ ๋ผ๋ ๊ตฌ์กฐ๋ฅผ ๋ง๋ค์์ต๋๋ค : Go-Skeleton
gin๊ณผ fresh ๋ชจ๋ negroni ์ฑ์ ์ค์๊ฐ ์๋ก๊ณ ์นจ(live reload)์ ์ง์ํฉ๋๋ค.
Negroni๋ Code Gangsta์ ์ํด ๋์์ธ ๋์์ต๋๋ค.