From e899d8a99eac4cfeadbeef09017b304d2b918b85 Mon Sep 17 00:00:00 2001 From: Manu Mtz-Almeida Date: Thu, 28 May 2015 03:22:34 +0200 Subject: [PATCH] Code cleanup + documentation --- context.go | 77 ++++++++++++++++++++++++------------------------------ gin.go | 11 ++------ tree.go | 27 +++++++++++++++++++ 3 files changed, 63 insertions(+), 52 deletions(-) diff --git a/context.go b/context.go index 381bbce2..07a90179 100644 --- a/context.go +++ b/context.go @@ -30,33 +30,6 @@ const ( const AbortIndex = math.MaxInt8 / 2 -// Param is a single URL parameter, consisting of a key and a value. -type Param struct { - Key string - Value string -} - -// Params is a Param-slice, as returned by the router. -// The slice is ordered, the first URL parameter is also the first slice value. -// It is therefore safe to read values by the index. -type Params []Param - -// ByName returns the value of the first Param which key matches the given name. -// If no matching Param is found, an empty string is returned. -func (ps Params) Get(name string) (string, bool) { - for _, entry := range ps { - if entry.Key == name { - return entry.Value, true - } - } - return "", false -} - -func (ps Params) ByName(name string) (va string) { - va, _ = ps.Get(name) - return -} - // 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. type Context struct { @@ -100,7 +73,7 @@ func (c *Context) Copy() *Context { } /************************************/ -/*************** FLOW ***************/ +/*********** FLOW CONTROL ***********/ /************************************/ // Next should be used only in the middlewares. @@ -114,27 +87,34 @@ func (c *Context) Next() { } } -// Forces the system to not continue calling the pending handlers in the chain. +// Returns if the currect context was aborted. +func (c *Context) IsAborted() bool { + return c.index == AbortIndex +} + +// Stops the system to continue calling the pending handlers in the chain. +// Let's say you have an authorization middleware that validates if the request is authorized +// if the authorization fails (the password does not match). This method (Abort()) should be called +// in order to stop the execution of the actual handler. func (c *Context) Abort() { c.index = AbortIndex } -// AbortWithStatus is the same as Abort but also writes the specified response status code. -// For example, the first handler checks if the request is authorized. If it's not, context.AbortWithStatus(401) should be called. +// It calls Abort() and writes the headers with the specified status code. +// For example, a failed attempt to authentificate a request could use: context.AbortWithStatus(401). func (c *Context) AbortWithStatus(code int) { c.Writer.WriteHeader(code) c.Abort() } +// It calls AbortWithStatus() and Error() internally. This method stops the chain, writes the status code and +// pushes the specified error to `c.Errors`. +// See Context.Error() for more details. func (c *Context) AbortWithError(code int, err error) *Error { c.AbortWithStatus(code) return c.Error(err) } -func (c *Context) IsAborted() bool { - return c.index == AbortIndex -} - /************************************/ /********* ERROR MANAGEMENT *********/ /************************************/ @@ -161,8 +141,8 @@ func (c *Context) Error(err error) *Error { /******** METADATA MANAGEMENT********/ /************************************/ -// Sets a new pair key/value just for the specified context. -// It also lazy initializes the hashmap. +// Sets a new pair key/value just for this context. +// It also lazy initializes the hashmap if it was not used previously. func (c *Context) Set(key string, value interface{}) { if c.Keys == nil { c.Keys = make(map[string]interface{}) @@ -170,7 +150,8 @@ func (c *Context) Set(key string, value interface{}) { c.Keys[key] = value } -// Get returns the value for the given key or an error if the key does not exist. +// Returns the value for the given key, ie: (value, true). +// If the value does not exists it returns (nil, false) func (c *Context) Get(key string) (value interface{}, exists bool) { if c.Keys != nil { value, exists = c.Keys[key] @@ -178,7 +159,7 @@ func (c *Context) Get(key string) (value interface{}, exists bool) { return } -// MustGet returns the value for the given key or panics if the value doesn't exist. +// Returns the value for the given key if it exists, otherwise it panics. func (c *Context) MustGet(key string) interface{} { if value, exists := c.Get(key); exists { return value @@ -254,6 +235,7 @@ func (c *Context) Bind(obj interface{}) error { return c.BindWith(obj, b) } +// Shortcut for c.Bind(obj, binding.JSON) func (c *Context) BindJSON(obj interface{}) error { return c.BindWith(obj, binding.JSON) } @@ -266,6 +248,8 @@ func (c *Context) BindWith(obj interface{}, b binding.Binding) error { return nil } +// Best effort algoritm to return the real client IP, it parses +// X-Real-IP and X-Forwarded-For in order to work properly with reverse-proxies such us: nginx or haproxy. func (c *Context) ClientIP() string { clientIP := strings.TrimSpace(c.Request.Header.Get("X-Real-IP")) if len(clientIP) > 0 { @@ -287,6 +271,9 @@ func (c *Context) ContentType() string { /******** RESPONSE RENDERING ********/ /************************************/ +// Intelligent shortcut for c.Writer.Header().Set(key, value) +// it writes a header in the response. +// If value == "", this method removes the header `c.Writer.Header().Del(key)` func (c *Context) Header(key, value string) { if len(value) == 0 { c.Writer.Header().Del(key) @@ -311,23 +298,27 @@ func (c *Context) HTML(code int, name string, obj interface{}) { c.Render(code, instance) } +// Serializes the given struct as pretty JSON (indented + endlines) into the response body. +// It also sets the Content-Type as "application/json". +// WARNING: we recommend to use this only for development propuses since printing pretty JSON is +// more CPU and bandwidth consuming. Use Context.JSON() instead. func (c *Context) IndentedJSON(code int, obj interface{}) { c.Render(code, render.IndentedJSON{Data: obj}) } -// Serializes the given struct as JSON into the response body in a fast and efficient way. +// Serializes the given struct as JSON into the response body. // It also sets the Content-Type as "application/json". func (c *Context) JSON(code int, obj interface{}) { c.Render(code, render.JSON{Data: obj}) } -// Serializes the given struct as XML into the response body in a fast and efficient way. +// Serializes the given struct as XML into the response body. // It also sets the Content-Type as "application/xml". func (c *Context) XML(code int, obj interface{}) { c.Render(code, render.XML{Data: obj}) } -// Writes the given string into the response body and sets the Content-Type to "text/plain". +// Writes the given string into the response body. func (c *Context) String(code int, format string, values ...interface{}) { c.Render(code, render.String{ Format: format, @@ -352,7 +343,7 @@ func (c *Context) Data(code int, contentType string, data []byte) { }) } -// Writes the specified file into the body stream +// Writes the specified file into the body stream in a efficient way. func (c *Context) File(filepath string) { http.ServeFile(c.Writer, c.Request, filepath) } diff --git a/gin.go b/gin.go index a47372a2..2e9c2e39 100644 --- a/gin.go +++ b/gin.go @@ -195,20 +195,13 @@ func (engine *Engine) RunUnix(file string) (err error) { // ServeHTTP makes the router implement the http.Handler interface. func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) { - c := engine.getcontext(w, req) - engine.handleHTTPRequest(c) - engine.putcontext(c) -} - -func (engine *Engine) getcontext(w http.ResponseWriter, req *http.Request) *Context { c := engine.pool.Get().(*Context) c.writermem.reset(w) c.Request = req c.reset() - return c -} -func (engine *Engine) putcontext(c *Context) { + engine.handleHTTPRequest(c) + engine.pool.Put(c) } diff --git a/tree.go b/tree.go index 169e5f10..db037776 100644 --- a/tree.go +++ b/tree.go @@ -9,6 +9,33 @@ import ( "unicode" ) +// Param is a single URL parameter, consisting of a key and a value. +type Param struct { + Key string + Value string +} + +// Params is a Param-slice, as returned by the router. +// The slice is ordered, the first URL parameter is also the first slice value. +// It is therefore safe to read values by the index. +type Params []Param + +// ByName returns the value of the first Param which key matches the given name. +// If no matching Param is found, an empty string is returned. +func (ps Params) Get(name string) (string, bool) { + for _, entry := range ps { + if entry.Key == name { + return entry.Value, true + } + } + return "", false +} + +func (ps Params) ByName(name string) (va string) { + va, _ = ps.Get(name) + return +} + func min(a, b int) int { if a <= b { return a