Go 框架 iris 文档(一):https://developer.aliyun.com/article/1396584
用法
强制将文件发送到客户端:
func handler(ctx iris.Context) { src := "./files/first.zip" ctx.SendFile(src, "client.zip") }
将下载速度限制为 ~50Kb/s,突发 100KB:
func handler(ctx iris.Context) { src := "./files/big.zip" // optionally, keep it empty to resolve the filename based on the "src". dest := "" limit := 50.0 * iris.KB burst := 100 * iris.KB ctx.SendFileWithRate(src, dest, limit, burst) }
ServeContent(content io.ReadSeeker, filename string, modtime time.Time) ServeContentWithRate(content io.ReadSeeker, filename string, modtime time.Time, limit float64, burst int) ServeFile(filename string) error ServeFileWithRate(filename string, limit float64, burst int) error
用法
func handler(ctx iris.Context) { ctx.ServeFile("./public/main.js") }
模板渲染
Iris开箱即用支持8个模板引擎,开发者仍然可以使用任何外部golang模板引擎, 就像一个.Context.ResponseWriter()``io.Writer
所有模板引擎共享一个通用 API,即 使用嵌入资源、布局和特定于参与方的布局、模板功能、部分渲染等进行解析。
示例列表。
基准列表。
视图引擎可以按缔约方注册。要注册视图引擎,请使用如下所示的方法。Application/Party.RegisterView(ViewEngine)
从扩展名为“.html”的“./views”文件夹中加载所有模板,并使用标准包解析它们。html/template
// [app := iris.New...] tmpl := iris.HTML("./views", ".html") app.RegisterView(tmpl)
若要呈现或执行视图,请使用主路由处理程序中的方法。Context.View
if err := ctx.View("hi.html"); err!=nil { ctx.HTML("<h3>%s</h3>", err.Error()) return }
要通过中间件或主处理程序在视图中将 Go 值与键值模式绑定,请使用 go 值之前的方法。Context.ViewData``Context.View
绑定:与 .{{.message}}``"Hello world!"
ctx.ViewData("message", "Hello world!")
根绑定:
if err := ctx.View("user-page.html", User{}); err!=nil { ctx.HTML("<h3>%s</h3>", err.Error()) return } // root binding as {{.Name}} • 1 • 2 • 3 • 4 • 5 • 6
要**添加模板函数,**请使用首选视图引擎的方法。AddFunc
// func name, input arguments, render value tmpl.AddFunc("greet", func(s string) string { return "Greetings " + s + "!" })
要在每个请求上重新加载,请调用视图引擎的方法。Reload
tmpl.Reload(true)
要使用嵌入式模板而不依赖于本地文件系统,请使用 go-bindata 外部工具,并将其生成的函数传递给首选视图引擎的第一个输入参数。AssetFile()
tmpl := iris.HTML(AssetFile(), ".html")
示例代码:
// file: main.go package main import "github.com/kataras/iris/v12" func main() { app := iris.New() // Parse all templates from the "./views" folder // where extension is ".html" and parse them // using the standard `html/template` package. tmpl := iris.HTML("./views", ".html") // Set custom delimeters. tmpl.Delims("{{", "}}") // Enable re-build on local template files changes. tmpl.Reload(true) // Default template funcs are: // // - {{ urlpath "myNamedRoute" "pathParameter_ifNeeded" }} // - {{ render "header.html" . }} // and partial relative path to current page: // - {{ render_r "header.html" . }} // - {{ yield . }} // - {{ current }} // Register a custom template func: tmpl.AddFunc("greet", func(s string) string { return "Greetings " + s + "!" }) // Register the view engine to the views, // this will load the templates. app.RegisterView(tmpl) // Method: GET // Resource: http://localhost:8080 app.Get("/", func(ctx iris.Context) { // Bind: {{.message}} with "Hello world!" ctx.ViewData("message", "Hello world!") // Render template file: ./views/hi.html if err := ctx.View("hi.html"); err!=nil { ctx.HTML("<h3>%s</h3>", err.Error()) return } }) app.Listen(":8080") }
<!-- file: ./views/hi.html --> <html> <head> <title>Hi Page</title> </head> <body> <h1>{{.message}}</h1> <strong>{{greet "to you"}}</strong> </body> </html>
在 http://localhost:8080 处打开浏览器选项卡。
渲染的结果将如下所示:
<html> <head> <title>Hi Page</title> </head> <body> <h1>Hello world!</h1> <strong>Greetings to you!</strong> </body> </html>
多模板
Iris 允许每个应用程序注册无限数量的视图引擎。除此之外,您还可以为每个派对或通过中间件注册一个视图引擎!
// Register a view engine per group of routes. adminGroup := app.Party("/admin") adminGroup.RegisterView(iris.Blocks("./views/admin", ".html"))
通过中间件
func middleware(views iris.ViewEngine) iris.Handler { return func(ctx iris.Context) { ctx.ViewEngine(views) ctx.Next() } }
用法
// Register a view engine on-fly for the current chain of handlers. views := iris.Blocks("./views/on-fly", ".html") views.Load() app.Get("/", setViews(views), onFly)
重 定向
发出 HTTP 重定向很容易。支持内部和外部位置。我们所说的位置是指路径、子域、域等。
从处理程序
app.Get("/", func(ctx iris.Context) { ctx.Redirect("https://go.dev/dl", iris.StatusMovedPermanently) })
从开机自检发出 HTTP 重定向。
app.Post("/", func(ctx iris.Context) { ctx.Redirect("/login", iris.StatusFound) })
从处理程序发出本地路由器重定向,使用或类似如下。Application.ServeHTTPC``Exec()
app.Get("/test", func(ctx iris.Context) { r := ctx.Request() r.URL.Path = "/test2" ctx.Application().ServeHTTPC(ctx) // OR // ctx.Exec("GET", "/test2") }) app.Get("/test2", func(ctx iris.Context) { ctx.JSON(iris.Map{"hello": "world"}) })
全球
使用我们都喜欢的语法。
import "github.com/kataras/iris/v12/middleware/rewrite"
func main() { app := iris.New() // [...routes] redirects := rewrite.Load("redirects.yml") app.WrapRouter(redirects) app.Listen(":80") }
该文件如下所示:"redirects.yml"
RedirectMatch: # Redirects /seo/* to /* - 301 /seo/(.*) /$1 # Redirects /docs/v12* to /docs - 301 /docs/v12(.*) /docs # Redirects /old(.*) to / - 301 /old(.*) / # Redirects http or https://test.* to http or https://newtest.* - 301 ^(http|https)://test.(.*) $1://newtest.$2 # Handles /*.json or .xml as *?format=json or xml, # without redirect. See /users route. # When Code is 0 then it does not redirect the request, # instead it changes the request URL # and leaves a route handle the request. - 0 /(.*).(json|xml) /$1?format=$2 # Redirects root domain to www. # Creation of a www subdomain inside the Application is unnecessary, # all requests are handled by the root Application itself. PrimarySubdomain: www
完整的代码可以在重写中间件示例中找到。
自定义中间件
func Logger() iris.Handler { return func(ctx iris.Context) { t := time.Now() // Set a shared variable between handlers ctx.Values().Set("framework", "iris") // before request ctx.Next() // after request latency := time.Since(t) log.Print(latency) // access the status we are sending status := ctx.GetStatusCode() log.Println(status) } } func main() { app := iris.New() app.Use(Logger()) app.Get("/test", func(ctx iris.Context) { // retrieve a value set by the middleware. framework := ctx.Values().GetString("framework") // it would print: "iris" log.Println(framework) }) app.Listen(":8080") }
(adsbygoogle = window.adsbygoogle || []).push({});
使用基本身份验证
HTTP 基本身份验证是对 Web 资源实施访问控制的最简单技术,因为它不需要 Cookie、会话标识符或登录页面;相反,HTTP 基本身份验证使用 HTTP 标头中的标准字段。
基本身份验证中间件包含在 Iris 框架中,因此无需单独安装。
1. 导入中间件
import "github.com/kataras/iris/v12/middleware/basicauth"
2. 使用其结构配置中间件:Options
opts := basicauth.Options{ Allow: basicauth.AllowUsers(map[string]string{ "username": "password", }), Realm: "Authorization Required", ErrorHandler: basicauth.DefaultErrorHandler, // [...more options] }
3. 初始化中间件:
auth := basicauth.New(opts)
3.1 以上步骤与函数相同:Default
auth := basicauth.Default(map[string]string{ "username": "password", })
3.2 使用自定义用户切片:
// The struct value MUST contain a Username and Passwords fields // or GetUsername() string and GetPassword() string methods. type User struct { Username string Password string } // [...] auth := basicauth.Default([]User{...})
3.3 从文件加载用户 或者,密码使用 pkg.go.dev/golang.org/x/crypto/bcrypt 包进行加密:
auth := basicauth.Load("users.yml", basicauth.BCRYPT)
3.3.1 使用(推荐)也可以实现相同的目的:Options
opts := basicauth.Options{ Allow: basicauth.AllowUsersFile("users.yml", basicauth.BCRYPT), Realm: basicauth.DefaultRealm, // [...more options] } auth := basicauth.New(opts)
其中可能看起来像这样:users.yml
- username: kataras password: $2a$10$Irg8k8HWkDlvL0YDBKLCYee6j6zzIFTplJcvZYKA.B8/clHPZn2Ey # encrypted of kataras_pass role: admin - username: makis password: $2a$10$3GXzp3J5GhHThGisbpvpZuftbmzPivDMo94XPnkTnDe7254x7sJ3O # encrypted of makis_pass role: member
4. 注册中间件:
// Register to all matched routes // under a Party and its children. app.Use(auth) // OR/and register to all http error routes. app.UseError(auth) // OR register under a path prefix of a specific Party, // including all http errors of this path prefix. app.UseRouter(auth) // OR register to a specific Route before its main handler. app.Post("/protected", auth, routeHandler)
5. 检索用户名和密码:
func routeHandler(ctx iris.Context) { username, password, _ := ctx.Request().BasicAuth() // [...] } • 1 • 2 • 3 • 4
5.1 检索 User 值(当您在以下位置注册自定义用户结构的一部分时很有用):Options.AllowUsers
func routeHandler(ctx iris.Context) { user := ctx.User().(*iris.SimpleUser) // user.Username // user.Password }
在 _examples/auth 中阅读更多授权和身份验证示例。
中间件中的 Goroutines
在中间件或处理程序中启动新的 Goroutines 时,您不应该使用其中的原始上下文,您必须使用只读副本。
func main() { app := iris.Default() app.Get("/long_async", func(ctx iris.Context) { // create a clone to be used inside the goroutine ctxCopy := ctx.Clone() go func() { // simulate a long task with time.Sleep(). 5 seconds time.Sleep(5 * time.Second) // note that you are using the copied context "ctxCopy", IMPORTANT log.Printf("Done! in path: %s", ctxCopy.Path()) }() }) app.Get("/long_sync", func(ctx iris.Context) { // simulate a long task with time.Sleep(). 5 seconds time.Sleep(5 * time.Second) // since we are NOT using a goroutine, we do not have to copy the context log.Printf("Done! in path: %s", ctx.Path()) }) app.Listen(":8080") }
自定义 HTTP 配置
在 _examples/http-server 文件夹中可以找到超过 12 个有关 http 服务器配置的示例。
直接使用,如下所示:http.ListenAndServe()
func main() { app := iris.New() // [...routes] if err := app.Build(); err!=nil{ panic(err) } http.ListenAndServe(":8080", app) }
请注意,在将其用作 .Build``http.Handler
再比如:
func main() { app := iris.New() // [...routes] app.Build() srv := &http.Server{ Addr: ":8080", Handler: app, ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, MaxHeaderBytes: 1 << 20, } srv.ListenAndServe() }
但是,您很少需要具有 IRIS 的外部实例。您可以使用任何 tcp 侦听器、http 服务器或通过方法自定义函数进行侦听。http.Server``Application.Run
app.Run(iris.Listener(l net.Listener)) // listen using a custom net.Listener app.Run(iris.Server(srv *http.Server)) // listen using a custom http.Server app.Run(iris.Addr(addr string)) // the app.Listen is a shortcut of this method. app.Run(iris.TLS(addr string, certFileOrContents, keyFileOrContents string)) // listen TLS. app.Run(iris.AutoTLS(addr, domain, email string)) // listen using letsencrypt (see below). // and any custom function that returns an error: app.Run(iris.Raw(f func() error))
套接字分片
此选项允许在多 CPU 服务器上线性扩展服务器性能。有关详细信息,请参阅 https://www.nginx.com/blog/socket-sharding-nginx-release-1-9-1/。使用配置器启用。iris.WithSocketSharding
示例代码:
package main import ( "time" "github.com/kataras/iris/v12" ) func main() { startup := time.Now() app := iris.New() app.Get("/", func(ctx iris.Context) { s := startup.Format(ctx.Application().ConfigurationReadOnly().GetTimeFormat()) ctx.Writef("This server started at: %s\n", s) }) app.Listen(":8080", iris.WithSocketSharding) // or app.Run(..., iris.WithSocketSharding) }
支持让我们加密
1 行 LetsEncrypt HTTPS 服务器的示例。
package main import ( "log" "github.com/iris-gonic/autotls" "github.com/kataras/iris/v12" ) func main() { app := iris.Default() // Ping handler app.Get("/ping", func(ctx iris.Context) { ctx.WriteString("pong") }) app.Run(iris.AutoTLS(":443", "example.com example2.com", "mail@example.com")) }
自定义 TLS 示例(您也可以绑定自动证书管理器):
app.Run( iris.TLS(":443", "", "", func(su *iris.Supervisor) { su.Server.TLSConfig = &tls.Config{ /* your custom fields */ }, }), )
所有方法(如:Addr,TLS,AutoTLS,Server,Listener和e.t.c)都接受可变参数,以在构建状态上配置http服务器实例。
iris.Runner``func(*iris.Supervisor)
使用鸢尾花运行多个服务
package main import ( "log" "net/http" "time" "github.com/kataras/iris/v12" "github.com/kataras/iris/v12/middleware/recover" "golang.org/x/sync/errgroup" ) var g errgroup.Group func startApp1() error { app := iris.New().SetName("app1") app.Use(recover.New()) app.Get("/", func(ctx iris.Context) { app.Get("/", func(ctx iris.Context) { ctx.JSON(iris.Map{ "code": iris.StatusOK, "message": "Welcome server 1", }) }) }) app.Build() return app.Listen(":8080") } func startApp2() error { app := iris.New().SetName("app2") app.Use(recover.New()) app.Get("/", func(ctx iris.Context) { ctx.JSON(iris.Map{ "code": iris.StatusOK, "message": "Welcome server 2", }) }) return app.Listen(":8081") } func main() { g.Go(startApp1) g.Go(startApp2) if err := g.Wait(); err != nil { log.Fatal(err) } }
通过包管理多个鸢尾花实例。在此处阅读更多内容。apps
(adsbygoogle = window.adsbygoogle || []).push({});
正常关机或重启
有几种方法可用于执行正常关机或重新启动。您可以使用专门为此构建的第三方包,也可以使用该方法。可以在此处找到示例。app.Shutdown(context.Context)
使用 CTRL/CMD+C 在 CTRL/CMD+C 上注册事件:iris.RegisterOnInterrupt
idleConnsClosed := make(chan struct{}) iris.RegisterOnInterrupt(func() { timeout := 10 * time.Second ctx, cancel := stdContext.WithTimeout(stdContext.Background(), timeout) defer cancel() // close all hosts. app.Shutdown(ctx) close(idleConnsClosed) }) // [...] app.Listen(":8080", iris.WithoutInterruptHandler, iris.WithoutServerError(iris.ErrServerClosed)) <-idleConnsClosed
使用模板构建单个二进制文件
您可以使用 [go-bindata][https://github.com/go-bindata/go-bindata] 的生成函数将服务器构建到包含模板的单个二进制文件中。AssetFile
$ go get -u github.com/go-bindata/go-bindata/... $ go-bindata -fs -prefix "templates" ./templates/... $ go run .
示例代码:
func main() { app := iris.New() tmpl := iris.HTML(AssetFile(), ".html") tmpl.Layout("layouts/layout.html") tmpl.AddFunc("greet", func(s string) string { return "Greetings " + s + "!" }) app.RegisterView(tmpl) // [...] }
在_examples/视图中查看完整示例。
尝试将身体绑定到不同的结构中
绑定请求正文的常规方法消耗,它们 不能多次调用,除非将配置器传递给 。ctx.Request().Body``iris.WithoutBodyConsumptionOnUnmarshal``app.Run/Listen
package main import "github.com/kataras/iris/v12" func main() { app := iris.New() app.Post("/", logAllBody, logJSON, logFormValues, func(ctx iris.Context) { // body, err := os.ReadAll(ctx.Request().Body) once or body, err := ctx.GetBody() // as many times as you need. if err != nil { ctx.StopWithError(iris.StatusInternalServerError, err) return } if len(body) == 0 { ctx.WriteString(`The body was empty.`) } else { ctx.WriteString("OK body is still:\n") ctx.Write(body) } }) app.Listen(":8080", iris.WithoutBodyConsumptionOnUnmarshal) } func logAllBody(ctx iris.Context) { body, err := ctx.GetBody() if err == nil && len(body) > 0 { ctx.Application().Logger().Infof("logAllBody: %s", string(body)) } ctx.Next() } func logJSON(ctx iris.Context) { var p interface{} if err := ctx.ReadJSON(&p); err == nil { ctx.Application().Logger().Infof("logJSON: %#+v", p) } ctx.Next() } func logFormValues(ctx iris.Context) { values := ctx.FormValues() if values != nil { ctx.Application().Logger().Infof("logFormValues: %v", values) } ctx.Next() }
可以使用 将结构绑定到基于客户端内容类型的请求。您还可以使用内容协商。下面是一个完整的示例:ReadBody
package main import ( "github.com/kataras/iris/v12" ) func main() { app := newApp() // See main_test.go for usage. app.Listen(":8080") } func newApp() *iris.Application { app := iris.New() // To automatically decompress using gzip: // app.Use(iris.GzipReader) app.Use(setAllowedResponses) app.Post("/", readBody) return app } type payload struct { Message string `json:"message" xml:"message" msgpack:"message" yaml:"Message" url:"message" form:"message"` } func readBody(ctx iris.Context) { var p payload // Bind request body to "p" depending on the content-type that client sends the data, // e.g. JSON, XML, YAML, MessagePack, Protobuf, Form and URL Query. err := ctx.ReadBody(&p) if err != nil { ctx.StopWithProblem(iris.StatusBadRequest, iris.NewProblem().Title("Parser issue").Detail(err.Error())) return } // For the sake of the example, log the received payload. ctx.Application().Logger().Infof("Received: %#+v", p) // Send back the payload depending on the accept content type and accept-encoding of the client, // e.g. JSON, XML and so on. ctx.Negotiate(p) } func setAllowedResponses(ctx iris.Context) { // Indicate that the Server can send JSON, XML, YAML and MessagePack for this request. ctx.Negotiation().JSON().XML().YAML().MsgPack() // Add more, allowed by the server format of responses, mime types here... // If client is missing an "Accept: " header then default it to JSON. ctx.Negotiation().Accept.JSON() ctx.Next() }
(adsbygoogle = window.adsbygoogle || []).push({});
HTTP2 服务器推送
完整的示例代码可以在 _examples/response-writer/http2push 中找到。
服务器推送让服务器抢先“推送”网站资产 到客户端,而无需用户明确请求它们。 谨慎使用时,我们可以发送我们知道用户要去的内容 需要他们请求的页面。
package main import ( "net/http" "github.com/kataras/iris/v12" ) func main() { app := iris.New() app.Get("/", pushHandler) app.Get("/main.js", simpleAssetHandler) app.Run(iris.TLS("127.0.0.1:443", "mycert.crt", "mykey.key")) // $ openssl req -new -newkey rsa:4096 -x509 -sha256 \ // -days 365 -nodes -out mycert.crt -keyout mykey.key } func pushHandler(ctx iris.Context) { // The target must either be an absolute path (like "/path") or an absolute // URL that contains a valid host and the same scheme as the parent request. // If the target is a path, it will inherit the scheme and host of the // parent request. target := "/main.js" if pusher, ok := ctx.ResponseWriter().Naive().(http.Pusher); ok { err := pusher.Push(target, nil) if err != nil { if err == iris.ErrPushNotSupported { ctx.StopWithText(iris.StatusHTTPVersionNotSupported, "HTTP/2 push not supported.") } else { ctx.StopWithError(iris.StatusInternalServerError, err) } return } } ctx.HTML(`<html><body><script src="%s"></script></body></html>`, target) } func simpleAssetHandler(ctx iris.Context) { ctx.ServeFile("./public/main.js") }
设置并获取饼干
安全 Cookie、编码和解码、会话(和会话缩放)、闪存消息等可以在 _examples/cookie 和 _examples/会话目录中找到。
import "github.com/kataras/iris/v12" func main() { app := iris.Default() app.Get("/cookie", func(ctx iris.Context) { value := ctx.GetCookie("my_cookie") if value == "" { value = "NotSet" ctx.SetCookieKV("my_cookie", value) // Alternatively: ctx.SetCookie(&http.Cookie{...}) ctx.SetCookie("", "test", 3600, "/", "localhost", false, true) } ctx.Writef("Cookie value: %s \n", cookie) }) app.Listen(":8080") }
如果要设置自定义路径:
ctx.SetCookieKV(name, value, iris.CookiePath("/custom/path/cookie/will/be/stored"))
如果希望仅对当前请求路径可见:
ctx.SetCookieKV(name, value, iris.CookieCleanPath /* or iris.CookiePath("") */)
更多:
iris.CookieAllowReclaim
iris.CookieAllowSubdomains
iris.CookieSecure
iris.CookieHTTPOnly
iris.CookieSameSite
iris.CookiePath
iris.CookieCleanPath
iris.CookieExpires
iris.CookieEncoding
您也可以在中间件中为整个请求添加 cookie 选项:
func setCookieOptions(ctx iris.Context) { ctx.AddCookieOptions(iris.CookieHTTPOnly(true), iris.CookieExpires(1*time.Hour)) ctx.Next() }
JSON 网络令牌
JSON Web 令牌 (JWT) 是一种开放标准 (RFC 7519),它定义了一种紧凑且独立的方式,用于在各方之间以 JSON 对象的形式安全地传输信息。此信息可以验证和信任,因为它是经过数字签名的。JWT 可以使用密钥(使用 HMAC 算法)或使用 RSA 或 ECDSA 的公钥/私钥对进行签名。
何时应使用 JSON Web 令牌?
以下是 JSON Web 令牌有用的一些方案:
授权:这是使用 JWT 的最常见方案。用户登录后,每个后续请求都将包含 JWT,允许用户访问使用该令牌允许的路由、服务和资源。单点登录是当今广泛使用 JWT 的一项功能,因为它的开销很小,并且能够跨不同域轻松使用。
信息交换:JSON Web 令牌是在各方之间安全传输信息的好方法。由于 JWT 可以签名(例如,使用公钥/私钥对),因此您可以确定发件人是他们所说的人。此外,由于签名是使用标头和有效负载计算的,因此您还可以验证内容是否未被篡改。
阅读更多关于智威汤逊的信息: https://jwt.io/introduction/
将 JWT 与虹膜配合使用
Iris JWT 中间件在设计时考虑了安全性、性能和简单性,它可以保护您的令牌免受您可能在其他库中发现的关键漏洞的影响。它基于kataras/jwt包。
package main import ( "time" "github.com/kataras/iris/v12" "github.com/kataras/iris/v12/middleware/jwt" ) var ( sigKey = []byte("signature_hmac_secret_shared_key") encKey = []byte("GCM_AES_256_secret_shared_key_32") ) type fooClaims struct { Foo string `json:"foo"` } func main() { app := iris.New() signer := jwt.NewSigner(jwt.HS256, sigKey, 10*time.Minute) // Enable payload encryption with: // signer.WithEncryption(encKey, nil) app.Get("/", generateToken(signer)) verifier := jwt.NewVerifier(jwt.HS256, sigKey) // Enable server-side token block feature (even before its expiration time): verifier.WithDefaultBlocklist() // Enable payload decryption with: // verifier.WithDecryption(encKey, nil) verifyMiddleware := verifier.Verify(func() interface{} { return new(fooClaims) }) protectedAPI := app.Party("/protected") // Register the verify middleware to allow access only to authorized clients. protectedAPI.Use(verifyMiddleware) // ^ or UseRouter(verifyMiddleware) to disallow unauthorized http error handlers too. protectedAPI.Get("/", protected) // Invalidate the token through server-side, even if it's not expired yet. protectedAPI.Get("/logout", logout) // http://localhost:8080 // http://localhost:8080/protected?token=$token (or Authorization: Bearer $token) // http://localhost:8080/protected/logout?token=$token // http://localhost:8080/protected?token=$token (401) app.Listen(":8080") } func generateToken(signer *jwt.Signer) iris.Handler { return func(ctx iris.Context) { claims := fooClaims{Foo: "bar"} token, err := signer.Sign(claims) if err != nil { ctx.StopWithStatus(iris.StatusInternalServerError) return } ctx.Write(token) } } func protected(ctx iris.Context) { // Get the verified and decoded claims. claims := jwt.Get(ctx).(*fooClaims) // Optionally, get token information if you want to work with them. // Just an example on how you can retrieve all the standard claims (set by signer's max age, "exp"). standardClaims := jwt.GetVerifiedToken(ctx).StandardClaims expiresAtString := standardClaims.ExpiresAt().Format(ctx.Application().ConfigurationReadOnly().GetTimeFormat()) timeLeft := standardClaims.Timeleft() ctx.Writef("foo=%s\nexpires at: %s\ntime left: %s\n", claims.Foo, expiresAtString, timeLeft) } func logout(ctx iris.Context) { err := ctx.Logout() if err != nil { ctx.WriteString(err.Error()) } else { ctx.Writef("token invalidated, a new token is required to access the protected API") } }
有关刷新令牌、阻止列表等的信息,请访问:_examples/auth/jwt。
测试
Iris为httpexpect提供了令人难以置信的支持,httpexpect是Web应用程序的测试框架。子包为 Iris + httpexpect 提供了帮助程序。iris/httptest
如果你更喜欢Go的标准net/http/httptest包,你仍然可以使用它。Iris与每个http Web框架都与任何用于测试的外部工具兼容,最后它是HTTP。
测试基本身份验证
在第一个示例中,我们将使用子包来测试基本身份验证。iris/httptest
1. 源文件如下所示:main.go
package main import ( "github.com/kataras/iris/v12" "github.com/kataras/iris/v12/middleware/basicauth" ) func newApp() *iris.Application { app := iris.New() opts := basicauth.Options{ Allow: basicauth.AllowUsers(map[string]string{"myusername": "mypassword"}), } authentication := basicauth.New(opts) // or just: basicauth.Default(map...) app.Get("/", func(ctx iris.Context) { ctx.Redirect("/admin") }) // to party needAuth := app.Party("/admin", authentication) { //http://localhost:8080/admin needAuth.Get("/", h) // http://localhost:8080/admin/profile needAuth.Get("/profile", h) // http://localhost:8080/admin/settings needAuth.Get("/settings", h) } return app } func h(ctx iris.Context) { // username, password, _ := ctx.Request().BasicAuth() // third parameter it will be always true because the middleware // makes sure for that, otherwise this handler will not be executed. // OR: user := ctx.User().(*iris.SimpleUser) ctx.Writef("%s %s:%s", ctx.Path(), user.Username, user.Password) // ctx.Writef("%s %s:%s", ctx.Path(), username, password) } func main() { app := newApp() app.Listen(":8080") }
**2.**现在,创建一个文件并复制粘贴以下内容。main_test.go
package main import ( "testing" "github.com/kataras/iris/v12/httptest" ) func TestNewApp(t *testing.T) { app := newApp() e := httptest.New(t, app) // redirects to /admin without basic auth e.GET("/").Expect().Status(httptest.StatusUnauthorized) // without basic auth e.GET("/admin").Expect().Status(httptest.StatusUnauthorized) // with valid basic auth e.GET("/admin").WithBasicAuth("myusername", "mypassword").Expect(). Status(httptest.StatusOK).Body().Equal("/admin myusername:mypassword") e.GET("/admin/profile").WithBasicAuth("myusername", "mypassword").Expect(). Status(httptest.StatusOK).Body().Equal("/admin/profile myusername:mypassword") e.GET("/admin/settings").WithBasicAuth("myusername", "mypassword").Expect(). Status(httptest.StatusOK).Body().Equal("/admin/settings myusername:mypassword") // with invalid basic auth e.GET("/admin/settings").WithBasicAuth("invalidusername", "invalidpassword"). Expect().Status(httptest.StatusUnauthorized) }
3. 打开命令行并执行:
$ go test -v
测试饼干
package main import ( "fmt" "testing" "github.com/kataras/iris/v12/httptest" ) func TestCookiesBasic(t *testing.T) { app := newApp() e := httptest.New(t, app, httptest.URL("http://example.com")) cookieName, cookieValue := "my_cookie_name", "my_cookie_value" // Test Set A Cookie. t1 := e.GET(fmt.Sprintf("/cookies/%s/%s", cookieName, cookieValue)). Expect().Status(httptest.StatusOK) // Validate cookie's existence, it should be available now. t1.Cookie(cookieName).Value().Equal(cookieValue) t1.Body().Contains(cookieValue) path := fmt.Sprintf("/cookies/%s", cookieName) // Test Retrieve A Cookie. t2 := e.GET(path).Expect().Status(httptest.StatusOK) t2.Body().Equal(cookieValue) // Test Remove A Cookie. t3 := e.DELETE(path).Expect().Status(httptest.StatusOK) t3.Body().Contains(cookieName) t4 := e.GET(path).Expect().Status(httptest.StatusOK) t4.Cookies().Empty() t4.Body().Empty() }
$ go test -v -run=TestCookiesBasic$
Iris Web 框架本身使用此包来测试自身。在_examples存储库目录中,您还可以找到一些有用的测试。有关更多信息,请查看并阅读 httpexpect 的文档。
地方化
介绍
本地化功能提供了一种检索各种语言字符串的便捷方法,使您可以在应用程序中轻松支持多种语言。语言字符串存储在目录中的文件中。在此目录中,应用程序支持的每种语言都应该有一个子目录:./locales
│ main.go └───locales ├───el-GR │ home.yml ├───en-US │ home.yml └───zh-CN home.yml
应用程序的默认语言是第一个注册语言。
app := iris.New() // First parameter: Glob filpath patern, // Second variadic parameter: Optional language tags, // the first one is the default/fallback one. app.I18n.Load("./locales/*/*", "en-US", "el-GR", "zh-CN")
或者,如果您通过以下方式加载所有语言:
app.I18n.Load("./locales/*/*") // Then set the default language using: app.I18n.SetDefault("en-US")
加载嵌入式区域设置
您可能希望在应用程序可执行文件中使用新的嵌入指令嵌入区域设置。
- 导入嵌入包;如果您不使用此包中的任何导出标识符,则可以使用 _ “embed” 进行空白导入。
import ( "embed" )
- 嵌入指令接受相对于包含 Go 源文件的目录的路径。我们可以嵌入多个文件,甚至是带有通配符的文件夹。这使用嵌入的变量。FS 类型,它实现一个简单的虚拟文件系统。
//go:embed embedded/locales/* var embeddedFS embed.FS
- 我们应该使用方法而不是方法。
Load``LoadFS
err := app.I18n.LoadFS(embeddedFS, "./embedded/locales/*/*.ini", "en-US", "el-GR") // OR to load all languages by filename: // app.I18n.LoadFS(embeddedFS, "./embedded/locales/*/*.ini") // Then set the default language using: // app.I18n.SetDefault("en-US")
定义翻译
语言环境文件可以以 YAML(推荐)、JSON、TOML 或 INI 形式编写。
每个文件都应包含密钥。键也可以有子键(我们称之为“部分”)。
每个键的值应具有形式或包含其翻译文本(或模板)或/及其复数键值。string``map
Iris i18n模块支持开箱即用的复数,见下文。
FMT 风格
hi: "Hi %s!"
ctx.Tr("Hi", "John") // Outputs: Hi John!
模板
hi: "Hi {{.Name}}!"
ctx.Tr("Hi", iris.Map{"Name": "John"}) // Outputs: Hi John!
多元化
Iris i18n 支持复数变量。要定义每个区域设置变量,您必须 定义键的新部分。Vars
变量可接受的键为:
one
"=x"
其中 x 是一个数字"<x"
other
format
例:
Vars: - Minutes: one: "minute" other: "minutes" - Houses: one: "house" other: "houses"
然后,每条消息都可以使用此变量,方法如下:
# Using variables in raw string YouLate: "You are %[1]d ${Minutes} late." # [x] is the argument position, # variables always have priority other fmt-style arguments, # that's why we see [1] for houses and [2] for the string argument. HouseCount: "%[2]s has %[1]d ${Houses}."
ctx.Tr("YouLate", 1) // Outputs: You are 1 minute late. ctx.Tr("YouLate", 10) // Outputs: You are 10 minutes late. ctx.Tr("HouseCount", 2, "John") // Outputs: John has 2 houses.
您可以根据给定的复数计数选择将显示的消息。
除了变量,每条消息也可以有其复数形式!
可接受的密钥:
zero
one
two
"=x"
"<x"
">x"
other
让我们创建一个简单的复数功能消息,它也可以使用我们在上面创建的 Minutes 变量。
FreeDay: "=3": "You have three days and %[2]d ${Minutes} off." # "FreeDay" 3, 15 one: "You have a day off." # "FreeDay", 1 other: "You have %[1]d free days." # "FreeDay", 5
ctx.Tr("FreeDay", 3, 15) // Outputs: You have three days and 15 minutes off. ctx.Tr("FreeDay", 1) // Outputs: You have a day off. ctx.Tr("FreeDay", 5) // Outputs: You have 5 free days.
让我们继续使用更高级的示例,使用模板文本 + 函数 + 复数 + 变量。
Vars: - Houses: one: "house" other: "houses" - Gender: "=1": "She" "=2": "He" VarTemplatePlural: one: "${Gender} is awesome!" other: "other (${Gender}) has %[3]d ${Houses}." "=5": "{{call .InlineJoin .Names}} are awesome."
const ( female = iota + 1 male ) ctx.Tr("VarTemplatePlural", iris.Map{ "PluralCount": 5, "Names": []string{"John", "Peter"}, "InlineJoin": func(arr []string) string { return strings.Join(arr, ", ") }, }) // Outputs: John, Peter are awesome ctx.Tr("VarTemplatePlural", 1, female) // Outputs: She is awesome! ctx.Tr("VarTemplatePlural", 2, female, 5) // Outputs: other (She) has 5 houses.
部分
如果密钥不是保留密钥(例如一、二…),则它充当子部分。这些部分由点字符 () 分隔。.
Welcome: Message: "Welcome {{.Name}}"
ctx.Tr("Welcome.Message", iris.Map{"Name": "John"}) // Outputs: Welcome John
确定当前区域设置
您可以使用该方法确定当前区域设置或检查区域设置是否为给定值:context.GetLocale
func(ctx iris.Context) { locale := ctx.GetLocale() // [...] }
区域设置界面如下所示。
// Locale is the interface which returns from a `Localizer.GetLocale` metod. // It serves the transltions based on "key" or format. See `GetMessage`. type Locale interface { // Index returns the current locale index from the languages list. Index() int // Tag returns the full language Tag attached tothis Locale, // it should be uniue across different Locales. Tag() *language.Tag // Language should return the exact languagecode of this `Locale` //that the user provided on `New` function. // // Same as `Tag().String()` but it's static. Language() string // GetMessage should return translated text based n the given "key". GetMessage(key string, args ...interface{}) string }
检索翻译
使用方法作为获取此请求的翻译文本的快捷方式。context.Tr
func(ctx iris.Context) { text := ctx.Tr("hi", "name") // [...] }
内部视图
func(ctx iris.Context) { err := ctx.View("index.html", iris.Map{ "tr": ctx.Tr, }) if err!=nil { ctx.HTML("<h3>%s</h3>", err.Error()) return } }
例
package main import ( "github.com/kataras/iris/v12" ) func newApp() *iris.Application { app := iris.New() // Configure i18n. // First parameter: Glob filpath patern, // Second variadic parameter: Optional language tags, the first one is the default/fallback one. app.I18n.Load("./locales/*/*.ini", "en-US", "el-GR", "zh-CN") // app.I18n.LoadAssets for go-bindata. // Default values: // app.I18n.URLParameter = "lang" // app.I18n.Subdomain = true // // Set to false to disallow path (local) redirects, // see https://github.com/kataras/iris/issues/1369. // app.I18n.PathRedirect = true app.Get("/", func(ctx iris.Context) { hi := ctx.Tr("hi", "iris") locale := ctx.GetLocale() ctx.Writef("From the language %s translated output: %s", locale.Language(), hi) }) app.Get("/some-path", func(ctx iris.Context) { ctx.Writef("%s", ctx.Tr("hi", "iris")) }) app.Get("/other", func(ctx iris.Context) { language := ctx.GetLocale().Language() fromFirstFileValue := ctx.Tr("key1") fromSecondFileValue := ctx.Tr("key2") ctx.Writef("From the language: %s, translated output:\n%s=%s\n%s=%s", language, "key1", fromFirstFileValue, "key2", fromSecondFileValue) }) // using in inside your views: view := iris.HTML("./views", ".html") app.RegisterView(view) app.Get("/templates", func(ctx iris.Context) { err := ctx.View("index.html", iris.Map{ "tr": ctx.Tr, // word, arguments... {call .tr "hi" "iris"}} }) if err != nil { ctx.HTML("<h3>%s</h3>", err.Error()) return } // Note that, // Iris automatically adds a "tr" global template function as well, // the only difference is the way you call it inside your templates and // that it accepts a language code as its first argument. }) // return app } func main() { app := newApp() // go to http://localhost:8080/el-gr/some-path // ^ (by path prefix) // // or http://el.mydomain.com8080/some-path // ^ (by subdomain - test locally with the hosts file) // // or http://localhost:8080/zh-CN/templates // ^ (by path prefix with uppercase) // // or http://localhost:8080/some-path?lang=el-GR // ^ (by url parameter) // // or http://localhost:8080 (default is en-US) // or http://localhost:8080/?lang=zh-CN // // go to http://localhost:8080/other?lang=el-GR // or http://localhost:8080/other (default is en-US) // or http://localhost:8080/other?lang=en-US // // or use cookies to set the language. app.Listen(":8080", iris.WithSitemap("http://localhost:8080")) }
网站地图
站点地图翻译会自动设置为每个路由,如果为 true,则按路径前缀设置,如果为 true,则按子域自动设置,如果不为空,则按 URL 查询参数自动设置。app.I18n.PathRedirect``app.I18n.Subdomain``app.I18n.URLParameter
阅读更多: https://support.google.com/webmasters/answer/189077?hl=en
GET http://localhost:8080/sitemap.xml
<?xml version="1.0" encoding="utf-8" standalone="yes"?> <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml"> <url> <loc>http://localhost:8080/</loc> <xhtml:link rel="alternate" hreflang="en-US" href="http://localhost:8080/"></xhtml:link> <xhtml:link rel="alternate" hreflang="el-GR" href="http://localhost:8080/el-GR/"></xhtml:link> <xhtml:link rel="alternate" hreflang="zh-CN" href="http://localhost:8080/zh-CN/"></xhtml:link> </url> <url> <loc>http://localhost:8080/some-path</loc> <xhtml:link rel="alternate" hreflang="en-US" href="http://localhost:8080/some-path"></xhtml:link> <xhtml:link rel="alternate" hreflang="el-GR" href="http://localhost:8080/el-GR/some-path"></xhtml:link> <xhtml:link rel="alternate" hreflang="zh-CN" href="http://localhost:8080/zh-CN/some-path"></xhtml:link> </url> <url> <loc>http://localhost:8080/other</loc> <xhtml:link rel="alternate" hreflang="en-US" href="http://localhost:8080/other"></xhtml:link> <xhtml:link rel="alternate" hreflang="el-GR" href="http://localhost:8080/el-GR/other"></xhtml:link> <xhtml:link rel="alternate" hreflang="zh-CN" href="http://localhost:8080/zh-CN/other"></xhtml:link> </url> <url> <loc>http://localhost:8080/templates</loc> <xhtml:link rel="alternate" hreflang="en-US" href="http://localhost:8080/templates"></xhtml:link> <xhtml:link rel="alternate" hreflang="el-GR" href="http://localhost:8080/el-GR/templates"></xhtml:link> <xhtml:link rel="alternate" hreflang="zh-CN" href="http://localhost:8080/zh-CN/templates"></xhtml:link> </url> </urlset>
这就是关于虹膜的所有基础知识。本文档涵盖了足够的初学者。想成为专家和认证鸢尾花开发人员,了解 MVC、i18n、依赖注入、gRPC、lambda 函数、websockets、最佳实践等吗?立即申请虹膜电子书,参与虹膜的开发!