Go语言学习路

lris路由

2021-08-24  本文已影响0人  TZX_0710

路由行为

Iris默认接受和注册形如/api/user这样的路径的路由,且尾部不带斜杠。如果客户端尝试访问$your_host/api/user/,Iris路由会自动永久重定向(301)到$your_host/api/user,以便注册的路由进行处理。这是设计APIs的现代化的方式。

如果你想禁用请求的路径更正的功能的话,你可以在app.Run传递iris.WithoutPathCorrection配置选项。例如

app.Run(iris.Addr(":8080"),iris.WithoutPathCorrection)
//如果向/api/user和/api/user、在不重定向的情况下拥有相同的处理器,只需要`iris.WithoutPathCorrectionRedirection`选项即可
app.Run(iris.Addr(":8080"),iris.WithoutPathCorrectionRedirection)

Api

支持所有的HTTP方法,开发者也可以在相同的路径的不同的方法注册处理器(比如/user的Get和POST)

第一个参数是HTTP方法,第二个参数是请求的路径,第三个可变参数应包含一个或者多个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中有一个特俗得方法可以使用。它被称为None.你可以使用它向外部隐藏一条路由,但仍然可以从其他路由处理中通过Context。Exec方法调用。每个API处理方法返回Route值。一个Route得IsOnline方法报告那个路由的当前状态。你可以通过它的Route.Method字段的值来改变路由离线状态为在线状态。每次需要调用app.RefreshRouter方法。

package main

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

func main() {
  app := iris.New()
  //离线路由
  none := app.None("/leaveRouter", func(context iris.Context) {
      context.Writef("hello world %v", context.Method())
  })
  //app.Get
  app.Get("/change", func(context iris.Context) {
      //如果在西安
      if none.IsOnline() {
          none.Method = iris.MethodNone
      } else {
          none.Method = iris.MethodGet
      }
  })
  app.Run(iris.Addr(":8080"))
}
//先采用 http://localhost:8080/leaveRouter 访问 返回
Not Found
//http://localhost:8080/change
//http://localhost:8080/leaveRoute  hello world GET

路由组

一些列路由可以通过路径的前缀分组。共享相同的中间件处理器和模板布局。一个组也可以有一个内嵌的组

package main

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

func main() {
  //初始化
  application := iris.New()
  v1 := application.Party("/v1")
  v1.Get("/getName", func(context iris.Context) {
      context.Writef("name:%v", "zhangsan")
  })
  v1.Get("/getAge", func(context iris.Context) {
      context.Writef("age:%v", 10)
  })
  application.Run(iris.Addr(":8080"))
}
//http://localhost:8080/v1/getAge
//http://localhost:8080/v1/getName
也可以使用PartyFunc方法编写相同的内容,它接受子路由器或者Party
package main

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

func main() {
  //初始化
  application := iris.New()
  application.PartyFunc("/v1", func(user iris.Party) {
      user.Get("/getName", func(context iris.Context) {
          context.Writef("name:%v", "zhangsan")
      })
      user.Get("/getAge", func(context iris.Context) {
          context.Writef("age:%v", 10)
      })
  })
  application.Run(iris.Addr(":8080"))
}

路径参数

与你见到的其他路由器不同,Iris的路由器可以处理各种 路由路径而不会发生冲突

//只匹配到/
app.Get("/")
//匹配到所有的user/前缀的请求
app.Get("/user/{user:path})
//匹配以profile前缀的GET请求
app.Get("/profile/{username:string}")
//匹配profile/me的Get请求
app.Get("/user/{userid:int min(1)}")
//匹配前缀为user的delete请求
app.DELETE("/user/{userId:int min(1)}")
//除了被其他路由器处理的请求,其他的都可以匹配到
app.Get("{root:path}")

路径参数类型

Iris拥有你见过的最简单和强大路由处理

Iris自己拥有于路由路径语法解析和判定的解释器

参数

一个路径的参数的名字应该仅仅包含字母。数字和类似"_"这样的符号是不允许的

不要迷惑于ctx.Params()ctx.Values()

  • 路径的参数值可以通过ctx.Params()取出
  • ctx中用于处理器与中间件之间通信的本地存储可以存储在ctx.Values()

内建参数类型

参数类型 golang类型 取值范围 取值方式
:string string 任何值(单个字段路径) Params().Get
:int int -9223372036854775808 - 9223372036854775807 (x64) </br>-2147483648 - 2147483647 (x32) Params().GetInt
:int8 int8 -128 - 127 Params().GetInt8
:int16 int16 -32768 - 32767 Params().GetInt16
:int32 int32 -2147483648 - 2147483647 Params().GetInt32
:int64 int64 -9223372036854775808 - 9223372036854775807 Params().GetInt64
:uint8 uint8 0 - 255 Params().GetUint8
:uint16 uint16 0 - 65535 Params().GetUint16
:uint32 uint32 0 - 4294967295 Params().GetUint32
:uint64 uint64 0 - 18446744073709551615 Params().GetUint64
:bool bool "1","t","T","TRUE","true","True","0","f", "F", "FALSE",,"false","False" Params().GetBool
:alphabetical string 小写或大写字母 Params().Get
:file string 大小写字母,数字,下划线(_),横线(-),点(.),以及没有空格或者其他对文件名无效的特殊字符 Params().Get
:path string 任何可以被斜线(/)分隔的路径段,但是应该为路由的最后一部分 Params().Get
内建函数
内建函数 参数类型
regexp(expr string) :string
prefix(prefix string) :string
suffix(suffix string) :string
contains(s string) :string
min(最小值),接收:int,int8,int16,int32,int64,uint8uint16,uint32,uint64,float32,float64) :string(字符长度),:int,:int16,:int32,:int64,</br>:uint,:uint16,:uint32,:uint64
max(最大值),接收:int,int8,int16,int32,int64,uint8uint16,uint32,uint64,float32,float64) :string(字符长度),:int,:int16,:int32,:int64,</br>:uint,:uint16,:uint32,:uint64
range(最小值,最大值),接收:int,int8,int16,int32,int64,uint8uint16,uint32,uint64,float32,float64) :int,:int16,:int32,:int64,</br>:uint,:uint16,:uint32,:uint64
自己做

