I'm working hard on the dev branch for the next release of Iris.
Do you remember, last Christmas? I did publish the version 6 with net/http and HTTP/2 support, and you've embraced Iris with so much love, ultimately it was a successful move.
I tend to make surprises by giving you the most unique and useful features, especially on Christmas period.
This year, I intend to give you more gifts.
Don't worry, it will not contain any breaking changes, except of some MVC concepts that are re-designed.
The new Iris' MVC Ecosystem is ready on the dev/mvc. It contains features that you've never saw before, in any programming language framework. It is also, by far, the fastest MVC implementation ever created, very close to raw handlers - it's Iris, it's superior, we couldn't expect something different after all :) Treat that with respect as it treats you :)
I'm doing my bests to get it ready before Christmas.
Star or watch the repository to stay up to date and get ready for the most amazing features!
Yours faithfully, Gerasimos Maropoulos.
Iris
Iris is a fast, simple and efficient web framework for Go.
Iris provides a beautifully expressive and easy to use foundation for your next website, API, or distributed app.
Learn what others say about Iris and star this github repository to stay up to date.
Benchmarks from third-party source over the rest web frameworks
Updated at: Tuesday, 21 November 2017
Built with ♥️
We have no doubt you will able to find other web frameworks written in Go
and even put up a real fight to learn and use them for quite some time but
make no mistake, sooner or later you will be using Iris, not because of the ergonomic, high-performant solution that it provides but its well-documented unique features, as these will transform you to a real rockstar geek.
No matter what you're trying to build, Iris covers
every type of application, from micro services to large monolithic web applications.
It's actually the best piece of software for back-end web developers
you can find online.
Iris may have reached version 8, but we're not stopping there. We have many feature ideas on our board that we're anxious to add and other innovative web development solutions that we're planning to build into Iris.
Accelerated by KeyCDN, a simple, fast and reliable CDN.
We are developing this project using the best code editor for Golang; Visual Studio Code supported by Microsoft.
If you're coming from nodejs world, Iris is the expressjs equivalent for Gophers.
Table Of Content
Installation
The only requirement is the Go Programming Language
Iris takes advantage of the vendor directory feature. You get truly reproducible builds, as this method guards against upstream renames and deletes.
A simple copy-paste and go get ./...
to resolve two dependencies: kataras/golog and the iris-contrib/httpexpect will work for ever even for older versions, the newest version can be retrieved by go get
but this branch contains an older version of Iris.
Follow the instructions below:
- install the Go Programming Language: https://golang.org/dl
- clear yours previously
$GOPATH/src/github.com/kataras/iris
folder or create new - download the Iris v8.5.9 (final): https://github.com/kataras/iris/archive/v8.zip
- extract the contents of the
iris-v8
folder that's inside the downloaded zip file to your $GOPATH/src/github.com/kataras/iris
- navigate to your
$GOPATH/src/github.com/kataras/iris
folder if you're not already there and open a terminal/command prompt, execute the command: go get ./...
and you're ready to GO:)
Getting Started
package main
import "github.com/kataras/iris"
func main() {
app := iris.New()
app.RegisterView(iris.HTML("./views", ".html"))
app.Get("/", func(ctx iris.Context) {
ctx.ViewData("message", "Hello world!")
ctx.View("hello.html")
})
app.Get("/user/{id:long}", func(ctx iris.Context) {
userID, _ := ctx.Params().GetInt64("id")
ctx.Writef("User ID: %d", userID)
})
app.Run(iris.Addr(":8080"))
}
Learn more about path parameter's types by clicking here.
<html>
<head>
<title>Hello Page</title>
</head>
<body>
<h1>{{.message}}</h1>
</body>
</html>
Wanna re-start your app automatically when source code changes happens? Install the rizla tool and run rizla main.go
instead of go run main.go
.
Guidelines for bootstrapping applications can be found at the _examples/structuring.
Quick MVC Tutorial
package main
import (
"github.com/kataras/iris"
"github.com/kataras/iris/mvc"
)
func main() {
app := iris.New()
app.Controller("/helloworld", new(HelloWorldController))
app.Run(iris.Addr("localhost:8080"))
}
type HelloWorldController struct {
mvc.C
}
func (c *HelloWorldController) Get() string {
return "This is my default action..."
}
func (c *HelloWorldController) GetBy(name string) string {
return "Hello " + name
}
func (c *HelloWorldController) GetWelcome() (string, int) {
return "This is the GetWelcome action func...", iris.StatusOK
}
func (c *HelloWorldController) GetWelcomeBy(name string, numTimes int) {
c.Ctx.Writef("Hello %s, NumTimes is: %d", name, numTimes)
}
The _examples/mvc and mvc/controller_test.go files explain each feature with simple paradigms, they show how you can take advandage of the Iris MVC Binder, Iris MVC Models and many more...
Every exported
func prefixed with an HTTP Method(Get
, Post
, Put
, Delete
...) in a controller is callable as an HTTP endpoint. In the sample above, all funcs writes a string to the response. Note the comments preceding each method.
An HTTP endpoint is a targetable URL in the web application, such as http://localhost:8080/helloworld
, and combines the protocol used: HTTP, the network location of the web server (including the TCP port): localhost:8080
and the target URI /helloworld
.
The first comment states this is an HTTP GET method that is invoked by appending "/helloworld" to the base URL. The third comment specifies an HTTP GET method that is invoked by appending "/helloworld/welcome" to the URL.
Controller knows how to handle the "name" on GetBy
or the "name" and "numTimes" at GetWelcomeBy
, because of the By
keyword, and builds the dynamic route without boilerplate; the third comment specifies an HTTP GET dynamic method that is invoked by any URL that starts with "/helloworld/welcome" and followed by two more path parts, the first one can accept any value and the second can accept only numbers, i,e: "http://localhost:8080/helloworld/welcome/golang/32719", otherwise a 404 Not Found HTTP Error will be sent to the client instead.
Quick MVC Tutorial #2
Iris has a very powerful and blazing fast MVC support, you can return any value of any type from a method function
and it will be sent to the client as expected.
- if
string
then it's the body. - if
string
is the second output argument then it's the content type. - if
int
then it's the status code. - if
error
and not nil then (any type) response will be omitted and error's text with a 400 bad request will be rendered instead. - if
(int, error)
and error is not nil then the response result will be the error's text with the status code as int
. - if
bool
is false then it throws 404 not found http error by skipping everything else. - if
custom struct
or interface{}
or slice
or map
then it will be rendered as json, unless a string
content type is following. - if
mvc.Result
then it executes its Dispatch
function, so good design patters can be used to split the model's logic where needed.
The example below is not intended to be used in production but it's a good showcase of some of the return types we saw before;
package main
import (
"github.com/kataras/iris"
"github.com/kataras/iris/middleware/basicauth"
"github.com/kataras/iris/mvc"
)
type Movie struct {
Name string `json:"name"`
Year int `json:"year"`
Genre string `json:"genre"`
Poster string `json:"poster"`
}
var movies = []Movie{
{
Name: "Casablanca",
Year: 1942,
Genre: "Romance",
Poster: "https://iris-go.com/images/examples/mvc-movies/1.jpg",
},
{
Name: "Gone with the Wind",
Year: 1939,
Genre: "Romance",
Poster: "https://iris-go.com/images/examples/mvc-movies/2.jpg",
},
{
Name: "Citizen Kane",
Year: 1941,
Genre: "Mystery",
Poster: "https://iris-go.com/images/examples/mvc-movies/3.jpg",
},
{
Name: "The Wizard of Oz",
Year: 1939,
Genre: "Fantasy",
Poster: "https://iris-go.com/images/examples/mvc-movies/4.jpg",
},
}
var basicAuth = basicauth.New(basicauth.Config{
Users: map[string]string{
"admin": "password",
},
})
func main() {
app := iris.New()
app.Use(basicAuth)
app.Controller("/movies", new(MoviesController))
app.Run(iris.Addr(":8080"))
}
type MoviesController struct {
mvc.C
}
func (c *MoviesController) Get() []Movie {
return movies
}
func (c *MoviesController) GetBy(id int) Movie {
return movies[id]
}
func (c *MoviesController) PutBy(id int) Movie {
m := movies[id]
file, info, err := c.Ctx.FormFile("poster")
if err != nil {
c.Ctx.StatusCode(iris.StatusInternalServerError)
return Movie{}
}
file.Close()
poster := info.Filename
genre := c.Ctx.FormValue("genre")
m.Poster = poster
m.Genre = genre
movies[id] = m
return m
}
func (c *MoviesController) DeleteBy(id int) iris.Map {
deleted := movies[id].Name
movies = append(movies[:id], movies[id+1:]...)
return iris.Map{"deleted": deleted}
}
Quick MVC Tutorial #3
Nothing stops you from using your favorite folder structure. Iris is a low level web framework, it has got MVC first-class support but it doesn't limit your folder structure, this is your choice.
Structuring depends on your own needs. We can't tell you how to design your own application for sure but you're free to take a closer look to one typical example below;
Shhh, let's spread the code itself.
Data Model Layer
package datamodels
type Movie struct {
ID int64 `json:"id"`
Name string `json:"name"`
Year int `json:"year"`
Genre string `json:"genre"`
Poster string `json:"poster"`
}
Data Source / Data Store Layer
package datasource
import "github.com/kataras/iris/_examples/mvc/overview/datamodels"
var Movies = map[int64]datamodels.Movie{
1: {
ID: 1,
Name: "Casablanca",
Year: 1942,
Genre: "Romance",
Poster: "https://iris-go.com/images/examples/mvc-movies/1.jpg",
},
2: {
ID: 2,
Name: "Gone with the Wind",
Year: 1939,
Genre: "Romance",
Poster: "https://iris-go.com/images/examples/mvc-movies/2.jpg",
},
3: {
ID: 3,
Name: "Citizen Kane",
Year: 1941,
Genre: "Mystery",
Poster: "https://iris-go.com/images/examples/mvc-movies/3.jpg",
},
4: {
ID: 4,
Name: "The Wizard of Oz",
Year: 1939,
Genre: "Fantasy",
Poster: "https://iris-go.com/images/examples/mvc-movies/4.jpg",
},
5: {
ID: 5,
Name: "North by Northwest",
Year: 1959,
Genre: "Thriller",
Poster: "https://iris-go.com/images/examples/mvc-movies/5.jpg",
},
}
Repositories
The layer which has direct access to the "datasource" and can manipulate data directly.
package repositories
import (
"errors"
"sync"
"github.com/kataras/iris/_examples/mvc/overview/datamodels"
)
type Query func(datamodels.Movie) bool
type MovieRepository interface {
Exec(query Query, action Query, limit int, mode int) (ok bool)
Select(query Query) (movie datamodels.Movie, found bool)
SelectMany(query Query, limit int) (results []datamodels.Movie)
InsertOrUpdate(movie datamodels.Movie) (updatedMovie datamodels.Movie, err error)
Delete(query Query, limit int) (deleted bool)
}
func NewMovieRepository(source map[int64]datamodels.Movie) MovieRepository {
return &movieMemoryRepository{source: source}
}
type movieMemoryRepository struct {
source map[int64]datamodels.Movie
mu sync.RWMutex
}
const (
ReadOnlyMode = iota
ReadWriteMode
)
func (r *movieMemoryRepository) Exec(query Query, action Query, actionLimit int, mode int) (ok bool) {
loops := 0
if mode == ReadOnlyMode {
r.mu.RLock()
defer r.mu.RUnlock()
} else {
r.mu.Lock()
defer r.mu.Unlock()
}
for _, movie := range r.source {
ok = query(movie)
if ok {
if action(movie) {
loops++
if actionLimit >= loops {
break
}
}
}
}
return
}
func (r *movieMemoryRepository) Select(query Query) (movie datamodels.Movie, found bool) {
found = r.Exec(query, func(m datamodels.Movie) bool {
movie = m
return true
}, 1, ReadOnlyMode)
if !found {
movie = datamodels.Movie{}
}
return
}
func (r *movieMemoryRepository) SelectMany(query Query, limit int) (results []datamodels.Movie) {
r.Exec(query, func(m datamodels.Movie) bool {
results = append(results, m)
return true
}, limit, ReadOnlyMode)
return
}
func (r *movieMemoryRepository) InsertOrUpdate(movie datamodels.Movie) (datamodels.Movie, error) {
id := movie.ID
if id == 0 {
var lastID int64
r.mu.RLock()
for _, item := range r.source {
if item.ID > lastID {
lastID = item.ID
}
}
r.mu.RUnlock()
id = lastID + 1
movie.ID = id
r.mu.Lock()
r.source[id] = movie
r.mu.Unlock()
return movie, nil
}
current, exists := r.Select(func(m datamodels.Movie) bool {
return m.ID == id
})
if !exists {
return datamodels.Movie{}, errors.New("failed to update a nonexistent movie")
}
if movie.Poster != "" {
current.Poster = movie.Poster
}
if movie.Genre != "" {
current.Genre = movie.Genre
}
r.mu.Lock()
r.source[id] = current
r.mu.Unlock()
return movie, nil
}
func (r *movieMemoryRepository) Delete(query Query, limit int) bool {
return r.Exec(query, func(m datamodels.Movie) bool {
delete(r.source, m.ID)
return true
}, limit, ReadWriteMode)
}
Services
The layer which has access to call functions from the "repositories" and "models" (or even "datamodels" if simple application). It should contain the most of the domain logic.
package services
import (
"github.com/kataras/iris/_examples/mvc/overview/datamodels"
"github.com/kataras/iris/_examples/mvc/overview/repositories"
)
type MovieService interface {
GetAll() []datamodels.Movie
GetByID(id int64) (datamodels.Movie, bool)
DeleteByID(id int64) bool
UpdatePosterAndGenreByID(id int64, poster string, genre string) (datamodels.Movie, error)
}
func NewMovieService(repo repositories.MovieRepository) MovieService {
return &movieService{
repo: repo,
}
}
type movieService struct {
repo repositories.MovieRepository
}
func (s *movieService) GetAll() []datamodels.Movie {
return s.repo.SelectMany(func(_ datamodels.Movie) bool {
return true
}, -1)
}
func (s *movieService) GetByID(id int64) (datamodels.Movie, bool) {
return s.repo.Select(func(m datamodels.Movie) bool {
return m.ID == id
})
}
func (s *movieService) UpdatePosterAndGenreByID(id int64, poster string, genre string) (datamodels.Movie, error) {
return s.repo.InsertOrUpdate(datamodels.Movie{
ID: id,
Poster: poster,
Genre: genre,
})
}
func (s *movieService) DeleteByID(id int64) bool {
return s.repo.Delete(func(m datamodels.Movie) bool {
return m.ID == id
}, 1)
}
View Models
There should be the view models, the structure that the client will be able to see.
Example:
import (
"github.com/kataras/iris/_examples/mvc/overview/datamodels"
"github.com/kataras/iris/context"
)
type Movie struct {
datamodels.Movie
}
func (m Movie) IsValid() bool {
return m.ID > 0
}
Iris is able to convert any custom data Structure into an HTTP Response Dispatcher,
so theoretically, something like the following is permitted if it's really necessary;
func (m Movie) Dispatch(ctx context.Context) {
if !m.IsValid() {
ctx.NotFound()
return
}
ctx.JSON(m, context.JSON{Indent: " "})
}
However, we will use the "datamodels" as the only one models package because
Movie structure doesn't contain any sensitive data, clients are able to see all of its fields
and we don't need any extra functionality or validation inside it.
Controllers
Handles web requests, bridge between the services and the client.
package controllers
import (
"errors"
"github.com/kataras/iris/_examples/mvc/overview/datamodels"
"github.com/kataras/iris/_examples/mvc/overview/services"
"github.com/kataras/iris"
"github.com/kataras/iris/mvc"
)
type MovieController struct {
mvc.C
Service services.MovieService
}
func (c *MovieController) Get() (results []datamodels.Movie) {
return c.Service.GetAll()
}
func (c *MovieController) GetBy(id int64) (movie datamodels.Movie, found bool) {
return c.Service.GetByID(id)
}
func (c *MovieController) PutBy(id int64) (datamodels.Movie, error) {
file, info, err := c.Ctx.FormFile("poster")
if err != nil {
return datamodels.Movie{}, errors.New("failed due form file 'poster' missing")
}
file.Close()
poster := info.Filename
genre := c.Ctx.FormValue("genre")
return c.Service.UpdatePosterAndGenreByID(id, poster, genre)
}
func (c *MovieController) DeleteBy(id int64) interface{} {
wasDel := c.Service.DeleteByID(id)
if wasDel {
return iris.Map{"deleted": id}
}
return iris.StatusBadRequest
}
package controllers
import (
"errors"
"github.com/kataras/iris/mvc"
)
type HelloController struct {
mvc.C
}
var helloView = mvc.View{
Name: "hello/index.html",
Data: map[string]interface{}{
"Title": "Hello Page",
"MyMessage": "Welcome to my awesome website",
},
}
func (c *HelloController) Get() mvc.Result {
return helloView
}
var errBadName = errors.New("bad name")
var badName = mvc.Response{Err: errBadName, Code: 400}
func (c *HelloController) GetBy(name string) mvc.Result {
if name != "iris" {
return badName
}
return mvc.View{
Name: "hello/name.html",
Data: name,
}
}
package middleware
import "github.com/kataras/iris/middleware/basicauth"
var BasicAuth = basicauth.New(basicauth.Config{
Users: map[string]string{
"admin": "password",
},
})
<html>
<head>
<title>{{.Title}} - My App</title>
</head>
<body>
<p>{{.MyMessage}}</p>
</body>
</html>
<html>
<head>
<title>{{.}}' Portfolio - My App</title>
</head>
<body>
<h1>Hello {{.}}</h1>
</body>
</html>
Navigate to the _examples/view for more examples
like shared layouts, tmpl funcs, reverse routing and more!
Main
This file creates any necessary component and links them together.
package main
import (
"github.com/kataras/iris/_examples/mvc/overview/datasource"
"github.com/kataras/iris/_examples/mvc/overview/repositories"
"github.com/kataras/iris/_examples/mvc/overview/services"
"github.com/kataras/iris/_examples/mvc/overview/web/controllers"
"github.com/kataras/iris/_examples/mvc/overview/web/middleware"
"github.com/kataras/iris"
)
func main() {
app := iris.New()
app.RegisterView(iris.HTML("./web/views", ".html"))
app.Controller("/hello", new(controllers.HelloController))
repo := repositories.NewMovieRepository(datasource.Movies)
movieService := services.NewMovieService(repo)
app.Controller("/movies", new(controllers.MovieController),
movieService,
middleware.BasicAuth)
app.Run(
iris.Addr("localhost:8080"),
iris.WithoutVersionChecker,
iris.WithoutServerError(iris.ErrServerClosed),
iris.WithOptimizations,
)
}
More folder structure guidelines can be found at the _examples/#structuring section.
Now you are ready to move to the next step and get closer to becoming a pro gopher
Congratulations, since you've made it so far, we've crafted just for you some next level content to turn you into a real pro gopher 😃
Don't forget to prepare yourself a cup of coffee, or tea, whatever enjoys you the most!
People
The author of Iris is @kataras, you can reach him via;
List of all Authors
List of all Contributors
Help this project to continue deliver awesome and unique features with the higher code quality as possible by donating any amount via PayPal or BTC.
For more information about contributing to the Iris project please check the CONTRIBUTING.md file.
We need your help with translations into your native language
Iris needs your help, please think about contributing to the translation of the README and https://iris-go.com, you will be rewarded.
Instructions can be found at: https://github.com/kataras/iris/issues/796
03, October 2017 | Iris User Experience Report
Be part of the first Iris User Experience Report by submitting a simple form, it won't take more than 2 minutes.
The form contains some questions that you may need to answer in order to learn more about you; learning more about you helps us to serve you with the best possible way!
https://docs.google.com/forms/d/e/1FAIpQLSdCxZXPANg_xHWil4kVAdhmh7EBBHQZ_4_xSZVDL-oCC_z5pA/viewform?usp=sf_link
Support this project by becoming a sponsor. Your logo will show up here with a link to your website. Become a sponsor
License
Iris is licensed under the 3-Clause BSD License. Iris is 100% open-source software.
For any questions regarding the license please contact us.