The fastest HTTP/2 Go Web Framework. New, modern and easy to learn. Fast development with Code you control. Unbeatable cost-performance ratio 🚀
Go to file
2018-08-04 05:50:22 +03:00
_benchmarks fix _benchmarks/iris-mvc-templates 2017-12-31 20:33:33 +02:00
_examples NEW TUTORIAL: Writing an API for Apache Kafka with Iris: _examples/tutorial/api-for-apache-kafka. Hope you will enjoy it as I did the whole night:) 2018-08-04 05:50:22 +03:00
.github Sessions are now in full sync with the registered database, on acquire(init), set, get, delete, clear, visit, len, release(destroy) as requested by almost everyone. https://github.com/kataras/iris/issues/969 2018-04-22 13:52:36 +03:00
cache misspell fixes 2018-03-21 08:59:37 +02:00
context NEW TUTORIAL: Writing an API for Apache Kafka with Iris: _examples/tutorial/api-for-apache-kafka. Hope you will enjoy it as I did the whole night:) 2018-08-04 05:50:22 +03:00
core update deps and move one vendor to our organisation for making a new branch in order to solve this: https://github.com/kataras/iris/issues/1040, although it's not ours problem... 2018-07-15 16:09:03 +03:00
hero add vscode extension link and badge | Some internal improvements (not completed yet) 2018-01-25 03:16:49 +02:00
httptest Some minor but helpful additions, like CookieOption. Relative: https://github.com/kataras/iris/issues/1018. Simple cookies example added too. Cookie encoding (side by side with the already session's cookie id encoding) and version upgrade will come tomorrow with a new HISTORY.md entry as well, stay tuned! 2018-06-02 07:28:40 +03:00
middleware middleware/logger: new configuration field, defaults to false: Query bool, if true prints the full path, including the URL query as requested at https://github.com/kataras/iris/issues/1017 2018-05-31 01:39:23 +03:00
mvc remove the complicated fallback handlers, that didn't work and not approve the coblexity addition of the https://github.com/kataras/iris/pull/919, RouteExists accepts first argument the Context, add new AllowMethods per party and fix cors by 048e2be034 https://github.com/kataras/iris/issues/922, rel: https://github.com/iris-contrib/middleware/issues/36, https://github.com/iris-contrib/middleware/issues/34, https://github.com/iris-contrib/middleware/issues/32, https://github.com/iris-contrib/middleware/issues/30, https://github.com/kataras/iris/pull/909 2018-03-10 14:22:56 +02:00
sessions update deps and move one vendor to our organisation for making a new branch in order to solve this: https://github.com/kataras/iris/issues/1040, although it's not ours problem... 2018-07-15 16:09:03 +03:00
typescript add a code snippet as a very simplistic overview and update the license dates from 2017 to 2017-2018 2017-12-31 06:45:12 +02:00
view Update vendor for Pug (Jade) Parser and add Iris + Pug examples in the _examples/view folder, relative to https://github.com/kataras/iris/issues/1003 2018-05-26 22:49:48 +03:00
websocket Minified the client.js using UglifyJs 2018-07-30 14:27:28 +05:30
.gitattributes update protobuf vendor for badger session database 2018-06-24 14:24:58 +03:00
.gitignore update protobuf vendor for badger session database 2018-06-24 14:24:58 +03:00
.travis.yml Sessions are now in full sync with the registered database, on acquire(init), set, get, delete, clear, visit, len, release(destroy) as requested by almost everyone. https://github.com/kataras/iris/issues/969 2018-04-22 13:52:36 +03:00
AUTHORS Update to version 8.5.5 | Read HISTORY.md 2017-11-02 05:54:33 +02:00
CODE_OF_CONDUCT.md Update to version 8.5.5 | Read HISTORY.md 2017-11-02 05:54:33 +02:00
configuration_test.go Do not create the global configuration in homedir by-default, as, correctly, requested on chat. Use iris.WithGlobalConfiguration with a pre-defined iris.yml file on your home directory, existing users will not have any issues, new one will have to create the file before 2018-03-27 09:59:29 +03:00
configuration.go Do not create the global configuration in homedir by-default, as, correctly, requested on chat. Use iris.WithGlobalConfiguration with a pre-defined iris.yml file on your home directory, existing users will not have any issues, new one will have to create the file before 2018-03-27 09:59:29 +03:00
CONTRIBUTING.md Sessions are now in full sync with the registered database, on acquire(init), set, get, delete, clear, visit, len, release(destroy) as requested by almost everyone. https://github.com/kataras/iris/issues/969 2018-04-22 13:52:36 +03:00
deprecated.go remove old deprecated code from previous version, no need to keep backward compatibility, it was two months ago v10.0.0 released so we should be ok now 2018-02-12 06:03:15 +02:00
doc.go Update to version 10.6.6. Read https://github.com/kataras/iris/blob/master/HISTORY.md#tu-05-june-2018--v1066 for more 2018-06-05 01:24:35 +03:00
Dockerfile Update to version 8.5.5 | Read HISTORY.md 2017-11-02 05:54:33 +02:00
Dockerfile.build no need to default the Configuration.RemoteAddrHeaders to a list of 'dissalowed' header names, the ctx.RemoteAddr() made unnecessary checks if no X-header was passed, even if they defaulted to false, this will not give a crazy improvement but it's a good practise 2018-01-25 06:31:05 +02:00
FAQ.md add vscode extension link and badge | Some internal improvements (not completed yet) 2018-01-25 03:16:49 +02:00
go19.go Some minor but helpful additions, like CookieOption. Relative: https://github.com/kataras/iris/issues/1018. Simple cookies example added too. Cookie encoding (side by side with the already session's cookie id encoding) and version upgrade will come tomorrow with a new HISTORY.md entry as well, stay tuned! 2018-06-02 07:28:40 +03:00
Gopkg.lock update deps and move one vendor to our organisation for making a new branch in order to solve this: https://github.com/kataras/iris/issues/1040, although it's not ours problem... 2018-07-15 16:09:03 +03:00
Gopkg.toml update deps and move one vendor to our organisation for making a new branch in order to solve this: https://github.com/kataras/iris/issues/1040, although it's not ours problem... 2018-07-15 16:09:03 +03:00
HISTORY_GR.md Update to version 10.6.6. Read https://github.com/kataras/iris/blob/master/HISTORY.md#tu-05-june-2018--v1066 for more 2018-06-05 01:24:35 +03:00
HISTORY_ID.md Update to version 10.6.6. Read https://github.com/kataras/iris/blob/master/HISTORY.md#tu-05-june-2018--v1066 for more 2018-06-05 01:24:35 +03:00
HISTORY_ZH.md Update to version 10.6.6. Read https://github.com/kataras/iris/blob/master/HISTORY.md#tu-05-june-2018--v1066 for more 2018-06-05 01:24:35 +03:00
HISTORY.md Update to version 10.6.6. Read https://github.com/kataras/iris/blob/master/HISTORY.md#tu-05-june-2018--v1066 for more 2018-06-05 01:24:35 +03:00
iris.go Cookies: Ability to set custom cookie encoders to encode the cookie's value before sent by ctx.SetCookie and ctx.SetCookieKV and cookie decoders to decode the cookie's value when retrieving from ctx.GetCookie. That was the second and final part relative to a community's question at: https://github.com/kataras/iris/issues/1018 2018-06-02 16:35:18 +03:00
LICENSE add a code snippet as a very simplistic overview and update the license dates from 2017 to 2017-2018 2017-12-31 06:45:12 +02:00
README_GR.md Update to version 10.6.6. Read https://github.com/kataras/iris/blob/master/HISTORY.md#tu-05-june-2018--v1066 for more 2018-06-05 01:24:35 +03:00
README_ID.md Update to version 10.6.6. Read https://github.com/kataras/iris/blob/master/HISTORY.md#tu-05-june-2018--v1066 for more 2018-06-05 01:24:35 +03:00
README_JPN.md Update to version 10.6.6. Read https://github.com/kataras/iris/blob/master/HISTORY.md#tu-05-june-2018--v1066 for more 2018-06-05 01:24:35 +03:00
README_PT_BR.md Update to version 10.6.6. Read https://github.com/kataras/iris/blob/master/HISTORY.md#tu-05-june-2018--v1066 for more 2018-06-05 01:24:35 +03:00
README_RU.md Update to version 10.6.6. Read https://github.com/kataras/iris/blob/master/HISTORY.md#tu-05-june-2018--v1066 for more 2018-06-05 01:24:35 +03:00
README_ZH.md Update to version 10.6.6. Read https://github.com/kataras/iris/blob/master/HISTORY.md#tu-05-june-2018--v1066 for more 2018-06-05 01:24:35 +03:00
README.md add some of the _examples to the README, nothing crazy here 2018-08-03 13:45:05 +03:00
VERSION Update to version 10.6.6. Read https://github.com/kataras/iris/blob/master/HISTORY.md#tu-05-june-2018--v1066 for more 2018-06-05 01:24:35 +03:00

