Iris是一个跨平台的软件。
唯一的要求是 Go 编程语言,版本 1.20 及更高版本。
$ mkdir myapp
$ cd myapp
$ go mod init myapp
$ go get github.com/kataras/iris/v12@latest
将其导入到代码中:
import "github.com/kataras/iris/v12"
如果在安装过程中遇到网络错误,请确保设置了有效的 GOPROXY 环境变量。
go env -w GOPROXY=https://goproxy.io,direct
如果上述方法均无效,请清理 go 模块缓存:
go clean --modcache
# assume the following codes in main.go file
$ cat main.go
package main
import "github.com/kataras/iris/v12"
func main() {
app := iris.New()
booksAPI := app.Party("/books")
{
booksAPI.Use(iris.Compression)
// GET: http://localhost:8080/books
booksAPI.Get("/", list)
// POST: http://localhost:8080/books
booksAPI.Post("/", create)
}
app.Listen(":8080")
}
// Book example.
type Book struct {
Title string `json:"title"`
}
func list(ctx iris.Context) {
books := []Book{
{"Mastering Concurrency in Go"},
{"Go Design Patterns"},
{"Black Hat Go"},
}
ctx.JSON(books)
// TIP: negotiate the response between server's prioritizes
// and client's requirements, instead of ctx.JSON:
// ctx.Negotiation().JSON().MsgPack().Protobuf()
// ctx.Negotiate(books)
}
func create(ctx iris.Context) {
var b Book
err := ctx.ReadJSON(&b)
// TIP: use ctx.ReadBody(&b) to bind
// any type of incoming data instead.
if err != nil {
ctx.StopWithProblem(iris.StatusBadRequest, iris.NewProblem().
Title("Book creation failure").DetailErr(err))
// TIP: use ctx.StopWithError(code, err) when only
// plain text responses are expected on errors.
return
}
println("Received Book: " + b.Title)
ctx.StatusCode(iris.StatusCreated)
}
MVC 等效项:
import "github.com/kataras/iris/v12/mvc"
m := mvc.New(booksAPI)
m.Handle(new(BookController))
type BookController struct {
/* dependencies */
}
// GET: http://localhost:8080/books
func (c *BookController) Get() []Book {
return []Book{
{"Mastering Concurrency in Go"},
{"Go Design Patterns"},
{"Black Hat Go"},
}
}
// POST: http://localhost:8080/books
func (c *BookController) Post(b Book) int {
println("Received Book: " + b.Title)
return iris.StatusCreated
}
运行您的鸢尾花网络服务器:
$ go run main.go
> Now listening on: http://localhost:8080
> Application started. Press CTRL+C to shut down.
列表书:
$ curl --header 'Accept-Encoding:gzip' http://localhost:8080/books
[
{
"title": "Mastering Concurrency in Go"
},
{
"title": "Go Design Patterns"
},
{
"title": "Black Hat Go"
}
]
创建新图书:
$ curl -i -X POST \
--header 'Content-Encoding:gzip' \
--header 'Content-Type:application/json' \
--data "{\"title\":\"Writing An Interpreter In Go\"}" \
http://localhost:8080/books
> HTTP/1.1 201 Created
这就是错误响应的样子:
$ curl -X POST --data "{\"title\" \"not valid one\"}" \
http://localhost:8080/books
> HTTP/1.1 400 Bad Request
{
"status": 400,
"title": "Book creation failure"
"detail": "invalid character '\"' after object key",
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NL5y3Dps-1686647378212)(null)]
Iris使用muxie的自定义版本。
查看所有基准
使用 int 的动态参数触发 200000 个请求,将 JSON 作为请求正文发送,并接收 JSON 作为响应。
名字 | 语言 | 要求/秒 | 延迟 | 吞吐量 | 完成时间 |
---|---|---|---|---|---|
虹膜 | 去 | 238954 | 521.69乌秒 | 64.15兆字节 | 0.84秒 |
琴酒 | 去 | 229665 | 541.96乌秒 | 62.86兆字节 | 0.87秒 |
池 | 去 | 228072 | 545.78乌秒 | 62.61兆字节 | 0.88秒 |
回波 | 去 | 224491 | 553.84乌秒 | 61.70兆字节 | 0.89秒 |
马提尼酒 | 去 | 198166 | 627.46乌秒 | 54.47兆字节 | 1.01秒 |
茶隼 | C# | 163486 | 766.90乌秒 | 47.42兆字节 | 1.23秒 |
水牛 | 去 | 102478 | 1.22毫秒 | 28.14兆字节 | 1.95秒 |
科阿 | 爪哇语 | 48425 | 2.56毫秒 | 15.39兆字节 | 4.14秒 |
表达 | 爪哇语 | 23622 | 5.25毫秒 | 9.04兆字节 | 8.41秒 |
您可以在 Iris 示例存储库中找到许多随时可以运行的示例。
func main() {
// 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.
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")
}
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) {
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")
}
内置可用参数类型:
参数类型 | 去类型 | 验证 | 检索帮助程序 |
---|---|---|---|
:string |
字符串 | 任何(单路径段) | Params().Get |
:uuid |
字符串 | uuidv4 或 v1(单路径段) | Params().Get |
:int |
国际 | -9223372036854775808 到 9223372036854775807 (x64) 或 -2147483648 到 2147483647 (x32),取决于主机架构 | Params().GetInt |
:int8 |
国际8 | -128 到 127 | Params().GetInt8 |
:int16 |
国际16 | -32768 到 32767 | Params().GetInt16 |
:int32 |
国际32 | -2147483648 到 2147483647 | Params().GetInt32 |
:int64 |
国际64 | -9223372036854775808 到 9223372036854775807 | Params().GetInt64 |
:uint |
乌因特 | 0 到 18446744073709551615 (x64) 或 0 到 4294967295 (x32),取决于主机架构 | Params().GetUint |
: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 |
布尔 | “1”或“t”或“T”或“真”或“真”或“真”或“0”或“f”或“F”或“假”或“假”或“假” | Params().GetBool |
:alphabetical |
字符串 | 小写或大写字母 | Params().Get |
:file |
字符串 | 小写或大写字母、数字、下划线 (_)、短划线 (-)、点 (.) 并且没有空格或其他对文件名无效的特殊字符 | Params().Get |
:path |
字符串 | 任何内容都可以用斜杠(路径段)分隔,但应该是路由路径的最后一部分 | Params().Get |
:mail |
字符串 | 未经域验证的电子邮件 | Params().Get |
:email |
字符串 | 具有域验证功能的电子邮件 | Params().Get |
:date |
字符串 | 年/月/日格式,例如 /博客/{参数:日期} 匹配 /博客/2022/04/21 | Params().GetTime 和Params().SimpleDate |
:weekday |
uint (0-6) 或字符串 | 时间串。工作日长名称格式(“星期日”到“星期一”或“星期日”到“星期一”)格式,例如 /schedule/{param:weekday} 匹配 /schedule/星期一 | Params().GetWeekday |
更多示例可在以下位置找到:_examples/路由。
func main() {
app := iris.Default()
// Query string parameters are parsed using the existing underlying request object.
// The request responds to a url matching: /welcome?firstname=Jane&lastname=Doe
app.Get("/welcome", func(ctx iris.Context) {
firstname := ctx.URLParamDefault("firstname", "Guest")
lastname := ctx.URLParam("lastname") // shortcut for ctx.Request().URL.Query().Get("lastname")
ctx.Writef("Hello %s %s", firstname, lastname)
})
app.Listen(":8080")
}
func main() {
app := iris.Default()
app.Post("/form_post", func(ctx iris.Context) {
message := ctx.PostValue("message")
nick := ctx.PostValueDefault("nick", "anonymous")
ctx.JSON(iris.Map{
"status": "posted",
"message": message,
"nick": nick,
})
})
app.Listen(":8080")
}
POST /post?id=1234&page=1 HTTP/1.1
Content-Type: application/x-www-form-urlencoded
name=kataras&message=this_is_great
func main() {
app := iris.Default()
app.Post("/post", func(ctx iris.Context) {
id, err := ctx.URLParamInt("id", 0)
if err != nil {
ctx.StopWithError(iris.StatusBadRequest, err)
return
}
page := ctx.URLParamIntDefault("page", 0)
name := ctx.PostValue("name")
message := ctx.PostValue("message")
ctx.Writef("id: %d; page: %d; name: %s; message: %s", id, page, name, message)
})
app.Listen(":8080")
}
id: 1234; page: 1; name: kataras; message: this_is_great
(adsbygoogle = window.adsbygoogle || []).push({});
POST /post?id=a&id=b&id=c&name=john&name=doe&name=kataras
Content-Type: application/x-www-form-urlencoded
func main() {
app := iris.Default()
app.Post("/post", func(ctx iris.Context) {
ids := ctx.URLParamSlice("id")
names, err := ctx.PostValues("name")
if err != nil {
ctx.StopWithError(iris.StatusBadRequest, err)
return
}
ctx.Writef("ids: %v; names: %v", ids, names)
})
app.Listen(":8080")
}
ids: [a b c], names: [john doe kataras]
const maxSize = 8 * iris.MB
func main() {
app := iris.Default()
app.Post("/upload", func(ctx iris.Context) {
// Set a lower memory limit for multipart forms (default is 32 MiB)
ctx.SetMaxRequestBodySize(maxSize)
// OR
// app.Use(iris.LimitRequestBodySize(maxSize))
// OR
// OR iris.WithPostMaxMemory(maxSize)
// single file
file, fileHeader, err:= ctx.FormFile("file")
if err != nil {
ctx.StopWithError(iris.StatusBadRequest, err)
return
}
// Upload the file to specific destination.
dest := filepath.Join("./uploads", fileHeader.Filename)
ctx.SaveFormFile(fileHeader, dest)
ctx.Writef("File: %s uploaded!", fileHeader.Filename)
})
app.Listen(":8080")
}
如何:curl
curl -X POST http://localhost:8080/upload \
-F "file=@/Users/kataras/test.zip" \
-H "Content-Type: multipart/form-data"
请参阅详细的示例代码。
func main() {
app := iris.Default()
app.Post("/upload", func(ctx iris.Context) {
files, n, err := ctx.UploadFormFiles("./uploads")
if err != nil {
ctx.StopWithStatus(iris.StatusInternalServerError)
return
}
ctx.Writef("%d files of %d total size uploaded!", len(files), n))
})
app.Listen(":8080", iris.WithPostMaxMemory(8 * iris.MB))
}
如何:curl
curl -X POST http://localhost:8080/upload \
-F "upload[]=@/Users/kataras/test1.zip" \
-F "upload[]=@/Users/kataras/test2.zip" \
-H "Content-Type: multipart/form-data"
func main() {
app := iris.Default()
// Simple group: v1
v1 := app.Party("/v1")
{
v1.Post("/login", loginEndpoint)
v1.Post("/submit", submitEndpoint)
v1.Post("/read", readEndpoint)
}
// Simple group: v2
v2 := app.Party("/v2")
{
v2.Post("/login", loginEndpoint)
v2.Post("/submit", submitEndpoint)
v2.Post("/read", readEndpoint)
}
app.Listen(":8080")
}
用
app := iris.New()
而不是
// 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 and Request ID middleware already attached.
app := iris.Default()
package main
import (
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/middleware/recover"
)
func main() {
// Creates an iris application without any middleware by default
app := iris.New()
// Global middleware using `UseRouter`.
//
// Recovery middleware recovers from any panics and writes a 500 if there was one.
app.UseRouter(recover.New())
// Per route middleware, you can add as many as you desire.
app.Get("/benchmark", MyBenchLogger(), benchEndpoint)
// Authorization group
// authorized := app.Party("/", AuthRequired())
// exactly the same as:
authorized := app.Party("/")
// per group middleware! in this case we use the custom created
// AuthRequired() middleware just in the "authorized" group.
authorized.Use(AuthRequired())
{
authorized.Post("/login", loginEndpoint)
authorized.Post("/submit", submitEndpoint)
authorized.Post("/read", readEndpoint)
// nested group
testing := authorized.Party("testing")
testing.Get("/analytics", analyticsEndpoint)
}
// Listen and serve on 0.0.0.0:8080
app.Listen(":8080")
}
(adsbygoogle = window.adsbygoogle || []).push({});
func main() {
app := iris.Default()
// Logging to a file.
// Colors are automatically disabled when writing to a file.
f, _ := os.Create("iris.log")
app.Logger().SetOutput(f)
// Use the following code if you need to write the logs
// to file and console at the same time.
// app.Logger().AddOutput(os.Stdout)
app.Get("/ping", func(ctx iris.Context) {
ctx.WriteString("pong")
})
app.Listen(":8080")
}
默认情况下,控制台上的日志输出应根据检测到的 TTY 进行着色。
自定义关卡标题,文本,颜色和样式。
导入和 :golog``pio
import (
"github.com/kataras/golog"
"github.com/kataras/pio"
// [...]
)
获取要自定义的级别,例如:DebugLevel
level := golog.Levels[golog.DebugLevel]
您可以完全控制他的文字、标题和风格:
// The Name of the Level
// that named (lowercased) will be used
// to convert a string level on `SetLevel`
// to the correct Level type.
Name string
// AlternativeNames are the names that can be referred to this specific log level.
// i.e Name = "warn"
// AlternativeNames = []string{"warning"}, it's an optional field,
// therefore we keep Name as a simple string and created this new field.
AlternativeNames []string
// Tha Title is the prefix of the log level.
// See `ColorCode` and `Style` too.
// Both `ColorCode` and `Style` should be respected across writers.
Title string
// ColorCode a color for the `Title`.
ColorCode int
// Style one or more rich options for the `Title`.
Style []pio.RichOption
示例代码:
level := golog.Levels[golog.DebugLevel]
level.Name = "debug" // default
level.Title = "[DBUG]" // default
level.ColorCode = pio.Yellow // default
要更改输出格式:
app.Logger().SetFormat("json", " ")
注册自定义格式化程序:
app.Logger().RegisterFormatter(new(myFormatter))
嘟嘟。格式化程序界面如下所示:
// Formatter is responsible to print a log to the logger's writer.
type Formatter interface {
// The name of the formatter.
String() string
// Set any options and return a clone,
// generic. See `Logger.SetFormat`.
Options(opts ...interface{}) Formatter
// Writes the "log" to "dest" logger.
Format(dest io.Writer, log *Log) bool
}
要更改每个级别的输出和格式:
app.Logger().SetLevelOutput("error", os.Stderr)
app.Logger().SetLevelFormat("json")
我们在上面看到的应用程序记录器用于记录与应用程序相关的信息和错误。另一方面,我们在下面看到的访问记录器用于记录传入的HTTP请求和响应。
package main
import (
"os"
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/middleware/accesslog"
)
// Read the example and its comments carefully.
func makeAccessLog() *accesslog.AccessLog {
// Initialize a new access log middleware.
ac := accesslog.File("./access.log")
// Remove this line to disable logging to console:
ac.AddOutput(os.Stdout)
// The default configuration:
ac.Delim = '|'
ac.TimeFormat = "2006-01-02 15:04:05"
ac.Async = false
ac.IP = true
ac.BytesReceivedBody = true
ac.BytesSentBody = true
ac.BytesReceived = false
ac.BytesSent = false
ac.BodyMinify = true
ac.RequestBody = true
ac.ResponseBody = false
ac.KeepMultiLineError = true
ac.PanicLog = accesslog.LogHandler
// Default line format if formatter is missing:
// Time|Latency|Code|Method|Path|IP|Path Params Query Fields|Bytes Received|Bytes Sent|Request|Response|
//
// Set Custom Formatter:
ac.SetFormatter(&accesslog.JSON{
Indent: " ",
HumanTime: true,
})
// ac.SetFormatter(&accesslog.CSV{})
// ac.SetFormatter(&accesslog.Template{Text: "{{.Code}}"})
return ac
}
func main() {
ac := makeAccessLog()
defer ac.Close() // Close the underline file.
app := iris.New()
// Register the middleware (UseRouter to catch http errors too).
app.UseRouter(ac.Handler)
app.Get("/", indexHandler)
app.Listen(":8080")
}
func indexHandler(ctx iris.Context) {
ctx.WriteString("OK")
}
阅读更多示例:_examples/logging/request-logger。
(adsbygoogle = window.adsbygoogle || []).push({});
若要将请求正文绑定到类型中,请使用模型绑定。我们目前支持绑定、、、、和标准表单值(foo=bar&boo=baz)。JSON``JSONProtobuf``Protobuf``MsgPack``XML``YAML
ReadJSON(outPtr interface{}) error
ReadJSONProtobuf(ptr proto.Message, opts ...ProtoUnmarshalOptions) error
ReadProtobuf(ptr proto.Message) error
ReadMsgPack(ptr interface{}) error
ReadXML(outPtr interface{}) error
ReadYAML(outPtr interface{}) error
ReadForm(formObject interface{}) error
ReadQuery(ptr interface{}) error
使用 时,Iris 会尝试根据内容类型标头推断绑定程序。如果您确定要绑定的内容,则可以使用特定方法,例如 或和等ReadBody``ReadXXX``ReadJSON``ReadProtobuf
ReadBody(ptr interface{}) error
明智的是,Iris没有内置的数据验证功能。但是,它确实允许您附加一个验证器,该验证器将自动调用诸如 , …在这个例子中,我们将学习如何使用 go-playground/validator/v10 进行请求正文验证。ReadJSON``ReadXML
请注意,您需要在要绑定的所有字段上设置相应的绑定标记。例如,从 JSON 绑定时,设置 .json:"fieldname"
您还可以指定特定字段为必填字段。如果字段在绑定时用空值修饰,并且值为空,则会返回错误。binding:"required"
package main
import (
"fmt"
"github.com/kataras/iris/v12"
"github.com/go-playground/validator/v10"
)
func main() {
app := iris.New()
app.Validator = validator.New()
userRouter := app.Party("/user")
{
userRouter.Get("/validation-errors", resolveErrorsDocumentation)
userRouter.Post("/", postUser)
}
app.Listen(":8080")
}
// User contains user information.
type User struct {
FirstName string `json:"fname" validate:"required"`
LastName string `json:"lname" validate:"required"`
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 houses a users address information.
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"`
}
type validationError struct {
ActualTag string `json:"tag"`
Namespace string `json:"namespace"`
Kind string `json:"kind"`
Type string `json:"type"`
Value string `json:"value"`
Param string `json:"param"`
}
func wrapValidationErrors(errs validator.ValidationErrors) []validationError {
validationErrors := make([]validationError, 0, len(errs))
for _, validationErr := range errs {
validationErrors = append(validationErrors, validationError{
ActualTag: validationErr.ActualTag(),
Namespace: validationErr.Namespace(),
Kind: validationErr.Kind().String(),
Type: validationErr.Type().String(),
Value: fmt.Sprintf("%v", validationErr.Value()),
Param: validationErr.Param(),
})
}
return validationErrors
}
func postUser(ctx iris.Context) {
var user User
err := ctx.ReadJSON(&user)
if err != nil {
// Handle the error, below you will find the right way to do that...
if errs, ok := err.(validator.ValidationErrors); ok {
// Wrap the errors with JSON format, the underline library returns the errors as interface.
validationErrors := wrapValidationErrors(errs)
// Fire an application/json+problem response and stop the handlers chain.
ctx.StopWithProblem(iris.StatusBadRequest, iris.NewProblem().
Title("Validation error").
Detail("One or more fields failed to be validated").
Type("/user/validation-errors").
Key("errors", validationErrors))
return
}
// It's probably an internal JSON error, let's dont give more info here.
ctx.StopWithStatus(iris.StatusInternalServerError)
return
}
ctx.JSON(iris.Map{"message": "OK"})
}
func resolveErrorsDocumentation(ctx iris.Context) {
ctx.WriteString("A page that should document to web developers or users of the API on how to resolve the validation errors")
}
样品请求
{
"fname": "",
"lname": "",
"age": 45,
"email": "[email protected]",
"favColor": "#000",
"addresses": [{
"street": "Eavesdown Docks",
"planet": "Persphone",
"phone": "none",
"city": "Unknown"
}]
}
示例响应
{
"title": "Validation error",
"detail": "One or more fields failed to be validated",
"type": "http://localhost:8080/user/validation-errors",
"status": 400,
"fields": [
{
"tag": "required",
"namespace": "User.FirstName",
"kind": "string",
"type": "string",
"value": "",
"param": ""
},
{
"tag": "required",
"namespace": "User.LastName",
"kind": "string",
"type": "string",
"value": "",
"param": ""
}
]
}
有关模型验证的更多信息,请访问:https://github.com/go-playground/validator/blob/master/_examples
该方法仅绑定查询参数,而不绑定帖子数据,而是用于绑定帖子数据。ReadQuery``ReadForm
package main
import "github.com/kataras/iris/v12"
type Person struct {
Name string `url:"name,required"`
Address string `url:"address"`
}
func main() {
app := iris.Default()
app.Any("/", index)
app.Listen(":8080")
}
func index(ctx iris.Context) {
var person Person
if err := ctx.ReadQuery(&person); err!=nil {
ctx.StopWithError(iris.StatusBadRequest, err)
return
}
ctx.Application().Logger().Infof("Person: %#+v", person)
ctx.WriteString("Success")
}
根据客户端发送数据的内容类型将请求正文绑定到“ptr”,例如 JSON、XML、YAML、MessagePack、Protobuf、Form 和 URL Query。
package main
import (
"time"
"github.com/kataras/iris/v12"
)
type Person struct {
Name string `form:"name" json:"name" url:"name" msgpack:"name"`
Address string `form:"address" json:"address" url:"address" msgpack:"address"`
Birthday time.Time `form:"birthday" time_format:"2006-01-02" time_utc:"1" json:"birthday" url:"birthday" msgpack:"birthday"`
CreateTime time.Time `form:"createTime" time_format:"unixNano" json:"create_time" url:"create_time" msgpack:"createTime"`
UnixTime time.Time `form:"unixTime" time_format:"unix" json:"unix_time" url:"unix_time" msgpack:"unixTime"`
}
func main() {
app := iris.Default()
app.Any("/", index)
app.Listen(":8080")
}
func index(ctx iris.Context) {
var person Person
if err := ctx.ReadBody(&person); err!=nil {
ctx.StopWithError(iris.StatusBadRequest, err)
return
}
ctx.Application().Logger().Infof("Person: %#+v", person)
ctx.WriteString("Success")
}
测试它:
$ curl -X GET "localhost:8085/testing?name=kataras&address=xyz&birthday=1992-03-15&createTime=1562400033000000123&unixTime=1562400033"
package main
import "github.com/kataras/iris/v12"
type myParams struct {
Name string `param:"name"`
Age int `param:"age"`
Tail []string `param:"tail"`
}
// All parameters are required, as we already know,
// the router will fire 404 if name or int or tail are missing.
func main() {
app := iris.Default()
app.Get("/{name}/{age:int}/{tail:path}", func(ctx iris.Context) {
var p myParams
if err := ctx.ReadParams(&p); err != nil {
ctx.StopWithError(iris.StatusInternalServerError, err)
return
}
ctx.Writef("myParams: %#v", p)
})
app.Listen(":8088")
}
请求
$ curl -v http://localhost:8080/kataras/27/iris/web/framework
(adsbygoogle = window.adsbygoogle || []).push({});
package main
import "github.com/kataras/iris/v12"
type myHeaders struct {
RequestID string `header:"X-Request-Id,required"`
Authentication string `header:"Authentication,required"`
}
func main() {
app := iris.Default()
r.GET("/", func(ctx iris.Context) {
var hs myHeaders
if err := ctx.ReadHeaders(&hs); err != nil {
ctx.StopWithError(iris.StatusInternalServerError, err)
return
}
ctx.JSON(hs)
})
app.Listen(":8080")
}
请求
curl -H "x-request-id:373713f0-6b4b-42ea-ab9f-e2e04bc38e73" -H "authentication: Bearer my-token" \
http://localhost:8080
响应
{
"RequestID": "373713f0-6b4b-42ea-ab9f-e2e04bc38e73",
"Authentication": "Bearer my-token"
}
package main
import "github.com/kataras/iris/v12"
func main() {
app := iris.New()
app.RegisterView(iris.HTML("./templates", ".html"))
app.Get("/", showForm)
app.Post("/", handleForm)
app.Listen(":8080")
}
func showForm(ctx iris.Context) {
if err := ctx.View("form.html"); err!=nil {
ctx.HTML("%s
", err.Error())
return
}
}
type formExample struct {
Colors []string `form:"colors[]"` // or just "colors".
}
func handleForm(ctx iris.Context) {
var form formExample
err := ctx.ReadForm(&form)
if err != nil {
ctx.StopWithError(iris.StatusBadRequest, err)
return
}
ctx.JSON(iris.Map{"Colors": form.Colors})
}
templates/form.html
响应
{
"Colors": [
"red",
"green",
"blue"
]
}
详细示例可在此处找到。
func main() {
app := iris.New()
// iris.Map is an alias of map[string]interface{}
app.Get("/json", func(ctx iris.Context) {
ctx.JSON(iris.Map{"message": "hello", "status": iris.StatusOK})
})
// Use Secure field to prevent json hijacking.
// It prepends `"while(1),"` to the body when the data is array.
app.Get("/json_secure", func(ctx iris.Context) {
response := []string{"val1", "val2", "val3"}
options := iris.JSON{Indent: "", Secure: true}
ctx.JSON(response, options)
// Will output: while(1);["val1","val2","val3"]
})
// Use ASCII field to generate ASCII-only JSON
// with escaped non-ASCII characters.
app.Get("/json_ascii", func(ctx iris.Context) {
response := iris.Map{"lang": "GO-虹膜", "tag": "
"}
options := iris.JSON{Indent: " ", ASCII: true}
ctx.JSON(response, options)
/* Will output:
{
"lang": "GO-\u8679\u819c",
"tag": "\u003cbr\u003e"
}
*/
})
// Normally, JSON replaces special HTML characters with their unicode entities.
// If you want to encode such characters literally,
// you SHOULD set the UnescapeHTML field to true.
app.Get("/json_raw", func(ctx iris.Context) {
options := iris.JSON{UnescapeHTML: true}
ctx.JSON(iris.Map{
"html": "Hello, world!",
}, options)
// Will output: {"html":"Hello, world!"}
})
app.Get("/json_struct", func(ctx iris.Context) {
// You also can use a struct.
var msg struct {
Name string `json:"user"`
Message string
Number int
}
msg.Name = "Mariah"
msg.Message = "hello"
msg.Number = 42
// Note that msg.Name becomes "user" in the JSON.
// Will output: {"user": "Mariah", "Message": "hello", "Number": 42}
ctx.JSON(msg)
})
app.Get("/jsonp", func(ctx iris.Context) {
ctx.JSONP(iris.Map{"hello": "jsonp"}, iris.JSONP{Callback: "callbackName"})
})
app.Get("/xml", func(ctx iris.Context) {
ctx.XML(iris.Map{"message": "hello", "status": iris.StatusOK})
})
app.Get("/markdown", func(ctx iris.Context) {
ctx.Markdown([]byte("# Hello Dynamic Markdown -- iris"))
})
app.Get("/yaml", func(ctx iris.Context) {
ctx.YAML(iris.Map{"message": "hello", "status": iris.StatusOK})
})
app.Get("/msgpack", func(ctx iris.Context) {
u := User{
Firstname: "John",
Lastname: "Doe",
City: "Neither FBI knows!!!",
Age: 25,
}
ctx.MsgPack(u)
})
// Render using jsoniter instead of the encoding/json:
app.Listen(":8080", iris.WithOptimizations)
}
Iris 支持原生 protobuf with 和 protobuf to JSON 编码和解码。Protobuf
package main
import (
"app/protos"
"github.com/kataras/iris/v12"
)
func main() {
app := iris.New()
app.Get("/", send)
app.Get("/json", sendAsJSON)
app.Post("/read", read)
app.Post("/read_json", readFromJSON)
app.Listen(":8080")
}
func send(ctx iris.Context) {
response := &protos.HelloReply{Message: "Hello, World!"}
ctx.Protobuf(response)
}
func sendAsJSON(ctx iris.Context) {
response := &protos.HelloReply{Message: "Hello, World!"}
options := iris.JSON{
Proto: iris.ProtoMarshalOptions{
AllowPartial: true,
Multiline: true,
Indent: " ",
},
}
ctx.JSON(response, options)
}
func read(ctx iris.Context) {
var request protos.HelloRequest
err := ctx.ReadProtobuf(&request)
if err != nil {
ctx.StopWithError(iris.StatusBadRequest, err)
return
}
ctx.Writef("HelloRequest.Name = %s", request.Name)
}
func readFromJSON(ctx iris.Context) {
var request protos.HelloRequest
err := ctx.ReadJSONProtobuf(&request)
if err != nil {
ctx.StopWithError(iris.StatusBadRequest, err)
return
}
ctx.Writef("HelloRequest.Name = %s", request.Name)
}
func main() {
app := iris.New()
app.Favicon("./resources/favicon.ico")
app.HandleDir("/assets", iris.Dir("./assets"))
app.Listen(":8080")
}
该方法接受第三个可选参数:HandleDir``DirOptions
type DirOptions struct {
// Defaults to "/index.html", if request path is ending with **/*/$IndexName
// then it redirects to **/*(/) which another handler is handling it,
// that another handler, called index handler, is auto-registered by the framework
// if end developer does not managed to handle it by hand.
IndexName string
// PushTargets filenames (map's value) to
// be served without additional client's requests (HTTP/2 Push)
// when a specific request path (map's key WITHOUT prefix)
// is requested and it's not a directory (it's an `IndexFile`).
//
// Example:
// "/": {
// "favicon.ico",
// "js/main.js",
// "css/main.css",
// }
PushTargets map[string][]string
// PushTargetsRegexp like `PushTargets` but accepts regexp which
// is compared against all files under a directory (recursively).
// The `IndexName` should be set.
//
// Example:
// "/": regexp.MustCompile("((.*).js|(.*).css|(.*).ico)$")
// See `iris.MatchCommonAssets` too.
PushTargetsRegexp map[string]*regexp.Regexp
// Cache to enable in-memory cache and pre-compress files.
Cache DirCacheOptions
// When files should served under compression.
Compress bool
// List the files inside the current requested directory if `IndexName` not found.
ShowList bool
// If `ShowList` is true then this function will be used instead
// of the default one to show the list of files of a current requested directory(dir).
// See `DirListRich` package-level function too.
DirList DirListFunc
// Files downloaded and saved locally.
Attachments Attachments
// Optional validator that loops through each requested resource.
AssetValidator func(ctx *context.Context, name string) bool
}
详细了解文件服务器。
(adsbygoogle = window.adsbygoogle || []).push({});
SendFile(filename string, destinationName string) error
SendFileWithRate(src, destName string, limit float64, burst int) error
用法
强制将文件发送到客户端:
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("%s
", 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("%s
", err.Error())
return
}
// root binding as {{.Name}}
要**添加模板函数,**请使用首选视图引擎的方法。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("%s
", err.Error())
return
}
})
app.Listen(":8080")
}
Hi Page
{{.message}}
{{greet "to you"}}
在 http://localhost:8080 处打开浏览器选项卡。
渲染的结果将如下所示:
Hi Page
Hello world!
Greetings to you!
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()
// [...]
}
5.1 检索 User 值(当您在以下位置注册自定义用户结构的一部分时很有用):Options.AllowUsers
func routeHandler(ctx iris.Context) {
user := ctx.User().(*iris.SimpleUser)
// user.Username
// user.Password
}
在 _examples/auth 中阅读更多授权和身份验证示例。
在中间件或处理程序中启动新的 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")
}
在 _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", "[email protected]"))
}
自定义 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({});
完整的示例代码可以在 _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(``, 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 Web 令牌 (JWT) 是一种开放标准 (RFC 7519),它定义了一种紧凑且独立的方式,用于在各方之间以 JSON 对象的形式安全地传输信息。此信息可以验证和信任,因为它是经过数字签名的。JWT 可以使用密钥(使用 HMAC 算法)或使用 RSA 或 ECDSA 的公钥/私钥对进行签名。
以下是 JSON Web 令牌有用的一些方案:
授权:这是使用 JWT 的最常见方案。用户登录后,每个后续请求都将包含 JWT,允许用户访问使用该令牌允许的路由、服务和资源。单点登录是当今广泛使用 JWT 的一项功能,因为它的开销很小,并且能够跨不同域轻松使用。
信息交换:JSON Web 令牌是在各方之间安全传输信息的好方法。由于 JWT 可以签名(例如,使用公钥/私钥对),因此您可以确定发件人是他们所说的人。此外,由于签名是使用标头和有效负载计算的,因此您还可以验证内容是否未被篡改。
阅读更多关于智威汤逊的信息: https://jwt.io/introduction/
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")
您可能希望在应用程序可执行文件中使用新的嵌入指令嵌入区域设置。
import (
"embed"
)
//go:embed embedded/locales/*
var embeddedFS embed.FS
Load``LoadFS
err := app.I18n.LoadFS(embeddedFS, "./embedded/locales/*/*.ini", "en-US", "el-GR")
// OR to load all languages by filename:
// app.I18n.LoadFS(embeddedFS, "./embedded/locales/*/*.ini")
// Then set the default language using:
// app.I18n.SetDefault("en-US")
语言环境文件可以以 YAML(推荐)、JSON、TOML 或 INI 形式编写。
每个文件都应包含密钥。键也可以有子键(我们称之为“部分”)。
每个键的值应具有形式或包含其翻译文本(或模板)或/及其复数键值。string``map
Iris i18n模块支持开箱即用的复数,见下文。
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 是一个数字"
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"
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("%s
", 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("%s
", 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
http://localhost:8080/
http://localhost:8080/some-path
http://localhost:8080/other
http://localhost:8080/templates
这就是关于虹膜的所有基础知识。本文档涵盖了足够的初学者。想成为专家和认证鸢尾花开发人员,了解 MVC、i18n、依赖注入、gRPC、lambda 函数、websockets、最佳实践等吗?立即申请虹膜电子书,参与虹膜的开发!