Go 框架 iris 文档(二)

简介: Go 框架 iris 文档(二)

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,即 使用嵌入资源、布局和特定于参与方的布局、模板功能、部分渲染等进行解析。

# 名字 解析 器
1 .HTML 网页/模板
2 卡塔拉斯/方块
3 姜戈 弗洛施/蓬戈2
4 帕格 小丑/翡翠
5 车把 艾默里克/雷蒙德
6 琥珀 eknkc/琥珀色
7 噴氣機 云套件/喷气机
8 高手 约西/艾斯

示例列表

基准列表

视图引擎可以按缔约方注册。要注册视图引擎,请使用如下所示的方法。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")

加载嵌入式区域设置

您可能希望在应用程序可执行文件中使用新的嵌入指令嵌入区域设置。

  1. 导入嵌入包;如果您不使用此包中的任何导出标识符,则可以使用 _ “embed” 进行空白导入。
import (
    "embed"
)
  1. 嵌入指令接受相对于包含 Go 源文件的目录的路径。我们可以嵌入多个文件,甚至是带有通配符的文件夹。这使用嵌入的变量。FS 类型,它实现一个简单的虚拟文件系统。
//go:embed embedded/locales/*
var embeddedFS embed.FS
  1. 我们应该使用方法而不是方法。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、最佳实践等吗?立即申请虹膜电子书,参与虹膜的开发!


目录
相关文章
|
18天前
|
开发框架 Go 计算机视觉
纯Go语言开发人脸检测、瞳孔/眼睛定位与面部特征检测插件-助力GoFly快速开发框架
开发纯go插件的原因是因为目前 Go 生态系统中几乎所有现有的人脸检测解决方案都是纯粹绑定到一些 C/C++ 库,如 OpenCV 或 dlib,但通过 cgo 调用 C 程序会引入巨大的延迟,并在性能方面产生显著的权衡。此外,在许多情况下,在各种平台上安装 OpenCV 是很麻烦的。使用纯Go开发的插件不仅在开发时方便,在项目部署和项目维护也能省很多时间精力。
|
27天前
|
Go API 数据库
Go 语言中常用的 ORM 框架,如 GORM、XORM 和 BeeORM,分析了它们的特点、优势及不足,并从功能特性、性能表现、易用性和社区活跃度等方面进行了比较,旨在帮助开发者根据项目需求选择合适的 ORM 框架。
本文介绍了 Go 语言中常用的 ORM 框架,如 GORM、XORM 和 BeeORM,分析了它们的特点、优势及不足,并从功能特性、性能表现、易用性和社区活跃度等方面进行了比较,旨在帮助开发者根据项目需求选择合适的 ORM 框架。
74 4
|
27天前
|
中间件 Go API
Go语言中几种流行的Web框架,如Beego、Gin和Echo,分析了它们的特点、性能及适用场景,并讨论了如何根据项目需求、性能要求、团队经验和社区支持等因素选择最合适的框架
本文概述了Go语言中几种流行的Web框架,如Beego、Gin和Echo,分析了它们的特点、性能及适用场景,并讨论了如何根据项目需求、性能要求、团队经验和社区支持等因素选择最合适的框架。
70 1
|
3月前
|
JSON Go API
使用Go语言和Gin框架构建RESTful API:GET与POST请求示例
使用Go语言和Gin框架构建RESTful API:GET与POST请求示例
|
4月前
|
Cloud Native JavaScript API
一文读懂云原生 go-zero 微服务框架
一文读懂云原生 go-zero 微服务框架
|
3月前
|
消息中间件 NoSQL Go
PHP转Go系列 | ThinkPHP与Gin框架之Redis延时消息队列技术实践
【9月更文挑战第7天】在从 PHP 的 ThinkPHP 框架迁移到 Go 的 Gin 框架时,涉及 Redis 延时消息队列的技术实践主要包括:理解延时消息队列概念,其能在特定时间处理消息,适用于定时任务等场景;在 ThinkPHP 中使用 Redis 实现延时队列;在 Gin 中结合 Go 的 Redis 客户端库实现类似功能;Go 具有更高性能和简洁性,适合处理大量消息。迁移过程中需考虑业务需求及系统稳定性。
|
4月前
|
SQL JavaScript Go
Go Web 服务框架实现详解
Go Web 服务框架实现详解
|
15天前
|
存储 Go 索引
go语言中数组和切片
go语言中数组和切片
26 7
|
15天前
|
Go 开发工具
百炼-千问模型通过openai接口构建assistant 等 go语言
由于阿里百炼平台通义千问大模型没有完善的go语言兼容openapi示例,并且官方答复assistant是不兼容openapi sdk的。 实际使用中发现是能够支持的,所以自己写了一个demo test示例,给大家做一个参考。