Iris Web Framework

build status report card vscode-iris chat view examples release

Iris is a fast, simple yet fully featured and very efficient web framework for Go.

Iris provides a beautifully expressive and easy to use foundation for your next website or API.

Iris offers a complete and decent solution and support for all gophers around the globe.

Learn what others say about Iris and star this github repository to stay up to date.

Installation

The only requirement is the Go Programming Language

$ go get -u github.com/kataras/iris

Iris takes advantage of the vendor directory feature. You get truly reproducible builds, as this method guards against upstream renames and deletes.

Iris vs .NET Core(C#) vs Node.js (Express)

Updated at: Tuesday, 21 November 2017

Benchmarks from third-party source over the rest web frameworks

Comparison with other frameworks

Known issues for code editors and IDEs at general

VS Code

For some reason the latest vscode-go language extension does not provide enough intelligence for the iris.Context type alias (input parameters documentation and definition navigation). Probably you have already experienced this issue with other Go libraries too, it is not an iris-specific issue, it is a general issue for all Golang type aliases.

Therefore if you use VS Code and you need these editor's features, import the original path; add an extra import statement of the original path of the Context, that will do it:

import (
    "github.com/kataras/iris"
    "github.com/kataras/iris/context" // <- HERE
)

Philosophy

The Iris philosophy is to provide robust tooling for HTTP, making it a great solution for single page applications, web sites, hybrids, or public HTTP APIs. Keep note that, so far, iris is the fastest web framework ever created in terms of performance.

Iris does not force you to use any specific ORM or template engine. With support for the most used template engines, you can quickly craft the perfect application.

Quick start

# assume the following codes in example.go file
$ cat example.go
package main

import "github.com/kataras/iris"

func main() {
    app := iris.Default()
    app.Get("/ping", func(ctx iris.Context) {
        ctx.JSON(iris.Map{
            "message": "pong",
        })
    })
    // listen and serve on http://0.0.0.0:8080.
    app.Run(iris.Addr(":8080"))
}
# run example.go and visit http://0.0.0.0:8080/ping on browser
$ go run example.go

API Examples

Using Get, Post, Put, Patch, Delete and Options

func main() {
    // Creates an application with default middleware:
    // logger and recovery (crash-free) middleware.
    app := iris.Default()

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

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

Parameters in path

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

    // This handler will match /user/john but will not match neither /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.
    app.Post("/user/{name:string}/{action:path}", func(ctx iris.Context) {
        name := ctx.Params().Get("name")
        action := ctx.Params().Get("action")
        message := name + " is " + action
        ctx.WriteString(message)
    })

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

If parameter type is missing then defaults to string, therefore {name:string} and {name} do the same exactly thing.

Learn more about path parameter's types by navigating here.

Querystring parameters

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")
        // shortcut for ctx.Request().URL.Query().Get("lastname").
        lastname := ctx.URLParam("lastname") 

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

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

Multipart/Urlencoded Form

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"))
}

Another example: query + post form

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")
        // or `ctx.PostValue` for POST, PUT & PATCH-only HTTP Methods.

        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

Upload files

Single file

Detail example code.

const maxSize = 5 << 20 // 5MB

func main() {
    app.Post("/upload", func(ctx iris.Context) {
        // Get the file from the request.
        file, info, err := ctx.FormFile("file")
        if err != nil {
            ctx.StatusCode(iris.StatusInternalServerError)
            ctx.HTML("Error while uploading: <b>" + err.Error() + "</b>")
            return
        }

        defer file.Close()
        fname := info.Filename

        // Create a file with the same name
        // assuming that you have a folder named 'uploads'
        out, err := os.OpenFile("./uploads/"+fname,
            os.O_WRONLY|os.O_CREATE, 0666)

        if err != nil {
            ctx.StatusCode(iris.StatusInternalServerError)
            ctx.HTML("Error while uploading: <b>" + err.Error() + "</b>")
            return
        }
        defer out.Close()

        io.Copy(out, file)
    })

    // start the server at http://localhost:8080 with post limit at 5 MB
    // (defaults to 32MB, read below).
    app.Run(iris.Addr(":8080"), iris.WithPostMaxMemory(maxSize))
}
curl -X POST http://localhost:8080/upload \
  -F "file=@./myfile.zip" \
  -H "Content-Type: multipart/form-data"
  • The default post max size is 32MB, you can extend it to read more data using the iris.WithPostMaxMemory(maxSize) configurator at app.Run, note that this will not be enough for your needs, read below.

  • The faster way to check the size is using the ctx.GetContentLength() which returns the whole request's size (plus a logical number like 2MB or even 10MB for the rest of the size like headers). You can create a middleware to adapt this to any necessary handler.

myLimiter := func(ctx iris.Context) {
    if ctx.GetContentLength() > maxSize { // + 2 << 20 {
        ctx.StatusCode(iris.StatusRequestEntityTooLarge)
        return
    }
    ctx.Next()
}

app.Post("/upload", myLimiter, myUploadHandler)
  • Most clients will set the "Content-Length" header (like browsers) but it's always better to make sure that any client can't send data that your server can't or doesn't want to handle. This can be happen using the app.Use(LimitRequestBodySize(maxSize)) (as app or route middleware) or the ctx.SetMaxRequestBodySize(maxSize) to limit the request based on a customized logic inside a particular handler, they're the same, read below.

  • You can force-limit the request body size inside a handler using the ctx.SetMaxRequestBodySize(maxSize), this will force the connection to close if the incoming data are larger (most clients will receive it as "connection reset"), use that to make sure that the client will not send data that your server can't or doesn't want to accept, as a fallback.

app.Post("/upload", iris.LimitRequestBodySize(maxSize), myUploadHandler)

OR

app.Post("/upload", func(ctx iris.Context){
    ctx.SetMaxRequestBodySize(maxSize)

    // [...]
})
  • Another way is to receive the data and check the second return value's Size value of the ctx.FormFile, i.e info.Size, this will give you the exact file size, not the whole incoming request data length.
app.Post("/upload", func(ctx iris.Context){
    file, info, err := ctx.FormFile("file")
    if err != nil {
        ctx.StatusCode(iris.StatusInternalServerError)
        ctx.HTML("Error while uploading: <b>" + err.Error() + "</b>")
        return
    }

    defer file.Close()

    if info.Size > maxSize {
        ctx.StatusCode(iris.StatusRequestEntityTooLarge)
        return
    }

    // [...]
})

Multiple files (easy way)

See the detail example code.

func main() {
    app := iris.Default()
    app.Post("/upload", func(ctx iris.Context) {
        //
        // UploadFormFiles
        // uploads any number of incoming files ("multiple" property on the form input).
        //

        // The second, optional, argument
        // can be used to change a file's name based on the request,
        // at this example we will showcase how to use it
        // by prefixing the uploaded file with the current user's ip.
        ctx.UploadFormFiles("./uploads", beforeSave)
    })

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

func beforeSave(ctx iris.Context, file *multipart.FileHeader) {
    ip := ctx.RemoteAddr()
    // make sure you format the ip in a way
    // that can be used for a file name (simple case):
    ip = strings.Replace(ip, ".", "_", -1)
    ip = strings.Replace(ip, ":", "_", -1)

    // you can use the time.Now, to prefix or suffix the files
    // based on the current time as well, as an exercise.
    // i.e unixTime :=	time.Now().Unix()
    // prefix the Filename with the $IP-
    // no need for more actions, internal uploader will use this
    // name to save the file into the "./uploads" folder.
    file.Filename = ip + "-" + file.Filename
}

Multiple files (manual way)

app.Post("/upload_manual", func(ctx iris.Context) {
    r := ctx.Request()
    // Get the max post value size passed via iris.WithPostMaxMemory.
    maxSize := ctx.Application().ConfigurationReadOnly().GetPostMaxMemory()

    err := r.ParseMultipartForm(maxSize)
    if err != nil {
        ctx.StatusCode(iris.StatusInternalServerError)
        ctx.WriteString(err.Error())
        return
    }

    form := r.MultipartForm

    files := form.File["files[]"]
    failures := 0
    for _, file := range files {
        _, err = saveUploadedFile(file, "./uploads")
        if err != nil {
            failures++
            ctx.Writef("failed to upload: %s\n", file.Filename)
        }
    }
    ctx.Writef("%d files uploaded", len(files)-failures)
})
func saveUploadedFile(fh *multipart.FileHeader, destDirectory string) (int64, error) {
    src, err := fh.Open()
    if err != nil {
        return 0, err
    }
    defer src.Close()

    out, err := os.OpenFile(filepath.Join(destDirectory, fh.Filename),
        os.O_WRONLY|os.O_CREATE, os.FileMode(0666))

    if err != nil {
        return 0, err
    }
    defer out.Close()

    return io.Copy(out, src)
}

How to curl:

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

Grouping routes

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.Run(iris.Addr(":8080"))
}

Blank Iris without middleware by default

Use

app := iris.New()

instead of

// Default with the Logger and Recovery middleware already attached.
app := iris.Default()

Using middleware

import (
    "github.com/kataras/iris"

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

func main() {
    // Creates an application without any middleware by default.
    app := iris.New()

    // Recover middleware recovers from any panics and writes a 500 if there was one.
    app.Use(recover.New())

    requestLogger := logger.New(logger.Config{
        // Status displays status code
        Status: true,
        // IP displays request's remote address
        IP: true,
        // Method displays the http method
        Method: true,
        // Path displays the request path
        Path: true,
        // Query appends the url query to the Path.
        Query: true,

        // if !empty then its contents derives from `ctx.Values().Get("logger_message")
        // will be added to the logs.
        MessageContextKeys: []string{"logger_message"},

        // if !empty then its contents derives from `ctx.GetHeader("User-Agent")
        MessageHeaderKeys: []string{"User-Agent"},
    })
    app.Use(requestLogger)

    // Per route middleware, you can add as many as you desire.
    app.Get("/benchmark", MyBenchLogger(), benchEndpoint)

    // Authorization party /user.
    // authorized := app.Party("/user", AuthRequired())
    // exactly the same as:
    authorized := app.Party("/user")
    // per party middleware! in this case we use the custom created
    // AuthRequired() middleware just in the "authorized" group/party.
    authorized.Use(AuthRequired())
    {
        authorized.Post("/login", loginEndpoint)
        authorized.Post("/submit", submitEndpoint)
        authorized.Post("/read", readEndpoint)

        // nested group: /user/testing
        testing := authorized.Party("/testing")
        testing.Get("/analytics", analyticsEndpoint)
    }

    // Listen and serve on http://0.0.0.0:8080
    app.Run(iris.Addr(":8080"))
}

How to write log file

package main

import (
	"os"
	"time"

	"github.com/kataras/iris"
)

// Get a filename based on the date, just for the sugar.
func todayFilename() string {
    today := time.Now().Format("Jan 02 2006")
    return today + ".txt"
}

func newLogFile() *os.File {
    filename := todayFilename()
    // Open the file, this will append to the today's file if server restarted.
    f, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
    if err != nil {
        panic(err)
    }

    return f
}

func main() {
    f := newLogFile()
    defer f.Close()

    app := iris.New()
    // Attach the file as logger, remember, iris' app logger is just an io.Writer.
    // Use the following code if you need to write the logs to file and console at the same time.
    // app.Logger().SetOutput(io.MultiWriter(f, os.Stdout))
    app.Logger().SetOutput(f)

    app.Get("/ping", func(ctx iris.Context) {
        // for the sake of simplicity, in order see the logs at the ./_today_.txt
        ctx.Application().Logger().Infof("Request path: %s", ctx.Path())
        ctx.WriteString("pong")
    })

    // Navigate to http://localhost:8080/ping
    // and open the ./logs{TODAY}.txt file.
    if err := app.Run(iris.Addr(":8080"), iris.WithoutBanner, iris.WithoutServerError(iris.ErrServerClosed)); err != nil {
        app.Logger().Warn("Shutdown with error: " + err.Error())
    }
}

Model binding and validation

Iris uses go-playground/validator.v9 for validation. Check the full docs on tags usage here.

Note that you need to set the corresponding binding tag on all fields you want to bind. For example, when binding from JSON, set json:"fieldname".

package main

import (
    "fmt"

    "github.com/kataras/iris"
    "gopkg.in/go-playground/validator.v9"
)

// User contains user information.
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"` // a person can have a home and cottage...
}

// 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"`
}

