mirror of
https://github.com/kataras/iris.git
synced 2025-01-23 02:31:04 +01:00
create the new FileServer and HandleDir, deprecate the rest APIBuilder/Party static methods and more
relative: https://github.com/kataras/iris/issues/1283 and removing pongo2 from vendor: https://github.com/kataras/iris/issues/1284 Former-commit-id: 3ec57b349f99faca2b8e36d9f7252db0b6ea080d
This commit is contained in:
parent
7f9e33cabb
commit
d0104defa8
|
@ -19,7 +19,7 @@ const (
|
|||
func main() {
|
||||
app := iris.New()
|
||||
app.RegisterView(iris.HTML("./views", ".html").Layout("shared/layout.html"))
|
||||
app.StaticWeb("/public", publicDir)
|
||||
app.HandleDir("/public", publicDir)
|
||||
app.OnAnyErrorCode(onError)
|
||||
|
||||
mvc.New(app).Handle(new(controllers.HomeController))
|
||||
|
|
|
@ -149,7 +149,7 @@ Navigate through examples for a better understanding.
|
|||
- [Write your own custom parameter types](routing/macros/main.go)
|
||||
- [Reverse routing](routing/reverse/main.go)
|
||||
- [Custom Router (high-level)](routing/custom-high-level-router/main.go)
|
||||
- [Custom Wrapper](routing/custom-wrapper/main.go)
|
||||
- [Custom Wrapper](routing/custom-wrapper/main.go) **UPDATED**
|
||||
- Custom Context
|
||||
* [method overriding](routing/custom-context/method-overriding/main.go)
|
||||
* [new implementation](routing/custom-context/new-implementation/main.go)
|
||||
|
@ -366,14 +366,14 @@ You can serve [quicktemplate](https://github.com/valyala/quicktemplate) and [her
|
|||
### File Server
|
||||
|
||||
- [Favicon](file-server/favicon/main.go)
|
||||
- [Basic](file-server/basic/main.go)
|
||||
- [Embedding Files Into App Executable File](file-server/embedding-files-into-app/main.go)
|
||||
- [Embedding Gziped Files Into App Executable File](file-server/embedding-gziped-files-into-app/main.go)
|
||||
- [Basic](file-server/basic/main.go) **UPDATED**
|
||||
- [Embedding Files Into App Executable File](file-server/embedding-files-into-app/main.go) **UPDATED**
|
||||
- [Embedding Gziped Files Into App Executable File](file-server/embedding-gziped-files-into-app/main.go) **UPDATED**
|
||||
- [Send/Force-Download Files](file-server/send-files/main.go)
|
||||
- Single Page Applications
|
||||
* [single Page Application](file-server/single-page-application/basic/main.go)
|
||||
* [embedded Single Page Application](file-server/single-page-application/embedded-single-page-application/main.go)
|
||||
* [embedded Single Page Application with other routes](file-server/single-page-application/embedded-single-page-application-with-other-routes/main.go)
|
||||
* [single Page Application](file-server/single-page-application/basic/main.go) **UPDATED**
|
||||
* [embedded Single Page Application](file-server/single-page-application/embedded-single-page-application/main.go) **UPDATED**
|
||||
* [embedded Single Page Application with other routes](file-server/single-page-application/embedded-single-page-application-with-other-routes/main.go) **UPDATED**
|
||||
|
||||
### How to Read from `context.Request() *http.Request`
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
### 概览
|
||||
|
||||
- [Hello world!](hello-world/main.go)
|
||||
- [Hello WebAssemply!](webassembly/basic/main.go) **NEW**
|
||||
- [Hello WebAssemply!](webassembly/basic/main.go)
|
||||
- [基础](overview/main.go)
|
||||
- [教程: 在线人数](tutorial/online-visitors/main.go)
|
||||
- [教程: 一个“待完成”MVC Application基于Iris和Vue.js](https://hackernoon.com/a-todo-mvc-application-using-iris-and-vue-js-5019ff870064)
|
||||
|
@ -21,7 +21,7 @@
|
|||
- [教程: DropzoneJS 上传](tutorial/dropzonejs)
|
||||
- [教程: Caddy 服务器使用](tutorial/caddy)
|
||||
- [教程: Iris + MongoDB](https://medium.com/go-language/iris-go-framework-mongodb-552e349eab9c)
|
||||
- [教程: Apache Kafka的API](tutorial/api-for-apache-kafka) **NEW**
|
||||
- [教程: Apache Kafka的API](tutorial/api-for-apache-kafka)
|
||||
|
||||
### 目录结构
|
||||
|
||||
|
@ -105,10 +105,10 @@ app.Get("{root:path}", rootWildcardHandler)
|
|||
- [自定义 HTTP 错误](routing/http-errors/main.go)
|
||||
- [动态路径](routing/dynamic-path/main.go)
|
||||
* [根级通配符路径](routing/dynamic-path/root-wildcard/main.go)
|
||||
- [编写你自己的参数类型](routing/macros/main.go) **NEW**
|
||||
- [编写你自己的参数类型](routing/macros/main.go)
|
||||
- [反向路由](routing/reverse/main.go)
|
||||
- [自定义路由(高层级)](routing/custom-high-level-router/main.go) **NEW**
|
||||
- [自定义包装](routing/custom-wrapper/main.go)
|
||||
- [自定义路由(高层级)](routing/custom-high-level-router/main.go)
|
||||
- [自定义包装](routing/custom-wrapper/main.go) **更新**
|
||||
- 自定义上下文
|
||||
* [方法重写](routing/custom-context/method-overriding/main.go)
|
||||
* [新实现方式](routing/custom-context/new-implementation/main.go)
|
||||
|
@ -121,8 +121,8 @@ app.Get("{root:path}", rootWildcardHandler)
|
|||
|
||||
- [基础](hero/basic/main.go)
|
||||
- [概览](hero/overview)
|
||||
- [Sessions](hero/sessions) **NEW**
|
||||
- [另一种依赖注入的例子和通常的较好实践](hero/smart-contract/main.go) **NEW**
|
||||
- [Sessions](hero/sessions)
|
||||
- [另一种依赖注入的例子和通常的较好实践](hero/smart-contract/main.go) **新**
|
||||
|
||||
### MVC 模式
|
||||
|
||||
|
@ -255,14 +255,14 @@ func(c *ExampleController) Get() string |
|
|||
|
||||
参考下面的示例
|
||||
|
||||
- [Hello world](mvc/hello-world/main.go) **UPDATED**
|
||||
- [Session Controller](mvc/session-controller/main.go) **UPDATED**
|
||||
- [Overview - Plus Repository and Service layers](mvc/overview) **UPDATED**
|
||||
- [Login showcase - Plus Repository and Service layers](mvc/login) **UPDATED**
|
||||
- [Singleton](mvc/singleton) **NEW**
|
||||
- [Websocket Controller](mvc/websocket) **NEW**
|
||||
- [Register Middleware](mvc/middleware) **NEW**
|
||||
- [Vue.js Todo MVC](tutorial/vuejs-todo-mvc) **NEW**
|
||||
- [Hello world](mvc/hello-world/main.go) **更新**
|
||||
- [Session Controller](mvc/session-controller/main.go) **更新**
|
||||
- [Overview - Plus Repository and Service layers](mvc/overview) **更新**
|
||||
- [Login showcase - Plus Repository and Service layers](mvc/login) **更新**
|
||||
- [Singleton](mvc/singleton) **新**
|
||||
- [Websocket Controller](mvc/websocket) **新**
|
||||
- [Register Middleware](mvc/middleware) **新**
|
||||
- [Vue.js Todo MVC](tutorial/vuejs-todo-mvc) **新**
|
||||
|
||||
### 子域名
|
||||
|
||||
|
@ -316,14 +316,14 @@ You can serve [quicktemplate](https://github.com/valyala/quicktemplate) and [her
|
|||
### 文件服务器
|
||||
|
||||
- [Favicon](file-server/favicon/main.go)
|
||||
- [基础操作](file-server/basic/main.go)
|
||||
- [把文件嵌入应用的可执行文件](file-server/embedding-files-into-app/main.go)
|
||||
- [嵌入Gzip压缩的文件到可咨询文件](file-server/embedding-gziped-files-into-app/main.go) **NEW**
|
||||
- [基础操作](file-server/basic/main.go) **更新**
|
||||
- [把文件嵌入应用的可执行文件](file-server/embedding-files-into-app/main.go) **更新**
|
||||
- [嵌入Gzip压缩的文件到可咨询文件](file-server/embedding-gziped-files-into-app/main.go) **更新**
|
||||
- [上传/(强制)下载文件](file-server/send-files/main.go)
|
||||
- 单页面应用(Single Page Applications)
|
||||
* [单页面应用](file-server/single-page-application/basic/main.go)
|
||||
* [嵌入式(embedded)单页面应用](file-server/single-page-application/embedded-single-page-application/main.go)
|
||||
* [使用额外路由的嵌入式单页面应用](file-server/single-page-application/embedded-single-page-application-with-other-routes/main.go)
|
||||
* [单页面应用](file-server/single-page-application/basic/main.go) **更新**
|
||||
* [嵌入式(embedded)单页面应用](file-server/single-page-application/embedded-single-page-application/main.go) **更新**
|
||||
* [使用额外路由的嵌入式单页面应用](file-server/single-page-application/embedded-single-page-application-with-other-routes/main.go) **更新**
|
||||
|
||||
### 如何读取`context.Request() *http.Request`
|
||||
|
||||
|
@ -346,7 +346,7 @@ You can serve [quicktemplate](https://github.com/valyala/quicktemplate) and [her
|
|||
- [写入Gzip压缩](http_responsewriter/write-gzip/main.go)
|
||||
- [流输出Stream Writer](http_responsewriter/stream-writer/main.go)
|
||||
- [数据传递Transactions](http_responsewriter/transactions/main.go)
|
||||
- [SSE](http_responsewriter/sse/main.go) **NEW**
|
||||
- [SSE](http_responsewriter/sse/main.go)
|
||||
- [SSE (third-party package usage for server sent events第三方库SSE)](http_responsewriter/sse-third-party/main.go)
|
||||
|
||||
> The `context/context#ResponseWriter()` returns an enchament version of a http.ResponseWriter, these examples show some places where the Context uses this object. Besides that you can use it as you did before iris.
|
||||
|
@ -430,7 +430,7 @@ iris websocket库依赖于它自己的[包](https://github.com/kataras/iris/tree
|
|||
设计这个包的目的是处理原始websockets,虽然它的API和著名的[socket.io](https://socket.io)很像。我最近读了一片文章,并且对我
|
||||
决定给iris设计一个**快速的**websocket**限定**包并且不是一个向后传递类socket.io的包。你可以阅读这个链接里的文章https://medium.com/@ivanderbyl/why-you-don-t-need-socket-io-6848f1c871cd。
|
||||
|
||||
- [Basic](websocket/basic) **NEW**
|
||||
- [Basic](websocket/basic) **新**
|
||||
* [Server](websocket/basic/server.go)
|
||||
* [Go Client](websocket/basic/go-client/client.go)
|
||||
* [Browser Client](websocket/basic/browser/index.html)
|
||||
|
|
4
_examples/cache/simple/main.go
vendored
4
_examples/cache/simple/main.go
vendored
|
@ -74,7 +74,7 @@ func writeMarkdown(ctx iris.Context) {
|
|||
ctx.Markdown(markdownContents)
|
||||
}
|
||||
|
||||
/* Note that `StaticWeb` does use the browser's disk caching by-default
|
||||
therefore, register the cache handler AFTER any StaticWeb calls,
|
||||
/* Note that `HandleDir` does use the browser's disk caching by-default
|
||||
therefore, register the cache handler AFTER any HandleDir calls,
|
||||
for a faster solution that server doesn't need to keep track of the response
|
||||
navigate to https://github.com/kataras/iris/blob/master/_examples/cache/client-side/main.go */
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
just a text.
|
|
@ -0,0 +1 @@
|
|||
<h1>Hello App2App3 index</h1>
|
1
_examples/file-server/basic/assets/app2/index.html
Normal file
1
_examples/file-server/basic/assets/app2/index.html
Normal file
|
@ -0,0 +1 @@
|
|||
<h1>Hello App2 index</h1>
|
|
@ -4,38 +4,48 @@ import (
|
|||
"github.com/kataras/iris"
|
||||
)
|
||||
|
||||
func main() {
|
||||
func newApp() *iris.Application {
|
||||
app := iris.New()
|
||||
|
||||
app.Favicon("./assets/favicon.ico")
|
||||
|
||||
// enable gzip, optionally:
|
||||
// if used before the `StaticXXX` handlers then
|
||||
// the content byte range feature is gone.
|
||||
// recommend: turn off for large files especially
|
||||
// when server has low memory,
|
||||
// turn on for medium-sized files
|
||||
// or for large-sized files if they are zipped already,
|
||||
// i.e "zippedDir/file.gz"
|
||||
//
|
||||
// app.Use(iris.Gzip)
|
||||
|
||||
// first parameter is the request path
|
||||
// second is the system directory
|
||||
//
|
||||
// app.StaticWeb("/css", "./assets/css")
|
||||
// app.StaticWeb("/js", "./assets/js")
|
||||
//
|
||||
app.StaticWeb("/static", "./assets")
|
||||
// app.HandleDir("/css", "./assets/css")
|
||||
// app.HandleDir("/js", "./assets/js")
|
||||
|
||||
app.HandleDir("/static", "./assets", iris.DirOptions{
|
||||
// Defaults to "/index.html", if request path is ending with **/*/$IndexName
|
||||
// then it redirects to **/*(/) which another handler is handling it,
|
||||
// that another handler, called index handler, is auto-registered by the framework
|
||||
// if end developer does not managed to handle it by hand.
|
||||
IndexName: "/index.html",
|
||||
// When files should served under compression.
|
||||
Gzip: false,
|
||||
// List the files inside the current requested directory if `IndexName` not found.
|
||||
ShowList: false,
|
||||
// If `ShowList` is true then this function will be used instead of the default one to show the list of files of a current requested directory(dir).
|
||||
// DirList: func(ctx context.Context, dirName string, dir http.File) error { ... }
|
||||
//
|
||||
// Optional validator that loops through each requested resource.
|
||||
// AssetValidator: func(ctx iris.Context, name string) bool { ... }
|
||||
})
|
||||
|
||||
// You can also register any index handler manually, order of registration does not matter:
|
||||
// app.Get("/static", [...custom middleware...], func(ctx iris.Context) {
|
||||
// [...custom code...]
|
||||
// ctx.ServeFile("./assets/index.html", false)
|
||||
// })
|
||||
|
||||
// http://localhost:8080/static
|
||||
// http://localhost:8080/static/css/main.css
|
||||
// http://localhost:8080/static/js/jquery-2.1.1.js
|
||||
// http://localhost:8080/static/favicon.ico
|
||||
app.Run(iris.Addr(":8080"))
|
||||
|
||||
// Note:
|
||||
// Routing doesn't allows something .StaticWeb("/", "./assets")
|
||||
//
|
||||
// To see how you can wrap the router in order to achieve
|
||||
// wildcard on root path, see "single-page-application".
|
||||
return app
|
||||
}
|
||||
|
||||
func main() {
|
||||
app := newApp()
|
||||
app.Run(iris.Addr(":8080"))
|
||||
}
|
||||
|
|
93
_examples/file-server/basic/main_test.go
Normal file
93
_examples/file-server/basic/main_test.go
Normal file
|
@ -0,0 +1,93 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/kataras/iris/httptest"
|
||||
)
|
||||
|
||||
type resource string
|
||||
|
||||
func (r resource) contentType() string {
|
||||
switch filepath.Ext(r.String()) {
|
||||
case ".js":
|
||||
return "application/javascript"
|
||||
case ".css":
|
||||
return "text/css"
|
||||
case ".ico":
|
||||
return "image/x-icon"
|
||||
case ".html", "":
|
||||
return "text/html"
|
||||
default:
|
||||
return "text/plain"
|
||||
}
|
||||
}
|
||||
|
||||
func (r resource) String() string {
|
||||
return string(r)
|
||||
}
|
||||
|
||||
func (r resource) strip(strip string) string {
|
||||
s := r.String()
|
||||
return strings.TrimPrefix(s, strip)
|
||||
}
|
||||
|
||||
func (r resource) loadFromBase(dir string) string {
|
||||
filename := r.String()
|
||||
|
||||
filename = r.strip("/static")
|
||||
if filepath.Ext(filename) == "" {
|
||||
// root /.
|
||||
filename = filename + "/index.html"
|
||||
}
|
||||
|
||||
fullpath := filepath.Join(dir, filename)
|
||||
|
||||
b, err := ioutil.ReadFile(fullpath)
|
||||
if err != nil {
|
||||
panic(fullpath + " failed with error: " + err.Error())
|
||||
}
|
||||
|
||||
result := string(b)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func TestFileServerBasic(t *testing.T) {
|
||||
var urls = []resource{
|
||||
"/static/css/main.css",
|
||||
"/static/js/jquery-2.1.1.js",
|
||||
"/static/favicon.ico",
|
||||
"/static/app2",
|
||||
"/static/app2/app2app3",
|
||||
"/static",
|
||||
}
|
||||
|
||||
app := newApp()
|
||||
// route := app.GetRouteReadOnly("GET/{file:path}")
|
||||
// if route == nil {
|
||||
// app.Logger().Fatalf("expected a route to serve files")
|
||||
// }
|
||||
|
||||
// if expected, got := "./assets", route.StaticDir(); expected != got {
|
||||
// app.Logger().Fatalf("expected route's static directory to be: '%s' but got: '%s'", expected, got)
|
||||
// }
|
||||
|
||||
// if !route.StaticDirContainsIndex() {
|
||||
// app.Logger().Fatalf("epxected ./assets to contain an %s file", "/index.html")
|
||||
// }
|
||||
|
||||
e := httptest.New(t, app)
|
||||
for _, u := range urls {
|
||||
url := u.String()
|
||||
contents := u.loadFromBase("./assets")
|
||||
|
||||
e.GET(url).Expect().
|
||||
Status(httptest.StatusOK).
|
||||
ContentType(u.contentType(), app.ConfigurationReadOnly().GetCharset()).
|
||||
Body().Equal(contents)
|
||||
}
|
||||
}
|
|
@ -14,8 +14,14 @@ import (
|
|||
// See `file-server/embedding-gziped-files-into-app` example as well.
|
||||
func newApp() *iris.Application {
|
||||
app := iris.New()
|
||||
app.Logger().SetLevel("debug")
|
||||
|
||||
app.StaticEmbedded("/static", "./assets", Asset, AssetNames)
|
||||
app.HandleDir("/static", "./assets", iris.DirOptions{
|
||||
Asset: Asset,
|
||||
AssetInfo: AssetInfo,
|
||||
AssetNames: AssetNames,
|
||||
ShowList: true,
|
||||
})
|
||||
|
||||
return app
|
||||
}
|
||||
|
|
|
@ -66,12 +66,21 @@ var urls = []resource{
|
|||
}
|
||||
|
||||
// if bindata's values matches with the assets/... contents
|
||||
// and secondly if the StaticEmbedded had successfully registered
|
||||
// and secondly if the HandleDir had successfully registered
|
||||
// the routes and gave the correct response.
|
||||
func TestEmbeddingFilesIntoApp(t *testing.T) {
|
||||
app := newApp()
|
||||
e := httptest.New(t, app)
|
||||
|
||||
route := app.GetRouteReadOnly("GET/static/{file:path}")
|
||||
if route == nil {
|
||||
t.Fatalf("expected a route to serve embedded files")
|
||||
}
|
||||
|
||||
if len(route.StaticSites()) > 0 {
|
||||
t.Fatalf("not expected a static site, the ./assets directory or its subdirectories do not contain any index.html")
|
||||
}
|
||||
|
||||
if runtime.GOOS != "windows" {
|
||||
// remove the embedded static favicon for !windows,
|
||||
// it should be built for unix-specific in order to be work
|
||||
|
|
|
@ -16,12 +16,13 @@ import (
|
|||
func newApp() *iris.Application {
|
||||
app := iris.New()
|
||||
|
||||
// Note the `GzipAsset` and `GzipAssetNames` are different from `go-bindata`'s `Asset` and `AssetNames,
|
||||
// that means that you can use both `go-bindata` and `bindata` tools,
|
||||
// the `go-bindata` can be used for the view engine's `Binary` method
|
||||
// and the `bindata` with the `StaticEmbeddedGzip` (x8 times faster than the StaticEmbeded with `go-bindata`).
|
||||
app.StaticEmbeddedGzip("/static", "./assets", GzipAsset, GzipAssetNames)
|
||||
|
||||
// Note the `GzipAsset` and `GzipAssetNames` are different from `go-bindata`'s `Asset`,
|
||||
// do not set the `Gzip` option to true, it's already managed by the kataras/bindata.
|
||||
app.HandleDir("/static", "./assets", iris.DirOptions{
|
||||
Asset: GzipAsset,
|
||||
AssetInfo: GzipAssetInfo,
|
||||
AssetNames: GzipAssetNames,
|
||||
})
|
||||
return app
|
||||
}
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ var urls = []resource{
|
|||
}
|
||||
|
||||
// if bindata's values matches with the assets/... contents
|
||||
// and secondly if the StaticEmbedded had successfully registered
|
||||
// and secondly if the HandleDir had successfully registered
|
||||
// the routes and gave the correct response.
|
||||
func TestEmbeddingGzipFilesIntoApp(t *testing.T) {
|
||||
app := newApp()
|
||||
|
|
|
@ -20,15 +20,7 @@ func newApp() *iris.Application {
|
|||
ctx.View("index.html")
|
||||
})
|
||||
|
||||
// or just serve index.html as it is:
|
||||
// app.Get("/{f:path}", func(ctx iris.Context) {
|
||||
// ctx.ServeFile("index.html", false)
|
||||
// })
|
||||
|
||||
assetHandler := app.StaticHandler("./public", false, false)
|
||||
// as an alternative of SPA you can take a look at the /routing/dynamic-path/root-wildcard
|
||||
// example too
|
||||
app.SPA(assetHandler)
|
||||
app.HandleDir("/", "./public")
|
||||
|
||||
return app
|
||||
}
|
||||
|
|
|
@ -9,11 +9,21 @@ import "github.com/kataras/iris"
|
|||
|
||||
func newApp() *iris.Application {
|
||||
app := iris.New()
|
||||
app.OnErrorCode(404, func(ctx iris.Context) {
|
||||
app.OnErrorCode(iris.StatusNotFound, func(ctx iris.Context) {
|
||||
ctx.Writef("404 not found here")
|
||||
})
|
||||
|
||||
app.StaticEmbedded("/", "./public", Asset, AssetNames)
|
||||
app.HandleDir("/", "./public", iris.DirOptions{
|
||||
Asset: Asset,
|
||||
AssetInfo: AssetInfo,
|
||||
AssetNames: AssetNames,
|
||||
// IndexName: "index.html", // default.
|
||||
// If you want to show a list of embedded files when inside a directory without an index file:
|
||||
// ShowList: true,
|
||||
// DirList: func(ctx iris.Context, dirName string, f http.File) error {
|
||||
// // [Optional, custom code to show the html list].
|
||||
// }
|
||||
})
|
||||
|
||||
// Note:
|
||||
// if you want a dynamic index page then see the file-server/embedded-single-page-application
|
||||
|
|
|
@ -22,16 +22,11 @@ func newApp() *iris.Application {
|
|||
ctx.View("index.html")
|
||||
})
|
||||
|
||||
assetHandler := iris.StaticEmbeddedHandler("./public", Asset, AssetNames, false) // keep that false if you use the `go-bindata` tool.
|
||||
// as an alternative of SPA you can take a look at the /routing/dynamic-path/root-wildcard
|
||||
// example too
|
||||
// or
|
||||
// app.StaticEmbedded if you don't want to redirect on index.html and simple serve your SPA app (recommended).
|
||||
|
||||
// public/index.html is a dynamic view, it's handlded by root,
|
||||
// and we don't want to be visible as a raw data, so we will
|
||||
// the return value of `app.SPA` to modify the `IndexNames` by;
|
||||
app.SPA(assetHandler).AddIndexName("index.html")
|
||||
app.HandleDir("/", "./public", iris.DirOptions{
|
||||
Asset: Asset,
|
||||
AssetInfo: AssetInfo,
|
||||
AssetNames: AssetNames,
|
||||
})
|
||||
|
||||
return app
|
||||
}
|
||||
|
@ -45,11 +40,3 @@ func main() {
|
|||
// http://localhost:8080/css/main.css
|
||||
app.Run(iris.Addr(":8080"))
|
||||
}
|
||||
|
||||
// Note that app.Use/UseGlobal/Done will be executed
|
||||
// only to the registered routes like our index (app.Get("/", ..)).
|
||||
// The file server is clean, but you can still add middleware to that by wrapping its "assetHandler".
|
||||
//
|
||||
// With this method, unlike StaticWeb("/" , "./public") which is not working by-design anymore,
|
||||
// all custom http errors and all routes are working fine with a file server that is registered
|
||||
// to the root path of the server.
|
||||
|
|
|
@ -2,19 +2,56 @@ package main
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/middleware/logger"
|
||||
|
||||
"github.com/kataras/golog"
|
||||
)
|
||||
|
||||
const deleteFileOnExit = false
|
||||
|
||||
func newRequestLogger(newWriter io.Writer) iris.Handler {
|
||||
c := logger.Config{}
|
||||
|
||||
// we don't want to use the logger
|
||||
// to log requests to assets and etc
|
||||
c.AddSkipper(func(ctx iris.Context) bool {
|
||||
path := ctx.Path()
|
||||
for _, ext := range excludeExtensions {
|
||||
if strings.HasSuffix(path, ext) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
c.LogFuncCtx = func(ctx iris.Context, latency time.Duration) {
|
||||
datetime := time.Now().Format(ctx.Application().ConfigurationReadOnly().GetTimeFormat())
|
||||
customHandlerMessage := ctx.Values().GetString("log_message")
|
||||
|
||||
file, line := ctx.HandlerFileLine()
|
||||
source := fmt.Sprintf("%s:%d", file, line)
|
||||
|
||||
// this will just append a line without an array of javascript objects, readers of this file should read one line per log javascript object,
|
||||
// however, you can improve it even more, this is just a simple example on how to use the `LogFuncCtx`.
|
||||
jsonStr := fmt.Sprintf(`{"datetime":"%s","level":"%s","source":"%s","latency": "%s","status": %d,"method":"%s","path":"%s","message":"%s"}`,
|
||||
datetime, "INFO", source, latency.String(), ctx.GetStatusCode(), ctx.Method(), ctx.Path(), customHandlerMessage)
|
||||
|
||||
fmt.Fprintln(newWriter, jsonStr)
|
||||
}
|
||||
|
||||
return logger.New(c)
|
||||
}
|
||||
|
||||
func h(ctx iris.Context) {
|
||||
ctx.Values().Set("log_message", "something to give more info to the request logger")
|
||||
|
||||
ctx.Writef("Hello from %s", ctx.Path())
|
||||
}
|
||||
|
||||
func main() {
|
||||
app := iris.New()
|
||||
|
||||
|
@ -26,56 +63,21 @@ func main() {
|
|||
}
|
||||
}()
|
||||
|
||||
// Handle the logs by yourself using the `app.Logger#Handle` method.
|
||||
// Return true if that handled, otherwise will print to the screen.
|
||||
// You can also use the `app.Logger#SetOutput/AddOutput` to change or add
|
||||
// multi (io.Writer) outputs if you just want to print the message
|
||||
// somewhere else than the terminal screen.
|
||||
app.Logger().Handle(func(l *golog.Log) bool {
|
||||
_, fn, line, _ := runtime.Caller(5)
|
||||
|
||||
var (
|
||||
// formatted date string based on the `golog#TimeFormat`, which can be customized.
|
||||
// Or use the golog.Log#Time field to get the exact time.Time instance.
|
||||
datetime = l.FormatTime()
|
||||
// the log's message level.
|
||||
level = golog.GetTextForLevel(l.Level, false)
|
||||
// the log's message.
|
||||
message = l.Message
|
||||
// the source code line of where it is called,
|
||||
// this can differ on your app, see runtime.Caller(%d).
|
||||
source = fmt.Sprintf("%s#%d", fn, line)
|
||||
)
|
||||
|
||||
// You can always use a custom json structure and json.Marshal and logFile.Write(its result)
|
||||
// but it is faster to just build your JSON string by yourself as we do below.
|
||||
jsonStr := fmt.Sprintf(`{"datetime":"%s","level":"%s","message":"%s","source":"%s"}`, datetime, level, message, source)
|
||||
fmt.Fprintln(logFile, jsonStr)
|
||||
|
||||
/* Example output:
|
||||
{"datetime":"2018/10/31 13:13","level":"[INFO]","message":"My server started","source":"c:/mygopath/src/github.com/kataras/iris/_examples/http_request/request-logger/request-logger-file-json/main.go#71"}
|
||||
*/
|
||||
return true
|
||||
})
|
||||
|
||||
r := newRequestLogger()
|
||||
r := newRequestLogger(logFile)
|
||||
|
||||
app.Use(r)
|
||||
app.OnAnyErrorCode(r, func(ctx iris.Context) {
|
||||
ctx.HTML("<h1> Error: Please try <a href ='/'> this </a> instead.</h1>")
|
||||
})
|
||||
|
||||
h := func(ctx iris.Context) {
|
||||
ctx.Writef("Hello from %s", ctx.Path())
|
||||
}
|
||||
|
||||
app.Get("/", h)
|
||||
|
||||
app.Get("/1", h)
|
||||
|
||||
app.Get("/2", h)
|
||||
|
||||
app.Logger().Info("My server started")
|
||||
app.Get("/", h)
|
||||
|
||||
// http://localhost:8080
|
||||
// http://localhost:8080/1
|
||||
// http://localhost:8080/2
|
||||
|
@ -92,29 +94,6 @@ var excludeExtensions = [...]string{
|
|||
".svg",
|
||||
}
|
||||
|
||||
func newRequestLogger() iris.Handler {
|
||||
c := logger.Config{
|
||||
Status: true,
|
||||
IP: true,
|
||||
Method: true,
|
||||
Path: true,
|
||||
}
|
||||
|
||||
// we don't want to use the logger
|
||||
// to log requests to assets and etc
|
||||
c.AddSkipper(func(ctx iris.Context) bool {
|
||||
path := ctx.Path()
|
||||
for _, ext := range excludeExtensions {
|
||||
if strings.HasSuffix(path, ext) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
return logger.New(c)
|
||||
}
|
||||
|
||||
// get a filename based on the date, file logs works that way the most times
|
||||
// but these are just a sugar.
|
||||
func todayFilename() string {
|
||||
|
|
|
@ -85,10 +85,10 @@ func newRequestLogger() (h iris.Handler, close func() error) {
|
|||
return err
|
||||
}
|
||||
|
||||
c.LogFunc = func(now time.Time, latency time.Duration, status, ip, method, path string, message interface{}, headerMessage interface{}) {
|
||||
output := logger.Columnize(now.Format("2006/01/02 - 15:04:05"), latency, status, ip, method, path, message, headerMessage)
|
||||
c.LogFunc = func(endTime time.Time, latency time.Duration, status, ip, method, path string, message interface{}, headerMessage interface{}) {
|
||||
output := logger.Columnize(endTime.Format("2006/01/02 - 15:04:05"), latency, status, ip, method, path, message, headerMessage)
|
||||
logFile.Write([]byte(output))
|
||||
}
|
||||
} // or make use of the `LogFuncCtx`, see the '../request-logger-file-json' example for more.
|
||||
|
||||
// we don't want to use the logger
|
||||
// to log requests to assets and etc
|
||||
|
|
|
@ -28,7 +28,7 @@ func main() {
|
|||
Reload(true)
|
||||
app.RegisterView(tmpl)
|
||||
|
||||
app.StaticWeb("/public", "./web/public")
|
||||
app.HandleDir("/public", "./web/public")
|
||||
|
||||
app.OnAnyErrorCode(func(ctx iris.Context) {
|
||||
ctx.ViewData("Message", ctx.Values().
|
||||
|
|
|
@ -559,89 +559,72 @@ Example Code:
|
|||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris"
|
||||
)
|
||||
|
||||
// In this example you'll just see one use case of .WrapRouter.
|
||||
// You can use the .WrapRouter to add custom logic when or when not the router should
|
||||
// be executed in order to execute the registered routes' handlers.
|
||||
//
|
||||
// To see how you can serve files on root "/" without a custom wrapper
|
||||
// just navigate to the "file-server/single-page-application" example.
|
||||
//
|
||||
// This is just for the proof of concept, you can skip this tutorial if it's too much for you.
|
||||
func newApp() *iris.Application {
|
||||
|
||||
app := iris.New()
|
||||
|
||||
app.OnErrorCode(iris.StatusNotFound, func(ctx iris.Context) {
|
||||
ctx.HTML("<b>Resource Not found</b>")
|
||||
})
|
||||
|
||||
app.Get("/profile/{username}", func(ctx iris.Context) {
|
||||
ctx.Writef("Hello %s", ctx.Params().Get("username"))
|
||||
})
|
||||
|
||||
app.HandleDir("/", "./public")
|
||||
|
||||
myOtherHandler := func(ctx iris.Context) {
|
||||
ctx.Writef("inside a handler which is fired manually by our custom router wrapper")
|
||||
}
|
||||
|
||||
// wrap the router with a native net/http handler.
|
||||
// if url does not contain any "." (i.e: .css, .js...)
|
||||
// (depends on the app , you may need to add more file-server exceptions),
|
||||
// then the handler will execute the router that is responsible for the
|
||||
// registered routes (look "/" and "/profile/{username}")
|
||||
// if not then it will serve the files based on the root "/" path.
|
||||
app.WrapRouter(func(w http.ResponseWriter, r *http.Request, router http.HandlerFunc) {
|
||||
path := r.URL.Path
|
||||
|
||||
if strings.HasPrefix(path, "/other") {
|
||||
// acquire and release a context in order to use it to execute
|
||||
// our custom handler
|
||||
// remember: we use net/http.Handler because here we are in the "low-level", before the router itself.
|
||||
ctx := app.ContextPool.Acquire(w, r)
|
||||
myOtherHandler(ctx)
|
||||
app.ContextPool.Release(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
router.ServeHTTP(w, r) // else continue serving routes as usual.
|
||||
})
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
func main() {
|
||||
app := iris.New()
|
||||
app := newApp()
|
||||
|
||||
app.OnErrorCode(iris.StatusNotFound, func(ctx iris.Context) {
|
||||
ctx.HTML("<b>Resource Not found</b>")
|
||||
})
|
||||
// http://localhost:8080
|
||||
// http://localhost:8080/index.html
|
||||
// http://localhost:8080/app.js
|
||||
// http://localhost:8080/css/main.css
|
||||
// http://localhost:8080/profile/anyusername
|
||||
// http://localhost:8080/other/random
|
||||
app.Run(iris.Addr(":8080"))
|
||||
|
||||
app.Get("/", func(ctx iris.Context) {
|
||||
ctx.ServeFile("./public/index.html", false)
|
||||
})
|
||||
|
||||
app.Get("/profile/{username}", func(ctx iris.Context) {
|
||||
ctx.Writef("Hello %s", ctx.Params().Get("username"))
|
||||
})
|
||||
|
||||
// serve files from the root "/", if we used .StaticWeb it could override
|
||||
// all the routes because of the underline need of wildcard.
|
||||
// Here we will see how you can by-pass this behavior
|
||||
// by creating a new file server handler and
|
||||
// setting up a wrapper for the router(like a "low-level" middleware)
|
||||
// in order to manually check if we want to process with the router as normally
|
||||
// or execute the file server handler instead.
|
||||
|
||||
// use of the .StaticHandler
|
||||
// which is the same as StaticWeb but it doesn't
|
||||
// registers the route, it just returns the handler.
|
||||
fileServer := app.StaticHandler("./public", false, false)
|
||||
|
||||
// wrap the router with a native net/http handler.
|
||||
// if url does not contain any "." (i.e: .css, .js...)
|
||||
// (depends on the app , you may need to add more file-server exceptions),
|
||||
// then the handler will execute the router that is responsible for the
|
||||
// registered routes (look "/" and "/profile/{username}")
|
||||
// if not then it will serve the files based on the root "/" path.
|
||||
app.WrapRouter(func(w http.ResponseWriter, r *http.Request, router http.HandlerFunc) {
|
||||
path := r.URL.Path
|
||||
// Note that if path has suffix of "index.html" it will auto-permant redirect to the "/",
|
||||
// so our first handler will be executed instead.
|
||||
|
||||
if !strings.Contains(path, ".") {
|
||||
// if it's not a resource then continue to the router as normally. <-- IMPORTANT
|
||||
router(w, r)
|
||||
return
|
||||
}
|
||||
// acquire and release a context in order to use it to execute
|
||||
// our file server
|
||||
// remember: we use net/http.Handler because here we are in the "low-level", before the router itself.
|
||||
ctx := app.ContextPool.Acquire(w, r)
|
||||
fileServer(ctx)
|
||||
app.ContextPool.Release(ctx)
|
||||
})
|
||||
|
||||
// http://localhost:8080
|
||||
// http://localhost:8080/index.html
|
||||
// http://localhost:8080/app.js
|
||||
// http://localhost:8080/css/main.css
|
||||
// http://localhost:8080/profile/anyusername
|
||||
app.Run(iris.Addr(":8080"))
|
||||
|
||||
// Note: In this example we just saw one use case,
|
||||
// you may want to .WrapRouter or .Downgrade in order to bypass the iris' default router, i.e:
|
||||
// you can use that method to setup custom proxies too.
|
||||
//
|
||||
// If you just want to serve static files on other path than root
|
||||
// you can just use the StaticWeb, i.e:
|
||||
// .StaticWeb("/static", "./public")
|
||||
// ________________________________requestPath, systemPath
|
||||
// Note: In this example we just saw one use case,
|
||||
// you may want to .WrapRouter or .Downgrade in order to bypass the iris' default router, i.e:
|
||||
// you can use that method to setup custom proxies too.
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -1355,7 +1338,7 @@ type Context interface {
|
|||
// You can define your own "Content-Type" with `context#ContentType`, before this function call.
|
||||
//
|
||||
// This function doesn't support resuming (by range),
|
||||
// use ctx.SendFile or router's `StaticWeb` instead.
|
||||
// use ctx.SendFile or router's `HandleDir` instead.
|
||||
ServeContent(content io.ReadSeeker, filename string, modtime time.Time, gzipCompression bool) error
|
||||
// ServeFile serves a file (to send a file, a zip for example to the client you should use the `SendFile` instead)
|
||||
// receives two parameters
|
||||
|
@ -1365,7 +1348,7 @@ type Context interface {
|
|||
// You can define your own "Content-Type" with `context#ContentType`, before this function call.
|
||||
//
|
||||
// This function doesn't support resuming (by range),
|
||||
// use ctx.SendFile or router's `StaticWeb` instead.
|
||||
// use ctx.SendFile or router's `HandleDir` instead.
|
||||
//
|
||||
// Use it when you want to serve dynamic files to the client.
|
||||
ServeFile(filename string, gzipCompression bool) error
|
||||
|
|
|
@ -10,11 +10,6 @@ import (
|
|||
// In this example you'll just see one use case of .WrapRouter.
|
||||
// You can use the .WrapRouter to add custom logic when or when not the router should
|
||||
// be executed in order to execute the registered routes' handlers.
|
||||
//
|
||||
// To see how you can serve files on root "/" without a custom wrapper
|
||||
// just navigate to the "file-server/single-page-application" example.
|
||||
//
|
||||
// This is just for the proof of concept, you can skip this tutorial if it's too much for you.
|
||||
func newApp() *iris.Application {
|
||||
|
||||
app := iris.New()
|
||||
|
@ -23,26 +18,15 @@ func newApp() *iris.Application {
|
|||
ctx.HTML("<b>Resource Not found</b>")
|
||||
})
|
||||
|
||||
app.Get("/", func(ctx iris.Context) {
|
||||
ctx.ServeFile("./public/index.html", false)
|
||||
})
|
||||
|
||||
app.Get("/profile/{username}", func(ctx iris.Context) {
|
||||
ctx.Writef("Hello %s", ctx.Params().Get("username"))
|
||||
})
|
||||
|
||||
// serve files from the root "/", if we used .StaticWeb it could override
|
||||
// all the routes because of the underline need of wildcard.
|
||||
// Here we will see how you can by-pass this behavior
|
||||
// by creating a new file server handler and
|
||||
// setting up a wrapper for the router(like a "low-level" middleware)
|
||||
// in order to manually check if we want to process with the router as normally
|
||||
// or execute the file server handler instead.
|
||||
app.HandleDir("/", "./public")
|
||||
|
||||
// use of the .StaticHandler
|
||||
// which is the same as StaticWeb but it doesn't
|
||||
// registers the route, it just returns the handler.
|
||||
fileServer := app.StaticHandler("./public", false, false)
|
||||
myOtherHandler := func(ctx iris.Context) {
|
||||
ctx.Writef("inside a handler which is fired manually by our custom router wrapper")
|
||||
}
|
||||
|
||||
// wrap the router with a native net/http handler.
|
||||
// if url does not contain any "." (i.e: .css, .js...)
|
||||
|
@ -52,19 +36,18 @@ func newApp() *iris.Application {
|
|||
// if not then it will serve the files based on the root "/" path.
|
||||
app.WrapRouter(func(w http.ResponseWriter, r *http.Request, router http.HandlerFunc) {
|
||||
path := r.URL.Path
|
||||
// Note that if path has suffix of "index.html" it will auto-permant redirect to the "/",
|
||||
// so our first handler will be executed instead.
|
||||
|
||||
if !strings.Contains(path, ".") { // if it's not a resource then continue to the router as normally.
|
||||
router(w, r)
|
||||
if strings.HasPrefix(path, "/other") {
|
||||
// acquire and release a context in order to use it to execute
|
||||
// our custom handler
|
||||
// remember: we use net/http.Handler because here we are in the "low-level", before the router itself.
|
||||
ctx := app.ContextPool.Acquire(w, r)
|
||||
myOtherHandler(ctx)
|
||||
app.ContextPool.Release(ctx)
|
||||
return
|
||||
}
|
||||
// acquire and release a context in order to use it to execute
|
||||
// our file server
|
||||
// remember: we use net/http.Handler because here we are in the "low-level", before the router itself.
|
||||
ctx := app.ContextPool.Acquire(w, r)
|
||||
fileServer(ctx)
|
||||
app.ContextPool.Release(ctx)
|
||||
|
||||
router.ServeHTTP(w, r) // else continue serving routes as usual.
|
||||
})
|
||||
|
||||
return app
|
||||
|
@ -78,14 +61,10 @@ func main() {
|
|||
// http://localhost:8080/app.js
|
||||
// http://localhost:8080/css/main.css
|
||||
// http://localhost:8080/profile/anyusername
|
||||
// http://localhost:8080/other/random
|
||||
app.Run(iris.Addr(":8080"))
|
||||
|
||||
// Note: In this example we just saw one use case,
|
||||
// you may want to .WrapRouter or .Downgrade in order to bypass the iris' default router, i.e:
|
||||
// you can use that method to setup custom proxies too.
|
||||
//
|
||||
// If you just want to serve static files on other path than root
|
||||
// you can just use the StaticWeb, i.e:
|
||||
// .StaticWeb("/static", "./public")
|
||||
// ________________________________requestPath, systemPath
|
||||
}
|
||||
|
|
|
@ -56,4 +56,6 @@ func TestCustomWrapper(t *testing.T) {
|
|||
Status(httptest.StatusOK).
|
||||
Body().Equal(contents)
|
||||
}
|
||||
|
||||
e.GET("/other/something").Expect().Status(httptest.StatusOK)
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<html>
|
||||
|
||||
<head>
|
||||
<title>{{ .Page.Title }}</title>
|
||||
<title>Index Page</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
|
@ -35,17 +35,17 @@ func main() {
|
|||
// maps to ./public/assets/css/bootstrap.min.css file at system location.
|
||||
// GET: http://localhost:8080/assets/js/react.min.js
|
||||
// maps to ./public/assets/js/react.min.js file at system location.
|
||||
app.StaticWeb("/assets", "./public/assets")
|
||||
app.HandleDir("/assets", "./public/assets")
|
||||
|
||||
/* OR
|
||||
|
||||
// GET: http://localhost:8080/js/react.min.js
|
||||
// maps to ./public/assets/js/react.min.js file at system location.
|
||||
app.StaticWeb("/js", "./public/assets/js")
|
||||
app.HandleDir("/js", "./public/assets/js")
|
||||
|
||||
// GET: http://localhost:8080/css/bootstrap.min.css
|
||||
// maps to ./public/assets/css/bootstrap.min.css file at system location.
|
||||
app.StaticWeb("/css", "./public/assets/css")
|
||||
app.HandleDir("/css", "./public/assets/css")
|
||||
|
||||
*/
|
||||
|
||||
|
|
|
@ -112,7 +112,7 @@ func (b *Bootstrapper) Bootstrap() *Bootstrapper {
|
|||
|
||||
// static files
|
||||
b.Favicon(StaticAssets + Favicon)
|
||||
b.StaticWeb(StaticAssets[1:len(StaticAssets)-1], StaticAssets)
|
||||
b.HandleDir(StaticAssets[1:len(StaticAssets)-1], StaticAssets)
|
||||
|
||||
// middleware, after static files
|
||||
b.Use(recover.New())
|
||||
|
|
|
@ -18,7 +18,7 @@ func main() {
|
|||
|
||||
app.RegisterView(iris.HTML("./views", ".html").Layout("shared/layout.html"))
|
||||
|
||||
app.StaticWeb("/public", "./public")
|
||||
app.HandleDir("/public", "./public")
|
||||
|
||||
mvc.Configure(app, configureMVC)
|
||||
|
||||
|
|
|
@ -11,8 +11,8 @@ func main() {
|
|||
* Setup static files
|
||||
*/
|
||||
|
||||
app.StaticWeb("/assets", "./public/assets")
|
||||
app.StaticWeb("/upload_resources", "./public/upload_resources")
|
||||
app.HandleDir("/assets", "./public/assets")
|
||||
app.HandleDir("/upload_resources", "./public/upload_resources")
|
||||
|
||||
dashboard := app.Party("dashboard.")
|
||||
{
|
||||
|
|
|
@ -41,7 +41,7 @@ func TestSubdomainWWW(t *testing.T) {
|
|||
}
|
||||
|
||||
host := "localhost:1111"
|
||||
e := httptest.New(t, app, httptest.URL("http://"+host), httptest.Debug(false))
|
||||
e := httptest.New(t, app, httptest.Debug(false))
|
||||
|
||||
for _, test := range tests {
|
||||
|
||||
|
|
|
@ -101,7 +101,7 @@ func main() {
|
|||
app.RegisterView(iris.HTML("./views", ".html"))
|
||||
|
||||
// Make the /public route path to statically serve the ./public/... contents
|
||||
app.StaticWeb("/public", "./public")
|
||||
app.HandleDir("/public", "./public")
|
||||
|
||||
// Render the actual form
|
||||
// GET: http://localhost:8080
|
||||
|
|
|
@ -168,7 +168,7 @@ func main() {
|
|||
app := iris.New()
|
||||
app.RegisterView(iris.HTML("./views", ".html"))
|
||||
|
||||
app.StaticWeb("/public", "./public")
|
||||
app.HandleDir("/public", "./public")
|
||||
|
||||
app.Get("/", func(ctx iris.Context) {
|
||||
ctx.View("upload.html")
|
||||
|
|
|
@ -124,7 +124,7 @@ func main() {
|
|||
app := iris.New()
|
||||
app.RegisterView(iris.HTML("./views", ".html"))
|
||||
|
||||
app.StaticWeb("/public", "./public")
|
||||
app.HandleDir("/public", "./public")
|
||||
|
||||
app.Get("/", func(ctx iris.Context) {
|
||||
ctx.View("upload.html")
|
||||
|
|
|
@ -8,7 +8,7 @@ Article is coming soon, follow and stay tuned
|
|||
Read [the fully functional example](main.go).
|
||||
|
||||
```sh
|
||||
$ go get -u github.com/mongodb/mongo-go-driver
|
||||
$ go get -u go.mongodb.org/mongo-driver/...
|
||||
$ go get -u github.com/joho/godotenv
|
||||
```
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package main
|
||||
|
||||
// go get -u github.com/mongodb/mongo-go-driver
|
||||
// go get -u go.mongodb.org/mongo-driver
|
||||
// go get -u github.com/joho/godotenv
|
||||
|
||||
import (
|
||||
|
@ -19,7 +19,7 @@ import (
|
|||
|
||||
"github.com/kataras/iris"
|
||||
|
||||
"github.com/mongodb/mongo-go-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
)
|
||||
|
||||
const version = "0.0.1"
|
||||
|
|
|
@ -4,11 +4,11 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/mongodb/mongo-go-driver/bson"
|
||||
"github.com/mongodb/mongo-go-driver/bson/primitive"
|
||||
"github.com/mongodb/mongo-go-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
// up to you:
|
||||
// "github.com/mongodb/mongo-go-driver/mongo/options"
|
||||
// "go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
type Movie struct {
|
||||
|
|
|
@ -23,7 +23,7 @@ func main() {
|
|||
app.Any("/iris-ws.js", websocket.ClientHandler())
|
||||
|
||||
// register static assets request path and system directory
|
||||
app.StaticWeb("/js", "./static/assets/js")
|
||||
app.HandleDir("/js", "./static/assets/js")
|
||||
|
||||
h := func(ctx iris.Context) {
|
||||
ctx.ViewData("", page{PageID: "index page"})
|
||||
|
|
|
@ -49,7 +49,7 @@ func newApp(db *DB) *iris.Application {
|
|||
app.RegisterView(tmpl)
|
||||
|
||||
// Serve static files (css)
|
||||
app.StaticWeb("/static", "./resources")
|
||||
app.HandleDir("/static", "./resources")
|
||||
|
||||
indexHandler := func(ctx iris.Context) {
|
||||
ctx.ViewData("URL_COUNT", db.Len())
|
||||
|
|
|
@ -500,7 +500,7 @@ func main() {
|
|||
// no need for any server-side template here,
|
||||
// actually if you're going to just use vue without any
|
||||
// back-end services, you can just stop afer this line and start the server.
|
||||
app.StaticWeb("/", "./public")
|
||||
app.HandleDir("/", "./public")
|
||||
|
||||
// configure the http sessions.
|
||||
sess := sessions.New(sessions.Config{
|
||||
|
|
|
@ -19,7 +19,7 @@ func main() {
|
|||
// no need for any server-side template here,
|
||||
// actually if you're going to just use vue without any
|
||||
// back-end services, you can just stop afer this line and start the server.
|
||||
app.StaticWeb("/", "./public")
|
||||
app.HandleDir("/", "./public")
|
||||
|
||||
// configure the http sessions.
|
||||
sess := sessions.New(sessions.Config{
|
||||
|
|
44
_examples/view/template_django_0/main.go
Normal file
44
_examples/view/template_django_0/main.go
Normal file
|
@ -0,0 +1,44 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/kataras/iris"
|
||||
|
||||
// optionally, registers filters like `timesince`.
|
||||
_ "github.com/flosch/pongo2-addons"
|
||||
)
|
||||
|
||||
var startTime = time.Now()
|
||||
|
||||
func main() {
|
||||
app := iris.New()
|
||||
|
||||
tmpl := iris.Django("./templates", ".html")
|
||||
tmpl.Reload(true) // reload templates on each request (development mode)
|
||||
tmpl.AddFunc("greet", func(s string) string { // {{greet(name)}}
|
||||
return "Greetings " + s + "!"
|
||||
})
|
||||
|
||||
// tmpl.RegisterFilter("myFilter", myFilter) // {{"simple input for filter"|myFilter}}
|
||||
app.RegisterView(tmpl)
|
||||
|
||||
app.Get("/", hi)
|
||||
|
||||
// http://localhost:8080
|
||||
app.Run(iris.Addr(":8080"), iris.WithCharset("UTF-8")) // defaults to that but you can change it.
|
||||
}
|
||||
|
||||
func hi(ctx iris.Context) {
|
||||
// ctx.ViewData("title", "Hi Page")
|
||||
// ctx.ViewData("name", "iris")
|
||||
// ctx.ViewData("serverStartTime", startTime)
|
||||
// or if you set all view data in the same handler you can use the
|
||||
// iris.Map/pongo2.Context/map[string]interface{}, look below:
|
||||
|
||||
ctx.View("hi.html", iris.Map{
|
||||
"title": "Hi Page",
|
||||
"name": "iris",
|
||||
"serverStartTime": startTime,
|
||||
})
|
||||
}
|
12
_examples/view/template_django_0/templates/hi.html
Normal file
12
_examples/view/template_django_0/templates/hi.html
Normal file
|
@ -0,0 +1,12 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>{{title}}</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hi {{name|capfirst}} </h1>
|
||||
|
||||
<h2>{{greet(name)}}</h2>
|
||||
|
||||
<h3>Server started about {{serverStartTime|timesince}}. Refresh the page to see different result</h3>
|
||||
</body>
|
||||
</html>
|
|
@ -14,7 +14,7 @@ func main() {
|
|||
|
||||
// we could serve your assets like this the shake of the example,
|
||||
// never include the .go files there in production.
|
||||
app.StaticWeb("/", "./client")
|
||||
app.HandleDir("/", "./client")
|
||||
|
||||
app.Get("/", func(ctx iris.Context) {
|
||||
ctx.ServeFile("./client/hello.html", false) // true for gzip.
|
||||
|
|
|
@ -52,7 +52,7 @@ func main() {
|
|||
})
|
||||
|
||||
// serves the npm browser websocket client usage example.
|
||||
app.StaticWeb("/browserify", "./browserify")
|
||||
app.HandleDir("/browserify", "./browserify")
|
||||
|
||||
app.Run(iris.Addr(":8080"), iris.WithoutServerError(iris.ErrServerClosed))
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ func main() {
|
|||
// see the inline javascript code i the websockets.html, this endpoint is used to connect to the server.
|
||||
app.Get("/my_endpoint", ws.Handler())
|
||||
|
||||
app.StaticWeb("/js", "./static/js") // serve our custom javascript code
|
||||
app.HandleDir("/js", "./static/js") // serve our custom javascript code
|
||||
|
||||
app.Get("/", func(ctx iris.Context) {
|
||||
ctx.ViewData("", clientPage{"Client Page", "localhost:8080"})
|
||||
|
|
|
@ -32,7 +32,7 @@ func main() {
|
|||
ctx.Write(websocket.ClientSource)
|
||||
})
|
||||
|
||||
app.StaticWeb("/js", "./static/js")
|
||||
app.HandleDir("/js", "./static/js")
|
||||
app.Get("/", func(ctx iris.Context) {
|
||||
// send our custom javascript source file before client really asks for that
|
||||
// using the go v1.8's HTTP/2 Push.
|
||||
|
|
|
@ -38,7 +38,7 @@ func main() {
|
|||
|
||||
// serve the index.html and the javascript libraries at
|
||||
// http://localhost:8080
|
||||
app.StaticWeb("/", "./public")
|
||||
app.HandleDir("/", "./public")
|
||||
|
||||
app.Run(iris.Addr("localhost:8080"), iris.WithoutPathCorrection)
|
||||
}
|
||||
|
|
4
cache/browser.go
vendored
4
cache/browser.go
vendored
|
@ -92,7 +92,7 @@ const ifNoneMatchHeaderKey = "If-None-Match"
|
|||
//
|
||||
// Usage with combination of `StaticCache`:
|
||||
// assets := app.Party("/assets", cache.StaticCache(24 * time.Hour), ETag)
|
||||
// assets.StaticWeb("/", "./assets") or StaticEmbedded("/", "./assets") or StaticEmbeddedGzip("/", "./assets").
|
||||
// assets.HandleDir("/", "./assets")
|
||||
//
|
||||
// Similar to `Cache304` but it doesn't depends on any "modified date", it uses just the ETag and If-None-Match headers.
|
||||
//
|
||||
|
@ -124,7 +124,7 @@ var ETag = func(ctx context.Context) {
|
|||
// by watching system directories changes manually and use of the `ctx.WriteWithExpiration`
|
||||
// with a "modtime" based on the file modified date,
|
||||
// can be used on Party's that contains a static handler,
|
||||
// i.e `StaticWeb`, `StaticEmbedded` or even `StaticEmbeddedGzip`.
|
||||
// i.e `HandleDir`.
|
||||
var Cache304 = func(expiresEvery time.Duration) context.Handler {
|
||||
return func(ctx context.Context) {
|
||||
now := time.Now()
|
||||
|
|
2
cache/client/handler.go
vendored
2
cache/client/handler.go
vendored
|
@ -71,8 +71,6 @@ func parseLifeChanger(ctx context.Context) entry.LifeChanger {
|
|||
}
|
||||
}
|
||||
|
||||
///TODO: debug this and re-run the parallel tests on larger scale,
|
||||
// because I think we have a bug here when `core/router#StaticWeb` is used after this middleware.
|
||||
func (h *Handler) ServeHTTP(ctx context.Context) {
|
||||
// check for pre-cache validators, if at least one of them return false
|
||||
// for this specific request, then skip the whole cache
|
||||
|
|
|
@ -209,6 +209,12 @@ type Context interface {
|
|||
Proceed(Handler) bool
|
||||
// HandlerName returns the current handler's name, helpful for debugging.
|
||||
HandlerName() string
|
||||
// HandlerFileLine returns the current running handler's function source file and line information.
|
||||
// Useful mostly when debugging.
|
||||
HandlerFileLine() (file string, line int)
|
||||
// RouteName returns the route name that this handler is running on.
|
||||
// Note that it will return empty on not found handlers.
|
||||
RouteName() string
|
||||
// Next calls all the next handler from the handlers chain,
|
||||
// it should be used inside a middleware.
|
||||
//
|
||||
|
@ -626,7 +632,7 @@ type Context interface {
|
|||
// Note that it has nothing to do with server-side caching.
|
||||
// It does those checks by checking if the "If-Modified-Since" request header
|
||||
// sent by client or a previous server response header
|
||||
// (e.g with WriteWithExpiration or StaticEmbedded or Favicon etc.)
|
||||
// (e.g with WriteWithExpiration or HandleDir or Favicon etc.)
|
||||
// is a valid one and it's before the "modtime".
|
||||
//
|
||||
// A check for !modtime && err == nil is necessary to make sure that
|
||||
|
@ -775,7 +781,7 @@ type Context interface {
|
|||
// You can define your own "Content-Type" with `context#ContentType`, before this function call.
|
||||
//
|
||||
// This function doesn't support resuming (by range),
|
||||
// use ctx.SendFile or router's `StaticWeb` instead.
|
||||
// use ctx.SendFile or router's `HandleDir` instead.
|
||||
ServeContent(content io.ReadSeeker, filename string, modtime time.Time, gzipCompression bool) error
|
||||
// ServeFile serves a file (to send a file, a zip for example to the client you should use the `SendFile` instead)
|
||||
// receives two parameters
|
||||
|
@ -785,7 +791,7 @@ type Context interface {
|
|||
// You can define your own "Content-Type" with `context#ContentType`, before this function call.
|
||||
//
|
||||
// This function doesn't support resuming (by range),
|
||||
// use ctx.SendFile or router's `StaticWeb` instead.
|
||||
// use ctx.SendFile or router's `HandleDir` instead.
|
||||
//
|
||||
// Use it when you want to serve dynamic files to the client.
|
||||
ServeFile(filename string, gzipCompression bool) error
|
||||
|
@ -1208,12 +1214,21 @@ func (ctx *context) Proceed(h Handler) bool {
|
|||
|
||||
// HandlerName returns the current handler's name, helpful for debugging.
|
||||
func (ctx *context) HandlerName() string {
|
||||
if name := ctx.currentRouteName; name != "" {
|
||||
return name
|
||||
}
|
||||
return HandlerName(ctx.handlers[ctx.currentHandlerIndex])
|
||||
}
|
||||
|
||||
// HandlerFileLine returns the current running handler's function source file and line information.
|
||||
// Useful mostly when debugging.
|
||||
func (ctx *context) HandlerFileLine() (file string, line int) {
|
||||
return HandlerFileLine(ctx.handlers[ctx.currentHandlerIndex])
|
||||
}
|
||||
|
||||
// RouteName returns the route name that this handler is running on.
|
||||
// Note that it will return empty on not found handlers.
|
||||
func (ctx *context) RouteName() string {
|
||||
return ctx.currentRouteName
|
||||
}
|
||||
|
||||
// Next is the function that executed when `ctx.Next()` is called.
|
||||
// It can be changed to a customized one if needed (very advanced usage).
|
||||
//
|
||||
|
@ -2476,7 +2491,7 @@ func (ctx *context) SetLastModified(modtime time.Time) {
|
|||
// Note that it has nothing to do with server-side caching.
|
||||
// It does those checks by checking if the "If-Modified-Since" request header
|
||||
// sent by client or a previous server response header
|
||||
// (e.g with WriteWithExpiration or StaticEmbedded or Favicon etc.)
|
||||
// (e.g with WriteWithExpiration or HandleDir or Favicon etc.)
|
||||
// is a valid one and it's before the "modtime".
|
||||
//
|
||||
// A check for !modtime && err == nil is necessary to make sure that
|
||||
|
@ -3122,7 +3137,10 @@ func (ctx *context) ServeContent(content io.ReadSeeker, filename string, modtime
|
|||
return nil
|
||||
}
|
||||
|
||||
ctx.ContentType(filename)
|
||||
if ctx.GetContentType() == "" {
|
||||
ctx.ContentType(filename)
|
||||
}
|
||||
|
||||
ctx.SetLastModified(modtime)
|
||||
var out io.Writer
|
||||
if gzipCompression && ctx.ClientSupportsGzip() {
|
||||
|
@ -3150,7 +3168,7 @@ func (ctx *context) ServeContent(content io.ReadSeeker, filename string, modtime
|
|||
func (ctx *context) ServeFile(filename string, gzipCompression bool) error {
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%d", 404)
|
||||
return fmt.Errorf("%d", http.StatusNotFound)
|
||||
}
|
||||
defer f.Close()
|
||||
fi, _ := f.Stat()
|
||||
|
|
|
@ -3,6 +3,7 @@ package context
|
|||
import (
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// A Handler responds to an HTTP request.
|
||||
|
@ -26,15 +27,35 @@ type Handler func(Context)
|
|||
// See `Handler` for more.
|
||||
type Handlers []Handler
|
||||
|
||||
// HandlerName returns the name, the handler function informations.
|
||||
// Same as `context.HandlerName`.
|
||||
// HandlerName returns the handler's function name.
|
||||
// See `context.HandlerName` to get function name of the current running handler in the chain.
|
||||
func HandlerName(h Handler) string {
|
||||
pc := reflect.ValueOf(h).Pointer()
|
||||
// l, n := runtime.FuncForPC(pc).FileLine(pc)
|
||||
// return fmt.Sprintf("%s:%d", l, n)
|
||||
return runtime.FuncForPC(pc).Name()
|
||||
}
|
||||
|
||||
// HandlerFileLine returns the handler's file and line information.
|
||||
// See `context.HandlerFileLine` to get the file, line of the current running handler in the chain.
|
||||
func HandlerFileLine(h Handler) (file string, line int) {
|
||||
pc := reflect.ValueOf(h).Pointer()
|
||||
return runtime.FuncForPC(pc).FileLine(pc)
|
||||
}
|
||||
|
||||
// MainHandlerName tries to find the main handler than end-developer
|
||||
// registered on the provided chain of handlers and returns its function name.
|
||||
func MainHandlerName(handlers Handlers) (name string) {
|
||||
for i := 0; i < len(handlers); i++ {
|
||||
name = HandlerName(handlers[i])
|
||||
if !strings.HasPrefix(name, "github.com/kataras/iris") ||
|
||||
strings.HasPrefix(name, "github.com/kataras/iris/core/router.StripPrefix") ||
|
||||
strings.HasPrefix(name, "github.com/kataras/iris/core/router.FileServer") {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Filter is just a type of func(Handler) bool which reports whether an action must be performed
|
||||
// based on the incoming request.
|
||||
//
|
||||
|
|
|
@ -60,7 +60,7 @@ type ResponseWriter interface {
|
|||
// Written should returns the total length of bytes that were being written to the client.
|
||||
// In addition iris provides some variables to help low-level actions:
|
||||
// NoWritten, means that nothing were written yet and the response writer is still live.
|
||||
// StatusCodeWritten, means that status code were written but no other bytes are written to the client, response writer may closed.
|
||||
// StatusCodeWritten, means that status code was written but no other bytes are written to the client, response writer may closed.
|
||||
// > 0 means that the reply was written and it's the total number of bytes were written.
|
||||
Written() int
|
||||
|
||||
|
|
|
@ -1,6 +1,13 @@
|
|||
package context
|
||||
|
||||
import "github.com/kataras/iris/macro"
|
||||
import (
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/kataras/iris/macro"
|
||||
)
|
||||
|
||||
// RouteReadOnly allows decoupled access to the current route
|
||||
// inside the context.
|
||||
|
@ -42,4 +49,59 @@ type RouteReadOnly interface {
|
|||
|
||||
// MainHandlerName returns the first registered handler for the route.
|
||||
MainHandlerName() string
|
||||
|
||||
// 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
|
||||
// route, manually or automatic by the framework,
|
||||
// get the route by `Application#GetRouteByPath(staticSite.RequestPath)`.
|
||||
StaticSites() []StaticSite
|
||||
}
|
||||
|
||||
// StaticSite is a structure which is used as field on the `Route`
|
||||
// and route registration on the `APIBuilder#HandleDir`.
|
||||
// See `GetStaticSites` and `APIBuilder#HandleDir`.
|
||||
type StaticSite struct {
|
||||
Dir string `json:"dir"`
|
||||
RequestPath string `json:"requestPath"`
|
||||
}
|
||||
|
||||
// GetStaticSites search for a relative filename of "indexName" in "rootDir" and all its subdirectories
|
||||
// and returns a list of structures which contains the directory found an "indexName" and the request path
|
||||
// that a route should be registered to handle this "indexName".
|
||||
// The request path is given by the directory which an index exists on.
|
||||
func GetStaticSites(rootDir, rootRequestPath, indexName string) (sites []StaticSite) {
|
||||
f, err := os.Open(rootDir)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
list, err := f.Readdir(-1)
|
||||
f.Close()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(list) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, l := range list {
|
||||
dir := filepath.Join(rootDir, l.Name())
|
||||
|
||||
if l.IsDir() {
|
||||
sites = append(sites, GetStaticSites(dir, path.Join(rootRequestPath, l.Name()), indexName)...)
|
||||
continue
|
||||
}
|
||||
|
||||
if l.Name() == strings.TrimPrefix(indexName, "/") {
|
||||
sites = append(sites, StaticSite{
|
||||
Dir: filepath.FromSlash(rootDir),
|
||||
RequestPath: rootRequestPath,
|
||||
})
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
|
|
@ -64,7 +64,7 @@ func (r *Reporter) AddErr(err error) bool {
|
|||
}
|
||||
|
||||
if stackErr, ok := err.(StackError); ok {
|
||||
r.addStack(stackErr.Stack())
|
||||
r.addStack("", stackErr.Stack())
|
||||
} else {
|
||||
r.mu.Lock()
|
||||
r.wrapper = r.wrapper.AppendErr(err)
|
||||
|
@ -108,7 +108,7 @@ func (r *Reporter) Describe(format string, err error) {
|
|||
return
|
||||
}
|
||||
if stackErr, ok := err.(StackError); ok {
|
||||
r.addStack(stackErr.Stack())
|
||||
r.addStack(format, stackErr.Stack())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -126,12 +126,15 @@ func (r *Reporter) Stack() []Error {
|
|||
return r.wrapper.Stack
|
||||
}
|
||||
|
||||
func (r *Reporter) addStack(stack []Error) {
|
||||
func (r *Reporter) addStack(format string, stack []Error) {
|
||||
for _, e := range stack {
|
||||
if e.Error() == "" {
|
||||
continue
|
||||
}
|
||||
r.mu.Lock()
|
||||
if format != "" {
|
||||
e = New(format).Format(e)
|
||||
}
|
||||
r.wrapper = r.wrapper.AppendErr(e)
|
||||
r.mu.Unlock()
|
||||
}
|
||||
|
|
|
@ -21,15 +21,15 @@ var (
|
|||
// "GET", "POST", "PUT", "DELETE", "CONNECT", "HEAD",
|
||||
// "PATCH", "OPTIONS", "TRACE".
|
||||
AllMethods = []string{
|
||||
"GET",
|
||||
"POST",
|
||||
"PUT",
|
||||
"DELETE",
|
||||
"CONNECT",
|
||||
"HEAD",
|
||||
"PATCH",
|
||||
"OPTIONS",
|
||||
"TRACE",
|
||||
http.MethodGet,
|
||||
http.MethodPost,
|
||||
http.MethodPut,
|
||||
http.MethodDelete,
|
||||
http.MethodConnect,
|
||||
http.MethodHead,
|
||||
http.MethodPatch,
|
||||
http.MethodOptions,
|
||||
http.MethodTrace,
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -37,20 +37,71 @@ var (
|
|||
// all the routes.
|
||||
type repository struct {
|
||||
routes []*Route
|
||||
pos map[string]int
|
||||
}
|
||||
|
||||
func (r *repository) register(route *Route) {
|
||||
for _, r := range r.routes {
|
||||
if r.String() == route.String() {
|
||||
return // do not register any duplicates, the sooner the better.
|
||||
func (repo *repository) remove(route *Route) bool {
|
||||
for i, r := range repo.routes {
|
||||
if r == route {
|
||||
return repo.removeByIndex(i)
|
||||
}
|
||||
}
|
||||
|
||||
r.routes = append(r.routes, route)
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *repository) get(routeName string) *Route {
|
||||
for _, r := range r.routes {
|
||||
func (repo *repository) removeByPath(tmplPath string) bool {
|
||||
if repo.pos != nil {
|
||||
if idx, ok := repo.pos[tmplPath]; ok {
|
||||
return repo.removeByIndex(idx)
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (repo *repository) removeByName(routeName string) bool {
|
||||
for i, r := range repo.routes {
|
||||
if r.Name == routeName {
|
||||
return repo.removeByIndex(i)
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (repo *repository) removeByIndex(idx int) bool {
|
||||
n := len(repo.routes)
|
||||
|
||||
if n == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
if idx >= n {
|
||||
return false
|
||||
}
|
||||
|
||||
if n == 1 && idx == 0 {
|
||||
repo.routes = repo.routes[0:0]
|
||||
repo.pos = nil
|
||||
return true
|
||||
}
|
||||
|
||||
r := repo.routes[idx]
|
||||
if r == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
repo.routes = append(repo.routes[:idx], repo.routes[idx+1:]...)
|
||||
if repo.pos != nil {
|
||||
delete(repo.pos, r.Path)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (repo *repository) get(routeName string) *Route {
|
||||
for _, r := range repo.routes {
|
||||
if r.Name == routeName {
|
||||
return r
|
||||
}
|
||||
|
@ -58,8 +109,37 @@ func (r *repository) get(routeName string) *Route {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (r *repository) getAll() []*Route {
|
||||
return r.routes
|
||||
func (repo *repository) getByPath(tmplPath string) *Route {
|
||||
if repo.pos != nil {
|
||||
if idx, ok := repo.pos[tmplPath]; ok {
|
||||
if len(repo.routes) > idx {
|
||||
return repo.routes[idx]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *repository) getAll() []*Route {
|
||||
return repo.routes
|
||||
}
|
||||
|
||||
func (repo *repository) register(route *Route) {
|
||||
for i, r := range repo.routes {
|
||||
if route.Equal(r) {
|
||||
// replace existing with the latest one.
|
||||
repo.routes = append(repo.routes[:i], repo.routes[i+1:]...)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
repo.routes = append(repo.routes, route)
|
||||
if repo.pos == nil {
|
||||
repo.pos = make(map[string]int)
|
||||
}
|
||||
|
||||
repo.pos[route.tmpl.Src] = len(repo.routes) - 1
|
||||
}
|
||||
|
||||
// APIBuilder the visible API for constructing the router
|
||||
|
@ -181,17 +261,13 @@ func (api *APIBuilder) SetExecutionRules(executionRules ExecutionRules) Party {
|
|||
return api
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// Returns a *Route, app will throw any errors later on.
|
||||
func (api *APIBuilder) Handle(method string, relativePath string, handlers ...context.Handler) *Route {
|
||||
func (api *APIBuilder) createRoutes(methods []string, relativePath string, handlers ...context.Handler) []*Route {
|
||||
// if relativePath[0] != '/' {
|
||||
// return nil, errors.New("path should start with slash and should not be empty")
|
||||
// }
|
||||
|
||||
if method == "" || method == "ALL" || method == "ANY" { // then use like it was .Any
|
||||
return api.Any(relativePath, handlers...)[0]
|
||||
if len(methods) == 0 || methods[0] == "ALL" || methods[0] == "ANY" { // then use like it was .Any
|
||||
return api.Any(relativePath, handlers...)
|
||||
}
|
||||
|
||||
// no clean path yet because of subdomain indicator/separator which contains a dot.
|
||||
|
@ -206,7 +282,7 @@ func (api *APIBuilder) Handle(method string, relativePath string, handlers ...co
|
|||
|
||||
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", method, fullpath)
|
||||
api.reporter.Add("missing handlers for route %s: %s", strings.Join(methods, ", "), fullpath)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -222,7 +298,8 @@ func (api *APIBuilder) Handle(method string, relativePath string, handlers ...co
|
|||
|
||||
mainHandlers := context.Handlers(handlers)
|
||||
// before join the middleware + handlers + done handlers and apply the execution rules.
|
||||
possibleMainHandlerName := context.HandlerName(mainHandlers[0])
|
||||
|
||||
possibleMainHandlerName := context.MainHandlerName(mainHandlers)
|
||||
|
||||
// TODO: for UseGlobal/DoneGlobal that doesn't work.
|
||||
applyExecutionRules(api.handlerExecutionRules, &beginHandlers, &doneHandlers, &mainHandlers)
|
||||
|
@ -237,24 +314,36 @@ func (api *APIBuilder) Handle(method string, relativePath string, handlers ...co
|
|||
subdomain, path := splitSubdomainAndPath(fullpath)
|
||||
|
||||
// if allowMethods are empty, then simply register with the passed, main, method.
|
||||
methods := append(api.allowMethods, method)
|
||||
methods = append(api.allowMethods, methods...)
|
||||
|
||||
var (
|
||||
route *Route // the latest one is this route registered, see methods append.
|
||||
err error // not used outside of loop scope.
|
||||
)
|
||||
routes := make([]*Route, len(methods), len(methods))
|
||||
|
||||
for _, m := range methods {
|
||||
route, err = NewRoute(m, subdomain, path, possibleMainHandlerName, routeHandlers, *api.macros)
|
||||
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, method, subdomain, path)
|
||||
return nil // fail on first error.
|
||||
api.reporter.Add("%v -> %s:%s:%s", err, m, subdomain, path)
|
||||
continue
|
||||
}
|
||||
|
||||
// Add UseGlobal & DoneGlobal Handlers
|
||||
route.use(api.beginGlobalHandlers)
|
||||
route.done(api.doneGlobalHandlers)
|
||||
route.Use(api.beginGlobalHandlers...)
|
||||
route.Done(api.doneGlobalHandlers...)
|
||||
|
||||
routes[i] = route
|
||||
}
|
||||
|
||||
return routes
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// Returns a *Route, app will throw any errors later on.
|
||||
func (api *APIBuilder) Handle(method string, relativePath string, handlers ...context.Handler) *Route {
|
||||
routes := api.createRoutes([]string{method}, relativePath, handlers...)
|
||||
|
||||
var route *Route // the last one is returned.
|
||||
for _, route = range routes {
|
||||
// global
|
||||
api.routes.register(route)
|
||||
}
|
||||
|
@ -301,6 +390,61 @@ func (api *APIBuilder) HandleMany(methodOrMulti string, relativePathorMulti stri
|
|||
return
|
||||
}
|
||||
|
||||
// HandleDir registers a handler that serves HTTP requests
|
||||
// with the contents of a file system (physical or embedded).
|
||||
//
|
||||
// first parameter : the route path
|
||||
// second parameter : the system or the embedded directory that needs to be served
|
||||
// third parameter : not required, the directory options, set fields is optional.
|
||||
//
|
||||
// for more options look router.FileServer.
|
||||
//
|
||||
// api.HandleDir("/static", "./assets", DirOptions {ShowList: true, Gzip: true, IndexName: "index.html"})
|
||||
//
|
||||
// Returns the GET *Route.
|
||||
//
|
||||
// Examples can be found at: https://github.com/kataras/iris/tree/master/_examples/file-server
|
||||
func (api *APIBuilder) HandleDir(requestPath, directory string, opts ...DirOptions) (getRoute *Route) {
|
||||
options := getDirOptions(opts...)
|
||||
|
||||
h := FileServer(directory, options)
|
||||
|
||||
// if subdomain, we get the full path of the path only,
|
||||
// because a subdomain can have parties as well
|
||||
// and we need that path to call the `StripPrefix`.
|
||||
if _, fullpath := splitSubdomainAndPath(joinPath(api.relativePath, requestPath)); fullpath != "/" {
|
||||
h = StripPrefix(fullpath, h)
|
||||
|
||||
}
|
||||
|
||||
requestPath = joinPath(requestPath, WildcardFileParam())
|
||||
routes := api.createRoutes([]string{http.MethodGet, http.MethodHead}, requestPath, h)
|
||||
getRoute = routes[0]
|
||||
// we get all index, including sub directories even if those
|
||||
// are already managed by the static handler itself.
|
||||
staticSites := context.GetStaticSites(directory, getRoute.StaticPath(), options.IndexName)
|
||||
for _, s := range staticSites {
|
||||
// if the end-dev did manage that index route manually already
|
||||
// then skip the auto-registration.
|
||||
//
|
||||
// Also keep note that end-dev is still able to replace this route and manage by him/herself
|
||||
// later on by a simple `Handle/Get/` call, refer to `repository#register`.
|
||||
if api.GetRouteByPath(s.RequestPath) != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
routes = append(routes, api.createRoutes([]string{http.MethodGet}, s.RequestPath, h)...)
|
||||
getRoute.StaticSites = append(getRoute.StaticSites, s)
|
||||
}
|
||||
|
||||
for _, route := range routes {
|
||||
route.MainHandlerName = `HandleDir(directory: "` + directory + `")`
|
||||
api.routes.register(route)
|
||||
}
|
||||
|
||||
return getRoute
|
||||
}
|
||||
|
||||
// Party groups routes which may have the same prefix and share same handlers,
|
||||
// returns that new rich subrouter.
|
||||
//
|
||||
|
@ -432,19 +576,9 @@ func (api *APIBuilder) GetRoute(routeName string) *Route {
|
|||
return api.routes.get(routeName)
|
||||
}
|
||||
|
||||
// GetRouteReadOnly returns the registered "read-only" route based on its name, otherwise nil.
|
||||
// One note: "routeName" should be case-sensitive. Used by the context to get the current route.
|
||||
// It returns an interface instead to reduce wrong usage and to keep the decoupled design between
|
||||
// the context and the routes.
|
||||
// Look `GetRoutesReadOnly` to fetch a list of all registered routes.
|
||||
//
|
||||
// Look `GetRoute` for more.
|
||||
func (api *APIBuilder) GetRouteReadOnly(routeName string) context.RouteReadOnly {
|
||||
r := api.GetRoute(routeName)
|
||||
if r == nil {
|
||||
return nil
|
||||
}
|
||||
return routeReadOnlyWrapper{r}
|
||||
// GetRouteByPath returns the registered route based on the template path (`Route.Tmpl().Src`).
|
||||
func (api *APIBuilder) GetRouteByPath(tmplPath string) *Route {
|
||||
return api.routes.getByPath(tmplPath)
|
||||
}
|
||||
|
||||
// GetRoutesReadOnly returns the registered routes with "read-only" access,
|
||||
|
@ -465,6 +599,31 @@ func (api *APIBuilder) GetRoutesReadOnly() []context.RouteReadOnly {
|
|||
return readOnlyRoutes
|
||||
}
|
||||
|
||||
// GetRouteReadOnly returns the registered "read-only" route based on its name, otherwise nil.
|
||||
// One note: "routeName" should be case-sensitive. Used by the context to get the current route.
|
||||
// It returns an interface instead to reduce wrong usage and to keep the decoupled design between
|
||||
// the context and the routes.
|
||||
// Look `GetRoutesReadOnly` to fetch a list of all registered routes.
|
||||
//
|
||||
// Look `GetRoute` for more.
|
||||
func (api *APIBuilder) GetRouteReadOnly(routeName string) context.RouteReadOnly {
|
||||
r := api.GetRoute(routeName)
|
||||
if r == nil {
|
||||
return nil
|
||||
}
|
||||
return routeReadOnlyWrapper{r}
|
||||
}
|
||||
|
||||
// GetRouteReadOnlyByPath returns the registered read-only route based on the template path (`Route.Tmpl().Src`).
|
||||
func (api *APIBuilder) GetRouteReadOnlyByPath(tmplPath string) context.RouteReadOnly {
|
||||
r := api.GetRouteByPath(tmplPath)
|
||||
if r == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return routeReadOnlyWrapper{r}
|
||||
}
|
||||
|
||||
// Use appends Handler(s) to the current Party's routes and child routes.
|
||||
// If the current Party is the root, then it registers the middleware to all child Parties' routes too.
|
||||
//
|
||||
|
@ -488,7 +647,7 @@ func (api *APIBuilder) Use(handlers ...context.Handler) {
|
|||
// It's always a good practise to call it right before the `Application#Run` function.
|
||||
func (api *APIBuilder) UseGlobal(handlers ...context.Handler) {
|
||||
for _, r := range api.routes.routes {
|
||||
r.use(handlers) // prepend the handlers to the existing routes
|
||||
r.Use(handlers...) // prepend the handlers to the existing routes
|
||||
}
|
||||
// set as begin handlers for the next routes as well.
|
||||
api.beginGlobalHandlers = append(api.beginGlobalHandlers, handlers...)
|
||||
|
@ -514,7 +673,7 @@ func (api *APIBuilder) Done(handlers ...context.Handler) {
|
|||
// It's always a good practise to call it right before the `Application#Run` function.
|
||||
func (api *APIBuilder) DoneGlobal(handlers ...context.Handler) {
|
||||
for _, r := range api.routes.routes {
|
||||
r.done(handlers) // append the handlers to the existing routes
|
||||
r.Done(handlers...) // append the handlers to the existing routes
|
||||
}
|
||||
// set as done handlers for the next routes as well.
|
||||
api.doneGlobalHandlers = append(api.doneGlobalHandlers, handlers...)
|
||||
|
@ -616,51 +775,9 @@ func (api *APIBuilder) Any(relativePath string, handlers ...context.Handler) (ro
|
|||
return
|
||||
}
|
||||
|
||||
func (api *APIBuilder) registerResourceRoute(target, reqPath string, h context.Handler) *Route {
|
||||
head := api.Head(reqPath, h)
|
||||
head.StaticTarget = target
|
||||
get := api.Get(reqPath, h)
|
||||
get.StaticTarget = target
|
||||
return get
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// Usage:
|
||||
// app := iris.New()
|
||||
// ...
|
||||
// mySubdomainFsServer := app.Party("mysubdomain.")
|
||||
// h := mySubdomainFsServer.StaticHandler("./static_files", false, false)
|
||||
// /* http://mysubdomain.mydomain.com/static/css/style.css */
|
||||
// mySubdomainFsServer.Get("/static", h)
|
||||
// ...
|
||||
//
|
||||
func (api *APIBuilder) StaticHandler(systemPath string, showList bool, gzip bool) context.Handler {
|
||||
// Note: this doesn't need to be here but we'll keep it for consistently
|
||||
return StaticHandler(systemPath, showList, gzip)
|
||||
}
|
||||
|
||||
// StaticServe serves a directory as web resource.
|
||||
// Same as `StaticWeb`.
|
||||
// DEPRECATED; use `StaticWeb` or `StaticHandler` (for more options) instead.
|
||||
func (api *APIBuilder) StaticServe(systemPath string, requestPath ...string) *Route {
|
||||
var reqPath string
|
||||
|
||||
if len(requestPath) == 0 {
|
||||
reqPath = strings.Replace(systemPath, string(os.PathSeparator), "/", -1) // replaces any \ to /
|
||||
reqPath = strings.Replace(reqPath, "//", "/", -1) // for any case, replaces // to /
|
||||
reqPath = strings.Replace(reqPath, ".", "", -1) // replace any dots (./mypath -> /mypath)
|
||||
} else {
|
||||
reqPath = requestPath[0]
|
||||
}
|
||||
|
||||
return api.StaticWeb(reqPath, systemPath)
|
||||
func (api *APIBuilder) registerResourceRoute(reqPath string, h context.Handler) *Route {
|
||||
api.Head(reqPath, h)
|
||||
return api.Get(reqPath, h)
|
||||
}
|
||||
|
||||
// StaticContent registers a GET and HEAD method routes to the requestPath
|
||||
|
@ -677,58 +794,7 @@ func (api *APIBuilder) StaticContent(reqPath string, cType string, content []byt
|
|||
}
|
||||
}
|
||||
|
||||
return api.registerResourceRoute(StaticContentTarget, reqPath, h)
|
||||
}
|
||||
|
||||
// StaticEmbedded used when files are distributed inside the app executable, using go-bindata mostly
|
||||
// First parameter is the request path, the path which the files in the vdir will be served to, for example "/static"
|
||||
// Second parameter is the (virtual) directory path, for example "./assets" (no trailing slash),
|
||||
// Third parameter is the Asset function
|
||||
// Forth parameter is the AssetNames function.
|
||||
//
|
||||
// Returns the GET *Route.
|
||||
//
|
||||
// Example: https://github.com/kataras/iris/tree/master/_examples/file-server/embedding-files-into-app
|
||||
func (api *APIBuilder) StaticEmbedded(requestPath string, vdir string, assetFn func(name string) ([]byte, error), namesFn func() []string) *Route {
|
||||
return api.staticEmbedded(requestPath, vdir, assetFn, namesFn, false)
|
||||
}
|
||||
|
||||
// StaticEmbeddedGzip registers a route which can serve embedded gziped files
|
||||
// that are embedded using the https://github.com/kataras/bindata tool and only.
|
||||
// It's 8 times faster than the `StaticEmbeddedHandler` with `go-bindata` but
|
||||
// it sends gzip response only, so the client must be aware that is expecting a gzip body
|
||||
// (browsers and most modern browsers do that, so you can use it without fair).
|
||||
//
|
||||
// First parameter is the request path, the path which the files in the vdir will be served to, for example "/static"
|
||||
// Second parameter is the (virtual) directory path, for example "./assets" (no trailing slash),
|
||||
// Third parameter is the GzipAsset function
|
||||
// Forth parameter is the GzipAssetNames function.
|
||||
//
|
||||
// Example: https://github.com/kataras/iris/tree/master/_examples/file-server/embedding-gziped-files-into-app
|
||||
func (api *APIBuilder) StaticEmbeddedGzip(requestPath string, vdir string, gzipAssetFn func(name string) ([]byte, error), gzipNamesFn func() []string) *Route {
|
||||
return api.staticEmbedded(requestPath, vdir, gzipAssetFn, gzipNamesFn, true)
|
||||
}
|
||||
|
||||
// look fs.go#StaticEmbeddedHandler
|
||||
func (api *APIBuilder) staticEmbedded(requestPath string, vdir string, assetFn func(name string) ([]byte, error), namesFn func() []string, assetsGziped bool) *Route {
|
||||
fullpath := joinPath(api.relativePath, requestPath)
|
||||
// if subdomain,
|
||||
// here we get the full path of the path only,
|
||||
// because a subdomain can have parties as well
|
||||
// and we need that path to call the `StripPrefix`.
|
||||
_, fullpath = splitSubdomainAndPath(fullpath)
|
||||
|
||||
paramName := "file"
|
||||
requestPath = joinPath(requestPath, WildcardParam(paramName))
|
||||
|
||||
h := StaticEmbeddedHandler(vdir, assetFn, namesFn, assetsGziped)
|
||||
|
||||
if fullpath != "/" {
|
||||
h = StripPrefix(fullpath, h)
|
||||
}
|
||||
|
||||
// it handles the subdomain(root Party) of this party as well, if any.
|
||||
return api.registerResourceRoute(vdir, requestPath, h)
|
||||
return api.registerResourceRoute(reqPath, h)
|
||||
}
|
||||
|
||||
// errDirectoryFileNotFound returns an error with message: 'Directory or file %s couldn't found. Trace: +error trace'
|
||||
|
@ -787,48 +853,7 @@ func (api *APIBuilder) Favicon(favPath string, requestPath ...string) *Route {
|
|||
reqPath = requestPath[0]
|
||||
}
|
||||
|
||||
return api.registerResourceRoute(favPath, reqPath, h)
|
||||
}
|
||||
|
||||
// StaticWeb returns a handler that serves HTTP requests
|
||||
// with the contents of the file system rooted at directory.
|
||||
//
|
||||
// first parameter: the route path
|
||||
// second parameter: the system directory
|
||||
//
|
||||
// for more options look router.StaticHandler.
|
||||
//
|
||||
// api.StaticWeb("/static", "./static")
|
||||
//
|
||||
// As a special case, the returned file server redirects any request
|
||||
// ending in "/index.html" to the same path, without the final
|
||||
// "/index.html", if `index.html` should be served then register a
|
||||
// new route for it, i.e
|
||||
// `app.Get("/static", func(ctx iris.Context){ ctx.ServeFile("./static/index.html", false) })`.
|
||||
//
|
||||
// StaticWeb calls the `StripPrefix(fullpath, NewStaticHandlerBuilder(systemPath).Listing(false).Build())`.
|
||||
//
|
||||
// Returns the GET *Route.
|
||||
func (api *APIBuilder) StaticWeb(requestPath string, systemPath string) *Route {
|
||||
fullpath := joinPath(api.relativePath, requestPath)
|
||||
|
||||
// if subdomain,
|
||||
// here we get the full path of the path only,
|
||||
// because a subdomain can have parties as well
|
||||
// and we need that path to call the `StripPrefix`.
|
||||
_, fullpath = splitSubdomainAndPath(fullpath)
|
||||
|
||||
paramName := "file"
|
||||
requestPath = joinPath(requestPath, WildcardParam(paramName))
|
||||
|
||||
h := NewStaticHandlerBuilder(systemPath).Listing(false).Build()
|
||||
|
||||
if fullpath != "/" {
|
||||
h = StripPrefix(fullpath, h)
|
||||
}
|
||||
|
||||
// it handles the subdomain(root Party) of this party as well, if any.
|
||||
return api.registerResourceRoute(systemPath, requestPath, h)
|
||||
return api.registerResourceRoute(reqPath, h)
|
||||
}
|
||||
|
||||
// OnErrorCode registers an error http status code
|
||||
|
|
89
core/router/deprecated.go
Normal file
89
core/router/deprecated.go
Normal file
|
@ -0,0 +1,89 @@
|
|||
package router
|
||||
|
||||
import (
|
||||
"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])
|
||||
|
||||
for {
|
||||
frame, more := frames.Next()
|
||||
|
||||
if (!strings.Contains(frame.File, "github.com/kataras/iris") ||
|
||||
strings.Contains(frame.File, "github.com/kataras/iris/_examples") ||
|
||||
strings.Contains(frame.File, "github.com/iris-contrib/examples") ||
|
||||
(strings.Contains(frame.File, "github.com/kataras/iris/core/router") && !strings.Contains(frame.File, "deprecated.go"))) &&
|
||||
!strings.HasSuffix(frame.Func.Name(), ".getCaller") && !strings.Contains(frame.File, "/go/src/testing") {
|
||||
return frame.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
|
||||
}
|
67
core/router/deprecated_example_test.go
Normal file
67
core/router/deprecated_example_test.go
Normal file
|
@ -0,0 +1,67 @@
|
|||
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: C:/mygopath/src/github.com/kataras/iris/core/router/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: C:/mygopath/src/github.com/kataras/iris/core/router/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: C:/mygopath/src/github.com/kataras/iris/core/router/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: C:/mygopath/src/github.com/kataras/iris/core/router/deprecated_example_test.go:55
|
||||
// Use .HandleDir("/static", "./assets", iris.DirOptions{Gzip: true, Asset: Asset, AssetInfo: AssetInfo, AssetNames: AssetNames}) instead.
|
||||
}
|
1311
core/router/fs.go
1311
core/router/fs.go
File diff suppressed because it is too large
Load Diff
|
@ -76,11 +76,14 @@ func NewDefaultHandler() RequestHandler {
|
|||
type RoutesProvider interface { // api builder
|
||||
GetRoutes() []*Route
|
||||
GetRoute(routeName string) *Route
|
||||
// GetStaticSites() []*StaticSite
|
||||
// Macros() *macro.Macros
|
||||
}
|
||||
|
||||
func (h *routerHandler) Build(provider RoutesProvider) error {
|
||||
registeredRoutes := provider.GetRoutes()
|
||||
h.trees = h.trees[0:0] // reset, inneed when rebuilding.
|
||||
rp := errors.NewReporter()
|
||||
registeredRoutes := provider.GetRoutes()
|
||||
|
||||
// sort, subdomains go first.
|
||||
sort.Slice(registeredRoutes, func(i, j int) bool {
|
||||
|
@ -111,11 +114,8 @@ func (h *routerHandler) Build(provider RoutesProvider) error {
|
|||
|
||||
// the rest are handled inside the node
|
||||
return lsub1 > lsub2
|
||||
|
||||
})
|
||||
|
||||
rp := errors.NewReporter()
|
||||
|
||||
for _, r := range registeredRoutes {
|
||||
// build the r.Handlers based on begin and done handlers, if any.
|
||||
r.BuildHandlers()
|
||||
|
@ -133,6 +133,7 @@ func (h *routerHandler) Build(provider RoutesProvider) error {
|
|||
rp.Add("%v -> %s", err, r.String())
|
||||
continue
|
||||
}
|
||||
|
||||
golog.Debugf(r.Trace())
|
||||
}
|
||||
|
||||
|
|
|
@ -120,6 +120,39 @@ type Party interface {
|
|||
// in order to handle more than one paths for the same controller instance.
|
||||
HandleMany(method string, relativePath string, handlers ...context.Handler) []*Route
|
||||
|
||||
// HandleDir registers a handler that serves HTTP requests
|
||||
// with the contents of a file system (physical or embedded).
|
||||
//
|
||||
// first parameter : the route path
|
||||
// second parameter : the system or the embedded directory that needs to be served
|
||||
// third parameter : not required, the directory options, set fields is optional.
|
||||
//
|
||||
// for more options look router.FileServer.
|
||||
//
|
||||
// api.HandleDir("/static", "./assets", DirOptions {ShowList: true, Gzip: true, IndexName: "index.html"})
|
||||
//
|
||||
// Returns the GET *Route.
|
||||
//
|
||||
// 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
|
||||
// party.Routes().Online(handleResultregistry.*Route, "GET") and
|
||||
|
@ -167,61 +200,11 @@ type Party interface {
|
|||
// Any registers a route for ALL of the http methods
|
||||
// (Get,Post,Put,Head,Patch,Options,Connect,Delete).
|
||||
Any(registeredPath string, handlers ...context.Handler) []*Route
|
||||
|
||||
// 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.
|
||||
//
|
||||
// Usage:
|
||||
// app := iris.New()
|
||||
// ...
|
||||
// mySubdomainFsServer := app.Party("mysubdomain.")
|
||||
// h := mySubdomainFsServer.StaticHandler("./static_files", false, false)
|
||||
// /* http://mysubdomain.mydomain.com/static/css/style.css */
|
||||
// mySubdomainFsServer.Get("/static", h)
|
||||
// ...
|
||||
//
|
||||
StaticHandler(systemPath string, showList bool, gzip bool) context.Handler
|
||||
|
||||
// StaticServe serves a directory as web resource
|
||||
// it's the simpliest form of the Static* functions
|
||||
// Almost same usage as StaticWeb
|
||||
// accepts only one required parameter which is the systemPath,
|
||||
// the same path will be used to register the GET and HEAD method routes.
|
||||
// If second parameter is empty, otherwise the requestPath is the second parameter
|
||||
// it uses gzip compression (compression on each request, no file cache).
|
||||
//
|
||||
// Returns the GET *Route.
|
||||
StaticServe(systemPath string, requestPath ...string) *Route
|
||||
// StaticContent registers a GET and HEAD method routes to the requestPath
|
||||
// that are ready to serve raw static bytes, memory cached.
|
||||
//
|
||||
// Returns the GET *Route.
|
||||
StaticContent(requestPath string, cType string, content []byte) *Route
|
||||
|
||||
// StaticEmbedded used when files are distributed inside the app executable, using go-bindata mostly
|
||||
// First parameter is the request path, the path which the files in the vdir will be served to, for example "/static"
|
||||
// Second parameter is the (virtual) directory path, for example "./assets"
|
||||
// Third parameter is the Asset function
|
||||
// Forth parameter is the AssetNames function.
|
||||
//
|
||||
// Returns the GET *Route.
|
||||
//
|
||||
// Example: https://github.com/kataras/iris/tree/master/_examples/file-server/embedding-files-into-app
|
||||
StaticEmbedded(requestPath string, vdir string, assetFn func(name string) ([]byte, error), namesFn func() []string) *Route
|
||||
// StaticEmbeddedGzip registers a route which can serve embedded gziped files
|
||||
// that are embedded using the https://github.com/kataras/bindata tool and only.
|
||||
// It's 8 times faster than the `StaticEmbeddedHandler` with `go-bindata` but
|
||||
// it sends gzip response only, so the client must be aware that is expecting a gzip body
|
||||
// (browsers and most modern browsers do that, so you can use it without fair).
|
||||
//
|
||||
//
|
||||
// Example: https://github.com/kataras/iris/tree/master/_examples/file-server/embedding-gziped-files-into-app
|
||||
StaticEmbeddedGzip(requestPath string, vdir string, gzipAssetFn func(name string) ([]byte, error), gzipNamesFn func() []string) *Route
|
||||
// Favicon serves static favicon
|
||||
// accepts 2 parameters, second is optional
|
||||
// favPath (string), declare the system directory path of the __.ico
|
||||
|
@ -234,24 +217,6 @@ type Party interface {
|
|||
//
|
||||
// Returns the GET *Route.
|
||||
Favicon(favPath string, requestPath ...string) *Route
|
||||
// StaticWeb returns a handler that serves HTTP requests
|
||||
// with the contents of the file system rooted at directory.
|
||||
//
|
||||
// first parameter: the route path
|
||||
// second parameter: the system directory
|
||||
//
|
||||
// for more options look router.StaticHandler.
|
||||
//
|
||||
// router.StaticWeb("/static", "./static")
|
||||
//
|
||||
// As a special case, the returned file server redirects any request
|
||||
// ending in "/index.html" to the same path, without the final
|
||||
// "index.html".
|
||||
//
|
||||
// StaticWeb calls the `StripPrefix(fullpath, NewStaticHandlerBuilder(systemPath).Listing(false).Build())`.
|
||||
//
|
||||
// Returns the GET *Route.
|
||||
StaticWeb(requestPath string, systemPath string) *Route
|
||||
|
||||
// Layout overrides the parent template layout with a more specific layout for this Party.
|
||||
// It returns the current Party.
|
||||
|
|
|
@ -25,6 +25,15 @@ func WildcardParam(name string) string {
|
|||
return prefix(name, WildcardParamStart)
|
||||
}
|
||||
|
||||
// WildcardFileParam wraps a named parameter "file" with the trailing "path" macro parameter type.
|
||||
// At build state this "file" parameter is prefixed with the request handler's `WildcardParamStart`.
|
||||
// Created mostly for routes that serve static files to be visibly collected by
|
||||
// the `Application#GetRouteReadOnly` via the `Route.Tmpl().Src` instead of
|
||||
// the underline request handler's representation (`Route.Path()`).
|
||||
func WildcardFileParam() string {
|
||||
return "{file:path}"
|
||||
}
|
||||
|
||||
func convertMacroTmplToNodePath(tmpl macro.Template) string {
|
||||
routePath := tmpl.Src
|
||||
if len(routePath) > 1 && routePath[len(routePath)-1] == '/' {
|
||||
|
|
|
@ -29,25 +29,19 @@ type Route struct {
|
|||
// Execution happens after Begin and main Handler(s), can be empty.
|
||||
doneHandlers context.Handlers
|
||||
|
||||
Path string `json:"path"` // "/api/user/{id:uint64}"
|
||||
Path string `json:"path"` // the underline router's representation, i.e "/api/user/:id"
|
||||
// FormattedPath all dynamic named parameters (if any) replaced with %v,
|
||||
// used by Application to validate param values of a Route based on its name.
|
||||
FormattedPath string `json:"formattedPath"`
|
||||
|
||||
// StaticTarget if not empty, refers to the system (or virtual if embedded) directory
|
||||
// that this route is serving static files/resources from
|
||||
// or to a single static filename if this route created via `APIBuilder#Favicon`
|
||||
// or to a `StaticContentTarget` type if this rotue created by `APIBuilder#StaticContent`.
|
||||
//
|
||||
// If a route is serving static files via `APIBuilder`
|
||||
// there are two routes with the same dir/filename set to this field,
|
||||
// one for "HEAD" and the other for the "GET" http method.
|
||||
StaticTarget string
|
||||
// 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
|
||||
// route, manually or automatic by the framework,
|
||||
// get the route by `Application#GetRouteByPath(staticSite.RequestPath)`.
|
||||
StaticSites []context.StaticSite `json:"staticSites"`
|
||||
}
|
||||
|
||||
// StaticContentTarget used whenever a `Route#StaticTarget` refers to a raw []byte static content instead of a directory or a file.
|
||||
const StaticContentTarget = "content"
|
||||
|
||||
// NewRoute returns a new route based on its method,
|
||||
// subdomain, the path (unparsed or original),
|
||||
// handlers and the macro container which all routes should share.
|
||||
|
@ -69,7 +63,7 @@ func NewRoute(method, subdomain, unparsedPath, mainHandlerName string,
|
|||
handlers = append(context.Handlers{macroEvaluatorHandler}, handlers...)
|
||||
}
|
||||
|
||||
path = cleanPath(path) // maybe unnecessary here but who cares in this moment
|
||||
path = cleanPath(path) // maybe unnecessary here.
|
||||
defaultName := method + subdomain + tmpl.Src
|
||||
formattedPath := formatPath(path)
|
||||
|
||||
|
@ -87,26 +81,26 @@ func NewRoute(method, subdomain, unparsedPath, mainHandlerName string,
|
|||
return route, nil
|
||||
}
|
||||
|
||||
// use adds explicit begin handlers(middleware) to this route,
|
||||
// It's being called internally, it's useless for outsiders
|
||||
// because `Handlers` field is exported.
|
||||
// The callers of this function are: `APIBuilder#UseGlobal` and `APIBuilder#Done`.
|
||||
// Use adds explicit begin handlers to this route.
|
||||
// Alternatively the end-dev can prepend to the `Handlers` field.
|
||||
// Should be used before the `BuildHandlers` which is
|
||||
// called by the framework itself on `Application#Run` (build state).
|
||||
//
|
||||
// BuildHandlers should be called to build the route's `Handlers`.
|
||||
func (r *Route) use(handlers context.Handlers) {
|
||||
// Used internally at `APIBuilder#UseGlobal` -> `beginGlobalHandlers` -> `APIBuilder#Handle`.
|
||||
func (r *Route) Use(handlers ...context.Handler) {
|
||||
if len(handlers) == 0 {
|
||||
return
|
||||
}
|
||||
r.beginHandlers = append(r.beginHandlers, handlers...)
|
||||
}
|
||||
|
||||
// use adds explicit done handlers to this route.
|
||||
// It's being called internally, it's useless for outsiders
|
||||
// because `Handlers` field is exported.
|
||||
// The callers of this function are: `APIBuilder#UseGlobal` and `APIBuilder#Done`.
|
||||
// Done adds explicit finish handlers to this route.
|
||||
// Alternatively the end-dev can append to the `Handlers` field.
|
||||
// Should be used before the `BuildHandlers` which is
|
||||
// called by the framework itself on `Application#Run` (build state).
|
||||
//
|
||||
// BuildHandlers should be called to build the route's `Handlers`.
|
||||
func (r *Route) done(handlers context.Handlers) {
|
||||
// Used internally at `APIBuilder#DoneGlobal` -> `doneGlobalHandlers` -> `APIBuilder#Handle`.
|
||||
func (r *Route) Done(handlers ...context.Handler) {
|
||||
if len(handlers) == 0 {
|
||||
return
|
||||
}
|
||||
|
@ -161,6 +155,13 @@ func (r Route) String() string {
|
|||
r.Method, r.Subdomain, r.Tmpl().Src)
|
||||
}
|
||||
|
||||
// Equal compares the method, subdomaind and the
|
||||
// underline representation of the route's path,
|
||||
// instead of the `String` function which returns the front representation.
|
||||
func (r *Route) Equal(other *Route) bool {
|
||||
return r.Method == other.Method && r.Subdomain == other.Subdomain && r.Path == other.Path
|
||||
}
|
||||
|
||||
// Tmpl returns the path template,
|
||||
// it contains the parsed template
|
||||
// for the route's path.
|
||||
|
@ -235,12 +236,12 @@ func (r Route) StaticPath() string {
|
|||
if bidx == -1 || len(src) <= bidx {
|
||||
return src // no dynamic part found
|
||||
}
|
||||
if bidx == 0 { // found at first index,
|
||||
// but never happens because of the prepended slash
|
||||
if bidx <= 1 { // found at first{...} or second index (/{...}),
|
||||
// although first index should never happen because of the prepended slash.
|
||||
return "/"
|
||||
}
|
||||
|
||||
return src[:bidx]
|
||||
return src[:bidx-1] // (/static/{...} -> /static)
|
||||
}
|
||||
|
||||
// ResolvePath returns the formatted path's %v replaced with the args.
|
||||
|
@ -272,10 +273,15 @@ func (r Route) Trace() string {
|
|||
}
|
||||
printfmt += fmt.Sprintf(" %s ", r.Tmpl().Src)
|
||||
|
||||
mainHandlerName := r.MainHandlerName
|
||||
if !strings.HasSuffix(mainHandlerName, ")") {
|
||||
mainHandlerName += "()"
|
||||
}
|
||||
|
||||
if l := r.RegisteredHandlersLen(); l > 1 {
|
||||
printfmt += fmt.Sprintf("-> %s() and %d more", r.MainHandlerName, l-1)
|
||||
printfmt += fmt.Sprintf("-> %s and %d more", mainHandlerName, l-1)
|
||||
} else {
|
||||
printfmt += fmt.Sprintf("-> %s()", r.MainHandlerName)
|
||||
printfmt += fmt.Sprintf("-> %s", mainHandlerName)
|
||||
}
|
||||
|
||||
// printfmt := fmt.Sprintf("%s: %s >> %s", r.Method, r.Subdomain+r.Tmpl().Src, r.MainHandlerName)
|
||||
|
@ -316,3 +322,7 @@ func (rd routeReadOnlyWrapper) Tmpl() macro.Template {
|
|||
func (rd routeReadOnlyWrapper) MainHandlerName() string {
|
||||
return rd.Route.MainHandlerName
|
||||
}
|
||||
|
||||
func (rd routeReadOnlyWrapper) StaticSites() []context.StaticSite {
|
||||
return rd.Route.StaticSites
|
||||
}
|
||||
|
|
56
core/router/route_test.go
Normal file
56
core/router/route_test.go
Normal file
|
@ -0,0 +1,56 @@
|
|||
// white-box testing
|
||||
|
||||
package router
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris/macro"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRouteStaticPath(t *testing.T) {
|
||||
var tests = []struct {
|
||||
tmpl string
|
||||
static string
|
||||
}{
|
||||
{
|
||||
tmpl: "/files/{file:path}",
|
||||
static: "/files",
|
||||
},
|
||||
{
|
||||
tmpl: "/path",
|
||||
static: "/path",
|
||||
},
|
||||
{
|
||||
tmpl: "/path/segment",
|
||||
static: "/path/segment",
|
||||
},
|
||||
{
|
||||
tmpl: "/path/segment/{n:int}",
|
||||
static: "/path/segment",
|
||||
},
|
||||
{
|
||||
tmpl: "/path/{n:uint64}/{n:int}",
|
||||
static: "/path",
|
||||
},
|
||||
{
|
||||
tmpl: "/path/{n:uint64}/static",
|
||||
static: "/path",
|
||||
},
|
||||
{
|
||||
tmpl: "/{name}",
|
||||
static: "/",
|
||||
},
|
||||
{
|
||||
tmpl: "/",
|
||||
static: "/",
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
route := Route{tmpl: macro.Template{Src: tt.tmpl}}
|
||||
if expected, got := tt.static, route.StaticPath(); expected != got {
|
||||
t.Fatalf("[%d:%s] expected static path to be: '%s' but got: '%s'", i, tt.tmpl, expected, got)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,167 +0,0 @@
|
|||
package router
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/kataras/iris/context"
|
||||
)
|
||||
|
||||
// AssetValidator returns true if "filename"
|
||||
// is asset, i.e: strings.Contains(filename, ".").
|
||||
type AssetValidator func(filename string) bool
|
||||
|
||||
// SPABuilder helps building a single page application server
|
||||
// which serves both routes and files from the root path.
|
||||
type SPABuilder struct {
|
||||
// Root defaults to "/", it's the root path that explicitly set-ed,
|
||||
// this can be changed if more than SPAs are used on the same
|
||||
// iris router instance.
|
||||
Root string
|
||||
// emptyRoot can be changed with `ChangeRoot` only,
|
||||
// is, statically, true if root is empty
|
||||
// and if root is empty then let 404 fire from server-side anyways if
|
||||
// the passed `AssetHandler` returns 404 for a specific request path.
|
||||
// Defaults to false.
|
||||
emptyRoot bool
|
||||
|
||||
IndexNames []string
|
||||
AssetHandler context.Handler
|
||||
AssetValidators []AssetValidator
|
||||
}
|
||||
|
||||
// AddIndexName will add an index name.
|
||||
// If path == $filename then it redirects to Root, which defaults to "/".
|
||||
//
|
||||
// It can be called BEFORE the server start.
|
||||
func (s *SPABuilder) AddIndexName(filename string) *SPABuilder {
|
||||
s.IndexNames = append(s.IndexNames, filename)
|
||||
return s
|
||||
}
|
||||
|
||||
// ChangeRoot modifies the `Root` request path that is
|
||||
// explicitly set-ed if the `AssetHandler` gave a Not Found (404)
|
||||
// previously, if request's path is the passed "path"
|
||||
// then it explicitly sets that and it retries executing the `AssetHandler`.
|
||||
//
|
||||
// Empty Root means that let 404 fire from server-side anyways.
|
||||
//
|
||||
// Change it ONLY if you use more than one typical SPAs on the same Iris Application instance.
|
||||
func (s *SPABuilder) ChangeRoot(path string) *SPABuilder {
|
||||
s.Root = path
|
||||
s.emptyRoot = path == ""
|
||||
return s
|
||||
}
|
||||
|
||||
// NewSPABuilder returns a new Single Page Application builder
|
||||
// It does what StaticWeb or StaticEmbedded expected to do when serving files and routes at the same time
|
||||
// from the root "/" path.
|
||||
//
|
||||
// Accepts a static asset handler, which can be an app.StaticHandler, app.StaticEmbeddedHandler...
|
||||
func NewSPABuilder(assetHandler context.Handler) *SPABuilder {
|
||||
if assetHandler == nil {
|
||||
assetHandler = func(ctx context.Context) {
|
||||
ctx.Writef("empty asset handler")
|
||||
}
|
||||
}
|
||||
|
||||
return &SPABuilder{
|
||||
Root: "/",
|
||||
IndexNames: nil,
|
||||
// "IndexNames" are empty by-default,
|
||||
// if the user wants to redirect to "/" from "/index.html" she/he can chage that to []string{"index.html"} manually
|
||||
// or use the `StaticHandler` as "AssetHandler" which does that already.
|
||||
AssetHandler: assetHandler,
|
||||
AssetValidators: []AssetValidator{
|
||||
func(path string) bool {
|
||||
return true // returns true by-default, if false then it fires 404.
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SPABuilder) isAsset(reqPath string) bool {
|
||||
for _, v := range s.AssetValidators {
|
||||
if !v(reqPath) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Handler serves the asset handler but in addition, it makes some checks before that,
|
||||
// based on the `AssetValidators` and `IndexNames`.
|
||||
func (s *SPABuilder) Handler(ctx context.Context) {
|
||||
path := ctx.Path()
|
||||
|
||||
// make a validator call, by-default all paths are valid and this codeblock doesn't mean anything
|
||||
// but for cases that users wants to bypass an asset she/he can do that by modifiying the `APIBuilder#AssetValidators` field.
|
||||
//
|
||||
// It's here for backwards compatibility as well, see #803.
|
||||
if !s.isAsset(path) {
|
||||
// it's not asset, execute the registered route's handlers
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
|
||||
for _, index := range s.IndexNames {
|
||||
if strings.HasSuffix(path, index) {
|
||||
if s.emptyRoot {
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
localRedirect(ctx, "."+s.Root)
|
||||
// s.Root should be manually registered to a route
|
||||
// (not always, only if custom handler used).
|
||||
// We don't setup an index handler here,
|
||||
// let full control to the developer via "AssetHandler"
|
||||
// (use of middleware, manually call of the ctx.ServeFile or ctx.View etc.)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
s.AssetHandler(ctx)
|
||||
|
||||
if context.StatusCodeNotSuccessful(ctx.GetStatusCode()) && !s.emptyRoot && path != s.Root {
|
||||
// If file was not something like a javascript file, or a css or anything that
|
||||
// the passed `AssetHandler` scan-ed then re-execute the `AssetHandler`
|
||||
// using the `Root` as the request path (virtually).
|
||||
//
|
||||
// If emptyRoot is true then
|
||||
// fire the response as it's, "AssetHandler" is fully responsible for it,
|
||||
// client-side's router for invalid paths will not work here else read below.
|
||||
//
|
||||
// Author's notes:
|
||||
// the server doesn't need to know all client routes,
|
||||
// client-side router is responsible for any kind of invalid paths,
|
||||
// so explicit set to root path.
|
||||
//
|
||||
// The most simple solution was to use a
|
||||
// func(ctx iris.Context) { ctx.ServeFile("$PATH/index.html") } as the "AssetHandler"
|
||||
// but many developers use the `StaticHandler` (as shown in the examples)
|
||||
// but it was not working as expected because it (correctly) fires
|
||||
// a 404 not found if a file based on the request path didn't found.
|
||||
//
|
||||
// We can't just do it before the "AssetHandler"'s execution
|
||||
// for two main reasons:
|
||||
// 1. if it's a file serve handler, like `StaticHandler` then it will never serve
|
||||
// the corresponding files!
|
||||
// 2. it may manually handle those things,
|
||||
// don't forget that "AssetHandler" can be
|
||||
// ANY iris handler, so we can't be sure what the developer may want to do there.
|
||||
//
|
||||
// "AssetHandler" as the "StaticHandler" a retry doesn't hurt,
|
||||
// it will give us a 404 if the file didn't found very fast WITHOUT moving to the
|
||||
// rest of its validation and serving implementation.
|
||||
//
|
||||
// Another idea would be to modify the "AssetHandler" on every `ChangeRoot`
|
||||
// call, which may give us some performance (ns) benefits
|
||||
// but this could be bad if root is set-ed before the "AssetHandler",
|
||||
// so keep it as it's.
|
||||
rootURL, err := ctx.Request().URL.Parse(s.Root)
|
||||
if err == nil {
|
||||
ctx.Request().URL = rootURL
|
||||
s.AssetHandler(ctx)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
110
doc.go
110
doc.go
|
@ -918,66 +918,19 @@ Run
|
|||
Static Files
|
||||
|
||||
|
||||
// StaticServe serves a directory as web resource
|
||||
// it's the simpliest form of the Static* functions
|
||||
// Almost same usage as StaticWeb
|
||||
// accepts only one required parameter which is the systemPath,
|
||||
// the same path will be used to register the GET and HEAD method routes.
|
||||
// If second parameter is empty, otherwise the requestPath is the second parameter
|
||||
// it uses gzip compression (compression on each request, no file cache).
|
||||
// HandleDir registers a handler that serves HTTP requests
|
||||
// with the contents of a file system (physical or embedded).
|
||||
//
|
||||
// first parameter : the route path
|
||||
// second parameter : the system or the embedded directory that needs to be served
|
||||
// third parameter : not required, the directory options, set fields is optional.
|
||||
//
|
||||
// for more options look router.FileServer.
|
||||
//
|
||||
// api.HandleDir("/static", "./assets", DirOptions {ShowList: true, Gzip: true, IndexName: "index.html"})
|
||||
//
|
||||
// Returns the GET *Route.
|
||||
StaticServe(systemPath string, requestPath ...string) (*Route, error)
|
||||
|
||||
// StaticContent registers a GET and HEAD method routes to the requestPath
|
||||
// that are ready to serve raw static bytes, memory cached.
|
||||
//
|
||||
// Returns the GET *Route.
|
||||
StaticContent(reqPath string, cType string, content []byte) (*Route, error)
|
||||
|
||||
// StaticEmbedded used when files are distributed inside the app executable, using go-bindata mostly
|
||||
// First parameter is the request path, the path which the files in the vdir will be served to, for example "/static"
|
||||
// Second parameter is the (virtual) directory path, for example "./assets"
|
||||
// Third parameter is the Asset function
|
||||
// Forth parameter is the AssetNames function.
|
||||
//
|
||||
// Returns the GET *Route.
|
||||
//
|
||||
// Example: https://github.com/kataras/iris/tree/master/_examples/file-server/embedding-files-into-app
|
||||
StaticEmbedded(requestPath string, vdir string, assetFn func(name string) ([]byte, error), namesFn func() []string) (*Route, error)
|
||||
|
||||
// Favicon serves static favicon
|
||||
// accepts 2 parameters, second is optional
|
||||
// favPath (string), declare the system directory path of the __.ico
|
||||
// requestPath (string), it's the route's path, by default this is the "/favicon.ico" because some browsers tries to get this by default first,
|
||||
// you can declare your own path if you have more than one favicon (desktop, mobile and so on)
|
||||
//
|
||||
// this func will add a route for you which will static serve the /yuorpath/yourfile.ico to the /yourfile.ico
|
||||
// (nothing special that you can't handle by yourself).
|
||||
// Note that you have to call it on every favicon you have to serve automatically (desktop, mobile and so on).
|
||||
//
|
||||
// Returns the GET *Route.
|
||||
Favicon(favPath string, requestPath ...string) (*Route, error)
|
||||
|
||||
// StaticWeb returns a handler that serves HTTP requests
|
||||
// with the contents of the file system rooted at directory.
|
||||
//
|
||||
// first parameter: the route path
|
||||
// second parameter: the system directory
|
||||
// third OPTIONAL parameter: the exception routes
|
||||
// (= give priority to these routes instead of the static handler)
|
||||
// for more options look app.StaticHandler.
|
||||
//
|
||||
// app.StaticWeb("/static", "./static")
|
||||
//
|
||||
// As a special case, the returned file server redirects any request
|
||||
// ending in "/index.html" to the same path, without the final
|
||||
// "index.html".
|
||||
//
|
||||
// StaticWeb calls the StaticHandler(systemPath, listingDirectories: false, gzip: false ).
|
||||
//
|
||||
// Returns the GET *Route.
|
||||
StaticWeb(requestPath string, systemPath string, exceptRoutes ...*Route) (*Route, error)
|
||||
HandleDir(requestPath, directory string, opts ...DirOptions) (getRoute *Route)
|
||||
|
||||
|
||||
Example code:
|
||||
|
@ -990,19 +943,40 @@ Example code:
|
|||
func main() {
|
||||
app := iris.New()
|
||||
|
||||
// This will serve the ./static/favicons/ion_32_32.ico to: localhost:8080/favicon.ico
|
||||
app.Favicon("./static/favicons/ion_32_32.ico")
|
||||
app.Favicon("./assets/favicon.ico")
|
||||
|
||||
// app.Favicon("./static/favicons/ion_32_32.ico", "/favicon_48_48.ico")
|
||||
// This will serve the ./static/favicons/ion_32_32.ico to: localhost:8080/favicon_48_48.ico
|
||||
// first parameter is the request path
|
||||
// second is the system directory
|
||||
//
|
||||
// app.HandleDir("/css", "./assets/css")
|
||||
// app.HandleDir("/js", "./assets/js")
|
||||
|
||||
app.Get("/", func(ctx iris.Context) {
|
||||
ctx.HTML(`<a href="/favicon.ico"> press here to see the favicon.ico</a>.
|
||||
At some browsers like chrome, it should be visible at the top-left side of the browser's window,
|
||||
because some browsers make requests to the /favicon.ico automatically,
|
||||
so iris serves your favicon in that path too (you can change it).`)
|
||||
}) // if favicon doesn't show to you, try to clear your browser's cache.
|
||||
app.HandleDir("/static", "./assets", iris.DirOptions{
|
||||
// Defaults to "/index.html", if request path is ending with IndexName
|
||||
// then it redirects to ../ which another handler is handling it,
|
||||
// that another handler, called index handler, is auto-registered by the framework
|
||||
// if end developer does not managed to handle it by hand.
|
||||
IndexName: "/index.html",
|
||||
// When files should served under compression.
|
||||
Gzip: false,
|
||||
// List the files inside the current requested directory if `IndexName` not found.
|
||||
ShowList: false,
|
||||
// If `ShowList` is true then this function will be used instead of the default one to show the list of files of a current requested directory(dir).
|
||||
// DirList: func(ctx context.Context, dirName string, dir http.File) error { ... }
|
||||
//
|
||||
// Optional validator that loops through each requested resource.
|
||||
// AssetValidator: func(ctx iris.Context, name string) bool { ... }
|
||||
})
|
||||
// You can also register any index handler manually, order of registration does not matter:
|
||||
// app.Get("/static", [...custom middleware...], func(ctx iris.Context) {
|
||||
// [...custom code...]
|
||||
// ctx.ServeFile("./assets/index.html", false)
|
||||
// })
|
||||
|
||||
// http://localhost:8080/static
|
||||
// http://localhost:8080/static/css/main.css
|
||||
// http://localhost:8080/static/js/jquery-2.1.1.js
|
||||
// http://localhost:8080/static/favicon.ico
|
||||
app.Run(iris.Addr(":8080"))
|
||||
}
|
||||
|
||||
|
|
4
go19.go
4
go19.go
|
@ -62,8 +62,8 @@ type (
|
|||
// Look the `core/router#APIBuilder` for its implementation.
|
||||
//
|
||||
// A shortcut for the `core/router#Party`, useful when `PartyFunc` is being used.
|
||||
Party = router.Party
|
||||
|
||||
Party = router.Party
|
||||
DirOptions = router.DirOptions
|
||||
// ExecutionRules gives control to the execution of the route handlers outside of the handlers themselves.
|
||||
// Usage:
|
||||
// Party#SetExecutionRules(ExecutionRules {
|
||||
|
|
|
@ -89,7 +89,7 @@ func New(t *testing.T, app *iris.Application, setters ...OptionSetter) *httpexpe
|
|||
app.Logger().SetLevel(conf.LogLevel)
|
||||
|
||||
if err := app.Build(); err != nil {
|
||||
if conf.Debug && (conf.LogLevel == "disable" || conf.LogLevel == "disabled") {
|
||||
if conf.LogLevel != "disable" && conf.LogLevel != "disabled" {
|
||||
app.Logger().Println(err.Error())
|
||||
return nil
|
||||
}
|
||||
|
|
25
iris.go
25
iris.go
|
@ -359,12 +359,8 @@ var (
|
|||
//
|
||||
// A shortcut for the `context#NewConditionalHandler`.
|
||||
NewConditionalHandler = context.NewConditionalHandler
|
||||
// StaticEmbeddedHandler returns a Handler which can serve
|
||||
// embedded into executable files.
|
||||
//
|
||||
//
|
||||
// Examples: https://github.com/kataras/iris/tree/master/_examples/file-server
|
||||
StaticEmbeddedHandler = router.StaticEmbeddedHandler
|
||||
|
||||
FileServer = router.FileServer
|
||||
// StripPrefix returns a handler that serves HTTP requests
|
||||
// by removing the given prefix from the request URL's Path
|
||||
// and invoking the handler h. StripPrefix handles a
|
||||
|
@ -372,7 +368,7 @@ var (
|
|||
// replying with an HTTP 404 not found error.
|
||||
//
|
||||
// Usage:
|
||||
// fileserver := Party#StaticHandler("./static_files", false, false)
|
||||
// fileserver := iris.FileServer("./static_files", DirOptions {...})
|
||||
// h := iris.StripPrefix("/static", fileserver)
|
||||
// app.Get("/static/{f:path}", h)
|
||||
// app.Head("/static/{f:path}", h)
|
||||
|
@ -437,7 +433,7 @@ var (
|
|||
// Developers are free to extend this method's behavior
|
||||
// by watching system directories changes manually and use of the `ctx.WriteWithExpiration`
|
||||
// with a "modtime" based on the file modified date,
|
||||
// simillary to the `StaticWeb`(which sends status OK(200) and browser disk caching instead of 304).
|
||||
// simillary to the `HandleDir`(which sends status OK(200) and browser disk caching instead of 304).
|
||||
//
|
||||
// A shortcut of the `cache#Cache304`.
|
||||
Cache304 = cache.Cache304
|
||||
|
@ -489,19 +485,6 @@ var (
|
|||
IsErrPath = context.IsErrPath
|
||||
)
|
||||
|
||||
// SPA accepts an "assetHandler" which can be the result of an
|
||||
// app.StaticHandler or app.StaticEmbeddedHandler.
|
||||
// Use that when you want to navigate from /index.html to / automatically
|
||||
// it's a helper function which just makes some checks based on the `IndexNames` and `AssetValidators`
|
||||
// before the assetHandler call.
|
||||
//
|
||||
// Example: https://github.com/kataras/iris/tree/master/_examples/file-server/single-page-application
|
||||
func (app *Application) SPA(assetHandler context.Handler) *router.SPABuilder {
|
||||
s := router.NewSPABuilder(assetHandler)
|
||||
app.APIBuilder.HandleMany("GET HEAD", "/{f:path}", s.Handler)
|
||||
return s
|
||||
}
|
||||
|
||||
// ConfigureHost accepts one or more `host#Configuration`, these configurators functions
|
||||
// can access the host created by `app.Run`,
|
||||
// they're being executed when application is ready to being served to the public.
|
||||
|
|
|
@ -68,7 +68,10 @@ type Config struct {
|
|||
// LogFunc is the writer which logs are written to,
|
||||
// if missing the logger middleware uses the app.Logger().Infof instead.
|
||||
// Note that message argument can be empty.
|
||||
LogFunc func(now time.Time, latency time.Duration, status, ip, method, path string, message interface{}, headerMessage interface{})
|
||||
LogFunc func(endTime time.Time, latency time.Duration, status, ip, method, path string, message interface{}, headerMessage interface{})
|
||||
// LogFuncCtx can be used instead of `LogFunc` if handlers need to customize the output based on
|
||||
// custom request-time information that the LogFunc isn't aware of.
|
||||
LogFuncCtx func(ctx context.Context, latency time.Duration)
|
||||
// Skippers used to skip the logging i.e by `ctx.Path()` and serve
|
||||
// the next/main handler immediately.
|
||||
Skippers []SkipperFunc
|
||||
|
@ -83,15 +86,16 @@ type Config struct {
|
|||
// LogFunc and Skippers to nil as well.
|
||||
func DefaultConfig() Config {
|
||||
return Config{
|
||||
Status: true,
|
||||
IP: true,
|
||||
Method: true,
|
||||
Path: true,
|
||||
Query: false,
|
||||
Columns: false,
|
||||
LogFunc: nil,
|
||||
Skippers: nil,
|
||||
skip: nil,
|
||||
Status: true,
|
||||
IP: true,
|
||||
Method: true,
|
||||
Path: true,
|
||||
Query: false,
|
||||
Columns: false,
|
||||
LogFunc: nil,
|
||||
LogFuncCtx: nil,
|
||||
Skippers: nil,
|
||||
skip: nil,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -100,6 +100,9 @@ func (l *requestLoggerMiddleware) ServeHTTP(ctx context.Context) {
|
|||
if logFunc := l.config.LogFunc; logFunc != nil {
|
||||
logFunc(endTime, latency, status, ip, method, path, message, headerMessage)
|
||||
return
|
||||
} else if logFuncCtx := l.config.LogFuncCtx; logFuncCtx != nil {
|
||||
logFuncCtx(ctx, latency)
|
||||
return
|
||||
}
|
||||
|
||||
if l.config.Columns {
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
|
||||
func main() {
|
||||
app := iris.New()
|
||||
app.StaticWeb("/scripts", "./www/scripts") // serve the scripts
|
||||
app.HandleDir("/scripts", "./www/scripts") // serve the scripts
|
||||
// when you edit a typescript file from the alm-tools
|
||||
// it compiles it to javascript, have fun!
|
||||
|
||||
|
|
12
typescript/_examples/editor/www/scripts/app.js
Normal file
12
typescript/_examples/editor/www/scripts/app.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
var User = (function () {
|
||||
function User(fullname) {
|
||||
this.name = fullname;
|
||||
}
|
||||
User.prototype.Hi = function (msg) {
|
||||
return msg + " " + this.name;
|
||||
};
|
||||
return User;
|
||||
}());
|
||||
var user = new User("iris web framework!");
|
||||
var hi = user.Hi("Hello");
|
||||
window.alert(hi);
|
|
@ -14,7 +14,7 @@ import (
|
|||
func main() {
|
||||
app := iris.New()
|
||||
|
||||
app.StaticWeb("/scripts", "./www") // serve the scripts
|
||||
app.HandleDir("/scripts", "./www") // serve the scripts
|
||||
|
||||
app.Get("/", func(ctx iris.Context) {
|
||||
ctx.ServeFile("./www/index.html", false)
|
||||
|
|
|
@ -11,24 +11,25 @@ import (
|
|||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/flosch/pongo2"
|
||||
"github.com/kataras/iris/context"
|
||||
|
||||
"github.com/flosch/pongo2"
|
||||
)
|
||||
|
||||
type (
|
||||
// Value conversion for pongo2.Value
|
||||
Value pongo2.Value
|
||||
// Error conversion for pongo2.Error
|
||||
Error pongo2.Error
|
||||
// FilterFunction conversion for pongo2.FilterFunction
|
||||
FilterFunction func(in *Value, param *Value) (out *Value, err *Error)
|
||||
// Value type alias for pongo2.Value
|
||||
Value = pongo2.Value
|
||||
// Error type alias for pongo2.Error
|
||||
Error = pongo2.Error
|
||||
// FilterFunction type alias for pongo2.FilterFunction
|
||||
FilterFunction = pongo2.FilterFunction
|
||||
|
||||
// Parser conversion for pongo2.Parser
|
||||
Parser pongo2.Parser
|
||||
// Token conversion for pongo2.Token
|
||||
Token pongo2.Token
|
||||
// INodeTag conversion for pongo2.InodeTag
|
||||
INodeTag pongo2.INodeTag
|
||||
// Parser type alias for pongo2.Parser
|
||||
Parser = pongo2.Parser
|
||||
// Token type alias for pongo2.Token
|
||||
Token = pongo2.Token
|
||||
// INodeTag type alias for pongo2.InodeTag
|
||||
INodeTag = pongo2.INodeTag
|
||||
// TagParser the function signature of the tag's parser you will have
|
||||
// to implement in order to create a new tag.
|
||||
//
|
||||
|
@ -41,44 +42,22 @@ type (
|
|||
//
|
||||
// Please see the Parser documentation on how to use the parser.
|
||||
// See `RegisterTag` for more information about writing a tag as well.
|
||||
TagParser func(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error)
|
||||
TagParser = pongo2.TagParser
|
||||
)
|
||||
|
||||
// AsValue converts any given value to a view.Value.
|
||||
// AsValue converts any given value to a pongo2.Value
|
||||
// Usually being used within own functions passed to a template
|
||||
// through a Context or within filter functions.
|
||||
func AsValue(i interface{}) *Value {
|
||||
return (*Value)(pongo2.AsValue(i))
|
||||
}
|
||||
//
|
||||
// Example:
|
||||
// AsValue("my string")
|
||||
//
|
||||
// Shortcut for `pongo2.AsValue`.
|
||||
var AsValue = pongo2.AsValue
|
||||
|
||||
// AsSafeValue works like AsValue, but does not apply the 'escape' filter.
|
||||
func AsSafeValue(i interface{}) *Value {
|
||||
return (*Value)(pongo2.AsSafeValue(i))
|
||||
}
|
||||
|
||||
// GetValue returns the `Value` as *pongo2.Value type.
|
||||
// This method was added by balthild at https://github.com/kataras/iris/pull/765
|
||||
func (value *Value) GetValue() *pongo2.Value {
|
||||
return (*pongo2.Value)(value)
|
||||
}
|
||||
|
||||
// GetError returns the `Error` as *pongo2.Error type.
|
||||
// This method was added by balthild at https://github.com/kataras/iris/pull/765
|
||||
func (error *Error) GetError() *pongo2.Error {
|
||||
return (*pongo2.Error)(error)
|
||||
}
|
||||
|
||||
// GetParser returns the `Parser` as *pongo2.Parser type.
|
||||
// This method was added by balthild at https://github.com/kataras/iris/pull/765
|
||||
func (parser *Parser) GetParser() *pongo2.Parser {
|
||||
return (*pongo2.Parser)(parser)
|
||||
}
|
||||
|
||||
// GetToken returns the `Token` as *pongo2.Token type.
|
||||
// This method was added by balthild at https://github.com/kataras/iris/pull/765
|
||||
func (token *Token) GetToken() *pongo2.Token {
|
||||
return (*pongo2.Token)(token)
|
||||
}
|
||||
// Shortcut for `pongo2.AsSafeValue`.
|
||||
var AsSafeValue = pongo2.AsSafeValue
|
||||
|
||||
type tDjangoAssetLoader struct {
|
||||
baseDir string
|
||||
|
@ -200,13 +179,8 @@ func (s *DjangoEngine) RegisterFilter(filterName string, filterBody FilterFuncti
|
|||
return s.registerFilter(filterName, filterBody)
|
||||
}
|
||||
|
||||
func (s *DjangoEngine) registerFilter(filterName string, filterBody FilterFunction) *DjangoEngine {
|
||||
fn := pongo2.FilterFunction(func(in *pongo2.Value, param *pongo2.Value) (*pongo2.Value, *pongo2.Error) {
|
||||
theOut, theErr := filterBody((*Value)(in), (*Value)(param))
|
||||
return (*pongo2.Value)(theOut), (*pongo2.Error)(theErr)
|
||||
})
|
||||
func (s *DjangoEngine) registerFilter(filterName string, fn FilterFunction) *DjangoEngine {
|
||||
pongo2.RegisterFilter(filterName, fn)
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
|
@ -216,12 +190,7 @@ func (s *DjangoEngine) registerFilter(filterName string, filterBody FilterFuncti
|
|||
//
|
||||
// See http://www.florian-schlachter.de/post/pongo2/ for more about
|
||||
// writing filters and tags.
|
||||
func (s *DjangoEngine) RegisterTag(tagName string, parserFn TagParser) error {
|
||||
fn := func(doc *pongo2.Parser, start *pongo2.Token, arguments *pongo2.Parser) (pongo2.INodeTag, *pongo2.Error) {
|
||||
t, err := parserFn((*Parser)(doc), (*Token)(start), (*Parser)(arguments))
|
||||
return t, (*pongo2.Error)(err)
|
||||
}
|
||||
|
||||
func (s *DjangoEngine) RegisterTag(tagName string, fn TagParser) error {
|
||||
return pongo2.RegisterTag(tagName, fn)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user