RegisterFunc可以接受任何返回func(paramValue string)bool的函数、如果验证失败则触发404或者任意else关键字拥有的状态码

package main

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

func main() {
  //改正则表达式 匹配0次或者1次-
  //0-9匹配1位到3位
  latLonExpr := "^-?[0-9]{1,3}(?:\\.[0-9]{1,10})?$"
  latLonRegex, _ := regexp.Compile(latLonExpr)

  app := iris.New()
  // 将您的自定义无参数宏函数注册到 :string 参数类型。
  // MatchString 是 func(string) bool 的一种类型,因此我们按原样使用它。
  app.Macros().Get("string").RegisterFunc("coordinate", latLonRegex.MatchString)

  //http://localhost:8080/coordinates/AAA/BBB 404 NOT found  因为正则表达式全是数字
  //http://localhost:8080/coordinates/111/222 通过
  app.Get("/coordinates/{lat:string coordinate()}/{lon:string coordinate()}",
      func(ctx iris.Context) {
          getString := ctx.Params().GetString("lat")
          fmt.Println(getString)
          ctx.Writef("Lat: %s | Lon: %s", ctx.Params().Get("lat"), ctx.Params().Get("lon"))
      })
  app.Run(iris.Addr(":8080"))
}
//注册接受2个int类型的函数
package main

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

func main() {
  //改正则表达式 匹配0次或者1次-
  //0-9匹配1位到3位

  app := iris.New()
  //这个函数接受2个数如果这个参数值 大于最小值 小于最大值 则通过 否则400
  app.Macros().Get("string").RegisterFunc("range", func(minLength, maxLength int) func(string) bool {
      return func(paramValue string) bool {
          return len(paramValue) >= minLength && len(paramValue) <= maxLength
      }
  })
  //limitchar后面的值在5,200的范围之内 否则400 
  app.Get("/limitchar/{name:string range(5,200) else 400}", func(ctx iris.Context) {
      name := ctx.Params().Get("name")
      ctx.Writef(`Hello %s | the name should be between 1 and 200 characters length otherwise this handler will not be executed`, name)
  })
  app.Run(iris.Addr(":8080"))
}

中间件
package main

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

func main() {
  //改正则表达式 匹配0次或者1次-
  //0-9匹配1位到3位

  app := iris.New()
  //这个函数接受2个数如果这个参数值 大于最小值 小于最大值 则通过 否则400
  app.Get("/get", before, mainHanlder, after)
  app.Run(iris.Addr(":8080"))

}
func before(ctx iris.Context) {
  fmt.Println("before the main Hanlder")
  ctx.Next()
}
func mainHanlder(ctx iris.Context) {
  fmt.Println("business")
  ctx.Next()
}
func after(ctx iris.Context) {
  fmt.Println("after ")
}
//result
before the main Hanlder
business
after 
处理HTTP错误

自定义处理器处理特定的http错误

func main(){
app:=iris.New()
app.OnErrorCode(iris.StatusNotFound,notFound)
app.Run(iris.Addr(":8080"))
}
func notFound(){
  ctx.View("errors/404.html")
}

问题类型

Iris内建支持HTTP APIs的错误详情

Context.Problem编写一个JSON或者XML问题响应,行为完全类似Context.JSON但是默认ProblemOptions.JSON的缩进是"",响应的Content-Typeapplication/problem+json