// Use a single instance of Validate, it caches struct info.
var validate *validator.Validate

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

    // Register validation for 'User'
    // NOTE: only have to register a non-pointer type for 'User', validator
    // interanlly dereferences during it's type checks.
    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.
        }

        // Returns InvalidValidationError for bad validation input, nil or ValidationErrors ( []FieldError )
        err := validate.Struct(user)
        if err != nil {

            // This check is only needed when your code could produce
            // an invalid value for validation such as interface with nil
            // value most including myself do not usually have code like this.
            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()) // Can differ when a custom TagNameFunc is registered or.
                fmt.Println(err.StructField())     // By passing alt name to ReportError like below.
                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()

                // Or collect these as json objects
                // and send back to the client the collected errors via ctx.JSON
                // {
                // 	"namespace":        err.Namespace(),
                // 	"field":            err.Field(),
                // 	"struct_namespace": err.StructNamespace(),
                // 	"struct_field":     err.StructField(),
                // 	"tag":              err.Tag(),
                // 	"actual_tag":       err.ActualTag(),
                // 	"kind":             err.Kind().String(),
                // 	"type":             err.Type().String(),
                // 	"value":            fmt.Sprintf("%v", err.Value()),
                // 	"param":            err.Param(),
                // }
            }

            // from here you can create your own error messages in whatever language you wish.
            return
        }

        // save user to database.
    })

    // use Postman or whatever to do a POST request
    // to the http://localhost:8080/user with RAW BODY:
    /*
        {
            "fname": "",
            "lname": "",
            "age": 45,
            "email": "mail@example.com",
            "favColor": "#000",
            "addresses": [{
                "street": "Eavesdown Docks",
                "planet": "Persphone",
                "phone": "none",
                "city": "Unknown"
            }]
        }
    */
    // Content-Type to application/json (optionally but good practise).
    // This request will fail due to the empty `User.FirstName` (fname in json)
    // and `User.LastName` (lname in json).
    // Check your iris' application terminal output.
    app.Run(iris.Addr(":8080"), iris.WithoutServerError(iris.ErrServerClosed))
}

