原创

go:iris使用

温馨提示:
本文最后更新于 2023年06月27日,已超过 378 天没有更新。若文章内的图片失效(无法正常加载),请留言反馈或直接联系我

安装

go get github.com/kataras/iris/v12@latest

或编辑你项目的 go.mod 文件。

module your_project_name

go 1.13

require (
    github.com/kataras/iris/v12 v12.1.3
)

开始

Iris 具有用于路由的表达语法,感觉就像在家一样(译者:原文 which feels like home,大概意思是如同在家里一样舒服)。路由算法由 muxie 项目 提供支持,和 httprouter 和 gin 或 echo 等替代方案相比,该算法能够更快地处理请求和匹配路由。

让我们心无旁骛的开始。

创建一个空文件,假设其名称为 main.go,然后将其打开并复制粘贴以下代码到其中。

package main
import "github.com/kataras/iris/v12"

func main() {
    app := iris.Default()
    app.Use(myMiddleware)

    app.Handle("GET", "/ping", func(ctx iris.Context) {
        ctx.JSON(iris.Map{"message": "pong"})
    })

	// 侦听并服务于传入的 http 请求
	// http://localhost:8080
    app.Run(iris.Addr(":8080"))
}

func myMiddleware(ctx iris.Context) {
    ctx.Application().Logger().Infof("Runs before %s", ctx.Path())
    ctx.Next()
}

启动终端 session 并执行以下操作。

# 运行 example.go 并且在浏览器上访问 http://localhost:8080/ping 
go run example.go

带 TLS 的自动公共地址

在更加 "真实的环境" (例如公共,远程地址而不是本地主机)中测试Web应用程序服务器是否很棒?

有很多提供这种功能的第三方工具,但是我认为ngrok是其中最好的一种。它像Iris一样受欢迎并经过多年的测试。

iris 提供 ngrok 的集成。此功能简单但功能强大。当你想在远程会议上向同事或项目负责人快速展示开发进度时,这确实很有帮助。

请按照以下步骤临时将你的本地 Iris Web 服务器转换为公共服务器。

继续 下载 ngrok,将其添加到$ PATH环境变量中,
简单的传递 WithTunneling 配置到你的 app.Run 方法中,
你就已经准备好开始了!

  • ctx.Application().ConfigurationReadOnly().GetVHost() 方法返回公共域值。虽然很少用,但是功能还是在的。 大多数情况下,你使用相对网址路径而不是绝对网址(或者必须使用).
  • ngrok 是否已在运行都无关紧要, Iris 框架聪明到可以使用 ngrok 的 web API 去创建一个隧道。
    完整的 Tunneling 配置:
app.Run(iris.Addr(":8080"), iris.WithConfiguration(
	iris.Configuration{
		Tunneling: iris.TunnelingConfiguration{
			AuthToken:    "my-ngrok-auth-client-token",
			Bin:          "/bin/path/for/ngrok",
			Region:       "eu",
			WebInterface: "127.0.0.1:4040",
			Tunnels: []iris.Tunnel{
				{
					Name: "MyApp",
					Addr: ":8080",
				},
			},
		},
}))

配置

在 前面的章节我们学习了 app.Run 方法的第一个参数,我们将要学习它的第二个参数。

让我们从基础开始。iris.New 函数返回 iris.Application。应用程序的值可以通过它的 Configure(...iris.Configurator) 函数 和 Run方法来配置应用。

app.Run 方法的第二个可选参数接受一个或者多个 iris.Configurator。一个 iris.Configurator 只是一个 func(app *iris.Application)类型。也可以传递自定义iris.Configurator 来修改你的 *iris.Application 。

每个核心的配置的字段都有内置的 iris.Configurator, 例如 iris.WithoutStartupLog, iris.WithCharset("UTF-8")iris.WithOptimizations 和 iris.WithConfiguration(iris.Configuration{...}) 方法。

每个 “模块” ,例如 iris 的视图引擎,websockets,sessions 和每个中间件都有自己的配置和选项,其中大多数与核心配置分开。

配置使用

所有 iris.Configuration 字段都默认是最常见的用例 。 Iris 在 app.Run 方法之前不需要任何配置, 但是如果要在服务器运行之前使用自定义的 iris.Configurator ,则可以使用 app.Configure 方法,以在那里传递配置。

config := iris.WithConfiguration(iris.Configuration {
  DisableStartupLog: true,
  Optimizations: true,
  Charset: "UTF-8",
})

app.Run(iris.Addr(":8080"), config)

从 YAML 中加载配置

使用iris.YAML("path")

文件: iris.yaml

FireMethodNotAllowed: true
DisableBodyConsumptionOnUnmarshal: true
TimeFormat: Mon, 01 Jan 2006 15:04:05 GMT
Charset: UTF-8

文件: main.go

config := iris.WithConfiguration(iris.YAML("./iris.yaml"))
app.Run(iris.Addr(":8080"), config)

从 TOML 中加载配置

使用 iris.TOML("path")

文件: iris.tml

FireMethodNotAllowed = true
DisableBodyConsumptionOnUnmarshal = false
TimeFormat = "Mon, 01 Jan 2006 15:04:05 GMT"
Charset = "UTF-8"
[Other]
    ServerName = "my fancy iris server"
    ServerOwner = "admin@example.com"

文件: main.go

config := iris.WithConfiguration(iris.TOML("./iris.tml"))
app.Run(iris.Addr(":8080"), config)

使用方法方式

正如我们已经提到的,在 app.Run 的第二个参数中,你可以传递任意数量的 iris.Configurator 。 Iris 为每个 iris.Configuration 的字段提供了相应选项。

app.Run(iris.Addr(":8080"), iris.WithoutInterruptHandler,
    iris.WithoutServerError(iris.ErrServerClosed),
    iris.WithoutBodyConsumptionOnUnmarshal,
    iris.WithoutAutoFireStatusCode,
    iris.WithOptimizations,
    iris.WithTimeFormat("Mon, 01 Jan 2006 15:04:05 GMT"),
)

当你想更改某些配置字段时,该选项非常好用。前缀: "With" 或者 "Without",代码编辑器可以毫无阻碍的帮助你浏览所有配置选项。

自定义值

iris.Configuration 包含一个名为 Other map[string]interface{} 的字段, 该字段接受任何自定义的 key:value 选项,因此你可以使用该字段来根据自定义要求传递你的应用期望的特定值。

app.Run(iris.Addr(":8080"), 
    iris.WithOtherValue("ServerName", "my amazing iris server"),
    iris.WithOtherValue("ServerOwner", "admin@example.com"),
)

可以通过 app.ConfigurationReadOnly 访问这些字段。

serverName := app.ConfigurationReadOnly().Other["MyServerName"]
serverOwner := app.ConfigurationReadOnly().Other["ServerOwner"]

从 Context 访问配置

在处理程序中,使用以下命令检索这些字段:

ctx.Application().ConfigurationReadOnly()

路由

处理程序类型

处理程序, 顾名思义,处理请求。

一个处理程序响应一个 HTTP 请求。它将回复标头和数据写入 Context.ResponseWriter()函数,然后返回它们。返回信号表明请求已完成; 在 Handler 调用完成之后或与之同时使用 Context 是无效的。

根据 HTTP 客户端软件,HTTP 协议版本,及客户端和 iris 服务之间的任何中介结构, 可能无法在写入 Context.Request().Body 之后从 Context.ResponseWriter() 函数中读取内容。谨慎的处理程序,应首先读取 Context.Request().Body,然后进行答复它们。

