Fixes routing bug

This commit is contained in:
Manu Mtz-Almeida 2014-06-30 03:59:00 +02:00
parent d10e2b6c0d
commit 1565111274
2 changed files with 27 additions and 48 deletions

73
gin.go
View File

@ -26,22 +26,11 @@ type (
Meta interface{} `json:"meta"` Meta interface{} `json:"meta"`
} }
ResponseWriter interface {
http.ResponseWriter
Status() int
Written() bool
}
responseWriter struct {
http.ResponseWriter
status int
}
// Context is the most important part of gin. It allows us to pass variables between middleware, // Context is the most important part of gin. It allows us to pass variables between middleware,
// manage the flow, validate the JSON of a request and render a JSON response for example. // manage the flow, validate the JSON of a request and render a JSON response for example.
Context struct { Context struct {
Req *http.Request Req *http.Request
Writer ResponseWriter Writer http.ResponseWriter
Keys map[string]interface{} Keys map[string]interface{}
Errors []ErrorMsg Errors []ErrorMsg
Params httprouter.Params Params httprouter.Params
@ -68,28 +57,11 @@ type (
} }
) )
func (rw *responseWriter) WriteHeader(s int) {
rw.ResponseWriter.WriteHeader(s)
rw.status = s
}
func (rw *responseWriter) Write(b []byte) (int, error) {
return rw.ResponseWriter.Write(b)
}
func (rw *responseWriter) Status() int {
return rw.status
}
func (rw *responseWriter) Written() bool {
return rw.status != 0
}
// Returns a new blank Engine instance without any middleware attached. // Returns a new blank Engine instance without any middleware attached.
// The most basic configuration // The most basic configuration
func New() *Engine { func New() *Engine {
engine := &Engine{} engine := &Engine{}
engine.RouterGroup = &RouterGroup{nil, "/", nil, engine} engine.RouterGroup = &RouterGroup{nil, "", nil, engine}
engine.router = httprouter.New() engine.router = httprouter.New()
engine.router.NotFound = engine.handle404 engine.router.NotFound = engine.handle404
return engine return engine
@ -112,13 +84,15 @@ func (engine *Engine) NotFound404(handlers ...HandlerFunc) {
} }
func (engine *Engine) handle404(w http.ResponseWriter, req *http.Request) { func (engine *Engine) handle404(w http.ResponseWriter, req *http.Request) {
handlers := engine.combineHandlers(engine.handlers404)
handlers := engine.allHandlers(engine.handlers404)
c := engine.createContext(w, req, nil, handlers) c := engine.createContext(w, req, nil, handlers)
c.Next() if engine.handlers404 == nil {
if !c.Writer.Written() {
http.NotFound(c.Writer, c.Req) http.NotFound(c.Writer, c.Req)
} else {
c.Writer.WriteHeader(404)
} }
c.Next()
} }
// ServeFiles serves files from the given file system root. // ServeFiles serves files from the given file system root.
@ -150,7 +124,7 @@ func (engine *Engine) Run(addr string) {
func (group *RouterGroup) createContext(w http.ResponseWriter, req *http.Request, params httprouter.Params, handlers []HandlerFunc) *Context { func (group *RouterGroup) createContext(w http.ResponseWriter, req *http.Request, params httprouter.Params, handlers []HandlerFunc) *Context {
return &Context{ return &Context{
Writer: &responseWriter{w, 0}, Writer: w,
Req: req, Req: req,
index: -1, index: -1,
engine: group.engine, engine: group.engine,
@ -169,7 +143,7 @@ func (group *RouterGroup) Use(middlewares ...HandlerFunc) {
func (group *RouterGroup) Group(component string, handlers ...HandlerFunc) *RouterGroup { func (group *RouterGroup) Group(component string, handlers ...HandlerFunc) *RouterGroup {
prefix := path.Join(group.prefix, component) prefix := path.Join(group.prefix, component)
return &RouterGroup{ return &RouterGroup{
Handlers: handlers, Handlers: group.combineHandlers(handlers),
parent: group, parent: group,
prefix: prefix, prefix: prefix,
engine: group.engine, engine: group.engine,
@ -188,7 +162,7 @@ func (group *RouterGroup) Group(component string, handlers ...HandlerFunc) *Rout
// communication with a proxy). // communication with a proxy).
func (group *RouterGroup) Handle(method, p string, handlers []HandlerFunc) { func (group *RouterGroup) Handle(method, p string, handlers []HandlerFunc) {
p = path.Join(group.prefix, p) p = path.Join(group.prefix, p)
handlers = group.allHandlers(handlers) handlers = group.combineHandlers(handlers)
group.engine.router.Handle(method, p, func(w http.ResponseWriter, req *http.Request, params httprouter.Params) { group.engine.router.Handle(method, p, func(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
group.createContext(w, req, params, handlers).Next() group.createContext(w, req, params, handlers).Next()
}) })
@ -219,13 +193,12 @@ func (group *RouterGroup) PUT(path string, handlers ...HandlerFunc) {
group.Handle("PUT", path, handlers) group.Handle("PUT", path, handlers)
} }
func (group *RouterGroup) allHandlers(handlers []HandlerFunc) []HandlerFunc { func (group *RouterGroup) combineHandlers(handlers []HandlerFunc) []HandlerFunc {
local := append(group.Handlers, handlers...) s := len(group.Handlers) + len(handlers)
if group.parent != nil { h := make([]HandlerFunc, 0, s)
return group.parent.allHandlers(local) h = append(h, group.Handlers...)
} else { h = append(h, handlers...)
return local return h
}
} }
/************************************/ /************************************/
@ -327,7 +300,9 @@ func (c *Context) ParseBody(item interface{}) error {
// Serializes the given struct as a JSON into the response body in a fast and efficient way. // Serializes the given struct as a JSON into the response body in a fast and efficient way.
// It also sets the Content-Type as "application/json" // It also sets the Content-Type as "application/json"
func (c *Context) JSON(code int, obj interface{}) { func (c *Context) JSON(code int, obj interface{}) {
c.Writer.WriteHeader(code) if code >= 0 {
c.Writer.WriteHeader(code)
}
c.Writer.Header().Set("Content-Type", "application/json") c.Writer.Header().Set("Content-Type", "application/json")
encoder := json.NewEncoder(c.Writer) encoder := json.NewEncoder(c.Writer)
if err := encoder.Encode(obj); err != nil { if err := encoder.Encode(obj); err != nil {
@ -339,7 +314,9 @@ func (c *Context) JSON(code int, obj interface{}) {
// Serializes the given struct as a XML into the response body in a fast and efficient way. // Serializes the given struct as a XML into the response body in a fast and efficient way.
// It also sets the Content-Type as "application/xml" // It also sets the Content-Type as "application/xml"
func (c *Context) XML(code int, obj interface{}) { func (c *Context) XML(code int, obj interface{}) {
c.Writer.WriteHeader(code) if code >= 0 {
c.Writer.WriteHeader(code)
}
c.Writer.Header().Set("Content-Type", "application/xml") c.Writer.Header().Set("Content-Type", "application/xml")
encoder := xml.NewEncoder(c.Writer) encoder := xml.NewEncoder(c.Writer)
if err := encoder.Encode(obj); err != nil { if err := encoder.Encode(obj); err != nil {
@ -352,7 +329,9 @@ func (c *Context) XML(code int, obj interface{}) {
// It also update the HTTP code and sets the Content-Type as "text/html". // It also update the HTTP code and sets the Content-Type as "text/html".
// See http://golang.org/doc/articles/wiki/ // See http://golang.org/doc/articles/wiki/
func (c *Context) HTML(code int, name string, data interface{}) { func (c *Context) HTML(code int, name string, data interface{}) {
c.Writer.WriteHeader(code) if code >= 0 {
c.Writer.WriteHeader(code)
}
c.Writer.Header().Set("Content-Type", "text/html") c.Writer.Header().Set("Content-Type", "text/html")
if err := c.engine.HTMLTemplates.ExecuteTemplate(c.Writer, name, data); err != nil { if err := c.engine.HTMLTemplates.ExecuteTemplate(c.Writer, name, data); err != nil {
c.Error(err, map[string]interface{}{ c.Error(err, map[string]interface{}{

View File

@ -15,6 +15,6 @@ func Logger() HandlerFunc {
c.Next() c.Next()
// Calculate resolution time // Calculate resolution time
log.Printf("[%d] %s in %v", c.Writer.Status(), c.Req.RequestURI, time.Since(t)) log.Printf("%s in %v", c.Req.RequestURI, time.Since(t))
} }
} }