mirror of
https://github.com/gin-gonic/gin.git
synced 2025-10-15 04:57:07 +08:00
Merge 5417a27f9e76394ff3768148814a8b862e13703c into a5ae88722b552adf00b87c7ae5f2fb84d56ebe6f
This commit is contained in:
commit
cb872a054c
22
AUTHORS.md
22
AUTHORS.md
@ -5,7 +5,7 @@ List of all the awesome people working to make Gin the best Web Framework in Go!
|
||||
##gin 0.x series authors
|
||||
|
||||
**Lead Developer:** Manu Martinez-Almeida (@manucorporat)
|
||||
**Stuff:**
|
||||
**Staff:**
|
||||
Javier Provecho (@javierprovecho)
|
||||
|
||||
People and companies, who have contributed, in alphabetical order.
|
||||
@ -22,10 +22,13 @@ People and companies, who have contributed, in alphabetical order.
|
||||
- Using template.Must to fix multiple return issue
|
||||
- ★ Added support for OPTIONS verb
|
||||
- ★ Setting response headers before calling WriteHeader
|
||||
- Improved documentation for model binding
|
||||
- ★ Added Content.Redirect()
|
||||
- ★ Added tons of Unit tests
|
||||
|
||||
|
||||
**@austinheap (Austin Heap)**
|
||||
- Adds travis CI integration
|
||||
- Added travis CI integration
|
||||
|
||||
|
||||
**@bluele (Jun Kimura)**
|
||||
@ -67,20 +70,23 @@ People and companies, who have contributed, in alphabetical order.
|
||||
**@mdigger (Dmitry Sedykh)**
|
||||
- Fixes Form binding when content-type is x-www-form-urlencoded
|
||||
- No repeat call c.Writer.Status() in gin.Logger
|
||||
- Fixed Content-Type for json render
|
||||
- Fixes Content-Type for json render
|
||||
|
||||
|
||||
**@mopemope (Yutaka Matsubara)**
|
||||
- ★ Adds Godep support (Dependencies Manager)
|
||||
- Fix variadic parameter in the flexible render API
|
||||
- Fix Corrupted plain render
|
||||
- Fix variadic parameter in new flexible render API
|
||||
|
||||
|
||||
|
||||
**@msemenistyi (Mykyta Semenistyi)**
|
||||
- update Readme.md. Add code to String method
|
||||
|
||||
|
||||
**@msoedov (Sasha Myasoedov)**
|
||||
- ★ Adds tons of unit tests.
|
||||
|
||||
|
||||
**@ngerakines (Nick Gerakines)**
|
||||
- ★ Improves API, c.GET() doesn't panic
|
||||
- Adds MustGet() method
|
||||
@ -95,4 +101,8 @@ People and companies, who have contributed, in alphabetical order.
|
||||
|
||||
|
||||
**@SkuliOskarsson (Skuli Oskarsson)**
|
||||
- Fixes some texts in README II
|
||||
- Fixes some texts in README II
|
||||
|
||||
|
||||
**@yuyabee**
|
||||
- Fixed README
|
10
CHANGELOG.md
10
CHANGELOG.md
@ -1,6 +1,12 @@
|
||||
##Changelog
|
||||
#Changelog
|
||||
|
||||
###Gin 0.4 (??)
|
||||
###Gin 0.4 (Aug 21, 2014)
|
||||
|
||||
- [NEW] Development mode
|
||||
- [NEW] Unit tests
|
||||
- [NEW] Add Content.Redirect()
|
||||
- [FIX] Deferring WriteHeader()
|
||||
- [FIX] Improved documentation for model binding
|
||||
|
||||
|
||||
###Gin 0.3 (Jul 18, 2014)
|
||||
|
13
README.md
13
README.md
@ -1,6 +1,6 @@
|
||||
#Gin Web Framework
|
||||
|
||||
[](https://godoc.org/github.com/gin-gonic/gin)
|
||||
[](https://godoc.org/github.com/gin-gonic/gin)
|
||||
[](https://travis-ci.org/gin-gonic/gin)
|
||||
|
||||
Gin is a web framework written in Golang. It features a martini-like API with much better performance, up to 40 times faster. If you need performance and good productivity, you will love Gin.
|
||||
@ -324,12 +324,13 @@ func main() {
|
||||
|
||||
Issuing a HTTP redirect is easy:
|
||||
|
||||
```r.GET("/test", func(c *gin.Context) {
|
||||
```go
|
||||
r.GET("/test", func(c *gin.Context) {
|
||||
c.Redirect(301, "http://www.google.com/")
|
||||
})
|
||||
|
||||
Both internal and external locations are supported.
|
||||
```
|
||||
Both internal and external locations are supported.
|
||||
|
||||
|
||||
#### Custom Middlewares
|
||||
|
||||
@ -425,7 +426,7 @@ func main() {
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
// note than you are using the copied context "c_cp", IMPORTANT
|
||||
log.Println("Done! in path " + c_cp.Req.URL.Path)
|
||||
log.Println("Done! in path " + c_cp.Request.URL.Path)
|
||||
}()
|
||||
})
|
||||
|
||||
@ -435,7 +436,7 @@ func main() {
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
// since we are NOT using a goroutine, we do not have to copy the context
|
||||
log.Println("Done! in path " + c.Req.URL.Path)
|
||||
log.Println("Done! in path " + c.Request.URL.Path)
|
||||
})
|
||||
|
||||
// Listen and server on 0.0.0.0:8080
|
||||
|
@ -65,6 +65,15 @@ type Context struct {
|
||||
index int8
|
||||
}
|
||||
|
||||
/************************************/
|
||||
/*********** TEST HELPERS ***********/
|
||||
/************************************/
|
||||
|
||||
func CreateContext(w http.ResponseWriter, req *http.Request) *Context {
|
||||
engine := New()
|
||||
return engine.createContext(w, req, nil, nil)
|
||||
}
|
||||
|
||||
/************************************/
|
||||
/********** ROUTES GROUPING *********/
|
||||
/************************************/
|
||||
|
@ -436,3 +436,23 @@ func TestBindingJSONMalformed(t *testing.T) {
|
||||
t.Errorf("Content-Type should not be application/json, was %s", w.HeaderMap.Get("Content-Type"))
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateContext(t *testing.T) {
|
||||
req, _ := http.NewRequest("GET", "/", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
c := CreateContext(w, req)
|
||||
c.JSON(200, H{"foo": "bar"})
|
||||
|
||||
if w.Code != 200 {
|
||||
t.Errorf("Response code should be Ok, was: %s", w.Code)
|
||||
}
|
||||
|
||||
if w.HeaderMap.Get("Content-Type") != "application/json" {
|
||||
t.Errorf("Content-Type should be application/json, was %s", w.HeaderMap.Get("Content-Type"))
|
||||
}
|
||||
|
||||
if w.Body.String() != "{\"foo\":\"bar\"}\n" {
|
||||
t.Errorf("Response should be {\"foo\":\"bar\"}, was: %s", w.Body.String())
|
||||
}
|
||||
}
|
||||
|
28
gin.go
28
gin.go
@ -1,6 +1,7 @@
|
||||
package gin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin/render"
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"html/template"
|
||||
@ -73,13 +74,21 @@ func Default() *Engine {
|
||||
}
|
||||
|
||||
func (engine *Engine) LoadHTMLGlob(pattern string) {
|
||||
templ := template.Must(template.ParseGlob(pattern))
|
||||
engine.SetHTMLTemplate(templ)
|
||||
if gin_mode == debugCode {
|
||||
engine.HTMLRender = render.HTMLDebug
|
||||
} else {
|
||||
templ := template.Must(template.ParseGlob(pattern))
|
||||
engine.SetHTMLTemplate(templ)
|
||||
}
|
||||
}
|
||||
|
||||
func (engine *Engine) LoadHTMLFiles(files ...string) {
|
||||
templ := template.Must(template.ParseFiles(files...))
|
||||
engine.SetHTMLTemplate(templ)
|
||||
if gin_mode == debugCode {
|
||||
engine.HTMLRender = render.HTMLDebug
|
||||
} else {
|
||||
templ := template.Must(template.ParseFiles(files...))
|
||||
engine.SetHTMLTemplate(templ)
|
||||
}
|
||||
}
|
||||
|
||||
func (engine *Engine) SetHTMLTemplate(templ *template.Template) {
|
||||
@ -105,12 +114,18 @@ func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
}
|
||||
|
||||
func (engine *Engine) Run(addr string) {
|
||||
if gin_mode == debugCode {
|
||||
fmt.Println("[GIN-debug] Listening and serving HTTP on " + addr)
|
||||
}
|
||||
if err := http.ListenAndServe(addr, engine); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (engine *Engine) RunTLS(addr string, cert string, key string) {
|
||||
if gin_mode == debugCode {
|
||||
fmt.Println("[GIN-debug] Listening and serving HTTPS on " + addr)
|
||||
}
|
||||
if err := http.ListenAndServeTLS(addr, cert, key, engine); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -160,6 +175,11 @@ func (group *RouterGroup) pathFor(p string) string {
|
||||
func (group *RouterGroup) Handle(method, p string, handlers []HandlerFunc) {
|
||||
p = group.pathFor(p)
|
||||
handlers = group.combineHandlers(handlers)
|
||||
if gin_mode == debugCode {
|
||||
nuHandlers := len(handlers)
|
||||
name := funcName(handlers[nuHandlers-1])
|
||||
fmt.Printf("[GIN-debug] %-5s %-25s --> %s (%d handlers)\n", method, p, name, nuHandlers)
|
||||
}
|
||||
group.engine.router.Handle(method, p, func(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
|
||||
c := group.engine.createContext(w, req, params, handlers)
|
||||
c.Next()
|
||||
|
@ -10,6 +10,10 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func init() {
|
||||
SetMode(TestMode)
|
||||
}
|
||||
|
||||
func PerformRequest(r http.Handler, method, path string) *httptest.ResponseRecorder {
|
||||
req, _ := http.NewRequest(method, path, nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
42
mode.go
Normal file
42
mode.go
Normal file
@ -0,0 +1,42 @@
|
||||
package gin
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
const GIN_MODE = "GIN_MODE"
|
||||
|
||||
const (
|
||||
DebugMode string = "debug"
|
||||
ReleaseMode string = "release"
|
||||
TestMode string = "test"
|
||||
)
|
||||
const (
|
||||
debugCode = iota
|
||||
releaseCode = iota
|
||||
testCode = iota
|
||||
)
|
||||
|
||||
var gin_mode int = debugCode
|
||||
|
||||
func SetMode(value string) {
|
||||
switch value {
|
||||
case DebugMode:
|
||||
gin_mode = debugCode
|
||||
case ReleaseMode:
|
||||
gin_mode = releaseCode
|
||||
case TestMode:
|
||||
gin_mode = testCode
|
||||
default:
|
||||
panic("gin mode unknown, the allowed modes are: " + DebugMode + " and " + ReleaseMode)
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
value := os.Getenv(GIN_MODE)
|
||||
if len(value) == 0 {
|
||||
SetMode(DebugMode)
|
||||
} else {
|
||||
SetMode(value)
|
||||
}
|
||||
}
|
@ -25,6 +25,9 @@ type (
|
||||
// Redirects
|
||||
redirectRender struct{}
|
||||
|
||||
// Redirects
|
||||
htmlDebugRender struct{}
|
||||
|
||||
// form binding
|
||||
HTMLRender struct {
|
||||
Template *template.Template
|
||||
@ -32,10 +35,11 @@ type (
|
||||
)
|
||||
|
||||
var (
|
||||
JSON = jsonRender{}
|
||||
XML = xmlRender{}
|
||||
Plain = plainRender{}
|
||||
Redirect = redirectRender{}
|
||||
JSON = jsonRender{}
|
||||
XML = xmlRender{}
|
||||
Plain = plainRender{}
|
||||
Redirect = redirectRender{}
|
||||
HTMLDebug = htmlDebugRender{}
|
||||
)
|
||||
|
||||
func writeHeader(w http.ResponseWriter, code int, contentType string) {
|
||||
@ -61,13 +65,6 @@ func (_ xmlRender) Render(w http.ResponseWriter, code int, data ...interface{})
|
||||
return encoder.Encode(data[0])
|
||||
}
|
||||
|
||||
func (html HTMLRender) Render(w http.ResponseWriter, code int, data ...interface{}) error {
|
||||
writeHeader(w, code, "text/html")
|
||||
file := data[0].(string)
|
||||
obj := data[1]
|
||||
return html.Template.ExecuteTemplate(w, file, obj)
|
||||
}
|
||||
|
||||
func (_ plainRender) Render(w http.ResponseWriter, code int, data ...interface{}) error {
|
||||
writeHeader(w, code, "text/plain")
|
||||
format := data[0].(string)
|
||||
@ -80,3 +77,21 @@ func (_ plainRender) Render(w http.ResponseWriter, code int, data ...interface{}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (_ htmlDebugRender) Render(w http.ResponseWriter, code int, data ...interface{}) error {
|
||||
writeHeader(w, code, "text/html")
|
||||
file := data[0].(string)
|
||||
obj := data[1]
|
||||
t, err := template.ParseFiles(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return t.ExecuteTemplate(w, file, obj)
|
||||
}
|
||||
|
||||
func (html HTMLRender) Render(w http.ResponseWriter, code int, data ...interface{}) error {
|
||||
writeHeader(w, code, "text/html")
|
||||
file := data[0].(string)
|
||||
obj := data[1]
|
||||
return html.Template.ExecuteTemplate(w, file, obj)
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ func (w *responseWriter) reset(writer http.ResponseWriter) {
|
||||
}
|
||||
|
||||
func (w *responseWriter) WriteHeader(code int) {
|
||||
if code != 0 {
|
||||
if code > 0 {
|
||||
w.status = code
|
||||
if w.written {
|
||||
log.Println("[GIN] WARNING. Headers were already written!")
|
||||
|
6
utils.go
6
utils.go
@ -2,6 +2,8 @@ package gin
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"reflect"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
type H map[string]interface{}
|
||||
@ -38,3 +40,7 @@ func filterFlags(content string) string {
|
||||
}
|
||||
return content
|
||||
}
|
||||
|
||||
func funcName(f interface{}) string {
|
||||
return runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name()
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user