除了读取正文,处理程序不应修改提供的 Context

如果 Handler 出现紧急情况,则服务器(Handler的调用方)将假定紧急情况的影响与活动请求无关。它恢复了紧急状态,将堆栈跟踪记录到服务器错误日志中并中断了连接。

type Handler func(iris.Context)

注册处理程序后,我们可以使用返回的 路由 实例为该实例命名处理程序注册,以便于调试或匹配视图中的相对路径。 

行为

Iris 的默认行为是接受和注册像 /api/user 这类路径的路由,而且路径末端不带斜杠。如果客户端尝试访问 $your_host/api/user/ ,则 Iris 路由器将自动将其重定向到 $your_host/api/user,以便由注册路由处理它。这是设计 API 的现代方法。

但是,假如你想要对请求的资源 禁用路径校正, 你可以传递 iris 配置的 iris.WithoutPathCorrection 选项到 app.Run 函数。 例如:

// [app := iris.New...]
// [...]

app.Run(iris.Addr(":8080"), iris.WithoutPathCorrection)

如果想为/api/user 和 /api/user/路径,保持相同的处理程序和路由而无需重定向(常见情况) ,那么仅仅使用 iris.WithoutPathCorrectionRedirection 选项即可:

app.Run(iris.Addr(":8080"), iris.WithoutPathCorrectionRedirection)

API

所有的 HTTP 方法都是被支持的,开发者也可以在相同的路径上注册不同的方法。

第一个参数是 HTTP 方法,第二个参数是路由的请求路径,第三个可变参数应包含一个或多个 iris.Handler,当客户端从客户端请求该特定资源路径时,这些 iris.Handler 将按照注册顺序执行。

示例代码:

app := iris.New()

app.Handle("GET", "/contact", func(ctx iris.Context) {
    ctx.HTML("<h1> Hello from /contact </h1>")
})

为了方便开发者, iris 为所有 HTTP 方法提供了辅助方法。 第一个参数是路由的请求路径,第二个参数应包含一个或多个 iris.Handler,当用户从服务器请求该特定资源路径时,这些 iris.Handler 将会按照注册顺序执行。

示例代码:

app := iris.New()

// 方法: "GET"
app.Get("/", handler)

// 方法: "POST"
app.Post("/", handler)

// 方法: "PUT"
app.Put("/", handler)

// 方法: "DELETE"
app.Delete("/", handler)

// 方法: "OPTIONS"
app.Options("/", handler)

// 方法: "TRACE"
app.Trace("/", handler)

// 方法: "CONNECT"
app.Connect("/", handler)

// 方法: "HEAD"
app.Head("/", handler)

// 方法: "PATCH"
app.Patch("/", handler)

// 注册支持所有 HTTP 方法的路由
app.Any("/", handler)

func handler(ctx iris.Context){
    ctx.Writef("Hello from method: %s and path: %s\n", ctx.Method(), ctx.Path())
}

离线路线

在 Iris 中有一种特别的方法。 它被称为 None 并且可以用它对外部隐藏一个路由, 但是你却可以通过 Context.Exec 方法从其他路由处理程序中调用它。每个 API 处理方法都会返回路由的 值。路由的 IsOnline 方法报告该路由的当前状态。你可以通过路由 Route.Method 字段的值,将路由的状态离线改变在线。 当然,在服务时路由器的每次更改都需要一个app.RefreshRouter() 调用,该调用可以安全使用。下面看一个更完整的示例:

// 文件: main.go
package main

import (
    "github.com/kataras/iris/v12"
)

func main() {
    app := iris.New()

    none := app.None("/invisible/{username}", func(ctx iris.Context) {
        ctx.Writef("Hello %s with method: %s", ctx.Params().Get("username"), ctx.Method())

        if from := ctx.Values().GetString("from"); from != "" {
            ctx.Writef("\nI see that you're coming from %s", from)
        }
    })

    app.Get("/change", func(ctx iris.Context) {

        if none.IsOnline() {
            none.Method = iris.MethodNone
        } else {
            none.Method = iris.MethodGet
        }

        //刷新服务中重建的路由器,以便
        // 收到新路线通知。
        app.RefreshRouter()
    })

    app.Get("/execute", func(ctx iris.Context) {
        if !none.IsOnline() {
            ctx.Values().Set("from", "/execute with offline access")
            ctx.Exec("NONE", "/invisible/iris")
            return
        }

        // 与导航到 "http://localhost:8080/invisible/iris" 相同
        // 当 /change 被调用并且路由状态从
        // "离线" 改变为 "在线"
        ctx.Values().Set("from", "/execute")
        // 值和 session 可以被共享,
        // 当调用 Exec 从一个"外部"的 Context。
        // 	ctx.Exec("NONE", "/invisible/iris")
        // 或者在 "/change" 之后:
        ctx.Exec("GET", "/invisible/iris")
    })

    app.Run(iris.Addr(":8080"))
}

如何运行

  1. go run main.go
  2. 打开一个位于 http://localhost:8080/invisible/iris 的浏览器, 然后你会获得一个 404 not found 错误,
  3. 但是 http://localhost:8080/execute 将能够执行该路由。
  4. 现在,如果导航到 http://localhost:8080/change 并刷新 /invisible/iris选项卡 ,你将会看到它。

路由分组

被路径前缀分组的一组路由可以(可选)共享相同的中间件处理程序和模板布局。 一个组同时也可以有一个嵌套组。

.Party 用于对路由进行分组,开发人员可以声明无限数量的(嵌套)组。

示例代码:

app := iris.New()

users := app.Party("/users", myAuthMiddlewareHandler)

// http://localhost:8080/users/42/profile
users.Get("/{id:uint64}/profile", userProfileHandler)
// http://localhost:8080/users/messages/1
users.Get("/messages/{id:uint64}", userMessageHandler)

也可以使用 PartyFunc 函数编写相同的方法,该方法接受子路由器(当前分组的子路由器)。

app := iris.New()

app.PartyFunc("/users", func(users iris.Party) {
    users.Use(myAuthMiddlewareHandler)

    // http://localhost:8080/users/42/profile
    users.Get("/{id:uint64}/profile", userProfileHandler)
    // http://localhost:8080/users/messages/1
    users.Get("/messages/{id:uint64}", userMessageHandler)
})

路径参数

和你以前见到的路由器不同,Iris 的路由器可以处理不同种类的路由而不会发生冲突。

仅仅匹配 GET "/" 路径。

app.Get("/", indexHandler)

匹配所有包含 "/assets/**/*" 前缀的 GET 请求, 它是一个 ctx.Params().Get("asset") 的通配符,等同于任何跟随在 /assets/之后的路径。

app.Get("/assets/{asset:path}", assetsWildcardHandler)

匹配所有以 "/profile/" 为前缀的 GET 请求,后跟随单个路径部分。

app.Get("/profile/{username:string}", userHandler)

仅匹配 "/profile/me" GET 请求并且不与 /profile/{username:string} 请求或者任何root 通配符 /{root:path} 请求相冲突。

app.Get("/profile/me", userHandler)

匹配所有以 /users/ 为前缀的GET请求,后跟一个等于或大于 1 的数字。

app.Get("/user/{userid:int min(1)}", getUserHandler)

匹配所有以 users为前缀的 DELETE 请求,后跟一个等于或大于 1 的数字。

app.Delete("/user/{userid:int min(1)}", deleteUserHandler)

