diff --git a/HISTORY.md b/HISTORY.md index a4b0f2fb..7f98dd2c 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -2,6 +2,19 @@ **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`. +## 4.2.0 -> 4.2.1 + +- **CHANGE**: No front-end changes if you used the default response engines before. Response Engines to Serializers, `iris.ResponseEngine` `serializer.Serializer`, comes from `kataras/go-serializer` which is installed automatically when you upgrade iris with `-u` flag. + + - the repo "github.com/iris-contrib/response" is a clone of "github.com/kataras/go-serializer", to keep compatibility state. examples and gitbook updated to work with the last. + + - `iris.UseResponse(iris.ResponseEngine, ...string)func (string)` was used to register custom response engines, now you use: `iris.UseSerializer(key string, s serializer.Serializer)`. + + - `iris.ResponseString` same defintion but differnet name now: `iris.SerializeToString` + +[Serializer examples](https://github.com/iris-contrib/examples/tree/master/serialize_engines) and [Book section](https://kataras.gitbooks.io/iris/content/serialize-engines.html) updated. + + ## 4.1.7 -> 4.2.0 - **ADDED**: `iris.TemplateSourceString(src string, binding interface{}) string` this will parse the src raw contents to the template engine and return the string result & `context.RenderTemplateSource(status int, src string, binding interface{}, options ...map[string]interface{}) error` this will parse the src raw contents to the template engine and render the result to the client, as requseted [here](https://github.com/kataras/iris/issues/409). diff --git a/README.md b/README.md index 6d33e3e1..613baf8f 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@
-Releases +Releases Examples @@ -178,7 +178,7 @@ I recommend writing your API tests using this new library, [httpexpect](https:// Versioning ------------ -Current: **v4.2.0** +Current: **v4.2.1** > Iris is an active project @@ -221,7 +221,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-v4.2.0-blue.svg?style=flat-square +[Release Widget]: https://img.shields.io/badge/release-v4.2.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 diff --git a/context.go b/context.go index 6a29e03c..746459df 100644 --- a/context.go +++ b/context.go @@ -534,6 +534,47 @@ func (ctx *Context) Gzip(b []byte, status int) { } } +// renderSerialized renders contents with a serializer with status OK which you can change using RenderWithStatus or ctx.SetStatusCode(iris.StatusCode) +func (ctx *Context) renderSerialized(contentType string, obj interface{}, options ...map[string]interface{}) error { + s := ctx.framework.serializers + finalResult, err := s.Serialize(contentType, obj, options...) + if err != nil { + return err + } + + gzipEnabled := ctx.framework.Config.Gzip + charset := ctx.framework.Config.Charset + if len(options) > 0 { + gzipEnabled = getGzipOption(gzipEnabled, options[0]) // located to the template.go below the RenderOptions + charset = getCharsetOption(charset, options[0]) + } + ctype := contentType + + if ctype == contentMarkdown { // remember the text/markdown is just a custom internal iris content type, which in reallity renders html + ctype = contentHTML + } + + if ctype != contentBinary { // set the charset only on non-binary data + ctype += "; charset=" + charset + } + ctx.SetContentType(ctype) + + if gzipEnabled && ctx.clientAllowsGzip() { + _, err := fasthttp.WriteGzip(ctx.RequestCtx.Response.BodyWriter(), finalResult) + if err != nil { + return err + } + ctx.RequestCtx.Response.Header.Add(varyHeader, acceptEncodingHeader) + ctx.SetHeader(contentEncodingHeader, "gzip") + } else { + ctx.Response.SetBody(finalResult) + } + + ctx.SetStatusCode(StatusOK) + + return nil +} + // RenderTemplateSource serves a template source(raw string contents) from the first template engines which supports raw parsing returns its result as string func (ctx *Context) RenderTemplateSource(status int, src string, binding interface{}, options ...map[string]interface{}) error { err := ctx.framework.templates.renderSource(ctx, src, binding, options...) @@ -544,13 +585,14 @@ func (ctx *Context) RenderTemplateSource(status int, src string, binding interfa return err } -// RenderWithStatus builds up the response from the specified template or a response engine. -// Note: the options: "gzip" and "charset" are built'n support by Iris, so you can pass these on any template engine or response engine +// RenderWithStatus builds up the response from the specified template or a serialize engine. +// Note: the options: "gzip" and "charset" are built'n support by Iris, so you can pass these on any template engine or serialize engines func (ctx *Context) RenderWithStatus(status int, name string, binding interface{}, options ...map[string]interface{}) (err error) { if strings.IndexByte(name, '.') > -1 { //we have template err = ctx.framework.templates.render(ctx, name, binding, options...) + } else { + err = ctx.renderSerialized(name, binding, options...) } - err = ctx.framework.responses.getBy(name).render(ctx, binding, options...) if err == nil { ctx.SetStatusCode(status) @@ -560,8 +602,8 @@ func (ctx *Context) RenderWithStatus(status int, name string, binding interface{ } // Render same as .RenderWithStatus but with status to iris.StatusOK (200) if no previous status exists -// builds up the response from the specified template or a response engine. -// Note: the options: "gzip" and "charset" are built'n support by Iris, so you can pass these on any template engine or response engine +// builds up the response from the specified template or a serialize engine. +// Note: the options: "gzip" and "charset" are built'n support by Iris, so you can pass these on any template engine or serialize engine func (ctx *Context) Render(name string, binding interface{}, options ...map[string]interface{}) error { errCode := ctx.RequestCtx.Response.StatusCode() if errCode <= 0 { @@ -571,8 +613,8 @@ func (ctx *Context) Render(name string, binding interface{}, options ...map[stri } // MustRender same as .Render but returns 500 internal server http status (error) if rendering fail -// builds up the response from the specified template or a response engine. -// Note: the options: "gzip" and "charset" are built'n support by Iris, so you can pass these on any template engine or response engine +// builds up the response from the specified template or a serialize engine. +// Note: the options: "gzip" and "charset" are built'n support by Iris, so you can pass these on any template engine or serialize engine func (ctx *Context) MustRender(name string, binding interface{}, options ...map[string]interface{}) { if err := ctx.Render(name, binding, options...); err != nil { ctx.Panic() @@ -591,7 +633,7 @@ func (ctx *Context) TemplateString(name string, binding interface{}, options ... // HTML writes html string with a http status func (ctx *Context) HTML(status int, htmlContents string) { if err := ctx.RenderWithStatus(status, contentHTML, htmlContents); err != nil { - // if no response engine found for text/html + // if no serialize engine found for text/html ctx.SetContentType(contentHTML + "; charset=" + ctx.framework.Config.Charset) ctx.RequestCtx.SetStatusCode(status) ctx.RequestCtx.WriteString(htmlContents) @@ -625,7 +667,7 @@ func (ctx *Context) XML(status int, v interface{}) error { // MarkdownString parses the (dynamic) markdown string and returns the converted html string func (ctx *Context) MarkdownString(markdownText string) string { - return ctx.framework.ResponseString(contentMarkdown, markdownText) + return ctx.framework.SerializeToString(contentMarkdown, markdownText) } // Markdown parses and renders to the client a particular (dynamic) markdown string diff --git a/iris.go b/iris.go index c3d3001b..b952174d 100644 --- a/iris.go +++ b/iris.go @@ -65,14 +65,9 @@ import ( "time" "github.com/gavv/httpexpect" - "github.com/iris-contrib/response/data" - "github.com/iris-contrib/response/json" - "github.com/iris-contrib/response/jsonp" - "github.com/iris-contrib/response/markdown" - "github.com/iris-contrib/response/text" - "github.com/iris-contrib/response/xml" "github.com/kataras/go-errors" "github.com/kataras/go-fs" + "github.com/kataras/go-serializer" "github.com/kataras/go-sessions" "github.com/kataras/go-template" "github.com/kataras/go-template/html" @@ -83,7 +78,7 @@ import ( const ( // Version of the iris - Version = "4.2.0" + Version = "4.2.1" banner = ` _____ _ |_ _| (_) @@ -152,7 +147,7 @@ type ( Go() error Close() error UseSessionDB(sessions.Database) - UseResponse(ResponseEngine, ...string) func(string) + UseSerializer(string, serializer.Serializer) UseTemplate(template.Engine) *template.Loader UseGlobal(...Handler) UseGlobalFunc(...HandlerFunc) @@ -162,7 +157,7 @@ type ( URL(string, ...interface{}) string TemplateString(string, interface{}, ...map[string]interface{}) string TemplateSourceString(string, interface{}) string - ResponseString(string, interface{}, ...map[string]interface{}) string + SerializeToString(string, interface{}, ...map[string]interface{}) string Tester(*testing.T) *httpexpect.Expect } @@ -174,7 +169,7 @@ type ( contextPool sync.Pool Config *Configuration sessions sessions.Sessions - responses *responseEngines + serializers serializer.Serializers templates *templateEngines // fields which are useful to the user/dev // the last added server is the main server @@ -214,7 +209,7 @@ func New(setters ...OptionSetter) *Framework { // rendering { - s.responses = newResponseEngines() + s.serializers = serializer.Serializers{} // set the templates s.templates = newTemplateEngines(map[string]interface{}{ "url": s.URL, @@ -262,28 +257,8 @@ func (s *Framework) Set(setters ...OptionSetter) { } func (s *Framework) initialize() { - // prepare the response engines, if no response engines setted for the default content-types - // then add them - - for _, ctype := range defaultResponseKeys { - if rengine := s.responses.getBy(ctype); rengine == nil { - // if not exists - switch ctype { - case contentText: - s.UseResponse(text.New(), ctype) - case contentBinary: - s.UseResponse(data.New(), ctype) - case contentJSON: - s.UseResponse(json.New(), ctype) - case contentJSONP: - s.UseResponse(jsonp.New(), ctype) - case contentXML: - s.UseResponse(xml.New(), ctype) - case contentMarkdown: - s.UseResponse(markdown.New(), ctype) - } - } - } + // prepare the serializers, if not any other serializers setted for the default serializer types(json,jsonp,xml,markdown,text,data) then the defaults are setted: + serializer.RegisterDefaults(s.serializers) // prepare the templates if enabled if !s.Config.DisableTemplateEngines { @@ -606,56 +581,38 @@ func (s *Framework) UseSessionDB(db sessions.Database) { s.sessions.UseDatabase(db) } -// UseResponse accepts a ResponseEngine and the key or content type on which the developer wants to register this response engine +// UseSerializer accepts a Serializer and the key or content type on which the developer wants to register this serializer // the gzip and charset are automatically supported by Iris, by passing the iris.RenderOptions{} map on the context.Render // context.Render renders this response or a template engine if no response engine with the 'key' found // with these engines you can inject the context.JSON,Text,Data,JSONP,XML also -// to do that just register with UseResponse(myEngine,"application/json") and so on -// look at the https://github.com/iris-contrib/response for examples +// to do that just register with UseSerializer(mySerializer,"application/json") and so on +// look at the https://github.com/kataras/go-serializer for examples // -// if more than one respone engine with the same key/content type exists then the results will be appended to the final request's body +// if more than one serializer with the same key/content type exists then the results will be appended to the final request's body // this allows the developer to be able to create 'middleware' responses engines // // Note: if you pass an engine which contains a dot('.') as key, then the engine will not be registered. // you don't have to import and use github.com/iris-contrib/json, jsonp, xml, data, text, markdown // because iris uses these by default if no other response engine is registered for these content types -// -// Note 2: -// one key has one content type but many response engines ( one to many) -// -// returns a function(string) which you can set the content type, if it's not already declared from the key. -// careful you should call this in the same execution. -// one last thing, you can have unlimited number of response engines for the same key and same content type. -// key and content type may be different, but one key is only for one content type, -// Do not use different content types with more than one response engine on the same key -func UseResponse(e ResponseEngine, forContentTypesOrKeys ...string) func(string) { - return Default.UseResponse(e, forContentTypesOrKeys...) +func UseSerializer(forContentType string, e serializer.Serializer) { + Default.UseSerializer(forContentType, e) } -// UseResponse accepts a ResponseEngine and the key or content type on which the developer wants to register this response engine +// UseSerializer accepts a Serializer and the key or content type on which the developer wants to register this serializer // the gzip and charset are automatically supported by Iris, by passing the iris.RenderOptions{} map on the context.Render // context.Render renders this response or a template engine if no response engine with the 'key' found // with these engines you can inject the context.JSON,Text,Data,JSONP,XML also -// to do that just register with UseResponse(myEngine,"application/json") and so on -// look at the https://github.com/iris-contrib/response for examples +// to do that just register with UseSerializer(mySerializer,"application/json") and so on +// look at the https://github.com/kataras/go-serializer for examples // -// if more than one respone engine with the same key/content type exists then the results will be appended to the final request's body +// if more than one serializer with the same key/content type exists then the results will be appended to the final request's body // this allows the developer to be able to create 'middleware' responses engines // // Note: if you pass an engine which contains a dot('.') as key, then the engine will not be registered. // you don't have to import and use github.com/iris-contrib/json, jsonp, xml, data, text, markdown // because iris uses these by default if no other response engine is registered for these content types -// -// Note 2: -// one key has one content type but many response engines ( one to many) -// -// returns a function(string) which you can set the content type, if it's not already declared from the key. -// careful you should call this in the same execution. -// one last thing, you can have unlimited number of response engines for the same key and same content type. -// key and content type may be different, but one key is only for one content type, -// Do not use different content types with more than one response engine on the same key -func (s *Framework) UseResponse(e ResponseEngine, forContentTypesOrKeys ...string) func(string) { - return s.responses.add(e, forContentTypesOrKeys...) +func (s *Framework) UseSerializer(forContentType string, e serializer.Serializer) { + s.serializers.For(forContentType, e) } // UseTemplate adds a template engine to the iris view system @@ -939,18 +896,18 @@ func (s *Framework) TemplateSourceString(src string, pageContext interface{}) st return res } -// ResponseString returns the string of a response engine, +// SerializeToString returns the string of a serializer, // does not render it to the client // returns empty string on error -func ResponseString(keyOrContentType string, obj interface{}, options ...map[string]interface{}) string { - return Default.ResponseString(keyOrContentType, obj, options...) +func SerializeToString(keyOrContentType string, obj interface{}, options ...map[string]interface{}) string { + return Default.SerializeToString(keyOrContentType, obj, options...) } -// ResponseString returns the string of a response engine, +// SerializeToString returns the string of a serializer, // does not render it to the client // returns empty string on error -func (s *Framework) ResponseString(keyOrContentType string, obj interface{}, options ...map[string]interface{}) string { - res, err := s.responses.getBy(keyOrContentType).toString(obj, options...) +func (s *Framework) SerializeToString(keyOrContentType string, obj interface{}, options ...map[string]interface{}) string { + res, err := s.serializers.SerializeToString(keyOrContentType, obj, options...) if err != nil { return "" } diff --git a/response.go b/response.go deleted file mode 100644 index 8310c776..00000000 --- a/response.go +++ /dev/null @@ -1,251 +0,0 @@ -package iris - -import ( - "strings" - - "github.com/kataras/go-errors" - "github.com/kataras/go-template" - "github.com/valyala/fasthttp" -) - -type ( - // notes for me: - // edw an kai kalh idea alla den 9a borw na exw ta defaults mesa sto iris - // kai 9a prepei na to metaferw auto sto context i sto utils kai pragmatika den leei - // na kanoun import auto gia na kanoun to response engine, ara na prospa9isw kapws aliws mesw context.IContext mono - // ektos an metaferw ta defaults mesa sto iris - // alla an to kanw auto 9a prepei na vrw tropo na kanoun configuration ta defaults - // kai to idio prepei na kanw kai sto template engine html tote... - // diladi na kanw prwta to render tou real engine kai meta na parw - // ta contents tou body kai na ta kanw gzip ? na kanw resetbody kai na ta ksanasteilw? - // alla auto einai argh methodos kai gia ton poutso dn m aresei - // kai ola auta gia na mh valw ena property parapanw? 9a valw ... - // 9a einai writer,headers,object,options... anagastika. - - /* notes for me: - english 'final' thoughs' results now: - - the response engine will be registered with its content type - for example: iris.UseResponse("application/json", the engine or func here, optionalOptions...) - if more than one response engines registered for the same content type - then all the content results will be sent to the client - there will be available a system something like middleware but for the response engines - this will be useful when one response engine's job is only to add more content to the existing/parent response engine's results . - for example when you want to add a standard json object like a last_fetch { "year":...,"month":...,"day":...} for all json responses. - The engine will not have access to context or something like that. - The default engines will registered when no other engine with the same content type - already registered, like I did with template engines, so if someone wants to make a 'middleware' for the default response engines - must register them explicit and after register his/her response engine too, for that reason - the default engines will not be located inside iris but inside iris-contrib (like the default,and others, template engine), - the reason is to be easier to the user/dev to remember what import path should use when he/she wants to edit something, - for templates it's 'iris-contrib/template', for response engines will be 'iris-contrib/response'. - The body content will return as []byte, al/mong with an error if something bad happened. - Now you may ask why not set the header inside from response engine? because to do that we could have one of these four downsides: - 1.to have access to context.IContext or *Context(if *Context then default engines should live here in iris repo) - and if we have context.IContext or *Context we will not be able to set a fast built'n gzip option, - because we would copy the contents to the gzip writer, and after copy these contents back to the response body's writer - but with an io.Writer as parameter we can simple change this writer to gzip writer and continue to the response engine after. - 2. we could make something like ResponseWriter struct { io.Writer,Header *fasthttp.ResponseHeader} - inside iris repo(then the default response engines should exists in the iris repo and configuration will depends on the iris' configs ) - or inside context/ folder inside iris repo, then the user/dev should import this path to - do his/her response engine, and I want simple things as usual, also we would make a pool for this response writer and create new if not available exist, - and this is performarnce downs witch I dissalow on Iris whne no need. - 3. to have 4 parameters, the writer, the headers(again the user should import the fasthttp to do his/her response engine and I want simple things, as I told before), - the object and the optional parameters - 4. one more function to implement like 'ContentType() string', but if we select this we lose the functionality for ResponseEngine created as simple function, - and the biggest issue will be that one response engine must explicit exists for one content type, the user/dev will not be available (to easly) - to set the content type for the engine. - these were the reasons I decide to set the content type by the frontend iris API itself and not taken by the response engine. - - The Response will have two parameters (one required only) interface{], ...options}, and two return values([]byte,error) - The (first) parameter will be an interface{}, for json a json struct, for xml an xml struct, for binary data .([]byte) and so on - There will be available a second optional parameter, map of options, the "gzip" option will be built'n implemented by iris - so the response engines no need to manually add gzip support(same with template engines). - The Charset will be added to the headers automatically, for the previous example of json and the default charset which is UTF-8 - the end "Content-Type" header content will be: "application/json; charset=UTF-8" - if the registered content type is not a $content/type then the text/plain will be sent to the client. - - OR WAIT, some engines maybe want to set the content type or other headers dynamically or render a response depends on cookies or some other existence headers - on that situtions it will be impossible with this implementation I explained before, so... - access to context.IContext and return the []byte, in order to be able to add the built'n gzip support - the dev/user will have to make this import no no no we stick to the previous though, because - if the user wants to check all that he/she can just use a middleware with .Use/.UseFunc - this is not a middleware implementation, this is a custom content rendering, let's stick to that. - - Ok I did that and I realized that template and response engines, final method structure (string,interface{},options...) is the same - so I make the ctx.Render/RenderWithStatus to work with both engines, so the developer can use any type of response engine and render it with ease. - Maybe at the future I could have one file 'render.go' which will contain the template engines and response engines, we will see, these all are unique so excuse me if something goes wrong xD - - That's all. Hope some one (other than me) will understand the english here... - */ - - // ResponseEngine is the interface which all response engines should implement to send responses - // ResponseEngine(s) can be registered with,for example: iris.UseResponse(json.New(), "application/json") - ResponseEngine interface { - Response(interface{}, ...map[string]interface{}) ([]byte, error) - } - // ResponseEngineFunc is the alternative way to implement a ResponseEngine using a simple function - ResponseEngineFunc func(interface{}, ...map[string]interface{}) ([]byte, error) - - // responseEngineMap is a wrapper with key (content type or name) values(engines) for the registered response engine - // it contains all response engines for a specific contentType and two functions, render and toString - // these will be used by the iris' context and iris' ResponseString, yes like TemplateToString - // it's an internal struct, no need to be exported and return that on registration, - // because the two top funcs will be easier to use by the user/dev for multiple engines - responseEngineMap struct { - values []ResponseEngine - // this is used in order to the wrapper to be gettable by the responseEngines iteral, - // if key is not a $content/type and contentType is not changed by the user/dev then the text/plain will be sent to the client - key string - contentType string - } -) - -var ( - // markdown is custom type, used inside iris to initialize the defaults response engines if no other engine registered with these keys - defaultResponseKeys = [...]string{contentText, contentXML, contentBinary, contentJSON, contentJSONP, contentMarkdown} -) - -// Response returns a response to the client(request's body content) -func (r ResponseEngineFunc) Response(obj interface{}, options ...map[string]interface{}) ([]byte, error) { - return r(obj, options...) -} - -var errNoResponseEngineFound = errors.New("No response engine found") - -// on context: Send(contentType string, obj interface{}, ...options) - -func (r *responseEngineMap) add(engine ResponseEngine) { - r.values = append(r.values, engine) -} - -// the gzip and charset options are built'n with iris -func (r *responseEngineMap) render(ctx *Context, obj interface{}, options ...map[string]interface{}) error { - - if r == nil { - //render, but no response engine registered, this caused by context.RenderWithStatus, and responseEngines. getBy - return errNoResponseEngineFound - } - - var finalResult []byte - - for i, n := 0, len(r.values); i < n; i++ { - result, err := r.values[i].Response(obj, options...) - if err != nil { // fail on first the first error - return err - } - finalResult = append(finalResult, result...) - } - - gzipEnabled := ctx.framework.Config.Gzip - charset := ctx.framework.Config.Charset - if len(options) > 0 { - gzipEnabled = template.GetGzipOption(gzipEnabled, options[0]) // located to the template.go below the RenderOptions - charset = template.GetCharsetOption(charset, options[0]) - } - ctype := r.contentType - - if r.contentType != contentBinary { // set the charset only on non-binary data - ctype += "; charset=" + charset - } - ctx.SetContentType(ctype) - - if gzipEnabled && ctx.clientAllowsGzip() { - _, err := fasthttp.WriteGzip(ctx.RequestCtx.Response.BodyWriter(), finalResult) - if err != nil { - return err - } - ctx.RequestCtx.Response.Header.Add(varyHeader, acceptEncodingHeader) - ctx.SetHeader(contentEncodingHeader, "gzip") - } else { - ctx.Response.SetBody(finalResult) - } - - return nil -} - -func (r *responseEngineMap) toString(obj interface{}, options ...map[string]interface{}) (string, error) { - if r == nil { - //render, but no response engine registered, this caused by context.RenderWithStatus, and responseEngines. getBy - return "", errNoResponseEngineFound - } - var finalResult []byte - for i, n := 0, len(r.values); i < n; i++ { - result, err := r.values[i].Response(obj, options...) - if err != nil { - return "", err - } - finalResult = append(finalResult, result...) - } - return string(finalResult), nil -} - -type responseEngines struct { - engines []*responseEngineMap -} - -func newResponseEngines() *responseEngines { - return &responseEngines{} -} - -// add accepts a simple response engine with its content type or key, key should not contains a dot('.'). -// if key is a content type then it's the content type, but if it not, set the content type from the returned function, -// if it not called/changed then the default content type text/plain will be used. -// different content types for the same key will produce bugs, as it should! -// one key has one content type but many response engines ( one to many) -// note that the func should be used on the same call -func (r *responseEngines) add(engine ResponseEngine, forContentTypesOrKeys ...string) func(string) { - if r.engines == nil { - r.engines = make([]*responseEngineMap, 0) - } - - var engineMap *responseEngineMap - for _, key := range forContentTypesOrKeys { - if strings.IndexByte(key, '.') != -1 { // the dot is not allowed as key - continue // skip this engine - } - - defaultCtypeAndKey := contentText - if len(key) == 0 { - //if empty key, then set it to text/plain - key = defaultCtypeAndKey - } - - engineMap = r.getBy(key) - if engineMap == nil { - - ctype := defaultCtypeAndKey - if strings.IndexByte(key, slashByte) != -1 { // pure check, but developer should know the content types at least. - // we have 'valid' content type - ctype = key - } - // the context.Markdown works without it but with .Render we will have problems without this: - if key == contentMarkdown { // remember the text/markdown is just a custom internal iris content type, which in reallity renders html - ctype = contentHTML - } - engineMap = &responseEngineMap{values: make([]ResponseEngine, 0), key: key, contentType: ctype} - r.engines = append(r.engines, engineMap) - } - engineMap.add(engine) - } - - return func(theContentType string) { - // and this - if theContentType == contentMarkdown { - theContentType = contentHTML - } - - engineMap.contentType = theContentType - } - -} - -func (r *responseEngines) getBy(key string) *responseEngineMap { - for i, n := 0, len(r.engines); i < n; i++ { - if r.engines[i].key == key { - return r.engines[i] - } - - } - return nil -} diff --git a/template.go b/template.go index 7d6cdb6e..575af69f 100644 --- a/template.go +++ b/template.go @@ -33,6 +33,24 @@ func newTemplateEngines(sharedFuncs map[string]interface{}) *templateEngines { return &templateEngines{Mux: template.NewMux(sharedFuncs)} } +// getGzipOption receives a default value and the render options map and returns if gzip is enabled for this render action +func getGzipOption(defaultValue bool, options map[string]interface{}) bool { + gzipOpt := options["gzip"] // we only need that, so don't create new map to keep the options. + if b, isBool := gzipOpt.(bool); isBool { + return b + } + return defaultValue +} + +// gtCharsetOption receives a default value and the render options map and returns the correct charset for this render action +func getCharsetOption(defaultValue string, options map[string]interface{}) string { + charsetOpt := options["charset"] + if s, isString := charsetOpt.(string); isString { + return s + } + return defaultValue +} + // render executes 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" @@ -43,8 +61,8 @@ func (t *templateEngines) render(ctx *Context, filename string, binding interfac gzipEnabled := ctx.framework.Config.Gzip charset := ctx.framework.Config.Charset if len(options) > 0 { - gzipEnabled = template.GetGzipOption(gzipEnabled, options[0]) - charset = template.GetCharsetOption(charset, options[0]) + gzipEnabled = getGzipOption(gzipEnabled, options[0]) + charset = getCharsetOption(charset, options[0]) } ctxLayout := ctx.GetString(TemplateLayoutContextKey)