mirror of
https://github.com/kataras/iris.git
synced 2025-03-15 05:26:26 +01:00
Replace response engines with serializers, same job but organized better, read README.md , no front-end changes if you used default engines.
This commit is contained in:
parent
f561b7a90d
commit
97431f2650
13
HISTORY.md
13
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`.
|
**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
|
## 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).
|
- **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).
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
<a href="https://github.com/kataras/iris/releases"><img src="https://img.shields.io/badge/%20version%20-%204.2.0%20-blue.svg?style=flat-square" alt="Releases"></a>
|
<a href="https://github.com/kataras/iris/releases"><img src="https://img.shields.io/badge/%20version%20-%204.2.1%20-blue.svg?style=flat-square" alt="Releases"></a>
|
||||||
|
|
||||||
<a href="https://github.com/iris-contrib/examples"><img src="https://img.shields.io/badge/%20examples-repository-3362c2.svg?style=flat-square" alt="Examples"></a>
|
<a href="https://github.com/iris-contrib/examples"><img src="https://img.shields.io/badge/%20examples-repository-3362c2.svg?style=flat-square" alt="Examples"></a>
|
||||||
|
|
||||||
|
@ -178,7 +178,7 @@ I recommend writing your API tests using this new library, [httpexpect](https://
|
||||||
Versioning
|
Versioning
|
||||||
------------
|
------------
|
||||||
|
|
||||||
Current: **v4.2.0**
|
Current: **v4.2.1**
|
||||||
|
|
||||||
> Iris is an active project
|
> Iris is an active project
|
||||||
|
|
||||||
|
@ -221,7 +221,7 @@ License can be found [here](LICENSE).
|
||||||
[Travis]: http://travis-ci.org/kataras/iris
|
[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 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
|
[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
|
[Release]: https://github.com/kataras/iris/releases
|
||||||
[Chat Widget]: https://img.shields.io/badge/community-chat-00BCD4.svg?style=flat-square
|
[Chat Widget]: https://img.shields.io/badge/community-chat-00BCD4.svg?style=flat-square
|
||||||
[Chat]: https://kataras.rocket.chat/channel/iris
|
[Chat]: https://kataras.rocket.chat/channel/iris
|
||||||
|
|
60
context.go
60
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
|
// 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 {
|
func (ctx *Context) RenderTemplateSource(status int, src string, binding interface{}, options ...map[string]interface{}) error {
|
||||||
err := ctx.framework.templates.renderSource(ctx, src, binding, options...)
|
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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// RenderWithStatus builds up the response from the specified template or a 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 response 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) {
|
func (ctx *Context) RenderWithStatus(status int, name string, binding interface{}, options ...map[string]interface{}) (err error) {
|
||||||
if strings.IndexByte(name, '.') > -1 { //we have template
|
if strings.IndexByte(name, '.') > -1 { //we have template
|
||||||
err = ctx.framework.templates.render(ctx, name, binding, options...)
|
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 {
|
if err == nil {
|
||||||
ctx.SetStatusCode(status)
|
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
|
// 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.
|
// 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 response 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 {
|
func (ctx *Context) Render(name string, binding interface{}, options ...map[string]interface{}) error {
|
||||||
errCode := ctx.RequestCtx.Response.StatusCode()
|
errCode := ctx.RequestCtx.Response.StatusCode()
|
||||||
if errCode <= 0 {
|
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
|
// 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.
|
// 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 response 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{}) {
|
func (ctx *Context) MustRender(name string, binding interface{}, options ...map[string]interface{}) {
|
||||||
if err := ctx.Render(name, binding, options...); err != nil {
|
if err := ctx.Render(name, binding, options...); err != nil {
|
||||||
ctx.Panic()
|
ctx.Panic()
|
||||||
|
@ -591,7 +633,7 @@ func (ctx *Context) TemplateString(name string, binding interface{}, options ...
|
||||||
// HTML writes html string with a http status
|
// HTML writes html string with a http status
|
||||||
func (ctx *Context) HTML(status int, htmlContents string) {
|
func (ctx *Context) HTML(status int, htmlContents string) {
|
||||||
if err := ctx.RenderWithStatus(status, contentHTML, htmlContents); err != nil {
|
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.SetContentType(contentHTML + "; charset=" + ctx.framework.Config.Charset)
|
||||||
ctx.RequestCtx.SetStatusCode(status)
|
ctx.RequestCtx.SetStatusCode(status)
|
||||||
ctx.RequestCtx.WriteString(htmlContents)
|
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
|
// MarkdownString parses the (dynamic) markdown string and returns the converted html string
|
||||||
func (ctx *Context) MarkdownString(markdownText string) 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
|
// Markdown parses and renders to the client a particular (dynamic) markdown string
|
||||||
|
|
95
iris.go
95
iris.go
|
@ -65,14 +65,9 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gavv/httpexpect"
|
"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-errors"
|
||||||
"github.com/kataras/go-fs"
|
"github.com/kataras/go-fs"
|
||||||
|
"github.com/kataras/go-serializer"
|
||||||
"github.com/kataras/go-sessions"
|
"github.com/kataras/go-sessions"
|
||||||
"github.com/kataras/go-template"
|
"github.com/kataras/go-template"
|
||||||
"github.com/kataras/go-template/html"
|
"github.com/kataras/go-template/html"
|
||||||
|
@ -83,7 +78,7 @@ import (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Version of the iris
|
// Version of the iris
|
||||||
Version = "4.2.0"
|
Version = "4.2.1"
|
||||||
|
|
||||||
banner = ` _____ _
|
banner = ` _____ _
|
||||||
|_ _| (_)
|
|_ _| (_)
|
||||||
|
@ -152,7 +147,7 @@ type (
|
||||||
Go() error
|
Go() error
|
||||||
Close() error
|
Close() error
|
||||||
UseSessionDB(sessions.Database)
|
UseSessionDB(sessions.Database)
|
||||||
UseResponse(ResponseEngine, ...string) func(string)
|
UseSerializer(string, serializer.Serializer)
|
||||||
UseTemplate(template.Engine) *template.Loader
|
UseTemplate(template.Engine) *template.Loader
|
||||||
UseGlobal(...Handler)
|
UseGlobal(...Handler)
|
||||||
UseGlobalFunc(...HandlerFunc)
|
UseGlobalFunc(...HandlerFunc)
|
||||||
|
@ -162,7 +157,7 @@ type (
|
||||||
URL(string, ...interface{}) string
|
URL(string, ...interface{}) string
|
||||||
TemplateString(string, interface{}, ...map[string]interface{}) string
|
TemplateString(string, interface{}, ...map[string]interface{}) string
|
||||||
TemplateSourceString(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
|
Tester(*testing.T) *httpexpect.Expect
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,7 +169,7 @@ type (
|
||||||
contextPool sync.Pool
|
contextPool sync.Pool
|
||||||
Config *Configuration
|
Config *Configuration
|
||||||
sessions sessions.Sessions
|
sessions sessions.Sessions
|
||||||
responses *responseEngines
|
serializers serializer.Serializers
|
||||||
templates *templateEngines
|
templates *templateEngines
|
||||||
// fields which are useful to the user/dev
|
// fields which are useful to the user/dev
|
||||||
// the last added server is the main server
|
// the last added server is the main server
|
||||||
|
@ -214,7 +209,7 @@ func New(setters ...OptionSetter) *Framework {
|
||||||
|
|
||||||
// rendering
|
// rendering
|
||||||
{
|
{
|
||||||
s.responses = newResponseEngines()
|
s.serializers = serializer.Serializers{}
|
||||||
// set the templates
|
// set the templates
|
||||||
s.templates = newTemplateEngines(map[string]interface{}{
|
s.templates = newTemplateEngines(map[string]interface{}{
|
||||||
"url": s.URL,
|
"url": s.URL,
|
||||||
|
@ -262,28 +257,8 @@ func (s *Framework) Set(setters ...OptionSetter) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Framework) initialize() {
|
func (s *Framework) initialize() {
|
||||||
// prepare the response engines, if no response engines setted for the default content-types
|
// 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:
|
||||||
// then add them
|
serializer.RegisterDefaults(s.serializers)
|
||||||
|
|
||||||
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 templates if enabled
|
// prepare the templates if enabled
|
||||||
if !s.Config.DisableTemplateEngines {
|
if !s.Config.DisableTemplateEngines {
|
||||||
|
@ -606,56 +581,38 @@ func (s *Framework) UseSessionDB(db sessions.Database) {
|
||||||
s.sessions.UseDatabase(db)
|
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
|
// 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
|
// 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
|
// 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
|
// to do that just register with UseSerializer(mySerializer,"application/json") and so on
|
||||||
// look at the https://github.com/iris-contrib/response for examples
|
// 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
|
// 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.
|
// 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
|
// 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
|
// because iris uses these by default if no other response engine is registered for these content types
|
||||||
//
|
func UseSerializer(forContentType string, e serializer.Serializer) {
|
||||||
// Note 2:
|
Default.UseSerializer(forContentType, e)
|
||||||
// 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...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
// 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
|
// 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
|
// 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
|
// to do that just register with UseSerializer(mySerializer,"application/json") and so on
|
||||||
// look at the https://github.com/iris-contrib/response for examples
|
// 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
|
// 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.
|
// 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
|
// 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
|
// because iris uses these by default if no other response engine is registered for these content types
|
||||||
//
|
func (s *Framework) UseSerializer(forContentType string, e serializer.Serializer) {
|
||||||
// Note 2:
|
s.serializers.For(forContentType, e)
|
||||||
// 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...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UseTemplate adds a template engine to the iris view system
|
// 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
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResponseString returns the string of a response engine,
|
// SerializeToString returns the string of a serializer,
|
||||||
// does not render it to the client
|
// does not render it to the client
|
||||||
// returns empty string on error
|
// returns empty string on error
|
||||||
func ResponseString(keyOrContentType string, obj interface{}, options ...map[string]interface{}) string {
|
func SerializeToString(keyOrContentType string, obj interface{}, options ...map[string]interface{}) string {
|
||||||
return Default.ResponseString(keyOrContentType, obj, options...)
|
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
|
// does not render it to the client
|
||||||
// returns empty string on error
|
// returns empty string on error
|
||||||
func (s *Framework) ResponseString(keyOrContentType string, obj interface{}, options ...map[string]interface{}) string {
|
func (s *Framework) SerializeToString(keyOrContentType string, obj interface{}, options ...map[string]interface{}) string {
|
||||||
res, err := s.responses.getBy(keyOrContentType).toString(obj, options...)
|
res, err := s.serializers.SerializeToString(keyOrContentType, obj, options...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
251
response.go
251
response.go
|
@ -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
|
|
||||||
}
|
|
22
template.go
22
template.go
|
@ -33,6 +33,24 @@ func newTemplateEngines(sharedFuncs map[string]interface{}) *templateEngines {
|
||||||
return &templateEngines{Mux: template.NewMux(sharedFuncs)}
|
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
|
// 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
|
// 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"
|
// 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
|
gzipEnabled := ctx.framework.Config.Gzip
|
||||||
charset := ctx.framework.Config.Charset
|
charset := ctx.framework.Config.Charset
|
||||||
if len(options) > 0 {
|
if len(options) > 0 {
|
||||||
gzipEnabled = template.GetGzipOption(gzipEnabled, options[0])
|
gzipEnabled = getGzipOption(gzipEnabled, options[0])
|
||||||
charset = template.GetCharsetOption(charset, options[0])
|
charset = getCharsetOption(charset, options[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
ctxLayout := ctx.GetString(TemplateLayoutContextKey)
|
ctxLayout := ctx.GetString(TemplateLayoutContextKey)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user