匹配除其他路由已处理的请求之外的所有 GET 请求。例如,在这个案例中,前面已经注册的路由; /, /assets/{asset:path}/profile/{username}, "/profile/me"/user/{userid:int ...}。它与其余路由(!)不冲突。

app.Get("{root:path}", rootWildcardHandler)

匹配以下所有 GET 请求:

  1. /u/abcd 映射到 :alphabetical (如果 :alphabetical 已经被注册,则使用 :string)
  2. /u/42 映射到 :uint (if :uint 已经被注册,则使用 :int)
  3. /u/-1 映射到 :int (if :int 已经被注册,则使用 :string)
  4. /u/abcd123 映射到 :string
app.Get("/u/{username:string}", func(ctx iris.Context) {
	ctx.Writef("username (string): %s", ctx.Params().Get("username"))
})

app.Get("/u/{id:int}", func(ctx iris.Context) {
	ctx.Writef("id (int): %d", ctx.Params().GetIntDefault("id", 0))
})

app.Get("/u/{uid:uint}", func(ctx iris.Context) {
	ctx.Writef("uid (uint): %d", ctx.Params().GetUintDefault("uid", 0))
})

app.Get("/u/{firstname:alphabetical}", func(ctx iris.Context) {
	ctx.Writef("firstname (alphabetical): %s", ctx.Params().Get("firstname"))
})

分别匹配 /abctenchars.xml 和 /abcdtenchars 的所有 GET 请求。

app.Get("/{alias:string regexp(^[a-z0-9]{1,10}\\.xml$)}", PanoXML)
app.Get("/{alias:string regexp(^[a-z0-9]{1,10}$)}", Tour)

HTTP方法覆盖

在开发和推动 REST API 时,使用特定的自定义 HTTP 标头(如 X-HTTP 方法重写)会非常方便。部署基于 REST API 的 Web 服务时,可能会遇到 [服务器] 和 [客户端] 一方的访问限制。

一些防火墙不支持 PUT, DELETE 或者 PATCH 请求方法。


方法重写包装器允许你在客户端不支持它的地方使用 HTTP 谓词,如 PUT 或 DELETE。

服务器

package main
import (
    "github.com/kataras/iris/v12"
    "github.com/kataras/iris/v12/middleware/methodoverride"
)

func main() {
    app := iris.New() 

    mo := methodoverride.New( 
        // 默认值为 nil. 
        // 
        methodoverride.SaveOriginalMethod("_originalMethod"), 
        // 默认值. 
        // 
        // methodoverride.Methods(http.MethodPost), 
        // methodoverride.Headers("X-HTTP-Method",
        //                        "X-HTTP-Method-Override",
        //                        "X-Method-Override"), 
        // methodoverride.FormField("_method"), 
        // methodoverride.Query("_method"), 
    ) 
    // 注册 `WrapRouter`
    app.WrapRouter(mo)

    app.Post("/path", func(ctx iris.Context) {
        ctx.WriteString("post response")
    })

    app.Delete("/path", func(ctx iris.Context) {
        ctx.WriteString("delete response")
    })

    // [...app.Run]
}

客户端

fetch("/path", {
    method: 'POST',
    headers: {
      "X-HTTP-Method": "DELETE"
    },
  })
  .then((resp)=>{
      // 响应正文将为"删除响应"。
 })).catch((err)=> { console.error(err) })

请求身份验证

Iris 通过它的 jwt 中间件提供请求身份验证。 在本章中,将学习如何将 JWT 与 Iris 结合使用的基础知识。

1.通过执行以下 shell 命令来安装它:

go get github.com/iris-contrib/middleware/jwt

2.要创建新的, 请使用 jwt.New 函数。 本示例通过 "token" url 参数提取令牌。经过身份验证的客户端应设计为使用签名令牌进行设置。缺省的 jwt 中间件提取令牌值的行为是通过 Authentication: Bearer $TOKEN 标头

jwt 中间件具有三种验证令牌的方法。

  • 第一个是 Serve 方法 - 它是一个 iris.Handler
  • 第二个是 CheckJWT(iris.Context) bool 和
  • 第三个是检索已验证令牌的助手 - Get(iris.Context) *jwt.Token
3.注册它, 你只需在jwt j.Serve 中间件之前添加特定的路由组,单个路由或全局路由即可。app.Get("/secured", j.Serve, myAuthenticatedHandler)
4.在处理程序内生成令牌,该处理程序接受用户有效负载并响应已签名的令牌, 此后稍后可以通过客户端请求标头或url参数发送该令牌,例如 jwt.NewToken 或 jwt.NewTokenWithClaims

示例

import (
    "github.com/kataras/iris/v12"
    "github.com/iris-contrib/middleware/jwt"
)

func getTokenHandler(ctx iris.Context) {
    token := jwt.NewTokenWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
        "foo": "bar",
    })

    // 使用密码签名并获取完整的编码令牌作为字符串
    tokenString, _ := token.SignedString([]byte("My Secret"))

    ctx.HTML(`Token: ` + tokenString + `<br/><br/>
    <a href="/secured?token=` + tokenString + `">/secured?token=` + tokenString + `</a>`)
}

func myAuthenticatedHandler(ctx iris.Context) {
    user := ctx.Values().Get("jwt").(*jwt.Token)

    ctx.Writef("This is an authenticated request\n")
    ctx.Writef("Claim content:\n")

    foobar := user.Claims.(jwt.MapClaims)
    for key, value := range foobar {
        ctx.Writef("%s = %s", key, value)
    }
}

func main(){
    app := iris.New()

    j := jwt.New(jwt.Config{
        // 通过 "token" URL参数提取。
        Extractor: jwt.FromParameter("token"),

        ValidationKeyGetter: func(token *jwt.Token) (interface{}, error) {
            return []byte("My Secret"), nil
        },
        SigningMethod: jwt.SigningMethodHS256,
    })

    app.Get("/", getTokenHandler)
    app.Get("/secured", j.Serve, myAuthenticatedHandler)
    app.Run(iris.Addr(":8080"))
}
可以将任何有效负载用作 "声明"。

URL请求参数

Iris 的 Context 有两个方法可以返回 net/http 标准 http.ResponseWriter 和 http.Request 值, 正如我们在前面的章节中已经提到的那样。

  • Context.Request()
  • Context.ResponseWriter()

然而,除了 Iris Context 提供的唯一的 iris 功能和辅助方法之外,为了简化开发, 我们还提供了一些现有 net/http 功能的封装。

这些是当你使用 URL 查询字符串的时候,能够给你帮助的完整方法列表。

