From 2da7aad7149945f42470a152cfa6e264f9b43279 Mon Sep 17 00:00:00 2001 From: kk0829 <244098979@qq.com> Date: Sun, 14 May 2023 10:54:39 +0800 Subject: [PATCH] feat: support template engine and static file hosting --- CHANGELOG.md | 9 +++++ consts.go | 1 + context.go | 12 +++++++ examples/static/app.go | 35 +++++++++++++++++++ examples/static/public/css/1.css | 3 ++ examples/static/public/js/1.js | 1 + examples/static/templates/index.tmpl | 16 +++++++++ lightning.go | 50 ++++++++++++++++++++++++++-- 8 files changed, 124 insertions(+), 3 deletions(-) create mode 100644 examples/static/app.go create mode 100644 examples/static/public/css/1.css create mode 100644 examples/static/public/js/1.js create mode 100644 examples/static/templates/index.tmpl diff --git a/CHANGELOG.md b/CHANGELOG.md index 860d41b..9c22bc3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## [0.5.0] - May 14, 2023 + +### Added + +- `ctx.HTML(code int, name string, data interface{})` writes an HTML response with the given status code, template name, and data. +- `ctx.Static(root string, prefix string)` serves static files from the given root directory with the given prefix. +- `app.LoadHTMLGlob(pattern string)` loads HTML templates from a glob pattern and sets them in the Application struct. +- `app.SetFuncMap(funcMap template.FuncMap)` sets the funcMap in the Application struct to the funcMap passed in as an argument. + ## [0.4.1] - May 13, 2023 ### Changed diff --git a/consts.go b/consts.go index bd00b76..3973647 100644 --- a/consts.go +++ b/consts.go @@ -3,6 +3,7 @@ package lightning // MIME types const ( MIMETextPlain = "text/plain" + MIMETextHTML = "text/html" MIMEApplicationXML = "application/xml" MIMEApplicationJSON = "application/json" ) diff --git a/context.go b/context.go index 2142806..5c7114a 100644 --- a/context.go +++ b/context.go @@ -224,6 +224,18 @@ func (c *Context) Text(code int, text string) { c.res.setBody([]byte(text)) } +// HTML writes an HTML response with the given status code, template name, and data. +func (c *Context) HTML(code int, name string, data interface{}) { + c.SetHeader(HeaderContentType, MIMETextHTML) + c.SetStatus(code) + + if err := c.App.htmlTemplates.ExecuteTemplate(c.Res, name, data); err != nil { + c.Text(500, err.Error()) + } else { + c.SkipFlush() + } +} + // XML writes an XML response with the given status code and object. func (c *Context) XML(code int, obj interface{}) { encodeData, err := xml.Marshal(obj) diff --git a/examples/static/app.go b/examples/static/app.go new file mode 100644 index 0000000..238fa59 --- /dev/null +++ b/examples/static/app.go @@ -0,0 +1,35 @@ +package main + +import ( + "fmt" + "github.com/go-labx/lightning" + "html/template" + "time" +) + +func formatDate(t time.Time) string { + year, month, day := t.Date() + return fmt.Sprintf("%d-%02d-%02d", year, month, day) +} + +func main() { + // Create a new Lightning app + app := lightning.DefaultApp() + + app.Static("./public", "/static") + app.SetFuncMap(template.FuncMap{ + "formatDate": formatDate, + }) + app.LoadHTMLGlob("templates/*") + + app.Get("/", func(ctx *lightning.Context) { + ctx.HTML(200, "index.tmpl", lightning.Map{ + "title": "Lightning", + "description": "Lightning is a lightweight and fast web framework for Go. It is designed to be easy to use and highly performant. ⚡️⚡️⚡️", + "now": time.Now(), + }) + }) + + // Run the app + app.Run() +} diff --git a/examples/static/public/css/1.css b/examples/static/public/css/1.css new file mode 100644 index 0000000..47bc6b4 --- /dev/null +++ b/examples/static/public/css/1.css @@ -0,0 +1,3 @@ +body { + background-color: cyan; +} \ No newline at end of file diff --git a/examples/static/public/js/1.js b/examples/static/public/js/1.js new file mode 100644 index 0000000..ad21821 --- /dev/null +++ b/examples/static/public/js/1.js @@ -0,0 +1 @@ +console.log("hello world") \ No newline at end of file diff --git a/examples/static/templates/index.tmpl b/examples/static/templates/index.tmpl new file mode 100644 index 0000000..1d655bb --- /dev/null +++ b/examples/static/templates/index.tmpl @@ -0,0 +1,16 @@ + + +
+ + + +{{.description}}
+{{.now | formatDate }}
+ + \ No newline at end of file diff --git a/lightning.go b/lightning.go index 85c9d73..bce4e27 100644 --- a/lightning.go +++ b/lightning.go @@ -3,7 +3,12 @@ package lightning import ( "encoding/json" "net/http" + "os" + "path" + "path/filepath" "reflect" + "strings" + "text/template" "github.com/go-labx/lightlog" ) @@ -16,9 +21,11 @@ type Middleware = HandlerFunc type Map map[string]any type Application struct { - Config *Config - router *router - middlewares []HandlerFunc + Config *Config + router *router + middlewares []HandlerFunc + htmlTemplates *template.Template + funcMap template.FuncMap Logger *lightlog.ConsoleLogger } @@ -153,6 +160,43 @@ func (app *Application) Group(prefix string) *Group { return newGroup(app, prefix) } +// Static serves static files from the given root directory with the given prefix. +// It uses the os.Executable function to get the path of the executable file, +// and then joins it with the root and the path after the prefix to get the full file path. +// If the file exists, it is served with a 200 status code using the http.ServeFile function. +// If the file does not exist, a 404 status code is returned with the text "Not Found". +func (app *Application) Static(root string, prefix string) { + ex, err := os.Executable() + if err != nil { + panic(err) + } + exPath := filepath.Dir(ex) + + app.Get(path.Join(prefix, "/*"), func(ctx *Context) { + fullFilePath := filepath.Join(exPath, root, strings.TrimPrefix(ctx.Path, prefix)) + + if _, err := os.Stat(fullFilePath); !os.IsNotExist(err) { + ctx.SkipFlush() + ctx.SetStatus(http.StatusOK) + http.ServeFile(ctx.Res, ctx.Req, fullFilePath) + } else { + ctx.Text(http.StatusNotFound, http.StatusText(http.StatusNotFound)) + } + }) +} + +// SetFuncMap sets the funcMap in the Application struct to the funcMap passed in as an argument. +func (app *Application) SetFuncMap(funcMap template.FuncMap) { + app.funcMap = funcMap +} + +// LoadHTMLGlob loads HTML templates from a glob pattern and sets them in the Application struct. +// It uses the template.Must function to panic if there is an error parsing the templates. +// It also sets the funcMap in the Application struct to the funcMap passed in as an argument. +func (app *Application) LoadHTMLGlob(pattern string) { + app.htmlTemplates = template.Must(template.New("").Funcs(app.funcMap).ParseGlob(pattern)) +} + // ServeHTTP is the function that handles HTTP requests. // It finds the matching route, creates a new Context, sets the route parameters, // and executes the MiddlewareFunc chain.