Iris入门

关于Iris的教程,直接去看官网。官方提供的文档真的是非常非常好,强烈推荐!!!

这里只是随便记录一下,还是要多看官网。

Quick start (iris-go.com)

Iris github:https://github.com/kataras/iris

从贡献者中可以看到有很多国人,难怪Iris的官方文档虽然是英文,但我看着还是感觉很亲切。

记录踩过的坑

在之前使用Gin或者是其他框架时,很少手动输入go get,都是直接在goland上import然后在Sync的。但是这种方法在使用Iris时会遇到一些问题,下载的包是不完整的,所以在使用时很可能报错。

所以Iris项目在go mod init之后,还要手动go get github.com/kataras/iris/v12@master

Installation

Quick start

main.go代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package main

import (
"fmt"

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

type Book struct {
Title string `json:"title"`
}

func main() {
app := iris.Default()
booksAPI := app.Party("/books")
{
booksAPI.Use(iris.Compression) // 此中间件能够使用最好的压缩进行写和读
booksAPI.Get("/", list)
booksAPI.Post("/", create)
}
app.Listen(":8080")
}

func list(ctx iris.Context) {
books := []Book{
{"Gone With the wind"},
{"Go Design Patterns"},
{"Black Hat Go"},
}
ctx.JSON(books)
}
func create(ctx iris.Context) {
var b Book
err := ctx.ReadJSON(&b) // 使用ctx.ReadBody()绑定任何类型的传入数据(不仅仅是JSON)
if err != nil {
// 当错误只需要纯文本响应时,使用ctx.StopWithError(code,err)
ctx.StopWithProblem(iris.StatusBadRequest, iris.NewProblem().
Title("Book creation failure").DetailErr(err))
return
}
fmt.Println("Received Book: " + b.Title)
ctx.StatusCode(iris.StatusCreated)
}

iris.New()iris.Default()的区别在iris.Default()的注释中已经说的很清楚了,官方文档也有。

Creates an iris application with default middleware:

  • Default with “debug” Logger Level.
  • Localization enabled on “./locales” directory and HTML templates on “./views” or “./templates” directory.
  • It runs with the AccessLog on “./access.log”, Recovery (crash-free) and Request ID middleware already attached.

MVC equivalent

MVC方式的写法,main.go如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package main

import (
"fmt"

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

type Book struct {
Title string `json:"title"`
}

type BookController struct {
/* dependencies */
}

func (c *BookController) Get() []Book {
return []Book{
{"Gone With the wind"},
{"Go Design Patterns"},
{"Black Hat Go"},
}
}

func (c *BookController) Post(b Book) int {
fmt.Println("Received Book: " + b.Title)
return iris.StatusCreated
}

func main() {
app := iris.New()
booksAPI := app.Party("/books")
m := mvc.New(booksAPI)
m.Handle(new(BookController))
app.Listen(":8080")
}

执行结果是一样的:

API Examples

You can find a number of ready-to-run examples at Iris examples repository.

GETPOST等等用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
func main() {
app := iris.Default()

app.Get("/someGet", getting)
app.Post("/somePost", posting)
app.Put("/somePut", putting)
app.Delete("/someDelete", deleting)
app.Patch("/somePatch", patching)
app.Header("/someHead", head)
app.Options("/someOptions", options)

app.Listen(":8080")
}

Parameters in path

看几个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
func main() {
app := iris.Default()

// This handler will match /user/john but will not match /user/ or /user
app.Get("/user/{name}", func(ctx iris.Context) { 将会匹配到类似/user/john的url
name := ctx.Params().Get("name")
ctx.Writef("Hello %s", name)
})

// However, this one will match /user/john/ and also /user/john/send
// If no other routers match /user/john, it will redirect to /user/john/
app.Get("/user/{name}/{action:path}", func(ctx iris.Context) {
name := ctx.Params().Get("name")
action := ctx.Params().Get("action")
message := name + " is " + action
ctx.WriteString(message)
})

// For each matched request Context will hold the route definition
app.Post("/user/{name:string}/{action:path}", func(ctx iris.Context) {
ctx.GetCurrentRoute().Tmpl().Src == "/user/{name:string}/{action:path}" // true
})

app.Listen(":8080")
}

Builtin available parameter types:

Param Type Go Type Validation Retrieve Helper
:string string anything (single path segment) Params().Get
:uuid string uuidv4 or v1 (single path segment) Params().Get
:int int -9223372036854775808 to 9223372036854775807 (x64) or -2147483648 to 2147483647 (x32), depends on the host arch Params().GetInt
:int8 int8 -128 to 127 Params().GetInt8
:int16 int16 -32768 to 32767 Params().GetInt16
:int32 int32 -2147483648 to 2147483647 Params().GetInt32
:int64 int64 -9223372036854775808 to 9223372036854775807 Params().GetInt64
:uint uint 0 to 18446744073709551615 (x64) or 0 to 4294967295 (x32), depends on the host arch Params().GetUint
:uint8 uint8 0 to 255 Params().GetUint8
:uint16 uint16 0 to 65535 Params().GetUint16
:uint32 uint32 0 to 4294967295 Params().GetUint32
:uint64 uint64 0 to 18446744073709551615 Params().GetUint64
:bool bool “1” or “t” or “T” or “TRUE” or “true” or “True” or “0” or “f” or “F” or “FALSE” or “false” or “False” Params().GetBool
:alphabetical string lowercase or uppercase letters Params().Get
:file string lowercase or uppercase letters, numbers, underscore (_), dash (-), point (.) and no spaces or other special characters that are not valid for filenames Params().Get
:path string anything, can be separated by /(path segments) but should be the last part of the route path Params().Get

More examples can be found at: _examples/routing.

Upload files

Upload files

Grouping routes

Grouping routes

Bind

JSON, JSONP, XML, Markdown, YAML and MsgPack rendering

JSON, JSONP, XML, Markdown, YAML and MsgPack rendering

Protobuf

Protobuf

JSON Web Tokens

JSON Web Tokens

之前在Gin中使用JWT,还需要自己写中间件(middleware/jwt.go)和Token的签发与解析逻辑(util/jwt.go),但是在Iris中,不需要自己手写那么多,Iris帮我们封装好了一些功能。

个人是更喜欢gin这种方式的,代码自己写的,逻辑会更清晰。

install:

1
2
go get github.com/kataras/iris/v12@master 
go get github.com/kataras/jwt

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
package main

import (
"log"
"time"

"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/context"
"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.Default()
signer := jwt.NewSigner(jwt.HS256, sigKey, time.Minute*10)
// 如果要对payload进行加密:
//signer.WithEncryption(encKey, nil)
app.Get("/login", generateToken(signer))

verifier := jwt.NewVerifier(jwt.HS256, sigKey)
verifier.WithDefaultBlocklist() // Enable server-side token block feature (even before its expiration time)
// 如果要对payload进行解密:
//verifier.WithDecryption(encKey, nil)
verifierMiddleware := verifier.Verify(func() interface{} {
return new(fooClaims)
})

protectedAPI := app.Party("/protected")
protectedAPI.Use(verifierMiddleware) // 注册中间件
{
//protectedAPI.UseRouter(verifierMiddleware) // disallow unauthorized http error handlers
protectedAPI.Get("/", protected)
protectedAPI.Get("/logout", logout) // 在服务端让token失效
}

app.Listen(":8080")
}

// 登出,在服务端让token失效
func logout(ctx *context.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")
}
}

// 测试token的接口,该接口将获取token中携带的信息
func protected(ctx *context.Context) {
claims, ok := jwt.Get(ctx).(*fooClaims) // Get the verified and decoded claims.
if !ok {
log.Fatalln("断言失败")
}
// 获取token信息
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)

}

// 签发token
func generateToken(signer *jwt.Signer) context.Handler {
return func(ctx *context.Context) {
claims := fooClaims{Foo: "bar"}
token, err := signer.Sign(claims)
if err != nil {
ctx.StopWithStatus(iris.StatusInternalServerError)
return
}
ctx.Write(token)
}
}
  1. http://localhost:8080/login:签发token(模拟登录)
  2. http://localhost:8080/protected?token=$token:使用携带token进行接口验证
  3. http://localhost:8080/protected/logout?token=$token:使该token失效
  4. http://localhost:8080/protected?token=$token:在第3个接口调用后,原本的token就失效了,此时再调用该接口,会报401Unauthorized

可以看到token携带的payload: