mirror of
https://github.com/kataras/iris.git
synced 2025-02-02 07:20:35 +01:00
Update to v4.0.0-alpha.1
This commit is contained in:
parent
5cfe19c793
commit
b99afb2875
20
HISTORY.md
20
HISTORY.md
|
@ -2,6 +2,26 @@
|
|||
|
||||
**How to upgrade**: remove your `$GOPATH/src/github.com/kataras/iris` folder, open your command-line and execute this command: `go get -u github.com/kataras/iris/iris`.
|
||||
|
||||
|
||||
## 3.0.0 -> 4.0.0-alpha.1
|
||||
|
||||
[logger](https://github.com/iris-contrib/logger), [rest](https://github.com/iris-contrib/rest) and all [template engines](https://github.com/iris-contrib/template) **moved** to the [iris-contrib](https://github.com/iris-contrib).
|
||||
|
||||
- `config.Logger` -> `iris.Logger.Config`
|
||||
- `config.Render/config.Render.Rest/config.Render.Template` -> **Removed**
|
||||
- `config.Render.Rest` -> `rest.Config`
|
||||
- `config.Render.Template` -> `$TEMPLATE_ENGINE.Config` except Directory,Extensions, Assets, AssetNames,
|
||||
- `config.Render.Template.Directory` -> `iris.UseEngine($TEMPLAET_ENGINE.New()).Directory("./templates", ".html")`
|
||||
- `config.Render.Template.Assets` -> `iris.UseEngine($TEMPLAET_ENGINE.New()).Directory("./templates",".html").Binary(assetFn func(name string) ([]byte, error), namesFn func() []string)`
|
||||
|
||||
- `context.ExecuteTemplate` -> **Removed**, you can use the `context.Response.BodyWriter()` to get its writer and execute html/template engine manually, but this is useless because we support the best support for template engines among all other (golang) web frameworks
|
||||
- **Added** `config.Server.ReadBufferSize & config.Server.WriteBufferSize` which can be passed as configuration fields inside `iris.ListenTo(config.Server{...})`, which does the same job as `iris.Listen`
|
||||
- **Added** `iris.UseEngine($TEMPLAET_ENGINE.New()).Directory("./templates", ".html")` to register a template engine, now iris supports multi template engines, each template engine has its own file extension, no big changes on context.Render except the last parameter:
|
||||
- `context.Render(filename string, binding interface{}, layout string{})` -> `context.Render(filename string, binding interface{}, options ...map[string]interface{}) | context.Render("myfile.html", myPage{}, iris.Map{"gzip":true,"layout":"layouts/MyLayout.html"}) |`
|
||||
|
||||
E-book and examples are not yet updated, no big changes.
|
||||
|
||||
|
||||
## 3.0.0-rc.4 -> 3.0.0-pre.release
|
||||
|
||||
- `context.PostFormValue` -> `context.FormValueString`, old func stays until the next revision
|
||||
|
|
35
README.md
35
README.md
|
@ -2,10 +2,10 @@
|
|||
|
||||
[![Travis Widget]][Travis] [![Release Widget]][Release] [![Report Widget]][Report] [![License Widget]][License] [![Chat Widget]][Chat] [![Documentation Widget]][Documentation]
|
||||
|
||||
The [fastest](#benchmarks) web framework for Go.
|
||||
The fastest web framework for Go.
|
||||
|
||||
|
||||
[![Alt: The results have been updated on July 1, 2016](https://raw.githubusercontent.com/smallnest/go-web-framework-benchmark/master/benchmark.png)](https://github.com/smallnest/go-web-framework-benchmark)
|
||||
[![Alt: Benchmark results with pipeline and 500ms processing time](https://raw.githubusercontent.com/iris-contrib/website/gh-pages/assets/benchmark_horizontal_transparent.png)](#benchmarks)
|
||||
|
||||
|
||||
|
||||
|
@ -22,7 +22,7 @@ func main() {
|
|||
c.JSON(iris.StatusOK, iris.Map{
|
||||
"Name": "Iris",
|
||||
"Born": "13 March 2016",
|
||||
"Stars": 3693,
|
||||
"Stars": 4262,
|
||||
})
|
||||
})
|
||||
iris.Listen(":8080")
|
||||
|
@ -46,7 +46,7 @@ The only requirement is the [Go Programming Language](https://golang.org/dl), at
|
|||
$ go get -u github.com/kataras/iris/iris
|
||||
```
|
||||
|
||||
>If you have installation issues and you are connected to the Internet through China please, [click here](https://kataras.gitbooks.io/iris/content/install.html).
|
||||
>If you have installation issues or you are connected to the Internet through China please, [click here](https://kataras.gitbooks.io/iris/content/install.html).
|
||||
|
||||
FAQ
|
||||
------------
|
||||
|
@ -76,6 +76,12 @@ Features
|
|||
|
||||
| Name | Description | Usage |
|
||||
| ------------------|:---------------------:|-------:|
|
||||
| [HTML/Default Engine ](https://github.com/iris-contrib/template/tree/master/html) | HTML Template Engine (Default) |[example 1](https://github.com/iris-contrib/examples/blob/master/template_engines/template_html_1/main.go), [book section](https://kataras.gitbooks.io/iris/content/render_templates.html)
|
||||
| [Django Engine ](https://github.com/iris-contrib/template/tree/master/django) | Django Template Engine |[example 1](https://github.com/iris-contrib/examples/blob/master/template_engines/template_django_1/main.go), [book section](https://kataras.gitbooks.io/iris/content/brender_templates.html)
|
||||
| [Pug/Jade Engine ](https://github.com/iris-contrib/template/tree/master/pug) | Pug Template Engine |[example 1](https://github.com/iris-contrib/examples/blob/master/template_engines/template_pug_1/main.go), [book section](https://kataras.gitbooks.io/iris/content/render_templates.html)
|
||||
| [Handlebars Engine ](https://github.com/iris-contrib/template/tree/master/handlebars) | Handlebars Template Engine |[example 1](https://github.com/iris-contrib/examples/blob/master/template_engines/template_handlebars_1/main.go), [book section](https://kataras.gitbooks.io/iris/content/render_templates.html)
|
||||
| [Amber Engine ](https://github.com/iris-contrib/template/tree/master/amber) | Amber Template Engine |[example 1](https://github.com/iris-contrib/examples/blob/master/template_engines/template_amber_1/main.go), [book section](https://kataras.gitbooks.io/iris/content/render_templates.html)
|
||||
| [Markdown Engine ](https://github.com/iris-contrib/template/tree/master/markdown) | Markdown Template Engine |[example 1](https://github.com/iris-contrib/examples/blob/master/template_engines/template_markdown_1/main.go), [book section](https://kataras.gitbooks.io/iris/content/render_templates.html)
|
||||
| [Basicauth Middleware ](https://github.com/iris-contrib/middleware/tree/master/basicauth) | HTTP Basic authentication |[example 1](https://github.com/iris-contrib/examples/blob/master/middleware_basicauth_1/main.go), [example 2](https://github.com/iris-contrib/examples/blob/master/middleware_basicauth_2/main.go), [book section](https://kataras.gitbooks.io/iris/content/basic-authentication.html) |
|
||||
| [JWT Middleware ](https://github.com/iris-contrib/middleware/tree/master/jwt) | JSON Web Tokens |[example ](https://github.com/iris-contrib/examples/blob/master/middleware_jwt/main.go), [book section](https://kataras.gitbooks.io/iris/content/jwt.html) |
|
||||
| [Cors Middleware ](https://github.com/iris-contrib/middleware/tree/master/cors) | Cross Origin Resource Sharing W3 specification | [how to use ](https://github.com/iris-contrib/middleware/tree/master/cors#how-to-use) |
|
||||
|
@ -133,30 +139,27 @@ Iris does not force you to use any specific ORM or template engine. With support
|
|||
Testing
|
||||
------------
|
||||
|
||||
Tests are located to the [iris-contrib/tests repository](https://github.com/iris-contrib/tests), community should write some code there!
|
||||
Community should write third-party or iris base tests to the [iris-contrib/tests repository](https://github.com/iris-contrib/tests).
|
||||
I recommend writing your API tests using this new library, [httpexpect](https://github.com/gavv/httpexpect) which supports Iris and fasthttp now, after my request [here](https://github.com/gavv/httpexpect/issues/2).
|
||||
|
||||
Versioning
|
||||
------------
|
||||
|
||||
Current: **v3.0.0**
|
||||
Current: **v4.0.0-alpha.1**
|
||||
> Iris is an active project
|
||||
|
||||
Read more about Semantic Versioning 2.0.0
|
||||
|
||||
- http://semver.org/
|
||||
- https://en.wikipedia.org/wiki/Software_versioning
|
||||
- https://wiki.debian.org/UpstreamGuide#Releases_and_Versions
|
||||
|
||||
|
||||
Todo
|
||||
------------
|
||||
> for the next version 'v4'
|
||||
|
||||
- [ ] Refactor & extend view engine, separate the engines from the main code base, easier for the community to create new view engines.
|
||||
- [ ] Create a router as optional plugin, for optional path parts. Its name, 'ryan', taken from the community-member and donator who requested this feature.
|
||||
- [x] Refactor & extend view engine, separate the engines from the main code base, easier for the community to create new view engines
|
||||
- [ ] Implement all [opened community's feature requests](https://github.com/kataras/iris/issues?q=is%3Aissue+is%3Aopen+label%3A%22feature+request%22)
|
||||
- [ ] Extend i18n middleware for easier and better internalization support
|
||||
- [ ] Create a router as optional plugin, for optional path parts. Its name, 'ryan', taken from the community-member and donator who requested this feature
|
||||
- [ ] Extend the iris control plugin
|
||||
- [ ] Remove deprecated functions.
|
||||
- [ ] Remove deprecated functions
|
||||
- [ ] Will think more :)
|
||||
|
||||
> completed for release 'v3'
|
||||
|
||||
|
@ -191,7 +194,7 @@ License can be found [here](LICENSE).
|
|||
[Travis]: http://travis-ci.org/kataras/iris
|
||||
[License Widget]: https://img.shields.io/badge/license-MIT%20%20License%20-E91E63.svg?style=flat-square
|
||||
[License]: https://github.com/kataras/iris/blob/master/LICENSE
|
||||
[Release Widget]: https://img.shields.io/badge/release-v3.0.0-blue.svg?style=flat-square
|
||||
[Release Widget]: https://img.shields.io/badge/release-v4.0.0--alpha.1-blue.svg?style=flat-square
|
||||
[Release]: https://github.com/kataras/iris/releases
|
||||
[Chat Widget]: https://img.shields.io/badge/community-chat-00BCD4.svg?style=flat-square
|
||||
[Chat]: https://kataras.rocket.chat/channel/iris
|
||||
|
|
|
@ -6,6 +6,13 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
// Charset character encoding for template rendering
|
||||
Charset = "UTF-8"
|
||||
)
|
||||
|
||||
var (
|
||||
// TimeFormat default time format for any kind of datetime parsing
|
||||
TimeFormat = "Mon, 02 Jan 2006 15:04:05 GMT"
|
||||
// StaticCacheDuration expiration duration for INACTIVE file handlers
|
||||
StaticCacheDuration = 20 * time.Second
|
||||
// CompressedFileSuffix is the suffix to add to the name of
|
||||
|
@ -13,4 +20,14 @@ var (
|
|||
//
|
||||
// Defaults to iris-fasthttp.gz
|
||||
CompressedFileSuffix = "iris-fasthttp.gz"
|
||||
|
||||
// ContentTypeHTML defaults to text/html but you can change it, changes the template's content type also
|
||||
ContentTypeHTML = "text/html"
|
||||
)
|
||||
|
||||
const (
|
||||
// NoLayout to disable layout for a particular template file
|
||||
NoLayout = "@.|.@iris_no_layout@.|.@"
|
||||
// TemplateLayoutContextKey is the name of the user values which can be used to set a template layout from a middleware and override the parent's
|
||||
TemplateLayoutContextKey = "templateLayout"
|
||||
)
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
package config
|
||||
|
||||
import "github.com/imdario/mergo"
|
||||
import (
|
||||
"github.com/imdario/mergo"
|
||||
"github.com/iris-contrib/rest"
|
||||
)
|
||||
|
||||
// Default values for base Iris conf
|
||||
const (
|
||||
|
@ -78,16 +81,19 @@ type (
|
|||
// http://debug.yourdomain:PORT/threadcreate
|
||||
// http://debug.yourdomain:PORT/pprof/block
|
||||
ProfilePath string
|
||||
|
||||
// Logger the configuration for the logger
|
||||
// Iris logs ONLY SEMANTIC errors and the banner if enabled
|
||||
Logger Logger
|
||||
// DisableTemplateEngines set to true to disable loading the default template engine (html/template) and disallow the use of iris.UseEngine
|
||||
// default is false
|
||||
DisableTemplateEngines bool
|
||||
// IsDevelopment iris will act like a developer, for example
|
||||
// If true then re-builds the templates on each request
|
||||
// default is false
|
||||
IsDevelopment bool
|
||||
|
||||
// Sessions contains the configs for sessions
|
||||
Sessions Sessions
|
||||
|
||||
// Render contains the configs for template and rest configuration
|
||||
Render Render
|
||||
// Rest contains the configs for rest render configuration
|
||||
Rest rest.Config
|
||||
|
||||
// Websocket contains the configs for Websocket's server integration
|
||||
Websocket *Websocket
|
||||
|
@ -95,41 +101,21 @@ type (
|
|||
// Tester contains the configs for the test framework, so far we have only one because all test framework's configs are setted by the iris itself
|
||||
Tester Tester
|
||||
}
|
||||
|
||||
// Render struct keeps organise all configuration about rendering, templates and rest currently.
|
||||
Render struct {
|
||||
// Template the configs for template
|
||||
Template Template
|
||||
// Rest configs for rendering.
|
||||
//
|
||||
// these options inside this config don't have any relation with the TemplateEngine
|
||||
// from github.com/kataras/iris/rest
|
||||
Rest Rest
|
||||
}
|
||||
)
|
||||
|
||||
// DefaultRender returns default configuration for templates and rest rendering
|
||||
func DefaultRender() Render {
|
||||
return Render{
|
||||
// set the default template config both not nil and default Engine to Standar
|
||||
Template: DefaultTemplate(),
|
||||
// set the default configs for rest
|
||||
Rest: DefaultRest(),
|
||||
}
|
||||
}
|
||||
|
||||
// Default returns the default configuration for the Iris staton
|
||||
func Default() Iris {
|
||||
return Iris{
|
||||
DisablePathCorrection: DefaultDisablePathCorrection,
|
||||
DisablePathEscape: DefaultDisablePathEscape,
|
||||
DisableBanner: false,
|
||||
ProfilePath: "",
|
||||
Logger: DefaultLogger(),
|
||||
Sessions: DefaultSessions(),
|
||||
Render: DefaultRender(),
|
||||
Websocket: DefaultWebsocket(),
|
||||
Tester: DefaultTester(),
|
||||
DisablePathCorrection: DefaultDisablePathCorrection,
|
||||
DisablePathEscape: DefaultDisablePathEscape,
|
||||
DisableBanner: false,
|
||||
DisableTemplateEngines: false,
|
||||
IsDevelopment: false,
|
||||
ProfilePath: "",
|
||||
Sessions: DefaultSessions(),
|
||||
Rest: rest.DefaultConfig(),
|
||||
Websocket: DefaultWebsocket(),
|
||||
Tester: DefaultTester(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -158,18 +144,3 @@ func (c Iris) MergeSingle(cfg Iris) (config Iris) {
|
|||
|
||||
return
|
||||
}
|
||||
|
||||
/* maybe some day
|
||||
// FromFile returns the configuration for Iris station
|
||||
//
|
||||
// receives one parameter
|
||||
// pathIni(string) the file path of the configuration-ini style
|
||||
//
|
||||
// returns an error if something bad happens
|
||||
func FromFile(pathIni string) (c Iris, err error) {
|
||||
c = Iris{}
|
||||
err = ini.MapTo(&c, pathIni)
|
||||
|
||||
return
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -69,6 +69,7 @@ type Server struct {
|
|||
//
|
||||
// Default buffer size is used if not set.
|
||||
WriteBufferSize int
|
||||
|
||||
// RedirectTo, defaults to empty, set it in order to override the station's handler and redirect all requests to this address which is of form(HOST:PORT or :PORT)
|
||||
//
|
||||
// NOTE: the http status is 'StatusMovedPermanently', means one-time-redirect(the browser remembers the new addr and goes to the new address without need to request something from this server
|
||||
|
|
28
context.go
28
context.go
|
@ -10,7 +10,6 @@ import (
|
|||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
|
@ -476,23 +475,23 @@ func (ctx *Context) Data(status int, v []byte) error {
|
|||
|
||||
// RenderWithStatus builds up the response from the specified template and bindings.
|
||||
// Note: parameter layout has meaning only when using the iris.HTMLTemplate
|
||||
func (ctx *Context) RenderWithStatus(status int, name string, binding interface{}, layout ...string) error {
|
||||
func (ctx *Context) RenderWithStatus(status int, name string, binding interface{}, options ...map[string]interface{}) error {
|
||||
ctx.SetStatusCode(status)
|
||||
return ctx.framework.templates.Render(ctx, name, binding, layout...)
|
||||
return ctx.framework.templates.GetBy(name).Execute(ctx, name, binding, options...)
|
||||
}
|
||||
|
||||
// Render same as .RenderWithStatus but with status to iris.StatusOK (200)
|
||||
func (ctx *Context) Render(name string, binding interface{}, layout ...string) error {
|
||||
func (ctx *Context) Render(name string, binding interface{}, options ...map[string]interface{}) error {
|
||||
errCode := ctx.RequestCtx.Response.StatusCode()
|
||||
if errCode <= 0 {
|
||||
errCode = StatusOK
|
||||
}
|
||||
return ctx.RenderWithStatus(errCode, name, binding, layout...)
|
||||
return ctx.RenderWithStatus(errCode, name, binding, options...)
|
||||
}
|
||||
|
||||
// MustRender same as .Render but returns 500 internal server http status (error) if rendering fail
|
||||
func (ctx *Context) MustRender(name string, binding interface{}, layout ...string) {
|
||||
if err := ctx.Render(name, binding, layout...); err != nil {
|
||||
func (ctx *Context) MustRender(name string, binding interface{}, options ...map[string]interface{}) {
|
||||
if err := ctx.Render(name, binding, options...); err != nil {
|
||||
ctx.Panic()
|
||||
ctx.framework.Logger.Dangerf("MustRender panics for client with IP: %s On template: %s.Trace: %s\n", ctx.RemoteAddr(), name, err)
|
||||
}
|
||||
|
@ -500,8 +499,8 @@ func (ctx *Context) MustRender(name string, binding interface{}, layout ...strin
|
|||
|
||||
// TemplateString accepts a template filename, its context data and returns the result of the parsed template (string)
|
||||
// if any error returns empty string
|
||||
func (ctx *Context) TemplateString(name string, binding interface{}, layout ...string) string {
|
||||
return ctx.framework.TemplateString(name, binding, layout...)
|
||||
func (ctx *Context) TemplateString(name string, binding interface{}, options ...map[string]interface{}) string {
|
||||
return ctx.framework.TemplateString(name, binding, options...)
|
||||
}
|
||||
|
||||
// JSON marshals the given interface object and writes the JSON response.
|
||||
|
@ -537,17 +536,6 @@ func (ctx *Context) Markdown(status int, markdown string) {
|
|||
ctx.HTML(status, ctx.MarkdownString(markdown))
|
||||
}
|
||||
|
||||
// ExecuteTemplate executes a simple html template, you can use that if you already have the cached templates
|
||||
// the recommended way to render is to use iris.Templates("./templates/path/*.html") and ctx.RenderFile("filename.html",struct{})
|
||||
// accepts 2 parameters
|
||||
// the first parameter is the template (*template.Template)
|
||||
// the second parameter is the page context (interfac{})
|
||||
// returns an error if any errors occurs while executing this template
|
||||
func (ctx *Context) ExecuteTemplate(tmpl *template.Template, pageContext interface{}) error {
|
||||
ctx.RequestCtx.SetContentType(contentHTML + ctx.framework.rest.CompiledCharset)
|
||||
return errTemplateExecute.With(tmpl.Execute(ctx.RequestCtx.Response.BodyWriter(), pageContext))
|
||||
}
|
||||
|
||||
// ServeContent serves content, headers are autoset
|
||||
// receives three parameters, it's low-level function, instead you can use .ServeFile(string)
|
||||
//
|
||||
|
|
|
@ -2,7 +2,6 @@ package context
|
|||
|
||||
import (
|
||||
"bufio"
|
||||
"html/template"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
|
@ -46,17 +45,16 @@ type (
|
|||
Write(string, ...interface{})
|
||||
HTML(int, string)
|
||||
Data(int, []byte) error
|
||||
RenderWithStatus(int, string, interface{}, ...string) error
|
||||
Render(string, interface{}, ...string) error
|
||||
MustRender(string, interface{}, ...string)
|
||||
TemplateString(string, interface{}, ...string) string
|
||||
RenderWithStatus(int, string, interface{}, ...map[string]interface{}) error
|
||||
Render(string, interface{}, ...map[string]interface{}) error
|
||||
MustRender(string, interface{}, ...map[string]interface{})
|
||||
TemplateString(string, interface{}, ...map[string]interface{}) string
|
||||
MarkdownString(string) string
|
||||
Markdown(int, string)
|
||||
JSON(int, interface{}) error
|
||||
JSONP(int, string, interface{}) error
|
||||
Text(int, string) error
|
||||
XML(int, interface{}) error
|
||||
ExecuteTemplate(*template.Template, interface{}) error
|
||||
ServeContent(io.ReadSeeker, string, time.Time, bool) error
|
||||
ServeFile(string, bool) error
|
||||
SendFile(string, string) error
|
||||
|
|
|
@ -89,9 +89,7 @@ func MustUseFunc(handlersFn ...HandlerFunc) {
|
|||
// Use it when you want to add a global middleware to all parties, to all routes in all subdomains
|
||||
// It can be called after other, (but before .Listen of course)
|
||||
func (s *Framework) MustUse(handlers ...Handler) {
|
||||
for _, r := range s.mux.lookups {
|
||||
r.middleware = append(handlers, r.middleware...)
|
||||
}
|
||||
s.UseGlobal(handlers...)
|
||||
}
|
||||
|
||||
// MustUseFunc registers HandlerFunc middleware to the beginning, prepends them instead of append
|
||||
|
@ -99,7 +97,7 @@ func (s *Framework) MustUse(handlers ...Handler) {
|
|||
// Use it when you want to add a global middleware to all parties, to all routes in all subdomains
|
||||
// It can be called after other, (but before .Listen of course)
|
||||
func (s *Framework) MustUseFunc(handlersFn ...HandlerFunc) {
|
||||
s.MustUse(convertToHandlers(handlersFn)...)
|
||||
s.UseGlobalFunc(handlersFn...)
|
||||
}
|
||||
|
||||
// PostFormMulti returns a slice of string from post request's data
|
||||
|
|
2
glide.lock
generated
2
glide.lock
generated
|
@ -101,7 +101,7 @@ imports:
|
|||
subpackages:
|
||||
- github.com\valyala\bytebufferpool
|
||||
- name: github.com/valyala/fasthttp
|
||||
version: 97d96cb3b7feaf22ce9b813313cd8ae20b3589b9
|
||||
version: 886e5411604884629c566961ea8ed2cec074e4b1
|
||||
subpackages:
|
||||
- github.com\valyala\fasthttp
|
||||
- github.com\valyala\fasthttp\fasthttpadaptor
|
||||
|
|
11
http.go
11
http.go
|
@ -13,8 +13,8 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/iris-contrib/errors"
|
||||
"github.com/iris-contrib/logger"
|
||||
"github.com/kataras/iris/config"
|
||||
"github.com/kataras/iris/logger"
|
||||
"github.com/kataras/iris/utils"
|
||||
"github.com/valyala/fasthttp"
|
||||
"github.com/valyala/fasthttp/fasthttpadaptor"
|
||||
|
@ -262,6 +262,11 @@ func newServer(cfg config.Server) *Server {
|
|||
// prepare just prepares the listening addr
|
||||
func (s *Server) prepare() {
|
||||
s.Config.ListeningAddr = config.ServerParseAddr(s.Config.ListeningAddr)
|
||||
if s.Server != nil {
|
||||
s.Server.MaxRequestBodySize = s.Config.MaxRequestBodySize
|
||||
s.Server.ReadBufferSize = s.Config.ReadBufferSize
|
||||
s.Server.WriteBufferSize = s.Config.WriteBufferSize
|
||||
}
|
||||
}
|
||||
|
||||
// IsListening returns true if server is listening/started, otherwise false
|
||||
|
@ -399,10 +404,6 @@ func (s *Server) Open(h fasthttp.RequestHandler) error {
|
|||
|
||||
s.prepare() // do it again for any case
|
||||
|
||||
s.Server.MaxRequestBodySize = s.Config.MaxRequestBodySize
|
||||
s.Server.ReadBufferSize = s.Config.ReadBufferSize
|
||||
s.Server.WriteBufferSize = s.Config.WriteBufferSize
|
||||
|
||||
if s.Config.RedirectTo != "" {
|
||||
// override the handler and redirect all requests to this addr
|
||||
s.Server.Handler = func(reqCtx *fasthttp.RequestCtx) {
|
||||
|
|
122
iris.go
122
iris.go
|
@ -65,11 +65,11 @@ import (
|
|||
|
||||
"github.com/gavv/httpexpect"
|
||||
"github.com/iris-contrib/errors"
|
||||
"github.com/iris-contrib/logger"
|
||||
"github.com/iris-contrib/rest"
|
||||
"github.com/iris-contrib/template/html"
|
||||
"github.com/kataras/iris/config"
|
||||
"github.com/kataras/iris/context"
|
||||
"github.com/kataras/iris/logger"
|
||||
"github.com/kataras/iris/render/rest"
|
||||
"github.com/kataras/iris/render/template"
|
||||
"github.com/kataras/iris/sessions"
|
||||
"github.com/kataras/iris/utils"
|
||||
"github.com/kataras/iris/websocket"
|
||||
|
@ -81,27 +81,7 @@ import (
|
|||
|
||||
const (
|
||||
// Version of the iris
|
||||
Version = "3.0.0"
|
||||
|
||||
// HTMLEngine conversion for config.HTMLEngine
|
||||
HTMLEngine = config.HTMLEngine
|
||||
// PongoEngine conversion for config.PongoEngine
|
||||
PongoEngine = config.PongoEngine
|
||||
// MarkdownEngine conversion for config.MarkdownEngine
|
||||
MarkdownEngine = config.MarkdownEngine
|
||||
// JadeEngine conversion for config.JadeEngine
|
||||
JadeEngine = config.JadeEngine
|
||||
// AmberEngine conversion for config.AmberEngine
|
||||
AmberEngine = config.AmberEngine
|
||||
// HandlebarsEngine conversion for config.HandlebarsEngine
|
||||
HandlebarsEngine = config.HandlebarsEngine
|
||||
// DefaultEngine conversion for config.DefaultEngine
|
||||
DefaultEngine = config.DefaultEngine
|
||||
// NoEngine conversion for config.NoEngine
|
||||
NoEngine = config.NoEngine
|
||||
// NoLayout to disable layout for a particular template file
|
||||
// conversion for config.NoLayout
|
||||
NoLayout = config.NoLayout
|
||||
Version = "4.0.0-alpha.1"
|
||||
|
||||
banner = ` _____ _
|
||||
|_ _| (_)
|
||||
|
@ -177,7 +157,7 @@ type (
|
|||
Lookups() []Route
|
||||
Path(string, ...interface{}) string
|
||||
URL(string, ...interface{}) string
|
||||
TemplateString(string, interface{}, ...string) string
|
||||
TemplateString(string, interface{}, ...map[string]interface{}) string
|
||||
Tester(t *testing.T) *httpexpect.Expect
|
||||
}
|
||||
|
||||
|
@ -187,12 +167,14 @@ type (
|
|||
Framework struct {
|
||||
*muxAPI
|
||||
rest *rest.Render
|
||||
templates *template.Template
|
||||
sessions *sessions.Manager
|
||||
templates *TemplateEngines
|
||||
|
||||
// fields which are useful to the user/dev
|
||||
// the last added server is the main server
|
||||
Servers *ServerList
|
||||
Config *config.Iris
|
||||
Servers *ServerList
|
||||
Config *config.Iris
|
||||
// configuration by instance.Logger.Config
|
||||
Logger *logger.Logger
|
||||
Plugins PluginContainer
|
||||
Websocket websocket.Server
|
||||
|
@ -217,9 +199,17 @@ func New(cfg ...config.Iris) *Framework {
|
|||
{
|
||||
///NOTE: set all with s.Config pointer
|
||||
// set the Logger
|
||||
s.Logger = logger.New(s.Config.Logger)
|
||||
s.Logger = logger.New(logger.DefaultConfig())
|
||||
// set the plugin container
|
||||
s.Plugins = &pluginContainer{logger: s.Logger}
|
||||
// set the templates
|
||||
s.templates = &TemplateEngines{
|
||||
helpers: map[string]interface{}{
|
||||
"url": s.URL,
|
||||
"urlpath": s.Path,
|
||||
},
|
||||
engines: make([]*TemplateEngineWrapper, 0),
|
||||
}
|
||||
// set the websocket server
|
||||
s.Websocket = websocket.NewServer(s.Config.Websocket)
|
||||
// set the servemux, which will provide us the public API also, with its context pool
|
||||
|
@ -241,10 +231,18 @@ func (s *Framework) initialize() {
|
|||
}
|
||||
|
||||
// set the rest
|
||||
s.rest = rest.New(s.Config.Render.Rest)
|
||||
|
||||
// set templates if not already setted
|
||||
s.prepareTemplates()
|
||||
s.rest = rest.New(s.Config.Rest)
|
||||
// prepare the templates if enabled
|
||||
if !s.Config.DisableTemplateEngines {
|
||||
if err := s.templates.loadAll(); err != nil {
|
||||
s.Logger.Panic(err) // panic on templates loading before listening if we have an error.
|
||||
}
|
||||
// check and prepare the templates
|
||||
if len(s.templates.engines) == 0 { // no template engine is registered, let's use the default
|
||||
s.UseEngine(html.New())
|
||||
}
|
||||
s.templates.setReload(s.Config.IsDevelopment)
|
||||
}
|
||||
|
||||
// listen to websocket connections
|
||||
websocket.RegisterServer(s, s.Websocket, s.Logger)
|
||||
|
@ -258,22 +256,6 @@ func (s *Framework) initialize() {
|
|||
}
|
||||
}
|
||||
|
||||
// prepareTemplates sets the templates if not nil, we make this check because of .TemplateString, which can be called before Listen
|
||||
func (s *Framework) prepareTemplates() {
|
||||
// prepare the templates
|
||||
if s.templates == nil {
|
||||
// These functions are directly contact with Iris' functionality.
|
||||
funcs := map[string]interface{}{
|
||||
"url": s.URL,
|
||||
"urlpath": s.Path,
|
||||
}
|
||||
|
||||
template.RegisterSharedFuncs(funcs)
|
||||
|
||||
s.templates = template.New(s.Config.Render.Template)
|
||||
}
|
||||
}
|
||||
|
||||
// Go starts the iris station, listens to all registered servers, and prepare only if Virtual
|
||||
func Go() error {
|
||||
return Default.Go()
|
||||
|
@ -290,7 +272,6 @@ func (s *Framework) Go() error {
|
|||
|
||||
// print the banner
|
||||
if !s.Config.DisableBanner {
|
||||
|
||||
openedServers := s.Servers.GetAllOpened()
|
||||
l := len(openedServers)
|
||||
hosts := make([]string, l, l)
|
||||
|
@ -495,6 +476,31 @@ func (s *Framework) Close() error {
|
|||
return s.Servers.CloseAll()
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
// set the template engines
|
||||
s.renderer = &renderer{
|
||||
engines: make([]TemplateEngine, 0),
|
||||
buffer: utils.NewBufferPool(64),
|
||||
helpers: map[string]interface{}{
|
||||
"url": s.URL,
|
||||
"urlpath": s.Path,
|
||||
},
|
||||
contentType: s.Config.Render.Template.ContentType + "; " + s.Config.Render.Template.Charset,
|
||||
}*/
|
||||
|
||||
// UseEngine adds a template engine to the iris view system
|
||||
// it does not build/load them yet
|
||||
func UseEngine(e TemplateEngine) *TemplateEngineLocation {
|
||||
return Default.UseEngine(e)
|
||||
}
|
||||
|
||||
// UseEngine adds a template engine to the iris view system
|
||||
// it does not build/load them yet
|
||||
func (s *Framework) UseEngine(e TemplateEngine) *TemplateEngineLocation {
|
||||
return s.templates.Add(e)
|
||||
}
|
||||
|
||||
// UseGlobal registers Handler middleware to the beginning, prepends them instead of append
|
||||
//
|
||||
// Use it when you want to add a global middleware to all parties, to all routes in all subdomains
|
||||
|
@ -713,17 +719,19 @@ func (s *Framework) URL(routeName string, args ...interface{}) (url string) {
|
|||
return
|
||||
}
|
||||
|
||||
// TemplateString executes a template and returns its result as string, useful when you want it for sending rich e-mails
|
||||
// TemplateString executes a template from the default template engine and returns its result as string, useful when you want it for sending rich e-mails
|
||||
// returns empty string on error
|
||||
func TemplateString(templateFile string, pageContext interface{}, layout ...string) string {
|
||||
return Default.TemplateString(templateFile, pageContext, layout...)
|
||||
func TemplateString(templateFile string, pageContext interface{}, options ...map[string]interface{}) string {
|
||||
return Default.TemplateString(templateFile, pageContext, options...)
|
||||
}
|
||||
|
||||
// TemplateString executes a template and returns its result as string, useful when you want it for sending rich e-mails
|
||||
// TemplateString executes a template from the default template engine and returns its result as string, useful when you want it for sending rich e-mails
|
||||
// returns empty string on error
|
||||
func (s *Framework) TemplateString(templateFile string, pageContext interface{}, layout ...string) string {
|
||||
s.prepareTemplates()
|
||||
res, err := s.templates.RenderString(templateFile, pageContext, layout...)
|
||||
func (s *Framework) TemplateString(templateFile string, pageContext interface{}, options ...map[string]interface{}) string {
|
||||
if s.Config.DisableTemplateEngines {
|
||||
return ""
|
||||
}
|
||||
res, err := s.templates.GetBy(templateFile).ExecuteToString(templateFile, pageContext, options...)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
|
|
@ -3,10 +3,9 @@ package main
|
|||
import (
|
||||
"os"
|
||||
|
||||
"github.com/iris-contrib/logger"
|
||||
"github.com/kataras/cli"
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/config"
|
||||
"github.com/kataras/iris/logger"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -49,7 +48,7 @@ func init() {
|
|||
app.Command(runAndWatchCmd)
|
||||
|
||||
// init the logger
|
||||
printer = logger.New(config.DefaultLogger())
|
||||
printer = logger.New(logger.DefaultConfig())
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
## Package information
|
||||
|
||||
I decide to split the logger from the main iris package because logger.go doesn't depends on any of the iris' types.
|
||||
|
||||
|
||||
**Examples and more info will be added soon.**
|
186
logger/logger.go
186
logger/logger.go
|
@ -1,186 +0,0 @@
|
|||
package logger
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/kataras/iris/config"
|
||||
"github.com/mattn/go-colorable"
|
||||
)
|
||||
|
||||
var (
|
||||
// Prefix is the prefix for the logger, default is [IRIS]
|
||||
Prefix = "[IRIS] "
|
||||
// bannersRan keeps track of the logger's print banner count
|
||||
bannersRan = 0
|
||||
)
|
||||
|
||||
// Logger the logger
|
||||
type Logger struct {
|
||||
config *config.Logger
|
||||
underline *color.Color
|
||||
}
|
||||
|
||||
// attr takes a color integer and converts it to color.Attribute
|
||||
func attr(sgr int) color.Attribute {
|
||||
return color.Attribute(sgr)
|
||||
}
|
||||
|
||||
// check if background color > 0 and if so then set it
|
||||
func (l *Logger) setBg(sgr int) {
|
||||
if sgr > 0 {
|
||||
l.underline.Add(attr(sgr))
|
||||
}
|
||||
}
|
||||
|
||||
// New creates a new Logger from config.Logger configuration
|
||||
func New(c config.Logger) *Logger {
|
||||
color.Output = colorable.NewColorable(c.Out)
|
||||
|
||||
l := &Logger{&c, color.New(attr(c.ColorFgDefault))}
|
||||
l.setBg(c.ColorBgDefault)
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
// SetEnable true enables, false disables the Logger
|
||||
func (l *Logger) SetEnable(enable bool) {
|
||||
l.config.Disabled = !enable
|
||||
}
|
||||
|
||||
// IsEnabled returns true if Logger is enabled, otherwise false
|
||||
func (l *Logger) IsEnabled() bool {
|
||||
return !l.config.Disabled
|
||||
}
|
||||
|
||||
// ResetColors sets the colors to the default
|
||||
// this func is called every time a success, info, warning, or danger message is printed
|
||||
func (l *Logger) ResetColors() {
|
||||
l.underline.Add(attr(l.config.ColorFgDefault))
|
||||
l.setBg(l.config.ColorBgDefault)
|
||||
}
|
||||
|
||||
// PrintBanner prints a text (banner) with BannerFgColor, BannerBgColor and a success message at the end
|
||||
// It doesn't cares if the logger is disabled or not, it will print this
|
||||
func (l *Logger) PrintBanner(banner string, successMessage string) {
|
||||
c := color.New(attr(l.config.ColorFgBanner))
|
||||
if l.config.ColorBgDefault > 0 {
|
||||
c.Add(attr(l.config.ColorBgDefault))
|
||||
}
|
||||
c.Println(banner)
|
||||
bannersRan++
|
||||
|
||||
if successMessage != "" {
|
||||
c.Add(attr(l.config.ColorFgSuccess))
|
||||
if l.config.ColorBgSuccess > 0 {
|
||||
c.Add(attr(l.config.ColorBgSuccess))
|
||||
}
|
||||
if bannersRan > 1 {
|
||||
c.Printf("Server[%#v]\n", bannersRan)
|
||||
|
||||
}
|
||||
c.Println(successMessage)
|
||||
}
|
||||
|
||||
c.DisableColor()
|
||||
c = nil
|
||||
}
|
||||
|
||||
// Printf calls l.Output to print to the logger.
|
||||
// Arguments are handled in the manner of fmt.Printf.
|
||||
func (l *Logger) Printf(format string, a ...interface{}) {
|
||||
if !l.config.Disabled {
|
||||
l.underline.Printf(l.config.Prefix+format, a...)
|
||||
}
|
||||
}
|
||||
|
||||
// Print calls l.Output to print to the logger.
|
||||
// Arguments are handled in the manner of fmt.Print.
|
||||
func (l *Logger) Print(a interface{}) {
|
||||
if !l.config.Disabled {
|
||||
l.ResetColors()
|
||||
l.Printf("%#v", a)
|
||||
}
|
||||
}
|
||||
|
||||
// Println calls l.Output to print to the logger.
|
||||
// Arguments are handled in the manner of fmt.Println.
|
||||
func (l *Logger) Println(a interface{}) {
|
||||
if !l.config.Disabled {
|
||||
l.Printf("%#v\n", a)
|
||||
}
|
||||
}
|
||||
|
||||
// Fatal is equivalent to l.Dangerf("%#v",interface{}) followed by a call to panic().
|
||||
func (l *Logger) Fatal(a interface{}) {
|
||||
l.Warningf("%#v", a)
|
||||
panic("")
|
||||
}
|
||||
|
||||
// Fatalf is equivalent to l.Warningf() followed by a call to os.Exit(1).
|
||||
func (l *Logger) Fatalf(format string, a ...interface{}) {
|
||||
l.Warningf(format, a...)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Panic is equivalent to l.Dangerf("%#v",interface{}) followed by a call to panic().
|
||||
func (l *Logger) Panic(a interface{}) {
|
||||
l.Dangerf("%s\n", a)
|
||||
panic(a)
|
||||
}
|
||||
|
||||
// Panicf is equivalent to l.Dangerf() followed by a call to panic().
|
||||
func (l *Logger) Panicf(format string, a ...interface{}) {
|
||||
l.Dangerf(format, a...)
|
||||
panic("")
|
||||
}
|
||||
|
||||
// Successf calls l.Output to print to the logger with the Success colors.
|
||||
// Arguments are handled in the manner of fmt.Printf.
|
||||
func (l *Logger) Successf(format string, a ...interface{}) {
|
||||
if !l.config.Disabled {
|
||||
l.underline.Add(attr(l.config.ColorFgSuccess))
|
||||
l.setBg(l.config.ColorBgSuccess)
|
||||
l.Printf(format, a...)
|
||||
}
|
||||
}
|
||||
|
||||
// Infof calls l.Output to print to the logger with the Info colors.
|
||||
// Arguments are handled in the manner of fmt.Printf.
|
||||
func (l *Logger) Infof(format string, a ...interface{}) {
|
||||
if !l.config.Disabled {
|
||||
l.underline.Add(attr(l.config.ColorFgInfo))
|
||||
l.setBg(l.config.ColorBgInfo)
|
||||
l.Printf(format, a...)
|
||||
}
|
||||
}
|
||||
|
||||
// Warningf calls l.Output to print to the logger with the Warning colors.
|
||||
// Arguments are handled in the manner of fmt.Printf.
|
||||
func (l *Logger) Warningf(format string, a ...interface{}) {
|
||||
if !l.config.Disabled {
|
||||
l.underline.Add(attr(l.config.ColorFgWarning))
|
||||
l.setBg(l.config.ColorBgWarning)
|
||||
l.Printf(format, a...)
|
||||
}
|
||||
}
|
||||
|
||||
// Dangerf calls l.Output to print to the logger with the Danger colors.
|
||||
// Arguments are handled in the manner of fmt.Printf.
|
||||
func (l *Logger) Dangerf(format string, a ...interface{}) {
|
||||
if !l.config.Disabled {
|
||||
l.underline.Add(attr(l.config.ColorFgDanger))
|
||||
l.setBg(l.config.ColorBgDanger)
|
||||
l.Printf(format, a...)
|
||||
}
|
||||
}
|
||||
|
||||
// Otherf calls l.Output to print to the logger with the Other colors.
|
||||
// Arguments are handled in the manner of fmt.Printf.
|
||||
func (l *Logger) Otherf(format string, a ...interface{}) {
|
||||
if !l.config.Disabled {
|
||||
l.underline.Add(attr(l.config.ColorFgOther))
|
||||
l.setBg(l.config.ColorBgOther)
|
||||
l.Printf(format, a...)
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@ import (
|
|||
|
||||
"github.com/iris-contrib/errors"
|
||||
|
||||
"github.com/kataras/iris/logger"
|
||||
"github.com/iris-contrib/logger"
|
||||
"github.com/kataras/iris/utils"
|
||||
)
|
||||
|
||||
|
|
|
@ -1,317 +0,0 @@
|
|||
package rest
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
|
||||
"github.com/klauspost/compress/gzip"
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
// Engine is the generic interface for all responses.
|
||||
type Engine interface {
|
||||
Render(*fasthttp.RequestCtx, interface{}) error
|
||||
//used only if config gzip is enabled
|
||||
RenderGzip(*fasthttp.RequestCtx, interface{}) error
|
||||
}
|
||||
|
||||
// Head defines the basic ContentType and Status fields.
|
||||
type Head struct {
|
||||
ContentType string
|
||||
Status int
|
||||
}
|
||||
|
||||
// Data built-in renderer.
|
||||
type Data struct {
|
||||
Head
|
||||
}
|
||||
|
||||
// JSON built-in renderer.
|
||||
type JSON struct {
|
||||
Head
|
||||
Indent bool
|
||||
UnEscapeHTML bool
|
||||
Prefix []byte
|
||||
StreamingJSON bool
|
||||
}
|
||||
|
||||
// JSONP built-in renderer.
|
||||
type JSONP struct {
|
||||
Head
|
||||
Indent bool
|
||||
Callback string
|
||||
}
|
||||
|
||||
// Text built-in renderer.
|
||||
type Text struct {
|
||||
Head
|
||||
}
|
||||
|
||||
// XML built-in renderer.
|
||||
type XML struct {
|
||||
Head
|
||||
Indent bool
|
||||
Prefix []byte
|
||||
}
|
||||
|
||||
// Write outputs the header content.
|
||||
func (h Head) Write(ctx *fasthttp.RequestCtx) {
|
||||
ctx.Response.Header.Set(ContentType, h.ContentType)
|
||||
ctx.SetStatusCode(h.Status)
|
||||
}
|
||||
|
||||
// Render a data response.
|
||||
func (d Data) Render(ctx *fasthttp.RequestCtx, v interface{}) error {
|
||||
c := string(ctx.Request.Header.Peek(ContentType))
|
||||
w := ctx.Response.BodyWriter()
|
||||
if c != "" {
|
||||
d.Head.ContentType = c
|
||||
}
|
||||
|
||||
d.Head.Write(ctx)
|
||||
w.Write(v.([]byte))
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
// RenderGzip a data response using gzip compression.
|
||||
func (d Data) RenderGzip(ctx *fasthttp.RequestCtx, v interface{}) error {
|
||||
c := string(ctx.Request.Header.Peek(ContentType))
|
||||
if c != "" {
|
||||
d.Head.ContentType = c
|
||||
}
|
||||
|
||||
d.Head.Write(ctx)
|
||||
_, err := fasthttp.WriteGzip(ctx.Response.BodyWriter(), v.([]byte))
|
||||
if err == nil {
|
||||
ctx.Response.Header.Add("Content-Encoding", "gzip")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Render a JSON response.
|
||||
func (j JSON) Render(ctx *fasthttp.RequestCtx, v interface{}) error {
|
||||
if j.StreamingJSON {
|
||||
return j.renderStreamingJSON(ctx, v)
|
||||
}
|
||||
|
||||
var result []byte
|
||||
var err error
|
||||
|
||||
if j.Indent {
|
||||
result, err = json.MarshalIndent(v, "", " ")
|
||||
result = append(result, '\n')
|
||||
} else {
|
||||
result, err = json.Marshal(v)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Unescape HTML if needed.
|
||||
if j.UnEscapeHTML {
|
||||
result = bytes.Replace(result, []byte("\\u003c"), []byte("<"), -1)
|
||||
result = bytes.Replace(result, []byte("\\u003e"), []byte(">"), -1)
|
||||
result = bytes.Replace(result, []byte("\\u0026"), []byte("&"), -1)
|
||||
}
|
||||
w := ctx.Response.BodyWriter()
|
||||
// JSON marshaled fine, write out the result.
|
||||
j.Head.Write(ctx)
|
||||
if len(j.Prefix) > 0 {
|
||||
w.Write(j.Prefix)
|
||||
}
|
||||
w.Write(result)
|
||||
return nil
|
||||
}
|
||||
|
||||
// RenderGzip a JSON response using gzip compression.
|
||||
func (j JSON) RenderGzip(ctx *fasthttp.RequestCtx, v interface{}) error {
|
||||
if j.StreamingJSON {
|
||||
return j.renderStreamingJSONGzip(ctx, v)
|
||||
}
|
||||
|
||||
var result []byte
|
||||
var err error
|
||||
|
||||
if j.Indent {
|
||||
result, err = json.MarshalIndent(v, "", " ")
|
||||
result = append(result, '\n')
|
||||
} else {
|
||||
result, err = json.Marshal(v)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx.Response.Header.Add("Content-Encoding", "gzip")
|
||||
|
||||
// Unescape HTML if needed.
|
||||
if j.UnEscapeHTML {
|
||||
result = bytes.Replace(result, []byte("\\u003c"), []byte("<"), -1)
|
||||
result = bytes.Replace(result, []byte("\\u003e"), []byte(">"), -1)
|
||||
result = bytes.Replace(result, []byte("\\u0026"), []byte("&"), -1)
|
||||
}
|
||||
w := gzip.NewWriter(ctx.Response.BodyWriter())
|
||||
// JSON marshaled fine, write out the result.
|
||||
j.Head.Write(ctx)
|
||||
if len(j.Prefix) > 0 {
|
||||
w.Write(j.Prefix)
|
||||
}
|
||||
w.Write(result)
|
||||
w.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (j JSON) renderStreamingJSON(ctx *fasthttp.RequestCtx, v interface{}) error {
|
||||
j.Head.Write(ctx)
|
||||
w := ctx.Response.BodyWriter()
|
||||
if len(j.Prefix) > 0 {
|
||||
w.Write(j.Prefix)
|
||||
}
|
||||
return json.NewEncoder(w).Encode(v)
|
||||
}
|
||||
|
||||
func (j JSON) renderStreamingJSONGzip(ctx *fasthttp.RequestCtx, v interface{}) error {
|
||||
ctx.Response.Header.Add("Content-Encoding", "gzip")
|
||||
j.Head.Write(ctx)
|
||||
w := gzip.NewWriter(ctx.Response.BodyWriter())
|
||||
if len(j.Prefix) > 0 {
|
||||
w.Write(j.Prefix)
|
||||
}
|
||||
w.Close()
|
||||
return json.NewEncoder(w).Encode(v)
|
||||
}
|
||||
|
||||
// Render a JSONP response.
|
||||
func (j JSONP) Render(ctx *fasthttp.RequestCtx, v interface{}) error {
|
||||
var result []byte
|
||||
var err error
|
||||
|
||||
if j.Indent {
|
||||
result, err = json.MarshalIndent(v, "", " ")
|
||||
} else {
|
||||
result, err = json.Marshal(v)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w := ctx.Response.BodyWriter()
|
||||
|
||||
// JSON marshaled fine, write out the result.
|
||||
j.Head.Write(ctx)
|
||||
w.Write([]byte(j.Callback + "("))
|
||||
w.Write(result)
|
||||
w.Write([]byte(");"))
|
||||
|
||||
// If indenting, append a new line.
|
||||
if j.Indent {
|
||||
w.Write([]byte("\n"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RenderGzip a JSONP response using gzip compression.
|
||||
func (j JSONP) RenderGzip(ctx *fasthttp.RequestCtx, v interface{}) error {
|
||||
var result []byte
|
||||
var err error
|
||||
|
||||
if j.Indent {
|
||||
result, err = json.MarshalIndent(v, "", " ")
|
||||
} else {
|
||||
result, err = json.Marshal(v)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w := gzip.NewWriter(ctx.Response.BodyWriter())
|
||||
|
||||
ctx.Response.Header.Add("Content-Encoding", "gzip")
|
||||
// JSON marshaled fine, write out the result.
|
||||
j.Head.Write(ctx)
|
||||
w.Write([]byte(j.Callback + "("))
|
||||
w.Write(result)
|
||||
w.Write([]byte(");"))
|
||||
|
||||
// If indenting, append a new line.
|
||||
if j.Indent {
|
||||
w.Write([]byte("\n"))
|
||||
}
|
||||
w.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Render a text response.
|
||||
func (t Text) Render(ctx *fasthttp.RequestCtx, v interface{}) error {
|
||||
c := string(ctx.Request.Header.Peek(ContentType))
|
||||
if c != "" {
|
||||
t.Head.ContentType = c
|
||||
}
|
||||
w := ctx.Response.BodyWriter()
|
||||
t.Head.Write(ctx)
|
||||
w.Write([]byte(v.(string)))
|
||||
return nil
|
||||
}
|
||||
|
||||
// RenderGzip a Text response using gzip compression.
|
||||
func (t Text) RenderGzip(ctx *fasthttp.RequestCtx, v interface{}) error {
|
||||
c := string(ctx.Request.Header.Peek(ContentType))
|
||||
if c != "" {
|
||||
t.Head.ContentType = c
|
||||
}
|
||||
ctx.Response.Header.Add("Content-Encoding", "gzip")
|
||||
t.Head.Write(ctx)
|
||||
fasthttp.WriteGzip(ctx.Response.BodyWriter(), []byte(v.(string)))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Render an XML response.
|
||||
func (x XML) Render(ctx *fasthttp.RequestCtx, v interface{}) error {
|
||||
var result []byte
|
||||
var err error
|
||||
|
||||
if x.Indent {
|
||||
result, err = xml.MarshalIndent(v, "", " ")
|
||||
result = append(result, '\n')
|
||||
} else {
|
||||
result, err = xml.Marshal(v)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// XML marshaled fine, write out the result.
|
||||
x.Head.Write(ctx)
|
||||
w := ctx.Response.BodyWriter()
|
||||
if len(x.Prefix) > 0 {
|
||||
w.Write(x.Prefix)
|
||||
}
|
||||
w.Write(result)
|
||||
return nil
|
||||
}
|
||||
|
||||
// RenderGzip an XML response using gzip compression.
|
||||
func (x XML) RenderGzip(ctx *fasthttp.RequestCtx, v interface{}) error {
|
||||
var result []byte
|
||||
var err error
|
||||
|
||||
if x.Indent {
|
||||
result, err = xml.MarshalIndent(v, "", " ")
|
||||
result = append(result, '\n')
|
||||
} else {
|
||||
result, err = xml.Marshal(v)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx.Response.Header.Add("Content-Encoding", "gzip")
|
||||
// XML marshaled fine, write out the result.
|
||||
x.Head.Write(ctx)
|
||||
w := gzip.NewWriter(ctx.Response.BodyWriter())
|
||||
if len(x.Prefix) > 0 {
|
||||
w.Write(x.Prefix)
|
||||
}
|
||||
w.Write(result)
|
||||
w.Close()
|
||||
return nil
|
||||
}
|
|
@ -1,173 +0,0 @@
|
|||
// Package rest is an edited file of https://github.com/unrolled/render
|
||||
package rest
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris/config"
|
||||
"github.com/kataras/iris/utils"
|
||||
"github.com/microcosm-cc/bluemonday"
|
||||
"github.com/russross/blackfriday"
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
const (
|
||||
// ContentBinary header value for binary data.
|
||||
ContentBinary = "application/octet-stream"
|
||||
// ContentJSON header value for JSON data.
|
||||
ContentJSON = "application/json"
|
||||
// ContentJSONP header value for JSONP data.
|
||||
ContentJSONP = "application/javascript"
|
||||
// ContentLength header constant.
|
||||
ContentLength = "Content-Length"
|
||||
// ContentText header value for Text data.
|
||||
ContentText = "text/plain"
|
||||
// ContentType header constant.
|
||||
ContentType = "Content-Type"
|
||||
// ContentXML header value for XML data.
|
||||
ContentXML = "text/xml"
|
||||
)
|
||||
|
||||
// bufPool represents a reusable buffer pool for executing templates into.
|
||||
var bufPool *utils.BufferPool
|
||||
|
||||
// Render is a service that provides functions for easily writing JSON, XML,
|
||||
// binary data, and HTML templates out to a HTTP Response.
|
||||
type Render struct {
|
||||
// Customize Secure with an Options struct.
|
||||
Config config.Rest
|
||||
CompiledCharset string
|
||||
}
|
||||
|
||||
// New constructs a new Render instance with the supplied configs.
|
||||
func New(cfg ...config.Rest) *Render {
|
||||
if bufPool == nil {
|
||||
bufPool = utils.NewBufferPool(64)
|
||||
}
|
||||
|
||||
c := config.DefaultRest().Merge(cfg)
|
||||
|
||||
r := &Render{
|
||||
Config: c,
|
||||
}
|
||||
|
||||
r.prepareConfig()
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *Render) prepareConfig() {
|
||||
// Fill in the defaults if need be.
|
||||
if len(r.Config.Charset) == 0 {
|
||||
r.Config.Charset = config.Charset
|
||||
}
|
||||
r.CompiledCharset = "; charset=" + r.Config.Charset
|
||||
}
|
||||
|
||||
// Render is the generic function called by XML, JSON, Data, HTML, and can be called by custom implementations.
|
||||
func (r *Render) Render(ctx *fasthttp.RequestCtx, e Engine, data interface{}) error {
|
||||
var err error
|
||||
if r.Config.Gzip {
|
||||
err = e.RenderGzip(ctx, data)
|
||||
} else {
|
||||
err = e.Render(ctx, data)
|
||||
}
|
||||
|
||||
if err != nil && !r.Config.DisableHTTPErrorRendering {
|
||||
ctx.Response.SetBodyString(err.Error())
|
||||
ctx.Response.SetStatusCode(fasthttp.StatusInternalServerError)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Data writes out the raw bytes as binary data.
|
||||
func (r *Render) Data(ctx *fasthttp.RequestCtx, status int, v []byte) error {
|
||||
head := Head{
|
||||
ContentType: ContentBinary,
|
||||
Status: status,
|
||||
}
|
||||
|
||||
d := Data{
|
||||
Head: head,
|
||||
}
|
||||
|
||||
return r.Render(ctx, d, v)
|
||||
}
|
||||
|
||||
// JSON marshals the given interface object and writes the JSON response.
|
||||
func (r *Render) JSON(ctx *fasthttp.RequestCtx, status int, v interface{}) error {
|
||||
head := Head{
|
||||
ContentType: ContentJSON + r.CompiledCharset,
|
||||
Status: status,
|
||||
}
|
||||
|
||||
j := JSON{
|
||||
Head: head,
|
||||
Indent: r.Config.IndentJSON,
|
||||
Prefix: r.Config.PrefixJSON,
|
||||
UnEscapeHTML: r.Config.UnEscapeHTML,
|
||||
StreamingJSON: r.Config.StreamingJSON,
|
||||
}
|
||||
|
||||
return r.Render(ctx, j, v)
|
||||
}
|
||||
|
||||
// JSONP marshals the given interface object and writes the JSON response.
|
||||
func (r *Render) JSONP(ctx *fasthttp.RequestCtx, status int, callback string, v interface{}) error {
|
||||
head := Head{
|
||||
ContentType: ContentJSONP + r.CompiledCharset,
|
||||
Status: status,
|
||||
}
|
||||
|
||||
j := JSONP{
|
||||
Head: head,
|
||||
Indent: r.Config.IndentJSON,
|
||||
Callback: callback,
|
||||
}
|
||||
|
||||
return r.Render(ctx, j, v)
|
||||
}
|
||||
|
||||
// Text writes out a string as plain text.
|
||||
func (r *Render) Text(ctx *fasthttp.RequestCtx, status int, v string) error {
|
||||
head := Head{
|
||||
ContentType: ContentText + r.CompiledCharset,
|
||||
Status: status,
|
||||
}
|
||||
|
||||
t := Text{
|
||||
Head: head,
|
||||
}
|
||||
|
||||
return r.Render(ctx, t, v)
|
||||
}
|
||||
|
||||
// XML marshals the given interface object and writes the XML response.
|
||||
func (r *Render) XML(ctx *fasthttp.RequestCtx, status int, v interface{}) error {
|
||||
head := Head{
|
||||
ContentType: ContentXML + r.CompiledCharset,
|
||||
Status: status,
|
||||
}
|
||||
|
||||
x := XML{
|
||||
Head: head,
|
||||
Indent: r.Config.IndentXML,
|
||||
Prefix: r.Config.PrefixXML,
|
||||
}
|
||||
|
||||
return r.Render(ctx, x, v)
|
||||
}
|
||||
|
||||
// Markdown parses and returns the converted html from a markdown []byte
|
||||
// accepts two parameters
|
||||
// first is the http status code
|
||||
// second is the markdown string
|
||||
//
|
||||
// Note that: Works different than the other rest's functions.
|
||||
func (r *Render) Markdown(markdownBytes []byte) string {
|
||||
buf := blackfriday.MarkdownCommon(markdownBytes)
|
||||
if r.Config.MarkdownSanitize {
|
||||
buf = bluemonday.UGCPolicy().SanitizeBytes(buf)
|
||||
}
|
||||
|
||||
return string(buf)
|
||||
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
# Folder Information
|
||||
|
||||
This folder contains the template support for Iris. The folder name is singular (template) so the `/template/engine`, because you can use **ONLY ONE** at the same time.
|
||||
|
||||
|
||||
## How to use
|
||||
|
||||
**Refer to the Book**
|
|
@ -1,80 +0,0 @@
|
|||
package amber
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"github.com/eknkc/amber"
|
||||
"github.com/kataras/iris/config"
|
||||
)
|
||||
|
||||
// Engine the amber template engine
|
||||
type Engine struct {
|
||||
Config *config.Template
|
||||
templateCache map[string]*template.Template
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
// New creates and returns a new amber engine
|
||||
func New(cfg config.Template) *Engine {
|
||||
return &Engine{Config: &cfg}
|
||||
}
|
||||
|
||||
// BuildTemplates builds the amber templates
|
||||
func (e *Engine) BuildTemplates() error {
|
||||
opt := amber.DirOptions{}
|
||||
opt.Recursive = true
|
||||
if e.Config.Extensions == nil || len(e.Config.Extensions) == 0 {
|
||||
e.Config.Extensions = []string{".html"}
|
||||
}
|
||||
|
||||
// prepare the global amber funcs
|
||||
funcs := template.FuncMap{}
|
||||
for k, v := range amber.FuncMap { // add the amber's default funcs
|
||||
funcs[k] = v
|
||||
}
|
||||
if e.Config.Amber.Funcs != nil { // add the config's funcs
|
||||
for k, v := range e.Config.Amber.Funcs {
|
||||
funcs[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
amber.FuncMap = funcs //set the funcs
|
||||
|
||||
opt.Ext = e.Config.Extensions[0]
|
||||
templates, err := amber.CompileDir(e.Config.Directory, opt, amber.DefaultOptions) // this returns the map with stripped extension, we want extension so we copy the map
|
||||
if err == nil {
|
||||
e.templateCache = make(map[string]*template.Template)
|
||||
for k, v := range templates {
|
||||
name := filepath.ToSlash(k + opt.Ext)
|
||||
e.templateCache[name] = v
|
||||
delete(templates, k)
|
||||
}
|
||||
|
||||
}
|
||||
return err
|
||||
|
||||
}
|
||||
func (e *Engine) fromCache(relativeName string) *template.Template {
|
||||
e.mu.Lock()
|
||||
tmpl, ok := e.templateCache[relativeName]
|
||||
if ok {
|
||||
e.mu.Unlock()
|
||||
return tmpl
|
||||
}
|
||||
e.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExecuteWriter executes a templates and write its results to the out writer
|
||||
func (e *Engine) ExecuteWriter(out io.Writer, name string, binding interface{}, layout string) error {
|
||||
if tmpl := e.fromCache(name); tmpl != nil {
|
||||
return tmpl.ExecuteTemplate(out, name, binding)
|
||||
}
|
||||
|
||||
return fmt.Errorf("[IRIS TEMPLATES] Template with name %s doesn't exists in the dir %s", name, e.Config.Directory)
|
||||
}
|
|
@ -1,162 +0,0 @@
|
|||
// Package handlebars the HandlebarsEngine's functionality
|
||||
package handlebars
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/aymerick/raymond"
|
||||
"github.com/kataras/iris/config"
|
||||
)
|
||||
|
||||
type (
|
||||
// Engine the Handlebars engine
|
||||
Engine struct {
|
||||
Config *config.Template
|
||||
templateCache map[string]*raymond.Template
|
||||
mu sync.Mutex
|
||||
}
|
||||
)
|
||||
|
||||
// New creates and returns the Handlebars template engine
|
||||
func New(c config.Template) *Engine {
|
||||
s := &Engine{Config: &c, templateCache: make(map[string]*raymond.Template, 0)}
|
||||
return s
|
||||
}
|
||||
|
||||
// BuildTemplates builds the handlebars templates
|
||||
func (e *Engine) BuildTemplates() error {
|
||||
if e.Config.Extensions == nil || len(e.Config.Extensions) == 0 {
|
||||
e.Config.Extensions = []string{".html"}
|
||||
}
|
||||
|
||||
// register the global helpers
|
||||
if e.Config.Handlebars.Helpers != nil {
|
||||
raymond.RegisterHelpers(e.Config.Handlebars.Helpers)
|
||||
}
|
||||
|
||||
// the render works like {{ render "myfile.html" theContext.PartialContext}}
|
||||
// instead of the html/template engine which works like {{ render "myfile.html"}} and accepts the parent binding, with handlebars we can't do that because of lack of runtime helpers (dublicate error)
|
||||
raymond.RegisterHelper("render", func(partial string, binding interface{}) raymond.SafeString {
|
||||
contents, err := e.executeTemplateBuf(partial, binding)
|
||||
if err != nil {
|
||||
return raymond.SafeString("Template with name: " + partial + " couldn't not be found.")
|
||||
}
|
||||
return raymond.SafeString(contents)
|
||||
})
|
||||
|
||||
var templateErr error
|
||||
|
||||
dir := e.Config.Directory
|
||||
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||
if info == nil || info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
rel, err := filepath.Rel(dir, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ext := ""
|
||||
if strings.Index(rel, ".") != -1 {
|
||||
ext = filepath.Ext(rel)
|
||||
}
|
||||
|
||||
for _, extension := range e.Config.Extensions {
|
||||
if ext == extension {
|
||||
|
||||
buf, err := ioutil.ReadFile(path)
|
||||
contents := string(buf)
|
||||
|
||||
if err != nil {
|
||||
templateErr = err
|
||||
break
|
||||
}
|
||||
|
||||
name := filepath.ToSlash(rel)
|
||||
|
||||
tmpl, err := raymond.Parse(contents)
|
||||
if err != nil {
|
||||
templateErr = err
|
||||
continue
|
||||
}
|
||||
e.mu.Lock()
|
||||
e.templateCache[name] = tmpl
|
||||
e.mu.Unlock()
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
return templateErr
|
||||
|
||||
}
|
||||
func (e *Engine) fromCache(relativeName string) *raymond.Template {
|
||||
e.mu.Lock()
|
||||
tmpl, ok := e.templateCache[relativeName]
|
||||
if ok {
|
||||
e.mu.Unlock()
|
||||
return tmpl
|
||||
}
|
||||
e.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Engine) executeTemplateBuf(name string, binding interface{}) (string, error) {
|
||||
if tmpl := e.fromCache(name); tmpl != nil {
|
||||
return tmpl.Exec(binding)
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// ExecuteWriter executes a templates and write its results to the out writer
|
||||
func (e *Engine) ExecuteWriter(out io.Writer, name string, binding interface{}, layout string) error {
|
||||
|
||||
isLayout := false
|
||||
|
||||
renderFilename := name
|
||||
if layout != "" {
|
||||
isLayout = true
|
||||
renderFilename = layout // the render becomes the layout, and the name is the partial.
|
||||
}
|
||||
|
||||
if tmpl := e.fromCache(renderFilename); tmpl != nil {
|
||||
if isLayout {
|
||||
var context map[string]interface{}
|
||||
if m, is := binding.(map[string]interface{}); is { //handlebars accepts maps,
|
||||
context = m
|
||||
} else {
|
||||
return fmt.Errorf("Please provide a map[string]interface{} type as the binding instead of the %#v", binding)
|
||||
}
|
||||
|
||||
contents, err := e.executeTemplateBuf(name, binding)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if context == nil {
|
||||
context = make(map[string]interface{}, 1)
|
||||
}
|
||||
// I'm implemented the {{ yield }} as with the rest of template engines, so this is not inneed for iris, but the user can do that manually if want
|
||||
// there is no performanrce different: raymond.RegisterPartialTemplate(name, tmpl)
|
||||
context["yield"] = raymond.SafeString(contents)
|
||||
}
|
||||
|
||||
res, err := tmpl.Exec(binding)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = fmt.Fprint(out, res)
|
||||
return err
|
||||
}
|
||||
|
||||
return fmt.Errorf("[IRIS TEMPLATES] Template with name %s[original name = %s] doesn't exists in the dir %s", renderFilename, name, e.Config.Directory)
|
||||
}
|
|
@ -1,245 +0,0 @@
|
|||
package html
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/Joker/jade"
|
||||
"github.com/kataras/iris/config"
|
||||
)
|
||||
|
||||
type (
|
||||
// Engine the html/template engine & Jade
|
||||
Engine struct {
|
||||
Config *config.Template
|
||||
Templates *template.Template
|
||||
}
|
||||
)
|
||||
|
||||
var emptyFuncs = template.FuncMap{
|
||||
"yield": func() (string, error) {
|
||||
return "", fmt.Errorf("yield was called, yet no layout defined")
|
||||
},
|
||||
"partial": func() (string, error) {
|
||||
return "", fmt.Errorf("block was called, yet no layout defined")
|
||||
},
|
||||
"current": func() (string, error) {
|
||||
return "", nil
|
||||
}, "render": func() (string, error) {
|
||||
return "", nil
|
||||
},
|
||||
}
|
||||
|
||||
// New creates and returns the HTMLTemplate template engine
|
||||
func New(c config.Template) *Engine {
|
||||
s := &Engine{Config: &c}
|
||||
return s
|
||||
}
|
||||
|
||||
// BuildTemplates builds the templates
|
||||
func (s *Engine) BuildTemplates() error {
|
||||
|
||||
if s.Config.Asset == nil || s.Config.AssetNames == nil {
|
||||
return s.buildFromDir()
|
||||
|
||||
}
|
||||
return s.buildFromAsset()
|
||||
|
||||
}
|
||||
|
||||
func (s *Engine) buildFromDir() error {
|
||||
if s.Config.Directory == "" {
|
||||
return nil //we don't return fill error here(yet)
|
||||
}
|
||||
|
||||
var templateErr error
|
||||
/*var minifier *minify.M
|
||||
if s.Config.Minify {
|
||||
minifier = minify.New()
|
||||
minifier.AddFunc("text/html", htmlMinifier.Minify)
|
||||
} // Note: minifier has bugs, I complety remove this from Iris.
|
||||
*/
|
||||
dir := s.Config.Directory
|
||||
s.Templates = template.New(dir)
|
||||
s.Templates.Delims(s.Config.HTMLTemplate.Left, s.Config.HTMLTemplate.Right)
|
||||
// Walk the supplied directory and compile any files that match our extension list.
|
||||
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||
if info == nil || info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
rel, err := filepath.Rel(dir, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ext := ""
|
||||
if strings.Index(rel, ".") != -1 {
|
||||
ext = filepath.Ext(rel)
|
||||
}
|
||||
|
||||
for _, extension := range s.Config.Extensions {
|
||||
if ext == extension {
|
||||
|
||||
buf, err := ioutil.ReadFile(path)
|
||||
contents := string(buf)
|
||||
/*if s.Config.Minify {
|
||||
buf, err = minifier.Bytes("text/html", buf)
|
||||
}*/
|
||||
|
||||
if err != nil {
|
||||
templateErr = err
|
||||
break
|
||||
}
|
||||
|
||||
name := filepath.ToSlash(rel)
|
||||
tmpl := s.Templates.New(name)
|
||||
|
||||
if s.Config.Engine == config.JadeEngine {
|
||||
contents, err = jade.Parse(name, contents)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
templateErr = err
|
||||
break
|
||||
}
|
||||
|
||||
// Add our funcmaps.
|
||||
if s.Config.HTMLTemplate.Funcs != nil {
|
||||
tmpl.Funcs(s.Config.HTMLTemplate.Funcs)
|
||||
}
|
||||
|
||||
tmpl.Funcs(emptyFuncs).Parse(contents)
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
return templateErr
|
||||
}
|
||||
|
||||
func (s *Engine) buildFromAsset() error {
|
||||
var templateErr error
|
||||
dir := s.Config.Directory
|
||||
s.Templates = template.New(dir)
|
||||
s.Templates.Delims(s.Config.HTMLTemplate.Left, s.Config.HTMLTemplate.Right)
|
||||
|
||||
for _, path := range s.Config.AssetNames() {
|
||||
if !strings.HasPrefix(path, dir) {
|
||||
continue
|
||||
}
|
||||
|
||||
rel, err := filepath.Rel(dir, path)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ext := ""
|
||||
if strings.Index(rel, ".") != -1 {
|
||||
ext = "." + strings.Join(strings.Split(rel, ".")[1:], ".")
|
||||
}
|
||||
|
||||
for _, extension := range s.Config.Extensions {
|
||||
if ext == extension {
|
||||
|
||||
buf, err := s.Config.Asset(path)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
contents := string(buf)
|
||||
name := filepath.ToSlash(rel)
|
||||
tmpl := s.Templates.New(name)
|
||||
|
||||
if s.Config.Engine == config.JadeEngine {
|
||||
contents, err = jade.Parse(name, contents)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Add our funcmaps.
|
||||
if s.Config.HTMLTemplate.Funcs != nil {
|
||||
tmpl.Funcs(s.Config.HTMLTemplate.Funcs)
|
||||
}
|
||||
|
||||
tmpl.Funcs(emptyFuncs).Parse(contents)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return templateErr
|
||||
}
|
||||
|
||||
func (s *Engine) executeTemplateBuf(name string, binding interface{}) (*bytes.Buffer, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
err := s.Templates.ExecuteTemplate(buf, name, binding)
|
||||
|
||||
return buf, err
|
||||
}
|
||||
|
||||
func (s *Engine) layoutFuncsFor(name string, binding interface{}) {
|
||||
funcs := template.FuncMap{
|
||||
"yield": func() (template.HTML, error) {
|
||||
buf, err := s.executeTemplateBuf(name, binding)
|
||||
// Return safe HTML here since we are rendering our own template.
|
||||
return template.HTML(buf.String()), err
|
||||
},
|
||||
"current": func() (string, error) {
|
||||
return name, nil
|
||||
},
|
||||
"partial": func(partialName string) (template.HTML, error) {
|
||||
fullPartialName := fmt.Sprintf("%s-%s", partialName, name)
|
||||
if s.Config.HTMLTemplate.RequirePartials || s.Templates.Lookup(fullPartialName) != nil {
|
||||
buf, err := s.executeTemplateBuf(fullPartialName, binding)
|
||||
return template.HTML(buf.String()), err
|
||||
}
|
||||
return "", nil
|
||||
},
|
||||
"render": func(fullPartialName string) (template.HTML, error) {
|
||||
buf, err := s.executeTemplateBuf(fullPartialName, binding)
|
||||
return template.HTML(buf.String()), err
|
||||
},
|
||||
}
|
||||
_userLayoutFuncs := s.Config.HTMLTemplate.LayoutFuncs
|
||||
if _userLayoutFuncs != nil && len(_userLayoutFuncs) > 0 {
|
||||
for k, v := range _userLayoutFuncs {
|
||||
funcs[k] = v
|
||||
}
|
||||
}
|
||||
if tpl := s.Templates.Lookup(name); tpl != nil {
|
||||
tpl.Funcs(funcs)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Engine) runtimeFuncsFor(name string, binding interface{}) {
|
||||
funcs := template.FuncMap{
|
||||
"render": func(fullPartialName string) (template.HTML, error) {
|
||||
buf, err := s.executeTemplateBuf(fullPartialName, binding)
|
||||
return template.HTML(buf.String()), err
|
||||
},
|
||||
}
|
||||
|
||||
if tpl := s.Templates.Lookup(name); tpl != nil {
|
||||
tpl.Funcs(funcs)
|
||||
}
|
||||
}
|
||||
|
||||
// ExecuteWriter executes a templates and write its results to the out writer
|
||||
func (s *Engine) ExecuteWriter(out io.Writer, name string, binding interface{}, layout string) error {
|
||||
if layout != "" {
|
||||
s.layoutFuncsFor(name, binding)
|
||||
name = layout
|
||||
|
||||
} else {
|
||||
s.runtimeFuncsFor(name, binding)
|
||||
}
|
||||
|
||||
return s.Templates.ExecuteTemplate(out, name, binding)
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
// Package jade the JadeEngine's functionality lives inside ../html now
|
||||
package jade
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris/config"
|
||||
"github.com/kataras/iris/render/template/engine/html"
|
||||
)
|
||||
|
||||
// New creates and returns the HTMLTemplate template engine
|
||||
func New(c config.Template) *html.Engine {
|
||||
// copy the Jade to the HTMLTemplate
|
||||
c.HTMLTemplate = config.HTMLTemplate(c.Jade)
|
||||
s := &html.Engine{Config: &c}
|
||||
return s
|
||||
}
|
|
@ -1,155 +0,0 @@
|
|||
package markdown
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"fmt"
|
||||
|
||||
"github.com/kataras/iris/config"
|
||||
"github.com/microcosm-cc/bluemonday"
|
||||
"github.com/russross/blackfriday"
|
||||
)
|
||||
|
||||
// Supports RAW markdown only, no context binding or layout, to use dynamic markdown with other template engine use the context.Markdown/MarkdownString
|
||||
|
||||
type (
|
||||
// Engine the jade engine
|
||||
Engine struct {
|
||||
Config *config.Template
|
||||
templateCache map[string][]byte
|
||||
mu sync.Mutex
|
||||
}
|
||||
)
|
||||
|
||||
// New creates and returns a Pongo template engine
|
||||
func New(c config.Template) *Engine {
|
||||
return &Engine{Config: &c, templateCache: make(map[string][]byte)}
|
||||
}
|
||||
|
||||
// BuildTemplates builds the templates
|
||||
func (e *Engine) BuildTemplates() error {
|
||||
if e.Config.Asset == nil || e.Config.AssetNames == nil {
|
||||
return e.buildFromDir()
|
||||
}
|
||||
return e.buildFromAsset()
|
||||
|
||||
}
|
||||
|
||||
func (e *Engine) buildFromDir() (templateErr error) {
|
||||
if e.Config.Directory == "" {
|
||||
return nil //we don't return fill error here(yet)
|
||||
}
|
||||
dir := e.Config.Directory
|
||||
|
||||
// Walk the supplied directory and compile any files that match our extension list.
|
||||
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||
|
||||
if info == nil || info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
rel, err := filepath.Rel(dir, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ext := ""
|
||||
if strings.Index(rel, ".") != -1 {
|
||||
ext = filepath.Ext(rel)
|
||||
}
|
||||
|
||||
for _, extension := range e.Config.Extensions {
|
||||
if ext == extension {
|
||||
buf, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
templateErr = err
|
||||
break
|
||||
}
|
||||
|
||||
buf = blackfriday.MarkdownCommon(buf)
|
||||
if e.Config.Markdown.Sanitize {
|
||||
buf = bluemonday.UGCPolicy().SanitizeBytes(buf)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
templateErr = err
|
||||
break
|
||||
}
|
||||
name := filepath.ToSlash(rel)
|
||||
e.templateCache[name] = buf
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Engine) buildFromAsset() error {
|
||||
var templateErr error
|
||||
dir := e.Config.Directory
|
||||
for _, path := range e.Config.AssetNames() {
|
||||
if !strings.HasPrefix(path, dir) {
|
||||
continue
|
||||
}
|
||||
|
||||
rel, err := filepath.Rel(dir, path)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ext := ""
|
||||
if strings.Index(rel, ".") != -1 {
|
||||
ext = "." + strings.Join(strings.Split(rel, ".")[1:], ".")
|
||||
}
|
||||
|
||||
for _, extension := range e.Config.Extensions {
|
||||
if ext == extension {
|
||||
|
||||
buf, err := e.Config.Asset(path)
|
||||
if err != nil {
|
||||
templateErr = err
|
||||
break
|
||||
}
|
||||
b := blackfriday.MarkdownCommon(buf)
|
||||
if e.Config.Markdown.Sanitize {
|
||||
b = bluemonday.UGCPolicy().SanitizeBytes(b)
|
||||
}
|
||||
name := filepath.ToSlash(rel)
|
||||
e.templateCache[name] = b
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return templateErr
|
||||
}
|
||||
|
||||
func (e *Engine) fromCache(relativeName string) []byte {
|
||||
e.mu.Lock()
|
||||
|
||||
tmpl, ok := e.templateCache[relativeName]
|
||||
|
||||
if ok {
|
||||
e.mu.Unlock() // defer is slow
|
||||
return tmpl
|
||||
}
|
||||
e.mu.Unlock() // defer is slow
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExecuteWriter executes a templates and write its results to the out writer
|
||||
// layout here is useless
|
||||
func (e *Engine) ExecuteWriter(out io.Writer, name string, binding interface{}, layout string) error {
|
||||
if tmpl := e.fromCache(name); tmpl != nil {
|
||||
_, err := out.Write(tmpl)
|
||||
return err
|
||||
}
|
||||
|
||||
return fmt.Errorf("[IRIS TEMPLATES] Template with name %s doesn't exists in the dir %s", name, e.Config.Directory)
|
||||
}
|
|
@ -1,183 +0,0 @@
|
|||
package pongo
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"fmt"
|
||||
|
||||
"github.com/flosch/pongo2"
|
||||
"github.com/kataras/iris/config"
|
||||
)
|
||||
|
||||
type (
|
||||
// Engine the pongo2 engine
|
||||
Engine struct {
|
||||
Config *config.Template
|
||||
templateCache map[string]*pongo2.Template
|
||||
mu sync.Mutex
|
||||
}
|
||||
)
|
||||
|
||||
// New creates and returns a Pongo template engine
|
||||
func New(c config.Template) *Engine {
|
||||
return &Engine{Config: &c, templateCache: make(map[string]*pongo2.Template)}
|
||||
}
|
||||
|
||||
// BuildTemplates builds the templates
|
||||
func (p *Engine) BuildTemplates() error {
|
||||
// Add our filters. first
|
||||
for k, v := range p.Config.Pongo.Filters {
|
||||
pongo2.RegisterFilter(k, v)
|
||||
}
|
||||
if p.Config.Asset == nil || p.Config.AssetNames == nil {
|
||||
return p.buildFromDir()
|
||||
|
||||
}
|
||||
return p.buildFromAsset()
|
||||
|
||||
}
|
||||
|
||||
func (p *Engine) buildFromDir() (templateErr error) {
|
||||
if p.Config.Directory == "" {
|
||||
return nil //we don't return fill error here(yet)
|
||||
}
|
||||
dir := p.Config.Directory
|
||||
|
||||
fsLoader, err := pongo2.NewLocalFileSystemLoader(dir) // I see that this doesn't read the content if already parsed, so do it manually via filepath.Walk
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
set := pongo2.NewSet("", fsLoader)
|
||||
set.Globals = getPongoContext(p.Config.Pongo.Globals)
|
||||
// Walk the supplied directory and compile any files that match our extension list.
|
||||
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||
// Fix same-extension-dirs bug: some dir might be named to: "users.tmpl", "local.html".
|
||||
// These dirs should be excluded as they are not valid golang templates, but files under
|
||||
// them should be treat as normal.
|
||||
// If is a dir, return immediately (dir is not a valid golang template).
|
||||
if info == nil || info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
rel, err := filepath.Rel(dir, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ext := ""
|
||||
if strings.Index(rel, ".") != -1 {
|
||||
ext = filepath.Ext(rel)
|
||||
}
|
||||
|
||||
for _, extension := range p.Config.Extensions {
|
||||
if ext == extension {
|
||||
buf, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
templateErr = err
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
templateErr = err
|
||||
break
|
||||
}
|
||||
name := filepath.ToSlash(rel)
|
||||
p.templateCache[name], templateErr = set.FromString(string(buf))
|
||||
|
||||
if templateErr != nil {
|
||||
return templateErr
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (p *Engine) buildFromAsset() error {
|
||||
var templateErr error
|
||||
dir := p.Config.Directory
|
||||
fsLoader, err := pongo2.NewLocalFileSystemLoader(dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
set := pongo2.NewSet("", fsLoader)
|
||||
set.Globals = getPongoContext(p.Config.Pongo.Globals)
|
||||
for _, path := range p.Config.AssetNames() {
|
||||
if !strings.HasPrefix(path, dir) {
|
||||
continue
|
||||
}
|
||||
|
||||
rel, err := filepath.Rel(dir, path)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ext := ""
|
||||
if strings.Index(rel, ".") != -1 {
|
||||
ext = "." + strings.Join(strings.Split(rel, ".")[1:], ".")
|
||||
}
|
||||
|
||||
for _, extension := range p.Config.Extensions {
|
||||
if ext == extension {
|
||||
|
||||
buf, err := p.Config.Asset(path)
|
||||
if err != nil {
|
||||
templateErr = err
|
||||
break
|
||||
}
|
||||
name := filepath.ToSlash(rel)
|
||||
p.templateCache[name], err = set.FromString(string(buf))
|
||||
if err != nil {
|
||||
templateErr = err
|
||||
break
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return templateErr
|
||||
}
|
||||
|
||||
// getPongoContext returns the pongo2.Context from map[string]interface{} or from pongo2.Context, used internaly
|
||||
func getPongoContext(templateData interface{}) pongo2.Context {
|
||||
if templateData == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if contextData, isPongoContext := templateData.(pongo2.Context); isPongoContext {
|
||||
return contextData
|
||||
}
|
||||
|
||||
return templateData.(map[string]interface{})
|
||||
}
|
||||
|
||||
func (p *Engine) fromCache(relativeName string) *pongo2.Template {
|
||||
p.mu.Lock() // defer is slow
|
||||
|
||||
tmpl, ok := p.templateCache[relativeName]
|
||||
|
||||
if ok {
|
||||
p.mu.Unlock()
|
||||
return tmpl
|
||||
}
|
||||
p.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExecuteWriter executes a templates and write its results to the out writer
|
||||
// layout here is useless
|
||||
func (p *Engine) ExecuteWriter(out io.Writer, name string, binding interface{}, layout string) error {
|
||||
if tmpl := p.fromCache(name); tmpl != nil {
|
||||
return tmpl.ExecuteWriter(getPongoContext(binding), out)
|
||||
}
|
||||
|
||||
return fmt.Errorf("[IRIS TEMPLATES] Template with name %s doesn't exists in the dir %s", name, p.Config.Directory)
|
||||
}
|
|
@ -1,231 +0,0 @@
|
|||
package template
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/klauspost/compress/gzip"
|
||||
|
||||
"sync"
|
||||
|
||||
"github.com/kataras/iris/config"
|
||||
"github.com/kataras/iris/context"
|
||||
"github.com/kataras/iris/render/template/engine/amber"
|
||||
"github.com/kataras/iris/render/template/engine/handlebars"
|
||||
"github.com/kataras/iris/render/template/engine/html"
|
||||
"github.com/kataras/iris/render/template/engine/jade"
|
||||
"github.com/kataras/iris/render/template/engine/markdown"
|
||||
"github.com/kataras/iris/render/template/engine/pongo"
|
||||
"github.com/kataras/iris/utils"
|
||||
)
|
||||
|
||||
type (
|
||||
// Engine the interface that all template engines must inheritance
|
||||
Engine interface {
|
||||
// BuildTemplates builds the templates for a directory
|
||||
BuildTemplates() error
|
||||
// ExecuteWriter finds and execute a template and write its result to the out writer
|
||||
ExecuteWriter(out io.Writer, name string, binding interface{}, layout string) error
|
||||
}
|
||||
|
||||
// Template the internal configs for the common configs for the template engines
|
||||
Template struct {
|
||||
// Engine the type of the Engine
|
||||
Engine Engine
|
||||
// Gzip enable gzip compression
|
||||
// default is false
|
||||
Gzip bool
|
||||
// IsDevelopment re-builds the templates on each request
|
||||
// default is false
|
||||
IsDevelopment bool
|
||||
// Directory the system path which the templates live
|
||||
// default is ./templates
|
||||
Directory string
|
||||
// Extensions the allowed file extension
|
||||
// default is []string{".html"}
|
||||
Extensions []string
|
||||
// ContentType is the Content-Type response header
|
||||
// default is text/html but you can change if if needed
|
||||
ContentType string
|
||||
// Layout the template file ( with its extension) which is the mother of all
|
||||
// use it to have it as a root file, and include others with {{ yield }}, refer the docs
|
||||
Layout string
|
||||
|
||||
buffer *utils.BufferPool // this is used only for RenderString
|
||||
gzipWriterPool sync.Pool
|
||||
}
|
||||
)
|
||||
|
||||
// sharedFuncs the funcs should be exists in all supported view template engines
|
||||
var sharedFuncs map[string]interface{}
|
||||
|
||||
// we do this because we don't want to override the user's funcs
|
||||
func setSharedFuncs(source map[string]interface{}, target map[string]interface{}) {
|
||||
if source == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if target == nil {
|
||||
target = make(map[string]interface{}, len(source))
|
||||
}
|
||||
|
||||
for k, v := range source {
|
||||
if target[k] == nil {
|
||||
target[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// New creates and returns a Template instance which keeps the Template Engine and helps with render
|
||||
func New(c config.Template) *Template {
|
||||
defer func() {
|
||||
sharedFuncs = nil
|
||||
}()
|
||||
|
||||
var e Engine
|
||||
// [ENGINE-2]
|
||||
switch c.Engine {
|
||||
case config.HTMLEngine:
|
||||
setSharedFuncs(sharedFuncs, c.HTMLTemplate.Funcs)
|
||||
e = html.New(c) // HTMLTemplate
|
||||
case config.JadeEngine:
|
||||
setSharedFuncs(sharedFuncs, c.Jade.Funcs)
|
||||
e = jade.New(c) // Jade
|
||||
case config.PongoEngine:
|
||||
setSharedFuncs(sharedFuncs, c.Pongo.Globals)
|
||||
e = pongo.New(c) // Pongo2
|
||||
case config.MarkdownEngine:
|
||||
e = markdown.New(c) // Markdown
|
||||
case config.AmberEngine:
|
||||
setSharedFuncs(sharedFuncs, c.Amber.Funcs)
|
||||
e = amber.New(c) // Amber
|
||||
case config.HandlebarsEngine:
|
||||
setSharedFuncs(sharedFuncs, c.Handlebars.Helpers)
|
||||
e = handlebars.New(c)
|
||||
default: // config.NoEngine
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := e.BuildTemplates(); err != nil { // first build the templates, if error then panic because this is called before server's run
|
||||
panic(err)
|
||||
}
|
||||
|
||||
compiledContentType := c.ContentType + "; charset=" + c.Charset
|
||||
|
||||
t := &Template{
|
||||
Engine: e,
|
||||
IsDevelopment: c.IsDevelopment,
|
||||
Gzip: c.Gzip,
|
||||
ContentType: compiledContentType,
|
||||
Layout: c.Layout,
|
||||
buffer: utils.NewBufferPool(64),
|
||||
gzipWriterPool: sync.Pool{New: func() interface{} {
|
||||
return &gzip.Writer{}
|
||||
}},
|
||||
}
|
||||
|
||||
return t
|
||||
|
||||
}
|
||||
|
||||
// RegisterSharedFunc registers a functionality that should be inherited from all supported template engines
|
||||
func RegisterSharedFunc(name string, fn interface{}) {
|
||||
if sharedFuncs == nil {
|
||||
sharedFuncs = make(map[string]interface{})
|
||||
}
|
||||
sharedFuncs[name] = fn
|
||||
}
|
||||
|
||||
// RegisterSharedFuncs registers functionalities that should be inherited from all supported template engines
|
||||
func RegisterSharedFuncs(theFuncs map[string]interface{}) {
|
||||
if sharedFuncs == nil || len(sharedFuncs) == 0 {
|
||||
sharedFuncs = theFuncs
|
||||
return
|
||||
}
|
||||
for k, v := range theFuncs {
|
||||
sharedFuncs[k] = v
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (t *Template) getLayout(ctx context.IContext, passedLayouts []string) string {
|
||||
|
||||
_layout := ""
|
||||
if len(passedLayouts) > 0 {
|
||||
_layout = passedLayouts[0]
|
||||
} else if ctx != nil {
|
||||
if layoutFromCtx := ctx.GetString(config.TemplateLayoutContextKey); layoutFromCtx != "" {
|
||||
_layout = layoutFromCtx
|
||||
}
|
||||
}
|
||||
if _layout == "" && _layout != config.NoLayout {
|
||||
if t.Layout != config.NoLayout { // the config's Layout is disabled if "" , config.NoLayout should be passed only on ctx.Render but for any case check if user set it as config.NoLayout also
|
||||
_layout = t.Layout
|
||||
}
|
||||
}
|
||||
|
||||
return _layout
|
||||
}
|
||||
|
||||
// Render renders a template using the context's writer
|
||||
func (t *Template) Render(ctx context.IContext, name string, binding interface{}, layout ...string) (err error) {
|
||||
|
||||
if t == nil { // No engine was given but .Render was called
|
||||
ctx.HTML(403, "<b> Iris </b> <br/> Templates are disabled via config.NoEngine, check your iris' configuration please.")
|
||||
return fmt.Errorf("[IRIS TEMPLATES] Templates are disabled via config.NoEngine, check your iris' configuration please.\n")
|
||||
}
|
||||
|
||||
// build templates again on each render if IsDevelopment.
|
||||
if t.IsDevelopment {
|
||||
if err = t.Engine.BuildTemplates(); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
_layout := t.getLayout(ctx, layout)
|
||||
|
||||
ctx.GetRequestCtx().Response.Header.Set("Content-Type", t.ContentType)
|
||||
|
||||
var out io.Writer
|
||||
if t.Gzip {
|
||||
ctx.GetRequestCtx().Response.Header.Add("Content-Encoding", "gzip")
|
||||
gzipWriter := t.gzipWriterPool.Get().(*gzip.Writer)
|
||||
gzipWriter.Reset(ctx.GetRequestCtx().Response.BodyWriter())
|
||||
defer gzipWriter.Close()
|
||||
defer t.gzipWriterPool.Put(gzipWriter)
|
||||
out = gzipWriter
|
||||
} else {
|
||||
out = ctx.GetRequestCtx().Response.BodyWriter()
|
||||
}
|
||||
|
||||
err = t.Engine.ExecuteWriter(out, name, binding, _layout)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// RenderString executes a template and returns its contents result (string)
|
||||
func (t *Template) RenderString(name string, binding interface{}, layout ...string) (result string, err error) {
|
||||
|
||||
if t == nil { // No engine was given but .Render was called
|
||||
err = fmt.Errorf("[IRIS TEMPLATES] Templates are disabled via config.NoEngine, check your iris' configuration please.\n")
|
||||
return
|
||||
}
|
||||
|
||||
// build templates again on each render if IsDevelopment.
|
||||
if t.IsDevelopment {
|
||||
if err = t.Engine.BuildTemplates(); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
_layout := t.getLayout(nil, layout)
|
||||
|
||||
out := t.buffer.Get()
|
||||
// if we have problems later consider that -> out.Reset()
|
||||
defer t.buffer.Put(out)
|
||||
err = t.Engine.ExecuteWriter(out, name, binding, _layout)
|
||||
if err == nil {
|
||||
result = out.String()
|
||||
}
|
||||
return
|
||||
}
|
277
template.go
Normal file
277
template.go
Normal file
|
@ -0,0 +1,277 @@
|
|||
package iris
|
||||
|
||||
import (
|
||||
"compress/gzip"
|
||||
"io"
|
||||
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"github.com/iris-contrib/errors"
|
||||
"github.com/kataras/iris/config"
|
||||
"github.com/kataras/iris/utils"
|
||||
)
|
||||
|
||||
var (
|
||||
builtinFuncs = [...]string{"url", "urlpath"}
|
||||
// these are used inside load all in order when no directory and no extension is provided by the dev
|
||||
|
||||
// DefaultTemplateDirectory the default directory if empty setted
|
||||
DefaultTemplateDirectory = "." + utils.PathSeparator + "templates"
|
||||
// DefaultTemplateExtension the default file extension if empty setted
|
||||
DefaultTemplateExtension = ".html"
|
||||
|
||||
// for conversional, exists on config because are shared:
|
||||
|
||||
// NoLayout pass it to the layout option on the context.Render to disable layout for this execution
|
||||
NoLayout = config.NoLayout
|
||||
)
|
||||
|
||||
type (
|
||||
// TemplateEngine the interface that all template engines must implement
|
||||
TemplateEngine interface {
|
||||
// LoadDirectory builds the templates, usually by directory and extension but these are engine's decisions
|
||||
LoadDirectory(directory string, extension string) error
|
||||
// LoadAssets loads the templates by binary
|
||||
// assetFn is a func which returns bytes, use it to load the templates by binary
|
||||
// namesFn returns the template filenames
|
||||
LoadAssets(virtualDirectory string, virtualExtension string, assetFn func(name string) ([]byte, error), namesFn func() []string) error
|
||||
|
||||
// ExecuteWriter finds, execute a template and write its result to the out writer
|
||||
// options are the optional runtime options can be passed by user and catched by the template engine when render
|
||||
// an example of this is the "layout" or "gzip" option
|
||||
ExecuteWriter(out io.Writer, name string, binding interface{}, options ...map[string]interface{}) error
|
||||
}
|
||||
|
||||
// TemplateEngineFuncs is optional interface for the TemplateEngine
|
||||
// used to insert the Iris' standard funcs, see var 'usedFuncs'
|
||||
TemplateEngineFuncs interface {
|
||||
// Funcs should returns the context or the funcs,
|
||||
// this property is used in order to register the iris' helper funcs
|
||||
Funcs() map[string]interface{}
|
||||
}
|
||||
)
|
||||
|
||||
type (
|
||||
// TemplateFuncs is is a helper type for map[string]interface{}
|
||||
TemplateFuncs map[string]interface{}
|
||||
// RenderOptions is a helper type for the optional runtime options can be passed by user and catched by the template engine when render
|
||||
// an example of this is the "layout" option
|
||||
RenderOptions map[string]interface{}
|
||||
)
|
||||
|
||||
// IsFree returns true if a function can be inserted to this map
|
||||
// return false if this key is already used by Iris
|
||||
func (t TemplateFuncs) IsFree(key string) bool {
|
||||
for i := range builtinFuncs {
|
||||
if builtinFuncs[i] == key {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
type (
|
||||
// TemplateEngineLocation contains the funcs to set the location for the templates by directory or by binary
|
||||
TemplateEngineLocation struct {
|
||||
directory string
|
||||
extension string
|
||||
assetFn func(name string) ([]byte, error)
|
||||
namesFn func() []string
|
||||
}
|
||||
// TemplateEngineBinaryLocation called after TemplateEngineLocation's Directory, used when files are distrubuted inside the app executable
|
||||
TemplateEngineBinaryLocation struct {
|
||||
location *TemplateEngineLocation
|
||||
}
|
||||
)
|
||||
|
||||
// Directory sets the directory to load from
|
||||
// returns the Binary location which is optional
|
||||
func (t *TemplateEngineLocation) Directory(dir string, fileExtension string) TemplateEngineBinaryLocation {
|
||||
t.directory = dir
|
||||
t.extension = fileExtension
|
||||
return TemplateEngineBinaryLocation{location: t}
|
||||
}
|
||||
|
||||
// Binary sets the asset(s) and asssets names to load from, works with Directory
|
||||
func (t *TemplateEngineBinaryLocation) Binary(assetFn func(name string) ([]byte, error), namesFn func() []string) {
|
||||
t.location.assetFn = assetFn
|
||||
t.location.namesFn = namesFn
|
||||
// if extension is not static(setted by .Directory)
|
||||
if t.location.extension == "" {
|
||||
if names := namesFn(); len(names) > 0 {
|
||||
t.location.extension = filepath.Ext(names[0]) // we need the extension to get the correct template engine on the Render method
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TemplateEngineLocation) isBinary() bool {
|
||||
return t.assetFn != nil && t.namesFn != nil
|
||||
}
|
||||
|
||||
// TemplateEngineWrapper is the wrapper of a template engine
|
||||
type TemplateEngineWrapper struct {
|
||||
TemplateEngine
|
||||
location *TemplateEngineLocation
|
||||
buffer *utils.BufferPool
|
||||
gzipWriterPool sync.Pool
|
||||
reload bool
|
||||
combiledContentType string
|
||||
}
|
||||
|
||||
var (
|
||||
errMissingDirectoryOrAssets = errors.New("Missing Directory or Assets by binary for the template engine!")
|
||||
errNoTemplateEngineForExt = errors.New("No template engine found to manage '%s' extensions")
|
||||
)
|
||||
|
||||
func (t *TemplateEngineWrapper) load() error {
|
||||
if t.location.isBinary() {
|
||||
t.LoadAssets(t.location.directory, t.location.extension, t.location.assetFn, t.location.namesFn)
|
||||
} else if t.location.directory != "" {
|
||||
t.LoadDirectory(t.location.directory, t.location.extension)
|
||||
} else {
|
||||
return errMissingDirectoryOrAssets.Return()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Execute execute a template and write its result to the context's body
|
||||
// options are the optional runtime options can be passed by user and catched by the template engine when render
|
||||
// an example of this is the "layout"
|
||||
// note that gzip option is an iris dynamic option which exists for all template engines
|
||||
func (t *TemplateEngineWrapper) Execute(ctx *Context, filename string, binding interface{}, options ...map[string]interface{}) (err error) {
|
||||
if t == nil {
|
||||
//file extension, but no template engine registered, this caused by context, and TemplateEngines. GetBy
|
||||
return errNoTemplateEngineForExt.Format(filepath.Ext(filename))
|
||||
}
|
||||
if t.reload {
|
||||
if err = t.load(); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// we do all these because we don't want to initialize a new map for each execution...
|
||||
gzipEnabled := false
|
||||
if len(options) > 0 {
|
||||
gzipOpt := options[0]["gzip"] // we only need that, so don't create new map to keep the options.
|
||||
if b, isBool := gzipOpt.(bool); isBool {
|
||||
gzipEnabled = b
|
||||
}
|
||||
}
|
||||
|
||||
ctxLayout := ctx.GetString(config.TemplateLayoutContextKey)
|
||||
if ctxLayout != "" {
|
||||
if len(options) > 0 {
|
||||
options[0]["layout"] = ctxLayout
|
||||
} else {
|
||||
options = []map[string]interface{}{map[string]interface{}{"layout": ctxLayout}}
|
||||
}
|
||||
}
|
||||
|
||||
var out io.Writer
|
||||
if gzipEnabled {
|
||||
ctx.Response.Header.Add("Content-Encoding", "gzip")
|
||||
gzipWriter := t.gzipWriterPool.Get().(*gzip.Writer)
|
||||
gzipWriter.Reset(ctx.Response.BodyWriter())
|
||||
defer gzipWriter.Close()
|
||||
defer t.gzipWriterPool.Put(gzipWriter)
|
||||
out = gzipWriter
|
||||
} else {
|
||||
out = ctx.Response.BodyWriter()
|
||||
}
|
||||
ctx.SetHeader("Content-Type", t.combiledContentType)
|
||||
|
||||
return t.ExecuteWriter(out, filename, binding, options...)
|
||||
}
|
||||
|
||||
// ExecuteToString executes a template from a specific template engine and returns its contents result as string, it doesn't renders
|
||||
func (t *TemplateEngineWrapper) ExecuteToString(filename string, binding interface{}, opt ...map[string]interface{}) (result string, err error) {
|
||||
if t == nil {
|
||||
//file extension, but no template engine registered, this caused by context, and TemplateEngines. GetBy
|
||||
return "", errNoTemplateEngineForExt.Format(filepath.Ext(filename))
|
||||
}
|
||||
if t.reload {
|
||||
if err = t.load(); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
out := t.buffer.Get()
|
||||
defer t.buffer.Put(out)
|
||||
err = t.ExecuteWriter(out, filename, binding, opt...)
|
||||
if err == nil {
|
||||
result = out.String()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// TemplateEngines is the container and manager of the template engines
|
||||
type TemplateEngines struct {
|
||||
engines []*TemplateEngineWrapper
|
||||
helpers map[string]interface{}
|
||||
reload bool
|
||||
}
|
||||
|
||||
func (t *TemplateEngines) setReload(b bool) {
|
||||
t.reload = b
|
||||
}
|
||||
|
||||
// GetBy receives a filename, gets its extension and returns the template engine responsible for that file extension
|
||||
func (t *TemplateEngines) GetBy(filename string) *TemplateEngineWrapper {
|
||||
extension := filepath.Ext(filename)
|
||||
for i, n := 0, len(t.engines); i < n; i++ {
|
||||
e := t.engines[i]
|
||||
|
||||
if e.location.extension == extension {
|
||||
return e
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Add adds but not loads a template engine
|
||||
func (t *TemplateEngines) Add(e TemplateEngine) *TemplateEngineLocation {
|
||||
|
||||
location := &TemplateEngineLocation{}
|
||||
// add the iris helper funcs
|
||||
if funcer, ok := e.(TemplateEngineFuncs); ok {
|
||||
if funcer.Funcs() != nil {
|
||||
for k, v := range t.helpers {
|
||||
funcer.Funcs()[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tmplEngine := &TemplateEngineWrapper{
|
||||
TemplateEngine: e,
|
||||
location: location,
|
||||
buffer: utils.NewBufferPool(20),
|
||||
gzipWriterPool: sync.Pool{New: func() interface{} {
|
||||
return &gzip.Writer{}
|
||||
}},
|
||||
reload: t.reload,
|
||||
combiledContentType: config.ContentTypeHTML + "; " + config.Charset,
|
||||
}
|
||||
|
||||
t.engines = append(t.engines, tmplEngine)
|
||||
return location
|
||||
}
|
||||
|
||||
// loadAll loads all templates using all template engines, returns the first error
|
||||
// called on iris' initialize
|
||||
func (t *TemplateEngines) loadAll() error {
|
||||
for i, n := 0, len(t.engines); i < n; i++ {
|
||||
e := t.engines[i]
|
||||
if e.location.directory == "" {
|
||||
e.location.directory = DefaultTemplateDirectory // the defualt dir ./templates
|
||||
}
|
||||
if e.location.extension == "" {
|
||||
e.location.extension = DefaultTemplateExtension // the default file ext .html
|
||||
}
|
||||
|
||||
if err := e.load(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -1,9 +1,9 @@
|
|||
package websocket
|
||||
|
||||
import (
|
||||
"github.com/iris-contrib/logger"
|
||||
"github.com/kataras/iris/config"
|
||||
"github.com/kataras/iris/context"
|
||||
"github.com/kataras/iris/logger"
|
||||
)
|
||||
|
||||
// to avoid the import cycle to /kataras/iris. The ws package is used inside iris' station configuration
|
||||
|
|
Loading…
Reference in New Issue
Block a user