// UserStructLevelValidation contains custom struct level validations that don't always
// make sense at the field validation level. For Example this function validates that either
// FirstName or LastName exist; could have done that with a custom field validation but then
// would have had to add it to both fields duplicating the logic + overhead, this way it's
// only validated once.
//
// NOTE: you may ask why wouldn't I just do this outside of validator, because doing this way
// hooks right into validator and you can combine with validation tags and still have a
// common error output format.
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", "")
    }

    // plus can to more, even with different tag than "fnameorlname".
}

Testing

First, let's write a simple application which will make use of the HTTP Cookies.

$ cat _examples/cookies/basic/main.go
package main

import "github.com/kataras/iris"

func newApp() *iris.Application {
    app := iris.New()

    // Set A Cookie.
    app.Get("/cookies/{name}/{value}", func(ctx iris.Context) {
        name := ctx.Params().Get("name")
        value := ctx.Params().Get("value")

        ctx.SetCookieKV(name, value)

        ctx.Writef("cookie added: %s = %s", name, value)
    })

    // Retrieve A Cookie.
    app.Get("/cookies/{name}", func(ctx iris.Context) {
        name := ctx.Params().Get("name")

        value := ctx.GetCookie(name)

        ctx.WriteString(value)
    })

    // Delete A Cookie.
    app.Delete("/cookies/{name}", func(ctx iris.Context) {
        name := ctx.Params().Get("name")

        ctx.RemoveCookie(name)

        ctx.Writef("cookie %s removed", name)
    })

    return app
}