// URLParam 如果url参数存在,则返回true,否则返回false。
URLParamExists(name string) bool
// URLParamDefault 从请求中返回get参数,
//如果未找到,则返回 "def"。
URLParamDefault(name string, def string) string
// URLParam 从请求中返回 get 参数(如果有)。
URLParam(name string) string
// URLParamTrim 返回带有以下内容的 url 查询参数
// 并从请求中删除的尾随空格。
URLParamTrim(name string) string
// URLParamTrim(原文就是 URLParamTrim,总感觉应该是  URLParamEscape,不过关系不大。) 从请求中返回转义的url查询参数。
URLParamEscape(name string) string
// URLParamInt 以请求中的int值形式返回url查询参数,
// 返回-1,如果解析失败,则返回错误。
URLParamInt(name string) (int, error)
// URLParamIntDefault t将url查询参数作为请求中的int值返回,
// 如果未找到或解析失败,则返回"def" 。
URLParamIntDefault(name string, def int) int
// URLParamInt32Default 将 url 查询参数作为来自请求的 int32 值返回,
// 如果未找到或解析失败,则返回 "def" 。
URLParamInt32Default(name string, def int32) int32
// URLParamInt64 将 url 查询参数作为来自请求的 int64 值返回,
// 返回-1,如果解析失败,则返回错误。
URLParamInt64(name string) (int64, error)
// URLParamInt64Default 从请求中返回 url 查询参数作为 int64 值,
// 如果未找到或解析失败,则返回 "def" 。
URLParamInt64Default(name string, def int64) int64
// URLParamFloat64 从请求中返回 url 查询参数作为 float64 值,
// 返回-1,如果解析失败,则返回错误。
URLParamFloat64(name string) (float64, error)
// URLParamFloat64Default从请求中返回 url 查询参数作为 float64 值,
// 如果未找到或解析失败,则返回 "def" 。
URLParamFloat64Default(name string, def float64) float64
// URLParamBool 以请求中的布尔值返回 url 查询参数,
// 如果解析失败或未找到,则返回错误。
URLParamBool(name string) (bool, error)
// URLParams 返回一个GET查询参数的映射,如果有多个,则以逗号分隔
// 如果找不到任何内容,则返回一个空映射。
URLParams() map[string]string

使用现有的基础请求对象来解析查询字符串参数。 该请求响应的URL匹配: /welcome?firstname=Jane&lastname=Doe.

ctx.URLParam("lastname") == ctx.Request().URL.Query().Get("lastname")

示例代码:

app.Get("/welcome", func(ctx iris.Context) {
	firstname := ctx.URLParamDefault("firstname", "Guest")
	lastname := ctx.URLParam("lastname") 

	ctx.Writef("Hello %s %s", firstname, lastname)
})

表单

表单,提交的数据和上传的文件能够被检索到, 通过使用下面的 Context 的方法:

// FormValueDefault 按“名称”返回一个已解析的表单值,
// 包括URL字段的查询参数和 POST 或 PUT 表单数据。
//
// 如果找不到,则返回 "def" 。
FormValueDefault(name string, def string) string
// FormValue 按“名称”返回一个已解析的表单值,
// 包括URL字段的查询参数和 POST 或 PUT 表单数据。
FormValue(name string) string
// FormValues 返回已解析的表单数据, 包括 URL
// 字段的查询参数以及 POST 或 PUT 表单数据。
//
// 默认表单的内存最大大小为32MB, 可以通过传递 `iris.WithPostMaxMemory` 
// 配置到  `app.Run` 的第二个参数去修改默认值。
//
// 注意:必须检查 nil 。
FormValues() map[string][]string

// PostValueDefault 通过名称来返回解析自 POST, PATCH,
// 或者 PUT 主体参数的数据。
//
// 如果未找到,则返回 "def" 。
PostValueDefault(name string, def string) string
// PostValue 通过名称来返回解析自 POST, PATCH,
// 或者 PUT 主体参数的数据。
PostValue(name string) string
// PostValueTrim 通过名称来返回解析自 POST, PATCH,
// 或者 PUT 主体参数不带空格的数据。
PostValueTrim(name string) string
// PostValueInt 通过名称来返回解析自 POST, PATCH,
// 或者 PUT 主体参数的 int 数据。
//
// 如果未找到,则返回-1和非 nil 错误。
PostValueInt(name string) (int, error)
// PostValueIntDefault 通过名称来返回解析自 POST, PATCH,
// 或者 PUT 主体参数的 int 数据。
//
// 如果未找到,则返回 "def" 。
PostValueIntDefault(name string, def int) int
// PostValueInt64 通过名称来返回解析自 POST, PATCH,
// 或者 PUT 主体参数的 int64(原文是 float64) 数据。
//
// 如果未找到,则返回-1和非 nil 错误。
PostValueInt64(name string) (int64, error)
// PostValueInt64Default 通过名称来返回解析自 POST, PATCH,
// 或者 PUT 主体参数的 int64 数据。
//
// 如果未找到,则返回 "def" 。
PostValueInt64Default(name string, def int64) int64
//  PostValueFloat64 (原文是 PostValueInt64Default ) 通过名称来返回解析自 POST, PATCH,
// 或者 PUT 主体参数的 float64 数据。
//
//  如果未找到,则返回-1和非 nil 错误。
PostValueFloat64(name string) (float64, error)
// PostValueFloat64Default (原文是 PostValueInt64Default ) 通过名称来返回解析自 POST, PATCH,
// 或者 PUT 主体参数的 float64 数据。
//
// 如果未找到,则返回 "def" 。
PostValueFloat64Default(name string, def float64) float64
//  PostValueBool (原文是 PostValueInt64Default ) 通过名称来返回解析自 POST, PATCH,
// 或者 PUT 主体参数的 bool 数据。
//
// 如果未找到或 value 为 false,则返回 false,否则返回 true。
PostValueBool(name string) (bool, error)
// PostValues 通过名称来返回所有解析自  POST, PATCH,
// 或者 PUT 主体参数的 struct 数据。
//
// 默认表单的内存最大大小为32MB, 可以通过传递 `iris.WithPostMaxMemory` 
// 配置到  `app.Run` 的第二个参数去修改默认值。
PostValues(name string) []string
// FormFile 返回从客户端收到的第一个上载文件。
//
// 默认表单的内存最大大小为32MB, 可以通过传递 `iris.WithPostMaxMemory` 
// 配置到  `app.Run` 的第二个参数去修改默认值。

FormFile(key string) (multipart.File, *multipart.FileHeader, error)

多部分/Url 编码 表单

func main() {
    app := iris.Default()

    app.Post("/form_post", func(ctx iris.Context) {
        message := ctx.FormValue("message")
        nick := ctx.FormValueDefault("nick", "anonymous")

        ctx.JSON(iris.Map{
            "status":  "posted",
            "message": message,
            "nick":    nick,
        })
    })

    app.Run(iris.Addr(":8080"))
}

另一个示例: 查询 + 表单提交

POST /post?id=1234&page=1 HTTP/1.1
Content-Type: application/x-www-form-urlencoded
name=manu&message=this_is_great
func main() {
    app := iris.Default()

    app.Post("/post", func(ctx iris.Context) {
        id := ctx.URLParam("id")
        page := ctx.URLParamDefault("page", "0")
        name := ctx.FormValue("name")
        message := ctx.FormValue("message")
        // 或者 `ctx.PostValue` for POST, PUT & PATCH-only HTTP 方法。

        app.Logger().Infof("id: %s; page: %s; name: %s; message: %s",
            id, page, name, message)
    })

    app.Run(iris.Addr(":8080"))
}
id: 1234; page: 1; name: manu; message: this_is_great

文件上传

Iris 的 Context 提供了一个用于上传文件的辅助方法 (将文件从请求文件数据保存到主机系统的硬盘)。 阅读下面有关 Context.UploadFormFiles 方法的更多信息。

UploadFormFiles 从客户端将所有收到的文件上传到系统物理位置 "destDirectory"。

第二个可选参数 "before" 使调用者有机会在保存到磁盘之前修改 *miltipart.FileHeader ,它可以根据当前请求用于更改文件名, 所有 FileHeader的选项都可以被改变。如果在将文件保存到磁盘之前不需要使用此功能,则可以忽略它。

请注意,它不会检查请求主体是否流式传输

