diff --git a/README.md b/README.md index 9dde693d..dfc98fb2 100644 --- a/README.md +++ b/README.md @@ -1228,13 +1228,17 @@ func main() { ### HTML rendering -Using LoadHTMLGlob() or LoadHTMLFiles() +Using LoadHTMLGlob(), LoadHTMLFiles() or LoadHTMLFilesRecursively ```go func main() { router := gin.Default() router.LoadHTMLGlob("templates/*") //router.LoadHTMLFiles("templates/template1.html", "templates/template2.html") + + // load recursively all .html files in "public" folder + //router.LoadHTMLFilesRecursively("public", []string{".html"}) + router.GET("/index", func(c *gin.Context) { c.HTML(http.StatusOK, "index.tmpl", gin.H{ "title": "Main website", diff --git a/gin.go b/gin.go index 1c2acbc8..32d0d149 100644 --- a/gin.go +++ b/gin.go @@ -11,6 +11,9 @@ import ( "net/http" "os" "path" + "path/filepath" + "sort" + "strings" "sync" "github.com/gin-gonic/gin/internal/bytesconv" @@ -205,6 +208,26 @@ func (engine *Engine) LoadHTMLFiles(files ...string) { engine.SetHTMLTemplate(templ) } +// LoadHTMLFilesRecursively loads recursively a slice of HTML files mathing an extension +// and associates the result with HTML renderer. +func (engine *Engine) LoadHTMLFilesRecursively(folder string, extensionsAllowed []string) { + + files := []string{} + sort.Strings(extensionsAllowed) + + filepath.Walk(folder, func(path string, info os.FileInfo, err error) error { + extension := strings.ToLower(filepath.Ext(path)) + indexFound := sort.SearchStrings(extensionsAllowed, extension) + if indexFound < len(extensionsAllowed) && extensionsAllowed[indexFound] == extension { + files = append(files, path) + } + + return nil + }) + + engine.LoadHTMLFiles(files...) +} + // SetHTMLTemplate associate a template with HTML renderer. func (engine *Engine) SetHTMLTemplate(templ *template.Template) { if len(engine.trees) > 0 { diff --git a/gin_test.go b/gin_test.go index 11bdd79c..56da0e4e 100644 --- a/gin_test.go +++ b/gin_test.go @@ -58,77 +58,134 @@ func setupHTMLFiles(t *testing.T, mode string, tls bool, loadMethod func(*Engine return ts } -func TestLoadHTMLGlobDebugMode(t *testing.T) { - ts := setupHTMLFiles( - t, - DebugMode, - false, - func(router *Engine) { - router.LoadHTMLGlob("./testdata/template/*") - }, - ) - defer ts.Close() - - res, err := http.Get(fmt.Sprintf("%s/test", ts.URL)) - if err != nil { - fmt.Println(err) - } - - resp, _ := ioutil.ReadAll(res.Body) - assert.Equal(t, "

Hello world

", string(resp)) +func setupHTMLFiles(t *testing.T, mode string, tls bool) func() { + go func() { + SetMode(mode) + router := New() + router.Delims("{[{", "}]}") + router.SetFuncMap(template.FuncMap{ + "formatAsDate": formatAsDate, + }) + router.LoadHTMLFiles("./fixtures/basic/hello.tmpl", "./fixtures/basic/raw.tmpl") + router.GET("/test", func(c *Context) { + c.HTML(http.StatusOK, "hello.tmpl", map[string]string{"name": "world"}) + }) + router.GET("/raw", func(c *Context) { + c.HTML(http.StatusOK, "raw.tmpl", map[string]interface{}{ + "now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC), + }) + }) + if tls { + // these files generated by `go run $GOROOT/src/crypto/tls/generate_cert.go --host 127.0.0.1` + router.RunTLS(":9999", "./fixtures/testdata/cert.pem", "./fixtures/testdata/key.pem") + } else { + router.Run(":8888") + } + }() + t.Log("waiting 1 second for server startup") + time.Sleep(1 * time.Second) + return func() {} } -func TestLoadHTMLGlobTestMode(t *testing.T) { - ts := setupHTMLFiles( - t, - TestMode, - false, - func(router *Engine) { - router.LoadHTMLGlob("./testdata/template/*") - }, - ) - defer ts.Close() - - res, err := http.Get(fmt.Sprintf("%s/test", ts.URL)) - if err != nil { - fmt.Println(err) - } - - resp, _ := ioutil.ReadAll(res.Body) - assert.Equal(t, "

Hello world

", string(resp)) +func setupHTMLGlob(t *testing.T, mode string, tls bool) func() { + go func() { + SetMode(mode) + router := New() + router.Delims("{[{", "}]}") + router.SetFuncMap(template.FuncMap{ + "formatAsDate": formatAsDate, + }) + router.LoadHTMLGlob("./fixtures/basic/*") + router.GET("/test", func(c *Context) { + c.HTML(http.StatusOK, "hello.tmpl", map[string]string{"name": "world"}) + }) + router.GET("/raw", func(c *Context) { + c.HTML(http.StatusOK, "raw.tmpl", map[string]interface{}{ + "now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC), + }) + }) + if tls { + // these files generated by `go run $GOROOT/src/crypto/tls/generate_cert.go --host 127.0.0.1` + router.RunTLS(":9999", "./fixtures/testdata/cert.pem", "./fixtures/testdata/key.pem") + } else { + router.Run(":8888") + } + }() + t.Log("waiting 1 second for server startup") + time.Sleep(1 * time.Second) + return func() {} } -func TestLoadHTMLGlobReleaseMode(t *testing.T) { - ts := setupHTMLFiles( - t, - ReleaseMode, - false, - func(router *Engine) { - router.LoadHTMLGlob("./testdata/template/*") - }, - ) - defer ts.Close() +func setupHTMLFilesRecursively(t *testing.T, mode string, tls bool) func() { + go func() { + SetMode(mode) + router := New() + router.Delims("{[{", "}]}") + router.SetFuncMap(template.FuncMap{ + "formatAsDate": formatAsDate, + }) + router.LoadHTMLFilesRecursively("./fixtures/basic/", []string{".tmpl"}) + router.GET("/test", func(c *Context) { + c.HTML(http.StatusOK, "hello.tmpl", map[string]string{"name": "world"}) + }) + router.GET("/raw", func(c *Context) { + c.HTML(http.StatusOK, "raw.tmpl", map[string]interface{}{ + "now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC), + }) + }) + if tls { + // these files generated by `go run $GOROOT/src/crypto/tls/generate_cert.go --host 127.0.0.1` + router.RunTLS(":9999", "./fixtures/testdata/cert.pem", "./fixtures/testdata/key.pem") + } else { + router.Run(":8888") + } + }() + t.Log("waiting 1 second for server startup") + time.Sleep(1 * time.Second) + return func() {} +} - res, err := http.Get(fmt.Sprintf("%s/test", ts.URL)) +func TestLoadHTMLGlob(t *testing.T) { + td := setupHTMLGlob(t, DebugMode, false) + res, err := http.Get("http://127.0.0.1:8888/test") if err != nil { fmt.Println(err) } resp, _ := ioutil.ReadAll(res.Body) - assert.Equal(t, "

Hello world

", string(resp)) + assert.Equal(t, "

Hello world

", string(resp[:])) + + td() +} + +func TestLoadHTMLGlob2(t *testing.T) { + td := setupHTMLGlob(t, TestMode, false) + res, err := http.Get("http://127.0.0.1:8888/test") + if err != nil { + fmt.Println(err) + } + + resp, _ := ioutil.ReadAll(res.Body) + assert.Equal(t, "

Hello world

", string(resp[:])) + + td() +} + +func TestLoadHTMLGlob3(t *testing.T) { + td := setupHTMLGlob(t, ReleaseMode, false) + res, err := http.Get("http://127.0.0.1:8888/test") + if err != nil { + fmt.Println(err) + } + + resp, _ := ioutil.ReadAll(res.Body) + assert.Equal(t, "

Hello world

", string(resp[:])) + + td() } func TestLoadHTMLGlobUsingTLS(t *testing.T) { - ts := setupHTMLFiles( - t, - DebugMode, - true, - func(router *Engine) { - router.LoadHTMLGlob("./testdata/template/*") - }, - ) - defer ts.Close() - + td := setupHTMLGlob(t, DebugMode, true) // Use InsecureSkipVerify for avoiding `x509: certificate signed by unknown authority` error tr := &http.Transport{ TLSClientConfig: &tls.Config{ @@ -136,33 +193,29 @@ func TestLoadHTMLGlobUsingTLS(t *testing.T) { }, } client := &http.Client{Transport: tr} - res, err := client.Get(fmt.Sprintf("%s/test", ts.URL)) + res, err := client.Get("https://127.0.0.1:9999/test") if err != nil { fmt.Println(err) } resp, _ := ioutil.ReadAll(res.Body) - assert.Equal(t, "

Hello world

", string(resp)) + assert.Equal(t, "

Hello world

", string(resp[:])) + + td() } func TestLoadHTMLGlobFromFuncMap(t *testing.T) { - ts := setupHTMLFiles( - t, - DebugMode, - false, - func(router *Engine) { - router.LoadHTMLGlob("./testdata/template/*") - }, - ) - defer ts.Close() - - res, err := http.Get(fmt.Sprintf("%s/raw", ts.URL)) + time.Now() + td := setupHTMLGlob(t, DebugMode, false) + res, err := http.Get("http://127.0.0.1:8888/raw") if err != nil { fmt.Println(err) } resp, _ := ioutil.ReadAll(res.Body) - assert.Equal(t, "Date: 2017/07/01\n", string(resp)) + assert.Equal(t, "Date: 2017/07/01\n", string(resp[:])) + + td() } func init() { @@ -283,6 +336,75 @@ func TestLoadHTMLFilesFuncMap(t *testing.T) { assert.Equal(t, "Date: 2017/07/01\n", string(resp)) } +func TestLoadHTMLFilesRecursively(t *testing.T) { + td := setupHTMLFilesRecursively(t, TestMode, false) + res, err := http.Get("http://127.0.0.1:8888/test") + if err != nil { + fmt.Println(err) + } + + resp, _ := ioutil.ReadAll(res.Body) + assert.Equal(t, "

Hello world

", string(resp[:])) + td() +} + +func TestLoadHTMLFilesRecursively2(t *testing.T) { + td := setupHTMLFilesRecursively(t, DebugMode, false) + res, err := http.Get("http://127.0.0.1:8888/test") + if err != nil { + fmt.Println(err) + } + + resp, _ := ioutil.ReadAll(res.Body) + assert.Equal(t, "

Hello world

", string(resp[:])) + td() +} + +func TestLoadHTMLFilesRecursively3(t *testing.T) { + td := setupHTMLFilesRecursively(t, ReleaseMode, false) + res, err := http.Get("http://127.0.0.1:8888/test") + if err != nil { + fmt.Println(err) + } + + resp, _ := ioutil.ReadAll(res.Body) + assert.Equal(t, "

Hello world

", string(resp[:])) + td() +} + +func TestLoadHTMLFilesRecursivelyUsingTLS(t *testing.T) { + td := setupHTMLFilesRecursively(t, TestMode, true) + // Use InsecureSkipVerify for avoiding `x509: certificate signed by unknown authority` error + tr := &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, + }, + } + client := &http.Client{Transport: tr} + res, err := client.Get("https://127.0.0.1:9999/test") + if err != nil { + fmt.Println(err) + } + + resp, _ := ioutil.ReadAll(res.Body) + assert.Equal(t, "

Hello world

", string(resp[:])) + td() +} + +func TestLoadHTMLFilesRecursivelyFuncMap(t *testing.T) { + time.Now() + td := setupHTMLFilesRecursively(t, TestMode, false) + res, err := http.Get("http://127.0.0.1:8888/raw") + if err != nil { + fmt.Println(err) + } + + resp, _ := ioutil.ReadAll(res.Body) + assert.Equal(t, "Date: 2017/07/01\n", string(resp[:])) + + td() +} + func TestAddRoute(t *testing.T) { router := New() router.addRoute("GET", "/", HandlersChain{func(_ *Context) {}})