Research
Security News
Malicious npm Package Targets Solana Developers and Hijacks Funds
A malicious npm package targets Solana developers, rerouting funds in 2% of transactions to a hardcoded address.
github.com/unionmexicanadelnorte/iris
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.
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.
Updated at: Tuesday, 21 November 2017
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
)
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.
# 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
The package hero contains features for binding any object or functions that handlers
can use, these are called dependencies.
With Iris you get truly safe bindings thanks to the hero package. It is blazing-fast, near to raw handlers performance because Iris calculates everything before even server goes online!
Below you will see some screenshots I prepared for you in order to be easier to understand:
hero funcs
are very easy to understand and when you start using them you never go back.
With Iris you also get real and blazing-fast MVC support which uses "hero" under the hoods.
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"))
}
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.
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"))
}
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"))
}
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
package main
import (
"github.com/kataras/iris"
"github.com/kataras/iris/context"
)
func main() {
app := iris.New()
app.Get("/", func(ctx context.Context) /* or iris.Context, it's the same for Go 1.9+. */ {
// request header "referer" or url parameter "referer".
r := ctx.GetReferrer()
switch r.Type {
case context.ReferrerSearch:
ctx.Writef("Search %s: %s\n", r.Label, r.Query)
ctx.Writef("Google: %s\n", r.GoogleType)
case context.ReferrerSocial:
ctx.Writef("Social %s\n", r.Label)
case context.ReferrerIndirect:
ctx.Writef("Indirect: %s\n", r.URL)
}
})
app.Run(iris.Addr(":8080"))
}
How to curl
:
curl http://localhost:8080?referer=https://twitter.com/Xinterio/status/1023566830974251008
curl http://localhost:8080?referer=https://www.google.com/search?q=Top+6+golang+web+frameworks&oq=Top+6+golang+web+frameworks
const maxSize = 5 << 20 // 5MB
func main() {
app := iris.Default()
app.Post("/upload", iris.LimitRequestBodySize(maxSize), 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
}
How to curl
:
curl -X POST http://localhost:8080/upload \
-F "files[]=@./myfile.zip" \
-F "files[]=@./mysecondfile.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.Run(iris.Addr(":8080"))
}
Use
app := iris.New()
instead of
// Default with the Logger and Recovery middleware already attached.
app := iris.Default()
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"))
}
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.
app.Run(
iris.Addr(":8080"),
iris.WithoutBanner,
iris.WithoutVersionChecker,
iris.WithoutServerError(iris.ErrServerClosed),
)
}
Iris uses go-playground/validator.v9 for validation. Check the full docs on tags usage here.
Example detail code.
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"`
}
// 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())
fmt.Println(err.StructField())
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()
}
return
}
// save user to database.
})
app.Run(iris.Addr(":8080"))
}
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", "")
}
}
{
"fname": "",
"lname": "",
"age": 45,
"email": "mail@example.com",
"favColor": "#000",
"addresses": [{
"street": "Eavesdown Docks",
"planet": "Persphone",
"phone": "none",
"city": "Unknown"
}]
}
package main
import (
"fmt"
"github.com/kataras/iris"
"github.com/kataras/iris/websocket"
)
func main() {
app := iris.New()
app.Get("/", func(ctx iris.Context) {
ctx.ServeFile("websockets.html", false) // second parameter: enable gzip?
})
setupWebsocket(app)
// x2
// http://localhost:8080
// http://localhost:8080
// write something, press submit, see the result.
app.Run(iris.Addr(":8080"))
}
func setupWebsocket(app *iris.Application) {
// create our echo websocket server
ws := websocket.New(websocket.Config{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
})
ws.OnConnection(handleConnection)
// register the server on an endpoint.
// see the inline javascript code in the websockets.html,
// this endpoint is used to connect to the server.
app.Get("/echo", ws.Handler())
// serve the javascript built'n client-side library,
// see websockets.html script tags, this path is used.
app.Any("/iris-ws.js", websocket.ClientHandler())
}
func handleConnection(c websocket.Connection) {
// Read events from browser
c.On("chat", func(msg string) {
// Print the message to the console, c.Context() is the iris's http context.
fmt.Printf("%s sent: %s\n", c.Context().RemoteAddr(), msg)
// Write message back to the client message owner with:
// c.Emit("chat", msg)
// Write message to all except this client with:
c.To(websocket.Broadcast).Emit("chat", msg)
})
}
websockets.html
<!-- the message's input -->
<input id="input" type="text" />
<!-- when clicked then an iris websocket event will be sent to the server,
at this example we registered the 'chat' -->
<button onclick="send()">Send</button>
<!-- the messages will be shown here -->
<pre id="output"></pre>
<!-- import the iris client-side library for browser-->
<script src="/iris-ws.js"></script>
<script>
var scheme = document.location.protocol == "https:" ? "wss" : "ws";
var port = document.location.port ? (":" + document.location.port) : "";
// see app.Get("/echo", ws.Handler()) on main.go
var wsURL = scheme + "://" + document.location.hostname + port+"/echo";
var input = document.getElementById("input");
var output = document.getElementById("output");
// Ws comes from the auto-served '/iris-ws.js'
var socket = new Ws(wsURL)
socket.OnConnect(function () {
output.innerHTML += "Status: Connected\n";
});
socket.OnDisconnect(function () {
output.innerHTML += "Status: Disconnected\n";
});
// read events from the server
socket.On("chat", function (msg) {
addMessage(msg);
});
function send() {
addMessage("Me: " + input.value); // write ourselves
socket.Emit("chat", input.value); // send chat event data to the websocket server
input.value = ""; // clear the input
}
function addMessage(msg) {
output.innerHTML += msg + "\n";
}
</script>
Navigate to the _examples/websocket folder for more.
Are you looking about http sessions instead?
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"))
}
http.Cookie
: ctx.SetCookie(&http.Cookie{...})
ctx.SetCookieKV(name, value, iris.CookiePath("/custom/path/cookie/will/be/stored"))
.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
approachIris 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()
}
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:
Did you build something similar? Let us know!
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.
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.
Thank you to all our backers! 🙏 Become a backer
For more information about contributing to the Iris project please check the CONTRIBUTING.md file.
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.
FAQs
Unknown package
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
A malicious npm package targets Solana developers, rerouting funds in 2% of transactions to a hardcoded address.
Security News
Research
Socket researchers have discovered malicious npm packages targeting crypto developers, stealing credentials and wallet data using spyware delivered through typosquats of popular cryptographic libraries.
Security News
Socket's package search now displays weekly downloads for npm packages, helping developers quickly assess popularity and make more informed decisions.