如果服务器未收到文件,如果是由于操作系统的权限或net/http.ErrMissingFile而无法创建至少一个新文件,则返回复制的长度为 int64,并返回一个不为零的错误。

如果要接收和接受文件并手动进行管理, 则可以改用Context.FormFile并创建适合你需要的复制功能,以下内容仅用于一般情况。

默认表单的内存最大大小为32MB, 可以通过传递 iris.WithPostMaxMemory
配置到 app.Run 的第二个参数去修改默认值。

UploadFormFiles(destDirectory string,
                before ...func(Context, *multipart.FileHeader)) (n int64, err error)

示例代码:

const maxSize = 5 << 20 // 5MB

func main() {
    app := iris.Default()
    app.Post("/upload", iris.LimitRequestBodySize(maxSize), func(ctx iris.Context) {
        //
        // UploadFormFiles
        // 上传任意数量的文件 (表单中的 "multiple" 属性)。
        //

        // 第二个可选参数
        // 可用于根据请求更改文件的名称,
        // 在此示例中,我们将展示如何使用它
        // 通过使用当前用户的ip作为上传文件的前缀。
        ctx.UploadFormFiles("./uploads", beforeSave)
    })

    app.Run(iris.Addr(":8080"))
}

func beforeSave(ctx iris.Context, file *multipart.FileHeader) {
    ip := ctx.RemoteAddr()
    // 确保你已经以某种方式格式化ip
    // 让人它可以用于命名文件 (简单案例):
    ip = strings.Replace(ip, ".", "_", -1)
    ip = strings.Replace(ip, ":", "_", -1)

    // 你也可以使用 time.Now 基于当前时间,
    // 为文件添加前缀或后缀,作为练习。
    // i.e unixTime :=	time.Now().Unix()
    // 在文件名前加上 $IP-
    // 无需执行其他操作,内部上传器
    // 将文件以此名称保存到 "./uploads" 文件夹中。
    file.Filename = ip + "-" + file.Filename
}

如何 curl:

curl -X POST http://localhost:8080/upload \
  -F "files[]=@./myfile.zip" \
  -F "files[]=@./mysecondfile.zip" \
  -H "Content-Type: multipart/form-data"

模型验证

Iris 不包含用于验证请求数据(例如“模型”)的内置方法。但是,你没有被限制。在此示例中,我们将学习如何使用 go-playground/validator.v9 进行请求值验证。

在 此处上查看有关标签用法的完整文档。

go get gopkg.in/go-playground/validator.v9

请注意,你需要在要绑定的所有字段上设置相应的绑定标签。例如,从JSON绑定时,设置 json:"fieldname"

package main

import (
    "fmt"

    "github.com/kataras/iris/v12"

    "gopkg.in/go-playground/validator.v9"
)

// User 包含用户信息。
type User struct {
    FirstName      string     `json:"fname"`
    LastName       string     `json:"lname"`
    Age            uint8      `json:"age" validate:"gte=0,lte=130"`
    Email          string     `json:"email" validate:"required,email"`
    FavouriteColor string     `json:"favColor" validate:"hexcolor|rgb|rgba"`
    Addresses      []*Address `json:"addresses" validate:"required,dive,required"`
}

// Address 包含用户的地址信息。
type Address struct {
    Street string `json:"street" validate:"required"`
    City   string `json:"city" validate:"required"`
    Planet string `json:"planet" validate:"required"`
    Phone  string `json:"phone" validate:"required"`
}

// 使用 Validate 的单个实例,它可以缓存结构信息。
var validate *validator.Validate

func main() {
    validate = validator.New()

    // 为 'User' 注册验证
    // 注意: 仅需为 'User' 注册一个非指针类型,
    // 验证者在进行类型检查时在内部取消引用。
    validate.RegisterStructValidation(UserStructLevelValidation, User{})

    app := iris.New()
    app.Post("/user", func(ctx iris.Context) {
        var user User
        if err := ctx.ReadJSON(&user); err != nil {
            // [handle error...]
        }

        // 对于无效的验证输入,返回 InvalidValidationError 
        // nil 或 ValidationErrors ( []FieldError )
        err := validate.Struct(user)
        if err != nil {

            // 仅仅当你的代码可以产生一个可以被验证器检查的值的时候,
            // 才需要检查。 例如一个为 nil 的接口,大多数人包括我自己都通常没有这样的代码。
            if _, ok := err.(*validator.InvalidValidationError); ok {
                ctx.StatusCode(iris.StatusInternalServerError)
                ctx.WriteString(err.Error())
                return
            }

            ctx.StatusCode(iris.StatusBadRequest)
            for _, err := range err.(validator.ValidationErrors) {
                fmt.Println()
                fmt.Println(err.Namespace())
                fmt.Println(err.Field())
                fmt.Println(err.StructNamespace())
                fmt.Println(err.StructField())
                fmt.Println(err.Tag())
                fmt.Println(err.ActualTag())
                fmt.Println(err.Kind())
                fmt.Println(err.Type())
                fmt.Println(err.Value())
                fmt.Println(err.Param())
                fmt.Println()
            }

            return
        }

        // [保存用户到数据库...]
    })

    app.Run(iris.Addr(":8080"))
}

func UserStructLevelValidation(sl validator.StructLevel) {
    user := sl.Current().Interface().(User)

    if len(user.FirstName) == 0 && len(user.LastName) == 0 {
        sl.ReportError(user.FirstName, "FirstName", "fname", "fnameorlname", "")
        sl.ReportError(user.LastName, "LastName", "lname", "fnameorlname", "")
    }
}

JSON形式的示例请求:

{
    "fname": "",
    "lname": "",
    "age": 45,
    "email": "mail@example.com",
    "favColor": "#000",
    "addresses": [{
        "street": "Eavesdown Docks",
        "planet": "Persphone",
        "phone": "none",
        "city": "Unknown"
    }]
}

可以在以下位置找到示例:https://github.com/kataras/iris/tree/master/_examples/http_request/read-json-struct-validation/main.go.

缓存

缓存是一种中间件,它为下一个处理程序提供服务器端缓存功能,可以想这样使用: app.Get("/", iris.Cache, aboutHandler)

Cache 接受一个参数:缓存过期时间。 如果这是一个无效的参数, <=2 秒, 这个参数讲被 "cache-control's maxage" 头控制。

可以缓存所有类型的响应,模板,json,文本等。

将其用于服务器端缓存,请参见 Cache304,以获取更适合你需要的替代方法。

func Cache(expiration time.Duration) Handler

有关更多选项和自定义,请使用 kataras/iris/cache.Cache ,它返回一个 可以在其中添加或删除 "规则"

NoCache

NoCache 是一种中间件,它覆盖 Cache-Control, Pragma and Expires 头,以便在浏览器的后退和前进功能期间禁用缓存。

在 HTM L路由上可以很好地使用此中间件。即使在浏览器的“后退”和“前进”箭头按钮上也刷新页面。

func NoCache(Context)

请参阅 StaticCache 以了解相反的行为。

StaticCache

StaticCache ,返回用于缓存静态文件的中间件,通过将 "Cache-Control" 和 "Expires" 头发送给客户端。它接受一个输入参数 "cacheDur",一个 time.Duration ,用于计算到期时间。

如果 "cacheDur" <=0 ,则它将返回 NoCache 中间件,以禁用浏览器的“后退”和“前进”动作之间的缓存。

使用方法: app.Use(iris.StaticCache(24 * time.Hour)) 或者app.Use(iris.StaticCache(-1))

func StaticCache(cacheDur time.Duration)Handler

