diff --git a/_examples/examples/url-shortener/main.go b/_examples/examples/url-shortener/main.go index 7ac86cf4..fea4291e 100644 --- a/_examples/examples/url-shortener/main.go +++ b/_examples/examples/url-shortener/main.go @@ -23,6 +23,18 @@ func main() { // Serve static files (css) app.StaticWeb("/static", "./static_files") + // other solution for both static files and urls in the root path(not recommended but it's here if you ever need it): + // fileserver := iris.StaticHandler("./static_files", false, false) + // app.Get("/*path", func(ctx *iris.Context) { + // fileserver.Serve(ctx) + // if ctx.StatusCode() >= 200 && ctx.StatusCode() < 300 { + // // then the file found and served correctly. + // } else { + // // otherwise check for urls.... + // execShortURL(ctx, ctx.Param("path")) + // } + // }) + var mu sync.Mutex var urls = map[string]string{ "iris": "http://support.iris-go.com", @@ -53,7 +65,7 @@ func main() { execShortURL(ctx, ctx.Param("shortkey")) }) - // for wildcard subdomain (yeah.. cool) http://dsaoj41u321dsa.localhost:8080 + // for wildcard subdomain (yeah.. cool) http://dsaoj41u321dsa.localhost:8080 // Note: // if you want subdomains (chrome doesn't works on localhost, so you have to define other hostname on app.Listen) // app.Party("*.", func(ctx *iris.Context) { diff --git a/adaptors/view/_examples/custom_renderer/main.go b/adaptors/view/_examples/custom_renderer/main.go new file mode 100644 index 00000000..e9fdb28d --- /dev/null +++ b/adaptors/view/_examples/custom_renderer/main.go @@ -0,0 +1,48 @@ +package main + +import ( + "io" + + "gopkg.in/kataras/iris.v6" + "gopkg.in/kataras/iris.v6/adaptors/httprouter" +) + +func main() { + app := iris.New() + // output startup banner and error logs on os.Stdout + app.Adapt(iris.DevLogger()) + // set the router, you can choose gorillamux too + app.Adapt(httprouter.New()) + + // Custom Render Policy to override or create new content-type render + // i,e: "text/html" with a prefix, + // we will just write to the writer and return false + // to continue to the next contentType-matched renderer if any. + app.Adapt(iris.RenderPolicy(func(out io.Writer, contentType string, binding interface{}, options ...map[string]interface{}) (bool, error) { + + if contentType == "text/html" { + if content, ok := binding.(string); ok { + out.Write([]byte("
My Custom prefix

" + content)) + } + + } + // continue to the next, no error + // note: if we wanted to stop here we would return true instead of false. + return false, nil + })) + + app.Get("", func(ctx *iris.Context) { + + // These content-types are not managed by our RenderPolicy: + // text, binary and html content-types are + // not rendered via serializers, you have to + // use the ctx.Render functions instead. + // so something like this: + // ctx.Text(iris.StatusOK, "my text content body here!") + // will NOT work with out custom render policy. + ctx.Render("text/html", + "my text content body here!") + }) + + app.Listen(":8080") +} diff --git a/adaptors/view/_examples/overview/main.go b/adaptors/view/_examples/overview/main.go index c78e39e4..379e2202 100644 --- a/adaptors/view/_examples/overview/main.go +++ b/adaptors/view/_examples/overview/main.go @@ -55,39 +55,5 @@ func main() { }) - // ------ first customization without even the need of *Context or a Handler-------- - // - // Custom new content-/type: - // app.Adapt(iris.RenderPolicy(func(out io.Writer, name string, binding interface{}, options ...map[string]interface{}) (error, bool) { - // if name == "customcontent-type" { - // - // // some very advanced things here: - // out.Write([]byte(binding.(string))) - // return nil, true - // } - // return nil, false - // })) - // - // app.Get("/custom", func(ctx *iris.Context) { - // ctx.RenderWithStatus(iris.StatusOK, // or MustRender - // "customcontent-type", - // "my custom content here!", - // ) - // }) - // - // ---- second ----------------------------------------------------------------------- - // - // Override the defaults (the json,xml,jsonp,text,data and so on), an existing content-type: - // app.Adapt(iris.RenderPolicy(func(out io.Writer, name string, binding interface{}, options ...map[string]interface{}) (error, bool) { - // if name == "text/plain" { - // out.Write([]byte("From the custom text/plain renderer: " + binding.(string))) - // return nil, true - // } - // - // return nil, false - // })) - // // the context.Text's behaviors was changed now by your custom renderer. - // - app.Listen(":8080") } diff --git a/fs.go b/fs.go index 221053f9..2160fe22 100644 --- a/fs.go +++ b/fs.go @@ -20,13 +20,35 @@ import ( "github.com/kataras/go-errors" ) +// StaticHandler returns a new Handler which is ready +// to serve all kind of static files. +// +// Developers can wrap this handler using the `iris.StripPrefix` +// for a fixed static path when the result handler is being, finally, registered to a route. +// +// +// Usage: +// app := iris.New() +// ... +// fileserver := iris.StaticHandler("./static_files", false, false) +// h := iris.StripPrefix("/static", fileserver) +// /* http://mydomain.com/static/css/style.css */ +// app.Get("/static", h) +// ... +// +func StaticHandler(systemPath string, showList bool, enableGzip bool, exceptRoutes ...RouteInfo) HandlerFunc { + return NewStaticHandlerBuilder(systemPath). + Listing(showList). + Gzip(enableGzip). + Except(exceptRoutes...). + Build() +} + // StaticHandlerBuilder is the web file system's Handler builder // use that or the iris.StaticHandler/StaticWeb methods type StaticHandlerBuilder interface { - Path(requestRoutePath string) StaticHandlerBuilder Gzip(enable bool) StaticHandlerBuilder Listing(listDirectoriesOnOff bool) StaticHandlerBuilder - StripPath(yesNo bool) StaticHandlerBuilder Except(r ...RouteInfo) StaticHandlerBuilder Build() HandlerFunc } @@ -40,8 +62,6 @@ type StaticHandlerBuilder interface { type fsHandler struct { // user options, only directory is required. directory http.Dir - requestPath string - stripPath bool gzip bool listDirectories bool // these are init on the Build() call @@ -82,10 +102,6 @@ func abs(path string) string { func NewStaticHandlerBuilder(dir string) StaticHandlerBuilder { return &fsHandler{ directory: http.Dir(abs(dir)), - // default route path is the same as the directory - requestPath: toWebPath(dir), - // enable strip path by-default - stripPath: true, // gzip is disabled by default gzip: false, // list directories disabled by default @@ -93,13 +109,6 @@ func NewStaticHandlerBuilder(dir string) StaticHandlerBuilder { } } -// Path sets the request path. -// Defaults to same as system path -func (w *fsHandler) Path(requestRoutePath string) StaticHandlerBuilder { - w.requestPath = toWebPath(requestRoutePath) - return w -} - // Gzip if enable is true then gzip compression is enabled for this static directory // Defaults to false func (w *fsHandler) Gzip(enable bool) StaticHandlerBuilder { @@ -114,11 +123,6 @@ func (w *fsHandler) Listing(listDirectoriesOnOff bool) StaticHandlerBuilder { return w } -func (w *fsHandler) StripPath(yesNo bool) StaticHandlerBuilder { - w.stripPath = yesNo - return w -} - // Except add a route exception, // gives priority to that Route over the static handler. func (w *fsHandler) Except(r ...RouteInfo) StaticHandlerBuilder { @@ -160,7 +164,7 @@ func (w *fsHandler) Build() HandlerFunc { w.once.Do(func() { w.filesystem = w.directory - hStatic := func(ctx *Context) { + fileserver := func(ctx *Context) { upath := ctx.Request.URL.Path if !strings.HasPrefix(upath, "/") { upath = "/" + upath @@ -203,14 +207,6 @@ func (w *fsHandler) Build() HandlerFunc { } } - var fileserver HandlerFunc - - if w.stripPath { - fileserver = StripPrefix(w.requestPath, hStatic) - } else { - fileserver = hStatic - } - if len(w.exceptions) > 0 { middleware := make(Middleware, len(w.exceptions)+1) for i := range w.exceptions { @@ -237,12 +233,26 @@ func (w *fsHandler) Build() HandlerFunc { // and invoking the handler h. StripPrefix handles a // request for a path that doesn't begin with prefix by // replying with an HTTP 404 not found error. +// +// Usage: +// fileserver := iris.StaticHandler("./static_files", false, false) +// h := iris.StripPrefix("/static", fileserver) +// app.Get("/static", h) +// func StripPrefix(prefix string, h HandlerFunc) HandlerFunc { if prefix == "" { return h } + // here we separate the path from the subdomain (if any), we care only for the path + // fixes a bug when serving static files via a subdomain + fixedPrefix := prefix + if dotWSlashIdx := strings.Index(fixedPrefix, subdomainIndicator); dotWSlashIdx > 0 { + fixedPrefix = fixedPrefix[dotWSlashIdx+1:] + } + fixedPrefix = toWebPath(fixedPrefix) + return func(ctx *Context) { - if p := strings.TrimPrefix(ctx.Request.URL.Path, prefix); len(p) < len(ctx.Request.URL.Path) { + if p := strings.TrimPrefix(ctx.Request.URL.Path, fixedPrefix); len(p) < len(ctx.Request.URL.Path) { ctx.Request.URL.Path = p h(ctx) } else { diff --git a/router.go b/router.go index 3f207f9b..313aafda 100644 --- a/router.go +++ b/router.go @@ -562,22 +562,27 @@ func (router *Router) Favicon(favPath string, requestPath ...string) RouteInfo { return router.registerResourceRoute(reqPath, h) } -// StaticHandler returns a new Handler which serves static files +// StaticHandler returns a new Handler which is ready +// to serve all kind of static files. +// +// Note: +// The only difference from package-level `StaticHandler` +// is that this `StaticHandler`` receives a request path which +// is appended to the party's relative path and stripped here, +// so `iris.StripPath` is useless and should not being used here. +// +// Usage: +// app := iris.New() +// ... +// mySubdomainFsServer := app.Party("mysubdomain.") +// h := mySubdomainFsServer.StaticHandler("/static", "./static_files", false, false) +// /* http://mysubdomain.mydomain.com/static/css/style.css */ +// mySubdomainFsServer.Get("/static", h) +// ... +// func (router *Router) StaticHandler(reqPath string, systemPath string, showList bool, enableGzip bool, exceptRoutes ...RouteInfo) HandlerFunc { - // here we separate the path from the subdomain (if any), we care only for the path - // fixes a bug when serving static files via a subdomain - fullpath := router.relativePath + reqPath - path := fullpath - if dotWSlashIdx := strings.Index(path, subdomainIndicator); dotWSlashIdx > 0 { - path = fullpath[dotWSlashIdx+1:] - } - - return NewStaticHandlerBuilder(systemPath). - Path(path). - Listing(showList). - Gzip(enableGzip). - Except(exceptRoutes...). - Build() + return StripPrefix(router.relativePath+reqPath, + StaticHandler(systemPath, showList, enableGzip)) } // StaticWeb returns a handler that serves HTTP requests @@ -602,15 +607,17 @@ func (router *Router) StaticWeb(reqPath string, systemPath string, exceptRoutes routePath := router.Context.Framework().RouteWildcardPath(reqPath, paramName) handler := func(ctx *Context) { h(ctx) - if fname := ctx.Param(paramName); fname != "" { - cType := typeByExtension(fname) - if cType != contentBinary && !strings.Contains(cType, "charset") { - cType += "; charset=" + ctx.framework.Config.Charset + // re-check the content type here for any case, + // although the new code does it automatically but it's good to have it here. + if ctx.StatusCode() >= 200 && ctx.StatusCode() < 400 { + if fname := ctx.Param(paramName); fname != "" { + cType := typeByExtension(fname) + if cType != contentBinary && !strings.Contains(cType, "charset") { + cType += "; charset=" + ctx.framework.Config.Charset + } + ctx.SetContentType(cType) } - - ctx.SetContentType(cType) } - } return router.registerResourceRoute(routePath, handler)