diff --git a/_examples/README.md b/_examples/README.md
index 92fc4810..5d416203 100644
--- a/_examples/README.md
+++ b/_examples/README.md
@@ -121,6 +121,9 @@ Navigate through examples for a better understanding.
- [Inject Data Between Handlers](view/context-view-data/main.go)
- [Embedding Templates Into App Executable File](view/embedding-templates-into-app/main.go)
+
+You can serve [quicktemplate](https://github.com/valyala/quicktemplate) files too, simply by using the `context#ResponseWriter`, take a look at the [http_responsewriter/quicktemplate](http_responsewriter/quicktemplate) example.
+
### Authentication
- [Basic Authentication](authentication/basicauth/main.go)
@@ -148,6 +151,7 @@ Navigate through examples for a better understanding.
### How to Write to `context.ResponseWriter() http.ResponseWriter`
+- [Write `valyala/quicktemplate` templates](http_responsewriter/quicktemplate)
- [Text, Markdown, HTML, JSON, JSONP, XML, Binary](http_responsewriter/write-rest/main.go)
- [Stream Writer](http_responsewriter/stream-writer/main.go)
- [Transactions](http_responsewriter/transactions/main.go)
diff --git a/_examples/http_responsewriter/quicktemplate/README.md b/_examples/http_responsewriter/quicktemplate/README.md
new file mode 100644
index 00000000..3d3a6f9b
--- /dev/null
+++ b/_examples/http_responsewriter/quicktemplate/README.md
@@ -0,0 +1,19 @@
+First of all, install [quicktemplate](https://github.com/valyala/quicktemplate) package and [quicktemplate compiler](https://github.com/valyala/quicktemplate/tree/master/qtc)
+
+```sh
+go get -u github.com/valyala/quicktemplate
+go get -u github.com/valyala/quicktemplate/qtc
+```
+
+The example has the Go code compiled already for you, therefore:
+```sh
+go run main.go # http://localhost:8080
+```
+
+However there is an instruction below, full documentation can be found at https://github.com/valyala/quicktemplate.
+
+Save your template files into `templates` folder under the extension *.qtpl, open your terminal and run `qtc` inside this folder.
+
+If all went ok, `*.qtpl.go` files must appear in the `templates` folder. These files contain the Go code for all `*.qtpl` files.
+
+> Remember, each time you change a a `/templates/*.qtpl` file you have to run the `qtc` command and re-build your application.
\ No newline at end of file
diff --git a/_examples/http_responsewriter/quicktemplate/controllers/execute_template.go b/_examples/http_responsewriter/quicktemplate/controllers/execute_template.go
new file mode 100644
index 00000000..fbe39872
--- /dev/null
+++ b/_examples/http_responsewriter/quicktemplate/controllers/execute_template.go
@@ -0,0 +1,14 @@
+package controllers
+
+import (
+ "github.com/kataras/iris/_examples/http_responsewriter/quicktemplate/templates"
+
+ "github.com/kataras/iris/context"
+)
+
+// ExecuteTemplate renders a "tmpl" partial template to the `context#ResponseWriter`.
+func ExecuteTemplate(ctx context.Context, tmpl templates.Partial) {
+ ctx.Gzip(true)
+ ctx.ContentType("text/html")
+ templates.WriteTemplate(ctx.ResponseWriter(), tmpl)
+}
diff --git a/_examples/http_responsewriter/quicktemplate/controllers/hello.go b/_examples/http_responsewriter/quicktemplate/controllers/hello.go
new file mode 100644
index 00000000..e95262ec
--- /dev/null
+++ b/_examples/http_responsewriter/quicktemplate/controllers/hello.go
@@ -0,0 +1,30 @@
+package controllers
+
+import (
+ "github.com/kataras/iris/_examples/http_responsewriter/quicktemplate/templates"
+
+ "github.com/kataras/iris/context"
+)
+
+// Hello renders our ../templates/hello.qtpl file using the compiled ../templates/hello.qtpl.go file.
+func Hello(ctx context.Context) {
+ // vars := make(map[string]interface{})
+ // vars["message"] = "Hello World!"
+ // vars["name"] = ctx.Params().Get("name")
+ // [...]
+ // &templates.Hello{ Vars: vars }
+ // [...]
+
+ // However, as an alternative, we recommend that you should the `ctx.ViewData(key, value)`
+ // in order to be able modify the `templates.Hello#Vars` from a middleware(other handlers) as well.
+ ctx.ViewData("message", "Hello World!")
+ ctx.ViewData("name", ctx.Params().Get("name"))
+
+ // set view data to the `Vars` template's field
+ tmpl := &templates.Hello{
+ Vars: ctx.GetViewData(),
+ }
+
+ // render the template
+ ExecuteTemplate(ctx, tmpl)
+}
diff --git a/_examples/http_responsewriter/quicktemplate/controllers/index.go b/_examples/http_responsewriter/quicktemplate/controllers/index.go
new file mode 100644
index 00000000..2462a8c3
--- /dev/null
+++ b/_examples/http_responsewriter/quicktemplate/controllers/index.go
@@ -0,0 +1,15 @@
+package controllers
+
+import (
+ "github.com/kataras/iris/_examples/http_responsewriter/quicktemplate/templates"
+
+ "github.com/kataras/iris/context"
+)
+
+// Index renders our ../templates/index.qtpl file using the compiled ../templates/index.qtpl.go file.
+func Index(ctx context.Context) {
+ tmpl := &templates.Index{}
+
+ // render the template
+ ExecuteTemplate(ctx, tmpl)
+}
diff --git a/_examples/http_responsewriter/quicktemplate/main.go b/_examples/http_responsewriter/quicktemplate/main.go
new file mode 100644
index 00000000..e25fab4e
--- /dev/null
+++ b/_examples/http_responsewriter/quicktemplate/main.go
@@ -0,0 +1,22 @@
+package main
+
+import (
+ "github.com/kataras/iris/_examples/http_responsewriter/quicktemplate/controllers"
+
+ "github.com/kataras/iris"
+)
+
+func newApp() *iris.Application {
+ app := iris.New()
+ app.Get("/", controllers.Index)
+ app.Get("/{name}", controllers.Hello)
+
+ return app
+}
+
+func main() {
+ app := newApp()
+ // http://localhost:8080
+ // http://localhost:8080/yourname
+ app.Run(iris.Addr(":8080"), iris.WithoutServerError(iris.ErrServerClosed))
+}
diff --git a/_examples/http_responsewriter/quicktemplate/main_test.go b/_examples/http_responsewriter/quicktemplate/main_test.go
new file mode 100644
index 00000000..db5a88fd
--- /dev/null
+++ b/_examples/http_responsewriter/quicktemplate/main_test.go
@@ -0,0 +1,47 @@
+package main
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/kataras/iris/httptest"
+)
+
+func TestResponseWriterQuicktemplate(t *testing.T) {
+ baseRawBody := `
+
+
+ Quicktemplate integration with Iris
+
+
+
+ Header contents here...
+
+
+
+
+
%s
+
+ %s
+
+
+
+
+
+
+
+`
+
+ expectedIndexRawBody := fmt.Sprintf(baseRawBody, "Index Page", "This is our index page's body.")
+ name := "yourname"
+ expectedHelloRawBody := fmt.Sprintf(baseRawBody, "Hello World!", "Hello "+name+"!")
+
+ app := newApp()
+
+ e := httptest.New(t, app)
+
+ e.GET("/").Expect().Status(httptest.StatusOK).Body().Equal(expectedIndexRawBody)
+ e.GET("/" + name).Expect().Status(httptest.StatusOK).Body().Equal(expectedHelloRawBody)
+}
diff --git a/_examples/http_responsewriter/quicktemplate/models/.gitkeep b/_examples/http_responsewriter/quicktemplate/models/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/_examples/http_responsewriter/quicktemplate/templates/base.qtpl b/_examples/http_responsewriter/quicktemplate/templates/base.qtpl
new file mode 100644
index 00000000..4e3fd923
--- /dev/null
+++ b/_examples/http_responsewriter/quicktemplate/templates/base.qtpl
@@ -0,0 +1,36 @@
+This is our templates' base implementation.
+
+{% interface
+Partial {
+ Body()
+}
+%}
+
+
+Template writes a template implementing the Partial interface.
+{% func Template(p Partial) %}
+
+
+ Quicktemplate integration with Iris
+
+
+
+ Header contents here...
+
+
+
+ {%= p.Body() %}
+
+
+
+
+
+{% endfunc %}
+
+
+Base template implementation. Other pages may inherit from it if they need
+overriding only certain Partial methods.
+{% code type Base struct {} %}
+{% func (b *Base) Body() %}This is the base body{% endfunc %}
diff --git a/_examples/http_responsewriter/quicktemplate/templates/base.qtpl.go b/_examples/http_responsewriter/quicktemplate/templates/base.qtpl.go
new file mode 100644
index 00000000..32a5e638
--- /dev/null
+++ b/_examples/http_responsewriter/quicktemplate/templates/base.qtpl.go
@@ -0,0 +1,128 @@
+// This file is automatically generated by qtc from "base.qtpl".
+// See https://github.com/valyala/quicktemplate for details.
+
+// This is our templates' base implementation.
+//
+
+//line base.qtpl:3
+package templates
+
+//line base.qtpl:3
+import (
+ qtio422016 "io"
+
+ qt422016 "github.com/valyala/quicktemplate"
+)
+
+//line base.qtpl:3
+var (
+ _ = qtio422016.Copy
+ _ = qt422016.AcquireByteBuffer
+)
+
+//line base.qtpl:4
+type Partial interface {
+ //line base.qtpl:4
+ Body() string
+ //line base.qtpl:4
+ StreamBody(qw422016 *qt422016.Writer)
+ //line base.qtpl:4
+ WriteBody(qq422016 qtio422016.Writer)
+//line base.qtpl:4
+}
+
+// Template writes a template implementing the Partial interface.
+
+//line base.qtpl:11
+func StreamTemplate(qw422016 *qt422016.Writer, p Partial) {
+ //line base.qtpl:11
+ qw422016.N().S(`
+
+
+ Quicktemplate integration with Iris
+
+
+
+{% endfunc %}
diff --git a/_examples/http_responsewriter/quicktemplate/templates/index.qtpl.go b/_examples/http_responsewriter/quicktemplate/templates/index.qtpl.go
new file mode 100644
index 00000000..e5297ac4
--- /dev/null
+++ b/_examples/http_responsewriter/quicktemplate/templates/index.qtpl.go
@@ -0,0 +1,62 @@
+// This file is automatically generated by qtc from "index.qtpl".
+// See https://github.com/valyala/quicktemplate for details.
+
+// Index template, implements the Partial's methods.
+//
+
+//line index.qtpl:3
+package templates
+
+//line index.qtpl:3
+import (
+ qtio422016 "io"
+
+ qt422016 "github.com/valyala/quicktemplate"
+)
+
+//line index.qtpl:3
+var (
+ _ = qtio422016.Copy
+ _ = qt422016.AcquireByteBuffer
+)
+
+//line index.qtpl:4
+type Index struct{}
+
+//line index.qtpl:7
+func (i *Index) StreamBody(qw422016 *qt422016.Writer) {
+ //line index.qtpl:7
+ qw422016.N().S(`
+
Index Page
+
+ This is our index page's body.
+
+`)
+//line index.qtpl:12
+}
+
+//line index.qtpl:12
+func (i *Index) WriteBody(qq422016 qtio422016.Writer) {
+ //line index.qtpl:12
+ qw422016 := qt422016.AcquireWriter(qq422016)
+ //line index.qtpl:12
+ i.StreamBody(qw422016)
+ //line index.qtpl:12
+ qt422016.ReleaseWriter(qw422016)
+//line index.qtpl:12
+}
+
+//line index.qtpl:12
+func (i *Index) Body() string {
+ //line index.qtpl:12
+ qb422016 := qt422016.AcquireByteBuffer()
+ //line index.qtpl:12
+ i.WriteBody(qb422016)
+ //line index.qtpl:12
+ qs422016 := string(qb422016.B)
+ //line index.qtpl:12
+ qt422016.ReleaseByteBuffer(qb422016)
+ //line index.qtpl:12
+ return qs422016
+//line index.qtpl:12
+}
diff --git a/context/context.go b/context/context.go
index 9c786bff..e91111a4 100644
--- a/context/context.go
+++ b/context/context.go
@@ -22,6 +22,7 @@ import (
"strings"
"time"
+ "github.com/fatih/structs"
"github.com/microcosm-cc/bluemonday"
"github.com/monoculum/formam"
"github.com/russross/blackfriday"
@@ -486,6 +487,17 @@ type Context interface {
// Example: https://github.com/kataras/iris/tree/master/_examples/view/context-view-data/
ViewData(key string, value interface{})
+ // GetViewData returns the values registered by `context#ViewData`.
+ // The return value is `map[string]interface{}`, this means that
+ // if a custom struct registered to ViewData then this function
+ // will try to parse it to map, if failed then the return value is nil
+ // A check for nil is always a good practise if different
+ // kind of values or no data are registered via `ViewData`.
+ //
+ // Similarly to `viewData := ctx.Values().Get("iris.viewData")` or
+ // `viewData := ctx.Values().Get(ctx.Application().ConfigurationReadOnly().GetViewDataContextKey())`.
+ GetViewData() map[string]interface{}
+
// View renders templates based on the adapted view engines.
// First argument accepts the filename, relative to the view engine's Directory,
// i.e: if directory is "./templates" and want to render the "./templates/users/index.html"
@@ -1626,6 +1638,43 @@ func (ctx *context) ViewData(key string, value interface{}) {
}
}
+// GetViewData returns the values registered by `context#ViewData`.
+// The return value is `map[string]interface{}`, this means that
+// if a custom struct registered to ViewData then this function
+// will try to parse it to map, if failed then the return value is nil
+// A check for nil is always a good practise if different
+// kind of values or no data are registered via `ViewData`.
+//
+// Similarly to `viewData := ctx.Values().Get("iris.viewData")` or
+// `viewData := ctx.Values().Get(ctx.Application().ConfigurationReadOnly().GetViewDataContextKey())`.
+func (ctx *context) GetViewData() map[string]interface{} {
+ viewDataContextKey := ctx.Application().ConfigurationReadOnly().GetViewDataContextKey()
+ v := ctx.Values().Get(viewDataContextKey)
+
+ // if no values found, then return nil
+ if v == nil {
+ return nil
+ }
+
+ // if struct, convert it to map[string]interface{}
+ if structs.IsStruct(v) {
+ return structs.Map(v)
+ }
+
+ // if pure map[string]interface{}
+ if viewData, ok := v.(map[string]interface{}); ok {
+ return viewData
+ }
+
+ // if context#Map
+ if viewData, ok := v.(Map); ok {
+ return viewData
+ }
+
+ // if failure, then return nil
+ return nil
+}
+
// View renders templates based on the adapted view engines.
// First argument accepts the filename, relative to the view engine's Directory,
// i.e: if directory is "./templates" and want to render the "./templates/users/index.html"
diff --git a/core/router/handler.go b/core/router/handler.go
index ab660493..9c9da320 100644
--- a/core/router/handler.go
+++ b/core/router/handler.go
@@ -210,23 +210,22 @@ func (h *routerHandler) HandleRequest(ctx context.Context) {
}
if ctx.Application().ConfigurationReadOnly().GetFireMethodNotAllowed() {
- var methodAllowed string
for i := range h.trees {
t := h.trees[i]
- methodAllowed = t.Method // keep track of the allowed method of the last checked tree
- if ctx.Method() != methodAllowed {
- continue
+ // a bit slower than previous implementation but @kataras let me to apply this change
+ // because it's more reliable.
+ //
+ // if `Configuration#FireMethodNotAllowed` is kept as defaulted(false) then this function will not
+ // run, therefore performance kept as before.
+ if t.Nodes.Exists(path) {
+ // RCF rfc2616 https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
+ // The response MUST include an Allow header containing a list of valid methods for the requested resource.
+ ctx.Header("Allow", t.Method)
+ ctx.StatusCode(http.StatusMethodNotAllowed)
+ return
}
}
-
- if ctx.Method() != methodAllowed {
- // RCF rfc2616 https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
- // The response MUST include an Allow header containing a list of valid methods for the requested resource.
- ctx.Header("Allow", methodAllowed)
- ctx.StatusCode(http.StatusMethodNotAllowed)
- return
- }
-
}
+
ctx.StatusCode(http.StatusNotFound)
}
diff --git a/core/router/node/node.go b/core/router/node/node.go
index 25912ba1..d8cb9438 100644
--- a/core/router/node/node.go
+++ b/core/router/node/node.go
@@ -266,6 +266,15 @@ func (nodes Nodes) Find(path string, params *context.RequestParams) context.Hand
return nil
}
+// Exists returns true if a node with that "path" exists,
+// otherise false.
+//
+// We don't care about parameters here.
+func (nodes Nodes) Exists(path string) bool {
+ n, _ := nodes.findChild(path, nil)
+ return n != nil && len(n.handlers) > 0
+}
+
func (nodes Nodes) findChild(path string, params []string) (*node, []string) {
// println("request path: " + path)
for _, n := range nodes {