中间件是一个简单的处理程序,也可以在另一个处理程序内调用,例如:

 cacheMiddleware := iris.StaticCache(...)

 func(ctx iris.Context){
  cacheMiddleware(ctx)
  [...]
}

Cache304

Cache304 返回一个中间件,每当 "If-Modified-Since" 请求头 (time) 是在 time.Now() + expiresEvery (始终与其 UTC 值进行比较)之前,发送一个 StatusNotModified (304) 。

与HTTP RCF兼容的客户端(所有浏览器均为,postman 等工具)将正确处理缓存。

使用该缓存而不是服务器端缓存的唯一缺点是此方法将发送304状态代码而不是200,因此,如果将它与其他微服务一起使用,则还必须检查该状态代码,以便有效的回应。

开发人员可以自由地扩展此方法的行为,方法是手动查看系统目录的更改,并根据文件修改日期将 ctx.WriteWithExpiration 与 "modtime" 一起使用,类似于 HandleDir(会发送OK(200)状态和浏览器磁盘缓存而不是304).

func Cache304(expiresEvery time.Duration) Handler

可以在以下网址找到示例:https://github.com/kataras/iris/tree/master/_examples/cache.

文件服务器

通过 Party.HandleDir 方法可以从特定目录(系统物理目录或嵌入式应用程序)中加载静态文件。

HandleDir 注册一个处理程序,该处理程序使用文件系统的内容(物理或嵌入式)为 HTTP 请求提供服务。

  • 第一个参数:路由
  • 第二个参数:需要提供服务的系统或嵌入式目录
  • 第三个参数:不是必需的,目录选项,设置字段是可选的。

返回 GET *Route。

HandleDir(requestPath, directory string, opts ...DirOptions) (getRoute *Route)

DirOptions 结构如下所示:

type DirOptions struct {
    // 默认为 "/index.html", 如果请求以 **/*/$IndexName 结尾
    // 当重定向到另一个处理程序正在处理的 **/*(/) 路由
    // 框架自动注册了另一个称为索引处理程序的处理程序
    // 如果最终开发人员无法手动/手动处理。
    IndexName string
    // 是否应在gzip压缩下提供文件?
    Gzip bool

    // 如果未找到 `IndexName`,列出当前请求目录中的文件 
    ShowList bool
    // 如果 `ShowList` 为 true , 则将使用此函数
    // 的默认值之一,以显示当前请求的目录(dir)的文件列表。
    DirList func(ctx iris.Context, dirName string, dir http.File) error

    // 当嵌入时。
    Asset      func(name string) ([]byte, error)   
    AssetInfo  func(name string) (os.FileInfo, error)
    AssetNames func() []string

    // 循环遍历每个找到的请求资源的可选验证器。
    AssetValidator func(ctx iris.Context, name string) bool
}

让我们假设你由一个可执行的 ./assets 文件夹 并且你想要通过http://localhost:8080/static/**/* 路由来访问这个文件夹。

app := iris.New()

app.HandleDir("/static", "./assets")

app.Run(iris.Addr(":8080"))

现在,如果你想将静态文件嵌入可执行文件内部以不依赖于系统目录,则可以使用 go-bindata 将文件转换为程序内的[]byte。让我们快速学习一下,以及 Iris 如何帮助提供这些数据。

安装 go-bindata:

go get -u github.com/go-bindata/go-bindata/...

进入你的程序目录,并确保里面存在的 ./ assets 子目录,并执行如下命令:

go-bindata ./assets/...

上面创建了一个生成的 go 文件,其中包含三个主要功能:AssetAssetInfo 和 AssetNames.。在 DirOptions上使用它们:


// [app := iris.New...]

app.HandleDir("/static", "./assets", iris.DirOptions {
    Asset: Asset,
    AssetInfo: AssetInfo,
    AssetNames: AssetNames,
    Gzip: false,
})

构建你的应用程序:

go build

HandleDir 支持物理目录和嵌入式目录的所有标准,包括内容范围。

但是,如果只需要一个处理程序来使用而无需注册路由,则可以改用 iris.FileServer 包级函数。

FileServer f函数返回一个处理程序,该处理程序为来自特定系统,物理目录,嵌入式目录的文件提供服务。

  • 第一个参数:是目录。
  • 第二个参数: 可选参数是任何可以被调用的可选设置
iris.FileServer(directory string, options ...DirOptions)

使用方法

handler := iris.FileServer("./assets", iris.DirOptions {
    ShowList: true, Gzip: true, IndexName: "index.html",
})

可以在以下位置找到示例:https://github.com/kataras/iris/tree/master/_examples/file-server

MVC

使用 Iris MVC 重构代码。

通过创建彼此独立的组件,开发人员可以在其他应用程序中快速轻松地重用组件。 可以将一个应用程序的相同(或相似)视图重构为具有不同数据的另一个应用程序,因为该视图只是处理数据向用户显示的方式。

Iris 拥有 对MVC(模型视图控制器)架构模式的一流支持, 在Go世界中其他任何地方都找不到这些东西。 你必须要引入 iris/mvc 子包。

import "github.com/kataras/iris/v12/mvc"

Iris Web 框架以最快的执行速度支持请求数据,模型,持久性数据和绑定。

如果你不熟悉 Web 后台开发, 请先阅读MVC架构模式,这是一个很好的开始。 wikipedia article.

特点

支持所有 HTTP 方法,例如, 如果要提供GET 服务, 则控制器应具有一个名为 Get()的函数,你可以在同一控制器中定义多个方法函数。

每个控制器可以通过 BeforeActivation 自定义回调事件,自定义控制器的 struct 的方法用作具有自定义路径(甚至带有regex参数化的路径)的处理程序。 例如:

import (
    "github.com/kataras/iris/v12"
    "github.com/kataras/iris/v12/mvc"
)

func main() {
    app := iris.New()
    mvc.Configure(app.Party("/root"), myMVC)
    app.Run(iris.Addr(":8080"))
}

func myMVC(app *mvc.Application) {
    // app.Register(...)
    // app.Router.Use/UseGlobal/Done(...)
    app.Handle(new(MyController))
}

type MyController struct {}

func (m *MyController) BeforeActivation(b mvc.BeforeActivation) {
    // b.Dependencies().Add/Remove
	// b.Router().Use/UseGlobal/Done
	// 和已知的任何标准 API 调用

    // 1-> 方法
    // 2-> 路径
    // 3-> 将控制器的函数名称解析为处理程序
    // 4-> 应该在 MyCustomHandler 之前运行的任何处理程序
    b.Handle("GET", "/something/{id:long}", "MyCustomHandler", anyMiddleware...)
}

// GET: http://localhost:8080/root
func (m *MyController) Get() string {
	return "Hey"
}

// GET: http://localhost:8080/root/something/{id:long}
func (m *MyController) MyCustomHandler(id int64) string {
	return "MyCustomHandler says Hey"
}

在你的控制器结构体中持久化数据(在请求之间共享数据),通过定义依赖的服务或者一个单列 控制器作用域。

共享控制器之间的依赖关系或在父 MVC 应用程序上注册它们, 并能够在Controller内部的BeforeActivation可选事件回调上修改每个控制器的依赖关系, 即, func(c *MyController) BeforeActivation(b mvc.BeforeActivation) { b.Dependencies().Add/Remove(...) }

一个控制器字段访问 Context(不需要手动绑定), 即 Ctx iris.Context 或者通过一个 方法的输入参数。 即 func(ctx iris.Context, otherArguments...)