使用options.RenderXMLXML字段来改变他的行为。用application/problem+xml的文本类型替代。

package main

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

func newProductPoblem(productName, detail string) iris.Problem {
  return iris.NewProblem().
      Type("/product-error").
      Title("Product validation problem").
      Detail(detail).
      Status(iris.StatusBadRequest).
      Key("productName", productName)
}
func main() {
  app := iris.New()
  app.Get("/problem", fireProblem)
  app.Run(iris.Addr(":8080"))
}

func fireProblem(ctx iris.Context) {
  ctx.Problem(newProductPoblem("productName", "problem details"),
      iris.ProblemOptions{
          JSON: iris.JSON{
              Indent: "",
          },
             // OR
            // Render as XML:
            // RenderXML: true,
            // XML:       iris.XML{Indent: "  "},
            // Sets the "Retry-After" response header.
            //
            // Can accept:
            // time.Time for HTTP-Date,
            // time.Duration, int64, float64, int for seconds
            // or string for date or duration.
            // Examples:
            // time.Now().Add(5 * time.Minute),
            // 300 * time.Second,
            // "5m",
            //
            RetryAfter: 300,
            // A function that, if specified, can dynamically set
            // retry-after based on the request.
            // Useful for ProblemOptions reusability.
            // Overrides the RetryAfter field.
            //
            // RetryAfterFunc: func(iris.Context) interface{} { [...] }
      })
}

API版本控制
go get github.com/kataras/iris/v12/versioning
  • 对于每个路由版本匹配,一个普通的iris处理程序,带有"switch"案例通过版本 处理程序的映射
  • 每组版本化的路由和启用API
  • 版本匹配,例如">=1.0 ,<2.0"或仅仅是"2.0.1"等
  • 找不到版本的处理程序(可以通过简单地添加versiong.NotFound)来自定义:customNotMatchVersionHandler在映射上
  • 版本是从"Accept"和"Accept-Version"头中检索到地
  • 如果版本找到,响应具有X-API-Version
  • 通过Deprecated包装器,弃用了自定义X-API-WarnX-API-Deprecation-DataX-API-Deprecation-Info头部的选项
获取版本

当前请求的版本通过versiong.GetVersion(ctx)获得

package main

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

func main() {
  app := iris.New()
  app.Get("/problem", func(context iris.Context) {
      //获取当前系统的版本号
      fmt.Println(versioning.GetVersion(context))
  })
  app.Run(iris.Addr(":8081"))
}
//我们可以通过中间件设置自定义版本 
func(ctx iris.Context){
    ctx.Values().Set(versiong.Key, ctx.URLParamDefault("version", "1.0"))
    ctx.Next()
}
将版本与处理程序匹配

versioning.NewMatcher(versiong.Map)iris.Handler创建一个简单处理器,这个处理器决定基于请求的版本。哪个处理器需要执行。

package main

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

func main() {
  application := iris.New()
  myMiddle := func(ctx iris.Context) {
      ctx.Next()
  }
  versionNotFound := func(ctx iris.Context) {
      ctx.StatusCode(404)
      ctx.Writef("version %v", versioning.GetVersion(ctx))
  }
  party := application.Party("/version")
  party.Get("/", myMiddle, versioning.NewMatcher(versioning.Map{
      "1.0": func(context iris.Context) {
          context.Writef("1.0")
      },
      "2.0": func(context iris.Context) {
          context.Writef("2.0")
      },
      versioning.NotFound: versionNotFound,
  }))
  application.Run(iris.Addr(":8081"))
}
//请求
curl -H "Accept-Version:2" http://localhost:8081/version //2.0
curl -H "Accept-Version:1" http://localhost:8081/version //1.0    
curl -H  http://localhost:8081/version                    //not found

弃用

使用Versioning.Deprecated(handler iris.Handler,options versioning.DeprecationOptions)iris.Handler函数

可以标记特定的处理器版本为被弃用的

package main

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

func main() {
  application := iris.New()
  myMiddle := func(ctx iris.Context) {
      ctx.Next()
  }
  versionNotFound := func(ctx iris.Context) {
      ctx.StatusCode(404)
      ctx.Writef("version %v", versioning.GetVersion(ctx))
  }
  deprecated := versioning.Deprecated(sendHandler, versioning.DeprecationOptions{
      DeprecationDate: time.Now(),
  })
  party := application.Party("/version")
  party.Get("/", myMiddle, versioning.NewMatcher(versioning.Map{
      "1.0": deprecated,
      "2.0": func(context iris.Context) {
          context.Writef("2.0")
      },
      versioning.NotFound: versionNotFound,
  }))

  application.Run(iris.Addr(":8081"))
}
func sendHandler(ct iris.Context) {
  ct.Writef("AAAAAAKKKKKK")
}

上一篇下一篇

猜你喜欢

热点阅读