mirror of
https://github.com/kataras/iris.git
synced 2026-01-21 02:45:59 +00:00
Upgrade to new go errors and some minor fixes and improvements including easier debugging of invalid routes and e.t.c.
Former-commit-id: 5809157b952ccc61a67a9861470774b3a6fee024
This commit is contained in:
@@ -1,14 +1,17 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/kataras/iris/context"
|
||||
"github.com/kataras/iris/core/errors"
|
||||
"github.com/kataras/iris/core/errgroup"
|
||||
"github.com/kataras/iris/macro"
|
||||
macroHandler "github.com/kataras/iris/macro/handler"
|
||||
)
|
||||
@@ -157,6 +160,15 @@ func (repo *repository) register(route *Route) {
|
||||
repo.pos[route.tmpl.Src] = len(repo.routes) - 1
|
||||
}
|
||||
|
||||
type apiError struct {
|
||||
error
|
||||
}
|
||||
|
||||
func (e *apiError) Is(err error) bool {
|
||||
_, ok := err.(*apiError)
|
||||
return ok
|
||||
}
|
||||
|
||||
// APIBuilder the visible API for constructing the router
|
||||
// and child routers.
|
||||
type APIBuilder struct {
|
||||
@@ -174,7 +186,7 @@ type APIBuilder struct {
|
||||
// the list of possible errors that can be
|
||||
// collected on the build state to log
|
||||
// to the end-user.
|
||||
reporter *errors.Reporter
|
||||
errors *errgroup.Group
|
||||
|
||||
// the per-party handlers, order
|
||||
// of handlers registration matters.
|
||||
@@ -212,7 +224,7 @@ func NewAPIBuilder() *APIBuilder {
|
||||
api := &APIBuilder{
|
||||
macros: macro.Defaults,
|
||||
errorCodeHandlers: defaultErrorCodeHandlers(),
|
||||
reporter: errors.NewReporter(),
|
||||
errors: errgroup.New("API Builder"),
|
||||
relativePath: "/",
|
||||
routes: new(repository),
|
||||
}
|
||||
@@ -228,14 +240,9 @@ func (api *APIBuilder) GetRelPath() string {
|
||||
return api.relativePath
|
||||
}
|
||||
|
||||
// GetReport returns an error may caused by party's methods.
|
||||
func (api *APIBuilder) GetReport() error {
|
||||
return api.reporter.Return()
|
||||
}
|
||||
|
||||
// GetReporter returns the reporter for adding errors
|
||||
func (api *APIBuilder) GetReporter() *errors.Reporter {
|
||||
return api.reporter
|
||||
// GetReporter returns the reporter for adding or receiving any errors caused when building the API.
|
||||
func (api *APIBuilder) GetReporter() *errgroup.Group {
|
||||
return api.errors
|
||||
}
|
||||
|
||||
// AllowMethods will re-register the future routes that will be registered
|
||||
@@ -295,9 +302,11 @@ func (api *APIBuilder) createRoutes(methods []string, relativePath string, handl
|
||||
}
|
||||
}
|
||||
|
||||
filename, line := getCaller()
|
||||
|
||||
fullpath := api.relativePath + relativePath // for now, keep the last "/" if any, "/xyz/"
|
||||
if len(handlers) == 0 {
|
||||
api.reporter.Add("missing handlers for route %s: %s", strings.Join(methods, ", "), fullpath)
|
||||
api.errors.Addf("missing handlers for route[%s:%d] %s: %s", filename, line, strings.Join(methods, ", "), fullpath)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -336,10 +345,13 @@ func (api *APIBuilder) createRoutes(methods []string, relativePath string, handl
|
||||
for i, m := range methods {
|
||||
route, err := NewRoute(m, subdomain, path, possibleMainHandlerName, routeHandlers, *api.macros)
|
||||
if err != nil { // template path parser errors:
|
||||
api.reporter.Add("%v -> %s:%s:%s", err, m, subdomain, path)
|
||||
api.errors.Addf("[%s:%d] %v -> %s:%s:%s", filename, line, err, m, subdomain, path)
|
||||
continue
|
||||
}
|
||||
|
||||
route.SourceFileName = filename
|
||||
route.SourceLineNumber = line
|
||||
|
||||
// Add UseGlobal & DoneGlobal Handlers
|
||||
route.Use(api.beginGlobalHandlers...)
|
||||
route.Done(api.doneGlobalHandlers...)
|
||||
@@ -350,6 +362,32 @@ func (api *APIBuilder) createRoutes(methods []string, relativePath string, handl
|
||||
return routes
|
||||
}
|
||||
|
||||
// https://golang.org/doc/go1.9#callersframes
|
||||
func getCaller() (string, int) {
|
||||
var pcs [32]uintptr
|
||||
n := runtime.Callers(1, pcs[:])
|
||||
frames := runtime.CallersFrames(pcs[:n])
|
||||
wd, _ := os.Getwd()
|
||||
for {
|
||||
frame, more := frames.Next()
|
||||
file := frame.File
|
||||
|
||||
if !strings.Contains(file, "/kataras/iris") || strings.Contains(file, "/kataras/iris/_examples") || strings.Contains(file, "iris-contrib/examples") {
|
||||
if relFile, err := filepath.Rel(wd, file); err == nil {
|
||||
file = "./" + relFile
|
||||
}
|
||||
|
||||
return file, frame.Line
|
||||
}
|
||||
|
||||
if !more {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return "???", 0
|
||||
}
|
||||
|
||||
// Handle registers a route to the server's api.
|
||||
// if empty method is passed then handler(s) are being registered to all methods, same as .Any.
|
||||
//
|
||||
@@ -381,8 +419,7 @@ func (api *APIBuilder) Handle(method string, relativePath string, handlers ...co
|
||||
// app.Handle("GET", "/user/{id:uint64}", userByIDHandler)
|
||||
// app.Handle("GET", "/user/me", userMeHandler)
|
||||
//
|
||||
// This method is used behind the scenes at the `Controller` function
|
||||
// in order to handle more than one paths for the same controller instance.
|
||||
// app.HandleMany("GET POST", "/path", handler)
|
||||
func (api *APIBuilder) HandleMany(methodOrMulti string, relativePathorMulti string, handlers ...context.Handler) (routes []*Route) {
|
||||
// at least slash
|
||||
// a space
|
||||
@@ -502,7 +539,7 @@ func (api *APIBuilder) Party(relativePath string, handlers ...context.Handler) P
|
||||
errorCodeHandlers: api.errorCodeHandlers,
|
||||
beginGlobalHandlers: api.beginGlobalHandlers,
|
||||
doneGlobalHandlers: api.doneGlobalHandlers,
|
||||
reporter: api.reporter,
|
||||
errors: api.errors,
|
||||
// per-party/children
|
||||
middleware: middleware,
|
||||
doneHandlers: api.doneHandlers[0:],
|
||||
@@ -542,7 +579,7 @@ func (api *APIBuilder) PartyFunc(relativePath string, partyBuilderFunc func(p Pa
|
||||
func (api *APIBuilder) Subdomain(subdomain string, middleware ...context.Handler) Party {
|
||||
if api.relativePath == SubdomainWildcardIndicator {
|
||||
// cannot concat wildcard subdomain with something else
|
||||
api.reporter.Add("cannot concat parent wildcard subdomain with anything else -> %s , %s",
|
||||
api.errors.Addf("cannot concat parent wildcard subdomain with anything else -> %s , %s",
|
||||
api.relativePath, subdomain)
|
||||
return api
|
||||
}
|
||||
@@ -562,7 +599,7 @@ func (api *APIBuilder) Subdomain(subdomain string, middleware ...context.Handler
|
||||
func (api *APIBuilder) WildcardSubdomain(middleware ...context.Handler) Party {
|
||||
if hasSubdomain(api.relativePath) {
|
||||
// cannot concat static subdomain with a dynamic one, wildcard should be at the root level
|
||||
api.reporter.Add("cannot concat static subdomain with a dynamic one. Dynamic subdomains should be at the root level -> %s",
|
||||
api.errors.Addf("cannot concat static subdomain with a dynamic one. Dynamic subdomains should be at the root level -> %s",
|
||||
api.relativePath)
|
||||
return api
|
||||
}
|
||||
@@ -831,7 +868,7 @@ func (api *APIBuilder) Favicon(favPath string, requestPath ...string) *Route {
|
||||
|
||||
f, err := os.Open(favPath)
|
||||
if err != nil {
|
||||
api.reporter.AddErr(errDirectoryFileNotFound.Format(favPath, err.Error()))
|
||||
api.errors.Addf("favicon: file or directory %s not found: %w", favPath, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -848,8 +885,7 @@ func (api *APIBuilder) Favicon(favPath string, requestPath ...string) *Route {
|
||||
// So we could panic but we don't,
|
||||
// we just interrupt with a message
|
||||
// to the (user-defined) logger.
|
||||
api.reporter.AddErr(errDirectoryFileNotFound.
|
||||
Format(favPath, "favicon: couldn't read the data bytes for file: "+err.Error()))
|
||||
api.errors.Addf("favicon: couldn't read the data bytes for %s: %w", favPath, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/kataras/iris/context"
|
||||
)
|
||||
|
||||
/*
|
||||
Relative to deprecation:
|
||||
- party.go#L138-154
|
||||
- deprecated_example_test.go
|
||||
*/
|
||||
|
||||
// https://golang.org/doc/go1.9#callersframes
|
||||
func getCaller() (string, int) {
|
||||
var pcs [32]uintptr
|
||||
n := runtime.Callers(1, pcs[:])
|
||||
frames := runtime.CallersFrames(pcs[:n])
|
||||
wd, _ := os.Getwd()
|
||||
for {
|
||||
frame, more := frames.Next()
|
||||
file := frame.File
|
||||
|
||||
if (!strings.Contains(file, "/kataras/iris") ||
|
||||
strings.Contains(file, "/kataras/iris/_examples") ||
|
||||
strings.Contains(file, "/iris-contrib/examples") ||
|
||||
(strings.Contains(file, "/kataras/iris/core/router") && !strings.Contains(file, "deprecated.go"))) &&
|
||||
!strings.HasSuffix(frame.Func.Name(), ".getCaller") && !strings.Contains(file, "/go/src/testing") {
|
||||
|
||||
if relFile, err := filepath.Rel(wd, file); err == nil {
|
||||
file = "./" + relFile
|
||||
}
|
||||
|
||||
return file, frame.Line
|
||||
}
|
||||
|
||||
if !more {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return "?", 0
|
||||
}
|
||||
|
||||
// StaticWeb is DEPRECATED. Use HandleDir(requestPath, directory) instead.
|
||||
func (api *APIBuilder) StaticWeb(requestPath string, directory string) *Route {
|
||||
file, line := getCaller()
|
||||
api.reporter.Add(`StaticWeb is DEPRECATED and it will be removed eventually.
|
||||
Source: %s:%d
|
||||
Use .HandleDir("%s", "%s") instead.`, file, line, requestPath, directory)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// StaticHandler is DEPRECATED.
|
||||
// Use iris.FileServer(directory, iris.DirOptions{ShowList: true, Gzip: true}) instead.
|
||||
//
|
||||
// Example https://github.com/kataras/iris/tree/master/_examples/file-server/basic
|
||||
func (api *APIBuilder) StaticHandler(directory string, showList bool, gzip bool) context.Handler {
|
||||
file, line := getCaller()
|
||||
api.reporter.Add(`StaticHandler is DEPRECATED and it will be removed eventually.
|
||||
Source: %s:%d
|
||||
Use iris.FileServer("%s", iris.DirOptions{ShowList: %v, Gzip: %v}) instead.`, file, line, directory, showList, gzip)
|
||||
return FileServer(directory, DirOptions{ShowList: showList, Gzip: gzip})
|
||||
}
|
||||
|
||||
// StaticEmbedded is DEPRECATED.
|
||||
// Use HandleDir(requestPath, directory, iris.DirOptions{Asset: Asset, AssetInfo: AssetInfo, AssetNames: AssetNames}) instead.
|
||||
//
|
||||
// Example: https://github.com/kataras/iris/tree/master/_examples/file-server/embedding-files-into-app
|
||||
func (api *APIBuilder) StaticEmbedded(requestPath string, directory string, assetFn func(name string) ([]byte, error), namesFn func() []string) *Route {
|
||||
file, line := getCaller()
|
||||
api.reporter.Add(`StaticEmbedded is DEPRECATED and it will be removed eventually.
|
||||
It is also miss the AssetInfo bindata function, which is required now.
|
||||
Source: %s:%d
|
||||
Use .HandleDir("%s", "%s", iris.DirOptions{Asset: Asset, AssetInfo: AssetInfo, AssetNames: AssetNames}) instead.`, file, line, requestPath, directory)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// StaticEmbeddedGzip is DEPRECATED.
|
||||
// Use HandleDir(requestPath, directory, iris.DirOptions{Gzip: true, Asset: Asset, AssetInfo: AssetInfo, AssetNames: AssetNames}) instead.
|
||||
//
|
||||
// Example: https://github.com/kataras/iris/tree/master/_examples/file-server/embedding-gziped-files-into-app
|
||||
func (api *APIBuilder) StaticEmbeddedGzip(requestPath string, directory string, assetFn func(name string) ([]byte, error), namesFn func() []string) *Route {
|
||||
file, line := getCaller()
|
||||
api.reporter.Add(`StaticEmbeddedGzip is DEPRECATED and it will be removed eventually.
|
||||
It is also miss the AssetInfo bindata function, which is required now.
|
||||
Source: %s:%d
|
||||
Use .HandleDir("%s", "%s", iris.DirOptions{Gzip: true, Asset: Asset, AssetInfo: AssetInfo, AssetNames: AssetNames}) instead.`, file, line, requestPath, directory)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func ExampleParty_StaticWeb() {
|
||||
api := NewAPIBuilder()
|
||||
api.StaticWeb("/static", "./assets")
|
||||
|
||||
err := api.GetReport()
|
||||
if err == nil {
|
||||
panic("expected report for deprecation")
|
||||
}
|
||||
|
||||
fmt.Print(err)
|
||||
// Output: StaticWeb is DEPRECATED and it will be removed eventually.
|
||||
// Source: ./deprecated_example_test.go:9
|
||||
// Use .HandleDir("/static", "./assets") instead.
|
||||
}
|
||||
|
||||
func ExampleParty_StaticHandler() {
|
||||
api := NewAPIBuilder()
|
||||
api.StaticHandler("./assets", false, true)
|
||||
|
||||
err := api.GetReport()
|
||||
if err == nil {
|
||||
panic("expected report for deprecation")
|
||||
}
|
||||
|
||||
fmt.Print(err)
|
||||
// Output: StaticHandler is DEPRECATED and it will be removed eventually.
|
||||
// Source: ./deprecated_example_test.go:24
|
||||
// Use iris.FileServer("./assets", iris.DirOptions{ShowList: false, Gzip: true}) instead.
|
||||
}
|
||||
|
||||
func ExampleParty_StaticEmbedded() {
|
||||
api := NewAPIBuilder()
|
||||
api.StaticEmbedded("/static", "./assets", nil, nil)
|
||||
|
||||
err := api.GetReport()
|
||||
if err == nil {
|
||||
panic("expected report for deprecation")
|
||||
}
|
||||
|
||||
fmt.Print(err)
|
||||
// Output: StaticEmbedded is DEPRECATED and it will be removed eventually.
|
||||
// It is also miss the AssetInfo bindata function, which is required now.
|
||||
// Source: ./deprecated_example_test.go:39
|
||||
// Use .HandleDir("/static", "./assets", iris.DirOptions{Asset: Asset, AssetInfo: AssetInfo, AssetNames: AssetNames}) instead.
|
||||
}
|
||||
|
||||
func ExampleParty_StaticEmbeddedGzip() {
|
||||
api := NewAPIBuilder()
|
||||
api.StaticEmbeddedGzip("/static", "./assets", nil, nil)
|
||||
|
||||
err := api.GetReport()
|
||||
if err == nil {
|
||||
panic("expected report for deprecation")
|
||||
}
|
||||
|
||||
fmt.Print(err)
|
||||
// Output: StaticEmbeddedGzip is DEPRECATED and it will be removed eventually.
|
||||
// It is also miss the AssetInfo bindata function, which is required now.
|
||||
// Source: ./deprecated_example_test.go:55
|
||||
// Use .HandleDir("/static", "./assets", iris.DirOptions{Gzip: true, Asset: Asset, AssetInfo: AssetInfo, AssetNames: AssetNames}) instead.
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/kataras/iris/context"
|
||||
"github.com/kataras/iris/core/errors"
|
||||
"github.com/kataras/iris/core/errgroup"
|
||||
"github.com/kataras/iris/core/netutil"
|
||||
macroHandler "github.com/kataras/iris/macro/handler"
|
||||
|
||||
@@ -82,7 +82,7 @@ type RoutesProvider interface { // api builder
|
||||
|
||||
func (h *routerHandler) Build(provider RoutesProvider) error {
|
||||
h.trees = h.trees[0:0] // reset, inneed when rebuilding.
|
||||
rp := errors.NewReporter()
|
||||
rp := errgroup.New("Routes Builder")
|
||||
registeredRoutes := provider.GetRoutes()
|
||||
|
||||
// before sort.
|
||||
@@ -138,7 +138,7 @@ func (h *routerHandler) Build(provider RoutesProvider) error {
|
||||
// the docs better. Or TODO: add a link here in order to help new users.
|
||||
if err := h.addRoute(r); err != nil {
|
||||
// node errors:
|
||||
rp.Add("%v -> %s", err, r.String())
|
||||
rp.Addf("%s: %w", r.String(), err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
@@ -146,7 +146,7 @@ func (h *routerHandler) Build(provider RoutesProvider) error {
|
||||
golog.Debugf(r.Trace()) // keep log different parameter types in the same path as different routes.
|
||||
}
|
||||
|
||||
return rp.Return()
|
||||
return errgroup.Check(rp)
|
||||
}
|
||||
|
||||
func bindMultiParamTypesHandler(top *Route, r *Route) {
|
||||
|
||||
@@ -2,7 +2,7 @@ package router
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris/context"
|
||||
"github.com/kataras/iris/core/errors"
|
||||
"github.com/kataras/iris/core/errgroup"
|
||||
"github.com/kataras/iris/macro"
|
||||
)
|
||||
|
||||
@@ -16,8 +16,8 @@ type Party interface {
|
||||
// if r := app.Party("/users"), then the `r.GetRelPath()` is the "/users".
|
||||
// if r := app.Party("www.") or app.Subdomain("www") then the `r.GetRelPath()` is the "www.".
|
||||
GetRelPath() string
|
||||
// GetReporter returns the reporter for adding errors
|
||||
GetReporter() *errors.Reporter
|
||||
// GetReporter returns the reporter for adding or receiving any errors caused when building the API.
|
||||
GetReporter() *errgroup.Group
|
||||
// Macros returns the macro collection that is responsible
|
||||
// to register custom macros with their own parameter types and their macro functions for all routes.
|
||||
//
|
||||
@@ -135,23 +135,6 @@ type Party interface {
|
||||
//
|
||||
// Examples can be found at: https://github.com/kataras/iris/tree/master/_examples/file-server
|
||||
HandleDir(requestPath, directory string, opts ...DirOptions) *Route
|
||||
// StaticWeb is DEPRECATED. Use HandleDir(requestPath, directory) instead.
|
||||
StaticWeb(requestPath string, directory string) *Route
|
||||
// StaticHandler is DEPRECATED.
|
||||
// Use iris.FileServer(directory, iris.DirOptions{ShowList: true, Gzip: true}) instead.
|
||||
//
|
||||
// Example https://github.com/kataras/iris/tree/master/_examples/file-server/basic
|
||||
StaticHandler(directory string, showList bool, gzip bool) context.Handler
|
||||
// StaticEmbedded is DEPRECATED.
|
||||
// Use HandleDir(requestPath, directory, iris.DirOptions{Asset: Asset, AssetInfo: AssetInfo, AssetNames: AssetNames}) instead.
|
||||
//
|
||||
// Example: https://github.com/kataras/iris/tree/master/_examples/file-server/embedding-files-into-app
|
||||
StaticEmbedded(requestPath string, directory string, assetFn func(name string) ([]byte, error), namesFn func() []string) *Route
|
||||
// StaticEmbeddedGzip is DEPRECATED.
|
||||
// Use HandleDir(requestPath, directory, iris.DirOptions{Gzip: true, Asset: Asset, AssetInfo: AssetInfo, AssetNames: AssetNames}) instead.
|
||||
//
|
||||
// Example: https://github.com/kataras/iris/tree/master/_examples/file-server/embedding-gziped-files-into-app
|
||||
StaticEmbeddedGzip(requestPath string, directory string, assetFn func(name string) ([]byte, error), namesFn func() []string) *Route
|
||||
|
||||
// None registers an "offline" route
|
||||
// see context.ExecRoute(routeName) and
|
||||
|
||||
@@ -34,6 +34,10 @@ type Route struct {
|
||||
// used by Application to validate param values of a Route based on its name.
|
||||
FormattedPath string `json:"formattedPath"`
|
||||
|
||||
// the source code's filename:filenumber that this route was created from.
|
||||
SourceFileName string
|
||||
SourceLineNumber int
|
||||
|
||||
// StaticSites if not empty, refers to the system (or virtual if embedded) directory
|
||||
// and sub directories that this "GET" route was registered to serve files and folders
|
||||
// that contain index.html (a site). The index handler may registered by other
|
||||
@@ -276,7 +280,7 @@ func (r Route) ResolvePath(args ...string) string {
|
||||
// Trace returns some debug infos as a string sentence.
|
||||
// Should be called after Build.
|
||||
func (r Route) Trace() string {
|
||||
printfmt := fmt.Sprintf("%s:", r.Method)
|
||||
printfmt := fmt.Sprintf("[%s:%d] %s:", r.SourceFileName, r.SourceLineNumber, r.Method)
|
||||
if r.Subdomain != "" {
|
||||
printfmt += fmt.Sprintf(" %s", r.Subdomain)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
"github.com/kataras/iris/context"
|
||||
"github.com/kataras/iris/core/errors"
|
||||
)
|
||||
|
||||
// Router is the "director".
|
||||
|
||||
Reference in New Issue
Block a user