控制器结构内部的模型(在Method函数中设置并由View呈现)。你可以从控制器的方法返回模型,或者在请求生命周期中设置一个字段,然后将该字段返回到同一请求生命周期中的另一个方法。

接下来如同你前面所知的一样, mvc 应用有它自己的 路由 ,它的路由是 iris/router.Party类型之一,同时也是标准的 iris api 。 控制器可以被注册到任何的 Party中,也包括子域, Party 将会如同期望的那样开始和执行处理程序的工作。

可选的 BeginRequest(ctx) 函数,将会执行任何初始化在方法执行前,可用于调用中间件或许多方法使用相同的数据集合时。

可选的 EndRequest(ctx) 函数, 将会执行任何终止操作在所有的方法执行之后。

继承,递归, 我 mvc session-controller example, 有 Session *sessions.Session 作为结构体字段, 它被会话管理的 Start 方法作为一个动态以来想填充到 MVC 应用: mvcApp.Register(sessions.New(sessions.Config{Cookie: "iris_session_id"}).Start).

通过控制器方法的输入参数访问动态路径参数,无需绑定。 当你使用 Iris 的默认语法从一个控制器中去解析处理程序 , 你需要给方法添加 By 后缀, 大写字母是一个新的子路径。 例如:

如果 mvc.New(app.Party("/user")).Handle(new(user.Controller))

  • func(*Controller) Get() - GET:/user.
  • func(*Controller) Post() - POST:/user.
  • func(*Controller) GetLogin() - GET:/user/login
  • func(*Controller) PostLogin() - POST:/user/login
  • func(*Controller) GetProfileFollowers() - GET:/user/profile/followers
  • func(*Controller) PostProfileFollowers() - POST:/user/profile/followers
  • func(*Controller) GetBy(id int64) - GET:/user/{param:long}
  • func(*Controller) PostBy(id int64) - POST:/user/{param:long}

如果 mvc.New(app.Party("/profile")).Handle(new(profile.Controller))

  • func(*Controller) GetBy(username string) - GET:/profile/{param:string}

如果 mvc.New(app.Party("/assets")).Handle(new(file.Controller))

  • func(*Controller) GetByWildard(path string) - GET:/assets/{param:path}

方法函数接收器支持的类型: int, int64, bool 和 string。

可选的, 通过输出参数响应,就如通我们在这章 Dependency Injection 内容中所展示的那样。 例如。

func(c *ExampleController) Get() string |
                                (string, string) |
                                (string, int) |
                                int |
                                (int, string) |
                                (string, error) |
                                error |
                                (int, error) |
                                (any, bool) |
                                (customStruct, error) |
                                customStruct |
                                (customStruct, int) |
                                (customStruct, string) |
                                mvc.Result or (mvc.Result, error)

mvc.Result 是 hero.Result 类型的一个别名, 同时它也是一个接口。

type Result interface {
    // Dispatch 应该发送响应到 context 的响应编写器中。
    Dispatch(ctx iris.Context)
}

示例

此示例等效于 https://github.com/kataras/iris/blob/master/_examples/hello-world/main.go

你需要添加代码,这看上去似乎有些得不偿失。但请记住,这个例子并没有使用 iris mvc 的模型, Persistence 或者视图引擎还有 Session 等功能。它仅仅是一个为学习准备的简单例子,你可能永远都不会在你的应用程序中使用如此简单的控制器。

在此示例中,在我的个人笔记本电脑上使用 MVC 模式,在"/hello" 路径上服务每 20MB JSON 的成本是 ~2MB 。
虽然这对于大多数应用程序来说是已经足够,但你还可以选择最适合你的 Iris 功能组合,低级处理程序: 性能更好或者高级控制器: 在大项目中更容易管理和简化代码。

请仔细阅读注释

package main

import (
	"github.com/kataras/iris/v12"
	"github.com/kataras/iris/v12/mvc"

	"github.com/kataras/iris/v12/middleware/logger"
	"github.com/kataras/iris/v12/middleware/recover"
)

func main() {
	app := iris.New()
	// 可选的,增加两个内置处理程序
	// 可以从任何 http 相对的恐慌中恢复
	// 和将请求记录到终端。
	app.Use(recover.New())
	app.Use(logger.New())

	//基于根路由器的控制器, "/".
	mvc.New(app).Handle(new(ExampleController))

	// http://localhost:8080
	// http://localhost:8080/ping
	// http://localhost:8080/hello
	// http://localhost:8080/custom_path
	app.Run(iris.Addr(":8080"))
}

// ExampleController 服务于 "/", "/ping" and "/hello"。
type ExampleController struct{}

// Get 服务
// 方法:   GET
// 资源: http://localhost:8080
func (c *ExampleController) Get() mvc.Result {
	return mvc.Response{
		ContentType: "text/html",
		Text:        "<h1>Welcome</h1>",
	}
}

// GetPing 服务
// 方法:   GET
// 资源: http://localhost:8080/ping
func (c *ExampleController) GetPing() string {
	return "pong"
}

// GetHello 服务
// 方法:   GET
// 资源: http://localhost:8080/hello
func (c *ExampleController) GetHello() interface{} {
	return map[string]string{"message": "Hello Iris!"}
}

// BeforeActivation 只调用一次, 在控制器适应主应用程序前
// 当然也是在主程序运行前。
// 在版本 9 之后,你还可以为特定控制器的方法添加自定义路由。
// 这里你也可以不使用 mvc , 只通过 `ca.Router` 函数使用标准的路由去
// 注册自定义方法的处理程序去做一些事情。 
// 并且增加绑定到控制器的字段或者方法函数输入参数上的依赖项。
func (c *ExampleController) BeforeActivation(b mvc.BeforeActivation) {
	anyMiddlewareHere := func(ctx iris.Context) {
		ctx.Application().Logger().Warnf("Inside /custom_path")
		ctx.Next()
	}

	b.Handle(
		"GET",
		"/custom_path",
		"CustomHandlerWithoutFollowingTheNamingGuide",
		anyMiddlewareHere,
	)

	// 或者甚至增加一个全局中间件到控制器路由中,
	// 在这个例子中是根路由 "/":
	// b.Router().Use(myMiddleware)
}

// CustomHandlerWithoutFollowingTheNamingGuide 服务
// 方法:   GET
// 资源: http://localhost:8080/custom_path
func (c *ExampleController) CustomHandlerWithoutFollowingTheNamingGuide() string {
	return "hello from the custom handler without following the naming guide"
}

// GetUserBy 服务
// 方法:   GET
// 资源: http://localhost:8080/user/{username:string}
// By 是一个保留 "关键字" 去告诉框架你将要绑定路径参数到函数的输入参数中,
// 同样它有助于你在控制器中同时创建"Get" 和 "GetBy" 函数。
//
// func (c *ExampleController) GetUserBy(username string) mvc.Result {
// 	return mvc.View{
// 		Name: "user/username.html",
// 		Data: username,
// 	}
// }

/* 
可以使用多个 HTTP 方法,工厂将会去确定
正确的 HTTP 方法是否已经注册到这个控制器中的每个路由上。
如果你想使用它们,请取消注释:

func (c *ExampleController) Post() {}
func (c *ExampleController) Put() {}
func (c *ExampleController) Delete() {}
func (c *ExampleController) Connect() {}
func (c *ExampleController) Head() {}
func (c *ExampleController) Patch() {}
func (c *ExampleController) Options() {}
func (c *ExampleController) Trace() {}
*/