func main() {
    app := newApp()

    // GET:    http://localhost:8080/cookies/my_name/my_value
    // GET:    http://localhost:8080/cookies/my_name
    // DELETE: http://localhost:8080/cookies/my_name
    app.Run(iris.Addr(":8080"))
}
  • Alternatively, use a regular http.Cookie: ctx.SetCookie(&http.Cookie{...})
  • If you want to set custom the path: ctx.SetCookieKV(name, value, iris.CookiePath("/custom/path/cookie/will/be/stored")).
  • If you want to be available only to the current request path: ctx.SetCookieKV(name, value, iris.CookieCleanPath /* or iris.CookiePath("") */)
    • iris.CookieExpires(time.Duration)
    • iris.CookieHTTPOnly(false)
  • ctx.Request().Cookie(name) is also available, it's the net/http approach
  • Learn more about path parameter's types by clicking here.

httptest

Next, is the critical part of this section, the HTTP Testing.

Iris offers an incredible support for the httpexpect, a Testing Framework for web applications. However, you are able to use the standard Go's net/http/httptest package as well but in this example we will use the kataras/iris/httptest.

package main

import (
    "fmt"
    "testing"

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

// go test -v -run=TestCookiesBasic$
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)
    t1.Cookie(cookieName).Value().Equal(cookieValue) // validate cookie's existence, it should be there now.
    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()
}

