mirror of
https://github.com/kataras/iris.git
synced 2025-02-02 15:30:36 +01:00
New feature: Fallback views. Read HISTORY.md
This commit is contained in:
parent
a2588e480d
commit
435f284815
12
HISTORY.md
12
HISTORY.md
|
@ -28,6 +28,18 @@ The codebase for Dependency Injection, Internationalization and localization and
|
||||||
|
|
||||||
## Fixes and Improvements
|
## Fixes and Improvements
|
||||||
|
|
||||||
|
- New `FallbackView` feature, per-party or per handler chain. Example can be found at: [_examples/view/fallback](_examples/view/fallback).
|
||||||
|
|
||||||
|
```go
|
||||||
|
app.FallbackView(iris.FallbackViewFunc(func(ctx iris.Context, err iris.ErrViewNotExist) error {
|
||||||
|
// err.Name is the previous template name.
|
||||||
|
// err.IsLayout reports whether the failure came from the layout template.
|
||||||
|
// err.Data is the template data provided to the previous View call.
|
||||||
|
// [...custom logic e.g. ctx.View("fallback.html", err.Data)]
|
||||||
|
return err
|
||||||
|
}))
|
||||||
|
```
|
||||||
|
|
||||||
- New `versioning.Aliases` middleware and up to 80% faster version resolve. Example Code:
|
- New `versioning.Aliases` middleware and up to 80% faster version resolve. Example Code:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
|
|
46
_examples/view/fallback/main.go
Normal file
46
_examples/view/fallback/main.go
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kataras/iris/v12"
|
||||||
|
)
|
||||||
|
|
||||||
|
const defaultViewName = "fallback"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
app := iris.New()
|
||||||
|
app.RegisterView(iris.HTML("./view", ".html"))
|
||||||
|
|
||||||
|
// Use the FallbackView helper Register a fallback view
|
||||||
|
// filename per-party when the provided was not found.
|
||||||
|
app.FallbackView(iris.FallbackView("fallback.html"))
|
||||||
|
|
||||||
|
// Use the FallbackViewLayout helper to register a fallback view layout.
|
||||||
|
app.FallbackView(iris.FallbackViewLayout("layout.html"))
|
||||||
|
|
||||||
|
// Register a custom fallback function per-party to handle everything.
|
||||||
|
// You can register more than one. If fails (returns a not nil error of ErrViewNotExists)
|
||||||
|
// then it proceeds to the next registered fallback.
|
||||||
|
app.FallbackView(iris.FallbackViewFunc(func(ctx iris.Context, err iris.ErrViewNotExist) error {
|
||||||
|
// err.Name is the previous template name.
|
||||||
|
// err.IsLayout reports whether the failure came from the layout template.
|
||||||
|
// err.Data is the template data provided to the previous View call.
|
||||||
|
// [...custom logic e.g. ctx.View("fallback.html", err.Data)]
|
||||||
|
return err
|
||||||
|
}))
|
||||||
|
|
||||||
|
app.Get("/", index)
|
||||||
|
|
||||||
|
app.Listen(":8080")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register fallback view(s) in a middleware.
|
||||||
|
// func fallbackInsideAMiddleware(ctx iris.Context) {
|
||||||
|
// ctx.FallbackView(...)
|
||||||
|
// To remove all previous registered fallbacks, pass nil.
|
||||||
|
// ctx.FallbackView(nil)
|
||||||
|
// ctx.Next()
|
||||||
|
// }
|
||||||
|
|
||||||
|
func index(ctx iris.Context) {
|
||||||
|
ctx.View("blabla.html")
|
||||||
|
}
|
1
_examples/view/fallback/view/fallback.html
Normal file
1
_examples/view/fallback/view/fallback.html
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<h1>Fallback view</h1>
|
18
aliases.go
18
aliases.go
|
@ -252,6 +252,24 @@ var (
|
||||||
Ace = view.Ace
|
Ace = view.Ace
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// ErrViewNotExist reports whether a template was not found in the parsed templates tree.
|
||||||
|
ErrViewNotExist = context.ErrViewNotExist
|
||||||
|
// FallbackViewFunc is a function that can be registered
|
||||||
|
// to handle view fallbacks. It accepts the Context and
|
||||||
|
// a special error which contains information about the previous template error.
|
||||||
|
// It implements the FallbackViewProvider interface.
|
||||||
|
//
|
||||||
|
// See `Context.View` method.
|
||||||
|
FallbackViewFunc = context.FallbackViewFunc
|
||||||
|
// FallbackView is a helper to register a single template filename as a fallback
|
||||||
|
// when the provided tempate filename was not found.
|
||||||
|
FallbackView = context.FallbackView
|
||||||
|
// FallbackViewLayout is a helper to register a single template filename as a fallback
|
||||||
|
// layout when the provided layout filename was not found.
|
||||||
|
FallbackViewLayout = context.FallbackViewLayout
|
||||||
|
)
|
||||||
|
|
||||||
// PrefixDir returns a new FileSystem that opens files
|
// PrefixDir returns a new FileSystem that opens files
|
||||||
// by adding the given "prefix" to the directory tree of "fs".
|
// by adding the given "prefix" to the directory tree of "fs".
|
||||||
//
|
//
|
||||||
|
|
|
@ -797,6 +797,11 @@ type Configuration struct {
|
||||||
//
|
//
|
||||||
// Defaults to "iris.view.data".
|
// Defaults to "iris.view.data".
|
||||||
ViewDataContextKey string `ini:"view_data_context_key" json:"viewDataContextKey,omitempty" yaml:"ViewDataContextKey" toml:"ViewDataContextKey"`
|
ViewDataContextKey string `ini:"view_data_context_key" json:"viewDataContextKey,omitempty" yaml:"ViewDataContextKey" toml:"ViewDataContextKey"`
|
||||||
|
// FallbackViewContextKey is the context's values key
|
||||||
|
// responsible to store the view fallback information.
|
||||||
|
//
|
||||||
|
// Defaults to "iris.view.fallback".
|
||||||
|
FallbackViewContextKey string `ini:"fallback_view_context_key" json:"fallbackViewContextKey,omitempty" yaml:"FallbackViewContextKey" toml:"FallbackViewContextKey"`
|
||||||
// RemoteAddrHeaders are the allowed request headers names
|
// RemoteAddrHeaders are the allowed request headers names
|
||||||
// that can be valid to parse the client's IP based on.
|
// that can be valid to parse the client's IP based on.
|
||||||
// By-default no "X-" header is consired safe to be used for retrieving the
|
// By-default no "X-" header is consired safe to be used for retrieving the
|
||||||
|
@ -999,6 +1004,11 @@ func (c Configuration) GetViewDataContextKey() string {
|
||||||
return c.ViewDataContextKey
|
return c.ViewDataContextKey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetFallbackViewContextKey returns the FallbackViewContextKey field.
|
||||||
|
func (c Configuration) GetFallbackViewContextKey() string {
|
||||||
|
return c.FallbackViewContextKey
|
||||||
|
}
|
||||||
|
|
||||||
// GetRemoteAddrHeaders returns the RemoteAddrHeaders field.
|
// GetRemoteAddrHeaders returns the RemoteAddrHeaders field.
|
||||||
func (c Configuration) GetRemoteAddrHeaders() []string {
|
func (c Configuration) GetRemoteAddrHeaders() []string {
|
||||||
return c.RemoteAddrHeaders
|
return c.RemoteAddrHeaders
|
||||||
|
@ -1155,6 +1165,9 @@ func WithConfiguration(c Configuration) Configurator {
|
||||||
if v := c.ViewDataContextKey; v != "" {
|
if v := c.ViewDataContextKey; v != "" {
|
||||||
main.ViewDataContextKey = v
|
main.ViewDataContextKey = v
|
||||||
}
|
}
|
||||||
|
if v := c.FallbackViewContextKey; v != "" {
|
||||||
|
main.FallbackViewContextKey = v
|
||||||
|
}
|
||||||
|
|
||||||
if v := c.RemoteAddrHeaders; len(v) > 0 {
|
if v := c.RemoteAddrHeaders; len(v) > 0 {
|
||||||
main.RemoteAddrHeaders = v
|
main.RemoteAddrHeaders = v
|
||||||
|
@ -1228,6 +1241,7 @@ func DefaultConfiguration() Configuration {
|
||||||
ViewEngineContextKey: "iris.view.engine",
|
ViewEngineContextKey: "iris.view.engine",
|
||||||
ViewLayoutContextKey: "iris.view.layout",
|
ViewLayoutContextKey: "iris.view.layout",
|
||||||
ViewDataContextKey: "iris.view.data",
|
ViewDataContextKey: "iris.view.data",
|
||||||
|
FallbackViewContextKey: "iris.view.fallback",
|
||||||
RemoteAddrHeaders: nil,
|
RemoteAddrHeaders: nil,
|
||||||
RemoteAddrHeadersForce: false,
|
RemoteAddrHeadersForce: false,
|
||||||
RemoteAddrPrivateSubnets: []netutil.IPRange{
|
RemoteAddrPrivateSubnets: []netutil.IPRange{
|
||||||
|
|
|
@ -62,6 +62,8 @@ type ConfigurationReadOnly interface {
|
||||||
GetViewLayoutContextKey() string
|
GetViewLayoutContextKey() string
|
||||||
// GetViewDataContextKey returns the ViewDataContextKey field.
|
// GetViewDataContextKey returns the ViewDataContextKey field.
|
||||||
GetViewDataContextKey() string
|
GetViewDataContextKey() string
|
||||||
|
// GetFallbackViewContextKey returns the FallbackViewContextKey field.
|
||||||
|
GetFallbackViewContextKey() string
|
||||||
|
|
||||||
// GetRemoteAddrHeaders returns RemoteAddrHeaders field.
|
// GetRemoteAddrHeaders returns RemoteAddrHeaders field.
|
||||||
GetRemoteAddrHeaders() []string
|
GetRemoteAddrHeaders() []string
|
||||||
|
|
|
@ -2971,6 +2971,147 @@ func (ctx *Context) GetViewData() map[string]interface{} {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FallbackViewProvider is an interface which can be registered to the `Party.FallbackView`
|
||||||
|
// or `Context.FallbackView` methods to handle fallback views.
|
||||||
|
// See FallbackView, FallbackViewLayout and FallbackViewFunc.
|
||||||
|
type FallbackViewProvider interface {
|
||||||
|
FallbackView(ctx *Context, err ErrViewNotExist) error
|
||||||
|
} /* Notes(@kataras): If ever requested, this fallback logic (of ctx, error) can go to all necessary methods.
|
||||||
|
I've designed with a bit more complexity here instead of a simple filename fallback in order to give
|
||||||
|
the freedom to the developer to do whatever he/she wants with that template/layout not exists error,
|
||||||
|
e.g. have a list of fallbacks views to loop through until succeed or fire a different error than the default.
|
||||||
|
We also provide some helpers for common fallback actions (FallbackView, FallbackViewLayout).
|
||||||
|
This naming was chosen in order to be easy to follow up with the previous view-relative context features.
|
||||||
|
Also note that here we catch a specific error, we want the developer
|
||||||
|
to be aware of the rest template errors (e.g. when a template having parsing issues).
|
||||||
|
*/
|
||||||
|
|
||||||
|
// FallbackViewFunc is a function that can be registered
|
||||||
|
// to handle view fallbacks. It accepts the Context and
|
||||||
|
// a special error which contains information about the previous template error.
|
||||||
|
// It implements the FallbackViewProvider interface.
|
||||||
|
//
|
||||||
|
// See `Context.View` method.
|
||||||
|
type FallbackViewFunc func(ctx *Context, err ErrViewNotExist) error
|
||||||
|
|
||||||
|
// FallbackView completes the FallbackViewProvider interface.
|
||||||
|
func (fn FallbackViewFunc) FallbackView(ctx *Context, err ErrViewNotExist) error {
|
||||||
|
return fn(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ FallbackViewProvider = FallbackView("")
|
||||||
|
_ FallbackViewProvider = FallbackViewLayout("")
|
||||||
|
)
|
||||||
|
|
||||||
|
// FallbackView is a helper to register a single template filename as a fallback
|
||||||
|
// when the provided tempate filename was not found.
|
||||||
|
type FallbackView string
|
||||||
|
|
||||||
|
// FallbackView completes the FallbackViewProvider interface.
|
||||||
|
func (f FallbackView) FallbackView(ctx *Context, err ErrViewNotExist) error {
|
||||||
|
if err.IsLayout { // Not responsible to render layouts.
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ctx.StatusCode(200) // Let's keep the previous status code here, developer can change it anyways.
|
||||||
|
return ctx.View(string(f), err.Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FallbackViewLayout is a helper to register a single template filename as a fallback
|
||||||
|
// layout when the provided layout filename was not found.
|
||||||
|
type FallbackViewLayout string
|
||||||
|
|
||||||
|
// FallbackView completes the FallbackViewProvider interface.
|
||||||
|
func (f FallbackViewLayout) FallbackView(ctx *Context, err ErrViewNotExist) error {
|
||||||
|
if !err.IsLayout {
|
||||||
|
// Responsible to render layouts only.
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.ViewLayout(string(f))
|
||||||
|
return ctx.View(err.Name, err.Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
const fallbackViewOnce = "iris.fallback.view.once"
|
||||||
|
|
||||||
|
func (ctx *Context) fireFallbackViewOnce(err ErrViewNotExist) error {
|
||||||
|
// Note(@kataras): this is our way to keep the same View method for
|
||||||
|
// both fallback and normal views, remember, we export the whole
|
||||||
|
// Context functionality to the end-developer through the fallback view provider.
|
||||||
|
if ctx.values.Get(fallbackViewOnce) != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
v := ctx.values.Get(ctx.app.ConfigurationReadOnly().GetFallbackViewContextKey())
|
||||||
|
if v == nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
providers, ok := v.([]FallbackViewProvider)
|
||||||
|
if !ok {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.values.Set(fallbackViewOnce, struct{}{})
|
||||||
|
|
||||||
|
var pErr error
|
||||||
|
for _, provider := range providers {
|
||||||
|
pErr = provider.FallbackView(ctx, err)
|
||||||
|
if pErr != nil {
|
||||||
|
if vErr, ok := pErr.(ErrViewNotExist); ok {
|
||||||
|
// This fallback view does not exist or it's not responsible to handle,
|
||||||
|
// try the next.
|
||||||
|
pErr = vErr
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If OK then we found the correct fallback.
|
||||||
|
// If the error was a parse error and not a template not found
|
||||||
|
// then exit and report the pErr error.
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return pErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// FallbackView registers one or more fallback views for a template or a template layout.
|
||||||
|
// When View cannot find the given filename to execute then this "provider"
|
||||||
|
// is responsible to handle the error or render a different view.
|
||||||
|
//
|
||||||
|
// Usage:
|
||||||
|
// FallbackView(iris.FallbackView("fallback.html"))
|
||||||
|
// FallbackView(iris.FallbackViewLayout("layouts/fallback.html"))
|
||||||
|
// OR
|
||||||
|
// FallbackView(iris.FallbackViewFunc(ctx iris.Context, err iris.ErrViewNotExist) error {
|
||||||
|
// err.Name is the previous template name.
|
||||||
|
// err.IsLayout reports whether the failure came from the layout template.
|
||||||
|
// err.Data is the template data provided to the previous View call.
|
||||||
|
// [...custom logic e.g. ctx.View("fallback", err.Data)]
|
||||||
|
// })
|
||||||
|
func (ctx *Context) FallbackView(providers ...FallbackViewProvider) {
|
||||||
|
key := ctx.app.ConfigurationReadOnly().GetFallbackViewContextKey()
|
||||||
|
if key == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
v := ctx.values.Get(key)
|
||||||
|
if v == nil {
|
||||||
|
ctx.values.Set(key, providers)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Can register more than one.
|
||||||
|
storedProviders, ok := v.([]FallbackViewProvider)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
storedProviders = append(storedProviders, providers...)
|
||||||
|
ctx.values.Set(key, storedProviders)
|
||||||
|
}
|
||||||
|
|
||||||
// View renders a template based on the registered view engine(s).
|
// View renders a template based on the registered view engine(s).
|
||||||
// First argument accepts the filename, relative to the view engine's Directory and Extension,
|
// First argument accepts the filename, relative to the view engine's Directory and Extension,
|
||||||
// i.e: if directory is "./templates" and want to render the "./templates/users/index.html"
|
// i.e: if directory is "./templates" and want to render the "./templates/users/index.html"
|
||||||
|
@ -2985,8 +3126,26 @@ func (ctx *Context) GetViewData() map[string]interface{} {
|
||||||
// Examples: https://github.com/kataras/iris/tree/master/_examples/view
|
// Examples: https://github.com/kataras/iris/tree/master/_examples/view
|
||||||
func (ctx *Context) View(filename string, optionalViewModel ...interface{}) error {
|
func (ctx *Context) View(filename string, optionalViewModel ...interface{}) error {
|
||||||
ctx.ContentType(ContentHTMLHeaderValue)
|
ctx.ContentType(ContentHTMLHeaderValue)
|
||||||
cfg := ctx.app.ConfigurationReadOnly()
|
|
||||||
|
|
||||||
|
err := ctx.renderView(filename, optionalViewModel...)
|
||||||
|
if errNotExists, ok := err.(ErrViewNotExist); ok {
|
||||||
|
err = ctx.fireFallbackViewOnce(errNotExists)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if ctx.app.Logger().Level == golog.DebugLevel {
|
||||||
|
// send the error back to the client, when debug mode.
|
||||||
|
ctx.StopWithError(http.StatusInternalServerError, err)
|
||||||
|
} else {
|
||||||
|
ctx.StopWithStatus(http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *Context) renderView(filename string, optionalViewModel ...interface{}) error {
|
||||||
|
cfg := ctx.app.ConfigurationReadOnly()
|
||||||
layout := ctx.values.GetString(cfg.GetViewLayoutContextKey())
|
layout := ctx.values.GetString(cfg.GetViewLayoutContextKey())
|
||||||
|
|
||||||
var bindingData interface{}
|
var bindingData interface{}
|
||||||
|
@ -3000,28 +3159,12 @@ func (ctx *Context) View(filename string, optionalViewModel ...interface{}) erro
|
||||||
if key := cfg.GetViewEngineContextKey(); key != "" {
|
if key := cfg.GetViewEngineContextKey(); key != "" {
|
||||||
if engineV := ctx.values.Get(key); engineV != nil {
|
if engineV := ctx.values.Get(key); engineV != nil {
|
||||||
if engine, ok := engineV.(ViewEngine); ok {
|
if engine, ok := engineV.(ViewEngine); ok {
|
||||||
err := engine.ExecuteWriter(ctx, filename, layout, bindingData)
|
return engine.ExecuteWriter(ctx, filename, layout, bindingData)
|
||||||
if err != nil {
|
|
||||||
ctx.app.Logger().Errorf("View [%v] [%T]: %v", ctx.getLogIdentifier(), engine, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err := ctx.app.View(ctx, filename, layout, bindingData) // if failed it logs the error.
|
return ctx.app.View(ctx, filename, layout, bindingData)
|
||||||
if err != nil {
|
|
||||||
if ctx.app.Logger().Level == golog.DebugLevel {
|
|
||||||
// send the error back to the client, when debug mode.
|
|
||||||
ctx.StopWithError(http.StatusInternalServerError, err)
|
|
||||||
} else {
|
|
||||||
ctx.StopWithStatus(http.StatusInternalServerError)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// getLogIdentifier returns the ID, or the client remote IP address,
|
// getLogIdentifier returns the ID, or the client remote IP address,
|
||||||
|
|
|
@ -1,6 +1,26 @@
|
||||||
package context
|
package context
|
||||||
|
|
||||||
import "io"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrViewNotExist it's an error.
|
||||||
|
// It reports whether a template was not found in the parsed templates tree.
|
||||||
|
type ErrViewNotExist struct {
|
||||||
|
Name string
|
||||||
|
IsLayout bool
|
||||||
|
Data interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error completes the `error` interface.
|
||||||
|
func (e ErrViewNotExist) Error() string {
|
||||||
|
title := "template"
|
||||||
|
if e.IsLayout {
|
||||||
|
title = "layout"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s '%s' does not exist", title, e.Name)
|
||||||
|
}
|
||||||
|
|
||||||
// ViewEngine is the interface which all view engines should be implemented in order to be registered inside iris.
|
// ViewEngine is the interface which all view engines should be implemented in order to be registered inside iris.
|
||||||
type ViewEngine interface {
|
type ViewEngine interface {
|
||||||
|
|
|
@ -1491,6 +1491,26 @@ func (api *APIBuilder) RegisterView(viewEngine context.ViewEngine) {
|
||||||
// to keep the iris.Application a compatible Party.
|
// to keep the iris.Application a compatible Party.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FallbackView registers one or more fallback views for a template or a template layout.
|
||||||
|
// Usage:
|
||||||
|
// FallbackView(iris.FallbackView("fallback.html"))
|
||||||
|
// FallbackView(iris.FallbackViewLayout("layouts/fallback.html"))
|
||||||
|
// OR
|
||||||
|
// FallbackView(iris.FallbackViewFunc(ctx iris.Context, err iris.ErrViewNotExist) error {
|
||||||
|
// err.Name is the previous template name.
|
||||||
|
// err.IsLayout reports whether the failure came from the layout template.
|
||||||
|
// err.Data is the template data provided to the previous View call.
|
||||||
|
// [...custom logic e.g. ctx.View("fallback", err.Data)]
|
||||||
|
// })
|
||||||
|
func (api *APIBuilder) FallbackView(provider context.FallbackViewProvider) {
|
||||||
|
handler := func(ctx *context.Context) {
|
||||||
|
ctx.FallbackView(provider)
|
||||||
|
ctx.Next()
|
||||||
|
}
|
||||||
|
api.Use(handler)
|
||||||
|
api.UseError(handler)
|
||||||
|
}
|
||||||
|
|
||||||
// Layout overrides the parent template layout with a more specific layout for this Party.
|
// Layout overrides the parent template layout with a more specific layout for this Party.
|
||||||
// It returns the current Party.
|
// It returns the current Party.
|
||||||
//
|
//
|
||||||
|
|
|
@ -312,6 +312,18 @@ type Party interface {
|
||||||
// To register a view engine per handler chain see the `Context.ViewEngine` instead.
|
// To register a view engine per handler chain see the `Context.ViewEngine` instead.
|
||||||
// Read `Configuration.ViewEngineContextKey` documentation for more.
|
// Read `Configuration.ViewEngineContextKey` documentation for more.
|
||||||
RegisterView(viewEngine context.ViewEngine)
|
RegisterView(viewEngine context.ViewEngine)
|
||||||
|
// FallbackView registers one or more fallback views for a template or a template layout.
|
||||||
|
// Usage:
|
||||||
|
// FallbackView(iris.FallbackView("fallback.html"))
|
||||||
|
// FallbackView(iris.FallbackViewLayout("layouts/fallback.html"))
|
||||||
|
// OR
|
||||||
|
// FallbackView(iris.FallbackViewFunc(ctx iris.Context, err iris.ErrViewNotExist) error {
|
||||||
|
// err.Name is the previous template name.
|
||||||
|
// err.IsLayout reports whether the failure came from the layout template.
|
||||||
|
// err.Data is the template data provided to the previous View call.
|
||||||
|
// [...custom logic e.g. ctx.View("fallback", err.Data)]
|
||||||
|
// })
|
||||||
|
FallbackView(provider context.FallbackViewProvider)
|
||||||
// Layout overrides the parent template layout with a more specific layout for this Party.
|
// Layout overrides the parent template layout with a more specific layout for this Party.
|
||||||
// It returns the current Party.
|
// It returns the current Party.
|
||||||
//
|
//
|
||||||
|
|
6
iris.go
6
iris.go
|
@ -410,11 +410,7 @@ func (app *Application) View(writer io.Writer, filename string, layout string, b
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err := app.view.ExecuteWriter(writer, filename, layout, bindingData)
|
return app.view.ExecuteWriter(writer, filename, layout, bindingData)
|
||||||
if err != nil {
|
|
||||||
app.logger.Error(err)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConfigureHost accepts one or more `host#Configuration`, these configurators functions
|
// ConfigureHost accepts one or more `host#Configuration`, these configurators functions
|
||||||
|
|
|
@ -217,7 +217,7 @@ func (s *AmberEngine) executeTemplateBuf(name string, binding interface{}) (stri
|
||||||
tmpl := s.fromCache(name)
|
tmpl := s.fromCache(name)
|
||||||
if tmpl == nil {
|
if tmpl == nil {
|
||||||
s.bufPool.Put(buf)
|
s.bufPool.Put(buf)
|
||||||
return "", ErrNotExist{name, false}
|
return "", ErrNotExist{name, false, binding}
|
||||||
}
|
}
|
||||||
|
|
||||||
err := tmpl.ExecuteTemplate(buf, name, binding)
|
err := tmpl.ExecuteTemplate(buf, name, binding)
|
||||||
|
@ -253,5 +253,5 @@ func (s *AmberEngine) ExecuteWriter(w io.Writer, filename string, layout string,
|
||||||
return tmpl.Execute(w, bindingData)
|
return tmpl.Execute(w, bindingData)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ErrNotExist{filename, false}
|
return ErrNotExist{filename, false, bindingData}
|
||||||
}
|
}
|
||||||
|
|
|
@ -307,5 +307,5 @@ func (s *DjangoEngine) ExecuteWriter(w io.Writer, filename string, _ string, bin
|
||||||
return tmpl.ExecuteWriter(getPongoContext(bindingData), w)
|
return tmpl.ExecuteWriter(getPongoContext(bindingData), w)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ErrNotExist{filename, false}
|
return ErrNotExist{filename, false, bindingData}
|
||||||
}
|
}
|
||||||
|
|
|
@ -235,5 +235,5 @@ func (s *HandlebarsEngine) ExecuteWriter(w io.Writer, filename string, layout st
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return ErrNotExist{fmt.Sprintf("%s (file: %s)", renderFilename, filename), false}
|
return ErrNotExist{fmt.Sprintf("%s (file: %s)", renderFilename, filename), false, bindingData}
|
||||||
}
|
}
|
||||||
|
|
|
@ -421,14 +421,14 @@ func (s *HTMLEngine) ExecuteWriter(w io.Writer, name string, layout string, bind
|
||||||
|
|
||||||
t := s.Templates.Lookup(name)
|
t := s.Templates.Lookup(name)
|
||||||
if t == nil {
|
if t == nil {
|
||||||
return ErrNotExist{name, false}
|
return ErrNotExist{name, false, bindingData}
|
||||||
}
|
}
|
||||||
s.runtimeFuncsFor(t, name, bindingData)
|
s.runtimeFuncsFor(t, name, bindingData)
|
||||||
|
|
||||||
if layout = getLayout(layout, s.layout); layout != "" {
|
if layout = getLayout(layout, s.layout); layout != "" {
|
||||||
lt := s.Templates.Lookup(layout)
|
lt := s.Templates.Lookup(layout)
|
||||||
if lt == nil {
|
if lt == nil {
|
||||||
return ErrNotExist{layout, true}
|
return ErrNotExist{layout, true, bindingData}
|
||||||
}
|
}
|
||||||
|
|
||||||
s.layoutFuncsFor(lt, name, bindingData)
|
s.layoutFuncsFor(lt, name, bindingData)
|
||||||
|
|
14
view/view.go
14
view/view.go
|
@ -21,19 +21,7 @@ type (
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrNotExist reports whether a template was not found in the parsed templates tree.
|
// ErrNotExist reports whether a template was not found in the parsed templates tree.
|
||||||
type ErrNotExist struct {
|
type ErrNotExist = context.ErrViewNotExist
|
||||||
Name string
|
|
||||||
IsLayout bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error implements the `error` interface.
|
|
||||||
func (e ErrNotExist) Error() string {
|
|
||||||
title := "template"
|
|
||||||
if e.IsLayout {
|
|
||||||
title = "layout"
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%s '%s' does not exist", title, e.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// View is just a wrapper on top of the registered template engine.
|
// View is just a wrapper on top of the registered template engine.
|
||||||
type View struct{ Engine }
|
type View struct{ Engine }
|
||||||
|
|
Loading…
Reference in New Issue
Block a user