/*
func (c *ExampleController) All() {}
//        或者
func (c *ExampleController) Any() {}

func (c *ExampleController) BeforeActivation(b mvc.BeforeActivation) {
	// 1 ->  HTTP 方法
	// 2 -> 路由的路径
	// 3 -> 此控制器的方法名称,该名称应为此路由的处理程序。
	b.Handle("GET", "/mypath/{param}", "DoIt", optionalMiddlewareHere...)
}

// 激活之后, 所有依赖都被设置为 -ed - 所以它们都是只读的。
// 但是任然也可以增加自定义控制器或者简单的标准处理程序。
func (c *ExampleController) AfterActivation(a mvc.AfterActivation) {}

*/

在控制其中,每个以 HTTP 方法(GetPostPutDelete...)为前缀的 导出的 函数,都可作为 HTTP 端点调用。在上面的例子中, ,所有函数都将字符串写入响应。注意每种方法之前的注释。

一个 HTTP 端点是一个Web应用程序中的可定位 URL, 例如http://localhost:8080/helloworld,它结合所使用的协议: HTTP,Web服务器的网络位置(包括 TCP 端口): localhost:8080 和目标 URI /helloworld

第一条注释指出这是一种HTTP GET 方法,该方法通过在基本 URL 后面附加 "/helloworld" 来调用。 第三条注释指定一个 HTTP GET 方法, 该方法通过在 URL 后面附加 "/helloworld/welcome" 来调用。

因为 By 关键字,控制器知道如何去处理 GetBy上的 "name" 参数 或者 GetWelcomeBy上的 "name" 和 "numTimes" 参数,并且构建无样板的动态路由;第三个注释指定一个 HTTP GET 动态方法,它由任何以"/helloworld/welcome" 开头并且跟随两个路径部分的 URL 调用。第一个部分可以接受任何值并且第二部分只能接受数字,即: "http://localhost:8080/helloworld/welcome/golang/32719", 否则,一个 404 Not Found HTTP 错误 将会被发送到客户端。

Websockets

WebSocket 是一种协议,可通过 TCP 连接启用双向持久通信通道。它用于聊天,股票行情,游戏等应用程序,你需要在Web 应用程序中具有实时功能的任何地方。

需要直接使用套接字连接时,请使用 WebSockets 。例如,你可能需要实时游戏的最佳性能。

首先,请阅读 kataras/neffos wiki 以掌握为 net/http 和 Iris 构建的新 websocket 库。

它是 Iris 预先安装的,但是你可以通过执行以下 shell 命令单独安装它。

$ go get github.com/kataras/neffos@latest

继续阅读如何将 neffos websocket 服务器注册到 Iris 应用程序中。

可在以下位置找到使用 websockets的综合示例列表:https://github.com/kataras/iris/tree/master/_examples/websocket.


iris/websocket 子软件包 包含(仅)适用于 neffos websocket framework 之一的 Iris 特定迁移和辅助方法。

例如,要访问请求的 Context 你可以从事件消息处理程序/回调内部调用 websocket.GetContext(Conn) :

// GetContext  从一个 Websocket 连接返回 Iris 的 Context 。
func GetContext(c *neffos.Conn) Context

使用  websocket.Handler 函数 注册一个 websocket neffos.Server  到一个路由:

// IDGenerator是用于新连接 iris 特定的 IDGenerator 。
type IDGenerator func(Context) string

// Handler 返回一个要在 Iris 应用程序的路由中使用的 Iris 处理程序。
// 接受 neffos websocket 服务器作为其第一个输入参数
//,也可以选择使用  iris 特定于的 `IDGenerator` 作为它的第二个。
func Handler(s *neffos.Server, IDGenerator ...IDGenerator) Handler

用法

import (
    "github.com/kataras/neffos"
    "github.com/kataras/iris/v12/websocket"
)

// [...]

onChat := func(ns *neffos.NSConn, msg neffos.Message) error {
    ctx := websocket.GetContext(ns.Conn)
    // [...]
    return nil
}

app := iris.New()
ws := neffos.New(websocket.DefaultGorillaUpgrader, neffos.Namespaces{
    "default": neffos.Events {
        "chat": onChat,
    },
})
app.Get("/websocket_endpoint", websocket.Handler(ws))

Websocket 控制器

Iris 具有通过一个 Go 结构注册 websocket 事件的简单方法。 websocket 控制器是 MVC 功能的一部分。

Iris 具有自己的 iris/mvc/Application.HandleWebsocket(v interface{}) *neffos.Struct 以在现有 Iris 的 MVC 应用程序中注册控制器(为请求值和静态服务提供功能齐全的依赖项注入容器)。

// HandleWebsocket 处理 Websocket 特定的控制器。
// 它的导出方法是事件。
// 如果存在 “名称空间” 字段或方法,则会设置名称空间,
// 否则此控制器将使用空名称空间。
//
// 请注意,一个websocket控制器已注册并
// 连接到名称空间的连接
// ,并且无法在该状态下发送HTTP响应。
// 但是,所有静态和动态依赖项的行为均符合预期。
func (*mvc.Application) HandleWebsocket(controller interface{}) *neffos.Struct

让我们看一个使用示例, 我们想将 OnNamespaceConnectedOnNamespaceDisconnect 内置事件和自定义的 "OnChat" 事件与控制器的方法绑定。

1. 我们通过将 NSConn 类型字段声明为  stateless 来创建控制器并编写所需的方法。

type websocketController struct {
    *neffos.NSConn `stateless:"true"`
    Namespace string

    Logger MyLoggerInterface
}

func (c *websocketController) OnNamespaceConnected(msg neffos.Message) error {
	return nil
}

func (c *websocketController) OnNamespaceDisconnect(msg neffos.Message) error {
	return nil
}

func (c *websocketController) OnChat(msg neffos.Message) error {
    return nil
}

Iris足 够聪明,可以捕获 Namespace stringstruct字段,以使用该字段将控制器的方法注册为该命名空间的事件,或者, 你可以创建 Namespace() string { return "default" }  的一个控制器方法或者使用 HandleWebsocket 的返回值到 .SetNamespace("default"),这取决于你。

2. 我们将 MVC 应用程序目标初始化为 websocket端点,就像以前使用常规 HTTP 控制器处理 HTTP 路由一样。

import (
    // [...]
    "github.com/kataras/iris/v12/mvc"
)
// [app := iris.New...]

mvcApp := mvc.New(app.Party("/websocket_endpoint"))

3. 我们注册了我们的依赖项(如果有的话)。

mvcApp.Register(
    &prefixedLogger{prefix: "DEV"},
)

4. 我们注册了一个或多个 websocket 控制器,每个 websocket 控制器都映射到一个名称空间 (一个就足够了,因为在大多数情况下,你不需要更多的名称,但这取决于你应用的需求)。

mvcApp.HandleWebsocket(&websocketController{Namespace: "default"})

5. 接下来,我们将 mvc 应用程序作为连接处理程序映射到 websocket 服务器 (你以通过 neffos.JoinConnHandlers(mvcApp1, mvcApp2) 为每个 websocket 服务器使用多个mvc应用程序)。

websocketServer := neffos.New(websocket.DefaultGorillaUpgrader, mvcApp)

6. 最后一步是通过常规 .Get 方法将该服务器注册到我们的端点。

mvcApp.Router.Get("/", websocket.Handler(websocketServer))
正文到此结束
该篇文章的评论功能已被站长关闭