Learn

First of all, the most correct way to begin with a web framework is to learn the basics of the programming language and the standard http capabilities, if your web application is a very simple personal project without performance and maintainability requirements you may want to proceed just with the standard packages. After that follow the guidelines:

  • Navigate through 100+1 examples and some iris starter kits we crafted for you
  • Read the godocs for any details
  • Prepare a cup of coffee or tea, whatever pleases you the most, and read some articles we found for you

Iris starter kits

  1. A basic web app built in Iris for Go
  2. A mini social-network created with the awesome Iris💖💖
  3. Iris isomorphic react/hot reloadable/redux/css-modules starter kit
  4. Demo project with react using typescript and Iris
  5. Self-hosted Localization Management Platform built with Iris and Angular
  6. Iris + Docker and Kubernetes
  7. Quickstart for Iris with Nanobox
  8. A Hasura starter project with a ready to deploy Golang hello-world web app with IRIS

Did you build something similar? Let us know!

Middleware

Iris has a great collection of handlers[1][2] that you can use side by side with your web apps. However you are not limited to them - you are free to use any third-party middleware that is compatible with the net/http package, _examples/convert-handlers will show you the way.

Iris, unlike others, is 100% compatible with the standards and that's why the majority of the big companies that adapt Go to their workflow, like a very famous US Television Network, trust Iris; it's up-to-date and it will be always aligned with the std net/http package which is modernized by the Go Authors on each new release of the Go Programming Language.

Articles

Video Courses

Support

  • HISTORY file is your best friend, it contains information about the latest features and changes
  • Did you happen to find a bug? Post it at github issues
  • Do you have any questions or need to speak with someone experienced to solve a problem at real-time? Join us to the community chat
  • Complete our form-based user experience report by clicking here
  • Do you like the framework? Tweet something about it! The People have spoken:

Get hired

There are many companies and start-ups looking for Go web developers with Iris experience as requirement, we are searching for you every day and we post those information via our facebook page, like the page to get notified, we have already posted some of them.

Backers

Thank you to all our backers! 🙏 Become a backer



For more information about contributing to the Iris project please check the CONTRIBUTING.md file.

List of all Contributors

License

Iris is licensed under the 3-Clause BSD License. Iris is 100% free and open-source software.

For any questions regarding the license please send e-mail.