Iris version 11.2 released
Tags: go, webdev, opensource, github
Published: 2019-07-19 19:27:10

It is my pleasure and honor to spread the news about the new [Iris](https://github.com/kataras/iris) release. 

<a href="https://iris-go.com"> <img align="right" width="169px" src="https://iris-go.com/images/icon.svg?v=a" title="logo created by @merry.dii" /> </a>

As open-source project authors and/or managers, we owe a lot to our users - to our co end-developers that they learn and work with our project, no matter how big or small it is, spreading its potentials to their co-workers and learning the deep and nice parts of a whole programming language through our guideliness. 

We are all exciting when a feature request of us goes live to a popular project, right? This happens almost every week, to Iris, every week a new user feature request is discussed, accepted and finally implemented. Iris is not just another open source web framework written in Go. It is a Community.

This release is not an exception to that long-term tradition. Iris version 11.2 is done through [130 new commits](https://github.com/kataras/iris/compare/v11.1.1...v11.2.3) to the main repository and [151 commits](https://github.com/kataras/neffos/commits/v0.0.8) to its new websocket implementation, [neffos](https://github.com/kataras/neffos) repository:
- 17 bugfixes and minor improvements
    - 13 of 17 bugfixes and improvements are reported and requested by its end-users themselves!
- 5 new features and major improvements
- all examples and middlewares are updated and tested with go 1.12

---------------

Let's start with the most easy to use feature for your daily development with Iris.

## Automatic Public Address with TLS

Wouldn't be great to test your web application server in a more "real-world environment" like a public, remote, address instead of localhost?

There are plenty of third-party tools offering such a feature, but in my opinion, the [ngrok](https://github.com/inconshreveable/ngrok) one is the best among them. It's popular and tested for years, like Iris, in fact, it has ~600 stars more than Iris itself. Great job [@inconshreveable](https://github.com/inconshreveable/)!

Iris v11.2 offers ngrok integration. This feature is simple yet very powerful. It really helps when you want to quickly show your development progress to your colleagues or the project leader at a remote conference.

Follow the steps below to, temporarily, convert your local Iris web server to a public one.

1. Go head and [download ngrok](https://ngrok.io), add it to your $PATH environment variable,
2. Simply pass the `WithTunneling` configurator in your `app.Run`,
3. You are ready to [GO](https://www.facebook.com/iris.framework/photos/a.2420499271295384/3261189020559734/?type=3&theater)!

[![tunneling_screenshot](https://user-images.githubusercontent.com/22900943/61413905-50596300-a8f5-11e9-8be0-7e806846d52f.png)](https://www.facebook.com/iris.framework/photos/a.2420499271295384/3261189020559734/?type=3&theater)

- `ctx.Application().ConfigurationReadOnly().GetVHost()` returns the public domain value. Rarely useful but it's there for you. Most of the times you use relative url paths instead of absolute(or you should to).
- It doesn't matter if ngrok is already running or not, Iris framework is smart enough to use ngrok's [web API](https://ngrok.com/docs) to create a tunnel.

Full `Tunneling` configuration:

```go
app.Run(iris.Addr(":8080"), iris.WithConfiguration(
	iris.Configuration{
		Tunneling: iris.TunnelingConfiguration{
			AuthToken:    "my-ngrok-auth-client-token",
			Bin:          "/bin/path/for/ngrok",
			Region:       "eu",
			WebInterface: "127.0.0.1:4040",
			Tunnels: []iris.Tunnel{
				{
					Name: "MyApp",
					Addr: ":8080",
				},
			},
		},
}))
```

## Routing: Handle different parameter types on the same path

Something like this works now without any issues (order: top as fallback)

```go
app.Get("/u/{username:string}", func(ctx iris.Context) {
	ctx.Writef("before username (string), current route name: %s\n", ctx.RouteName())
	ctx.Next()
}, func(ctx iris.Context) {
	ctx.Writef("username (string): %s", ctx.Params().Get("username"))
})

app.Get("/u/{id:int}", func(ctx iris.Context) {
	ctx.Writef("before id (int), current route name: %s\n", ctx.RouteName())
	ctx.Next()
}, func(ctx iris.Context) {
	ctx.Writef("id (int): %d", ctx.Params().GetIntDefault("id", 0))
})

app.Get("/u/{uid:uint}", func(ctx iris.Context) {
	ctx.Writef("before uid (uint), current route name: %s\n", ctx.RouteName())
	ctx.Next()
}, func(ctx iris.Context) {
	ctx.Writef("uid (uint): %d", ctx.Params().GetUintDefault("uid", 0))
})

app.Get("/u/{firstname:alphabetical}", func(ctx iris.Context) {
	ctx.Writef("before firstname (alphabetical), current route name: %s\n", ctx.RouteName())
	ctx.Next()
}, func(ctx iris.Context) {
	ctx.Writef("firstname (alphabetical): %s", ctx.Params().Get("firstname"))
})

/*
	/u/abcd maps to :alphabetical (if :alphabetical registered otherwise :string)
	/u/42 maps to :uint (if :uint registered otherwise :int)
	/u/-1 maps to :int (if :int registered otherwise :string)
	/u/abcd123 maps to :string
*/
```

## Content Negotiation

**Sometimes a server application needs to serve different representations of a resource at the same URI**. Of course this can be done by hand, manually checking the `Accept` request header and push the requested form of the content. However, as your app manages more resources and different kind of representations this can be very painful, as you may need to check for `Accept-Charset`, `Accept-Encoding`, put some server-side priorities, handle the errors correctly and e.t.c.

There are some web frameworks in Go already struggle to implement a feature like this but they don't do it correctly:

- they don't handle accept-charset at all
- they don't handle accept-encoding at all
- they don't send error status code (406 not acceptable) as RFC proposes and more...

But, fortunately for us, **Iris always follows the best practises and the Web standards**.

Based on:
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Content_negotiation
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Charset
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding

```go
type testdata struct {
	Name string `json:"name" xml:"Name"`
	Age  int    `json:"age" xml:"Age"`
}
```

Render a resource with "gzip" encoding algorithm
as application/json or text/xml or application/xml

- when client's accept header contains one of them
- or JSON (the first declared) if accept is empty,
- and when client's accept-encoding header contains "gzip" or it's empty.

```go
app.Get("/resource", func(ctx iris.Context) {
	data := testdata{
		Name: "test name",
		Age:  26,
	}

        ctx.Negotiation().JSON().XML().EncodingGzip()

	_, err := ctx.Negotiate(data)
	if err != nil {
		ctx.Writef("%v", err)
	}
})

```

**OR** define them in a middleware and call Negotiate with nil in the final handler.

```go
ctx.Negotiation().JSON(data).XML(data).Any("content for */*")
ctx.Negotiate(nil)
```

```go
app.Get("/resource2", func(ctx iris.Context) {
	jsonAndXML := testdata{
		Name: "test name",
		Age:  26,
	}

	ctx.Negotiation().
		JSON(jsonAndXML).
		XML(jsonAndXML).
		HTML("<h1>Test Name</h1><h2>Age 26</h2>")

	ctx.Negotiate(nil)
})
```

[Read the full example](https://github.com/kataras/iris/blob/8ee0de51c593fe0483fbea38117c3c88e065f2ef/_examples/http_responsewriter/content-negotiation/main.go#L22).

The [Context.Negotiation](https://github.com/kataras/iris/blob/8ee0de51c593fe0483fbea38117c3c88e065f2ef/context/context.go#L3342) method creates once and returns the negotiation builder
to build server-side available prioritized content for specific content type(s), charset(s) and encoding algorithm(s).

```go
Context.Negotiation() *context.NegotiationBuilder
```

The [Context.Negotiate](https://github.com/kataras/iris/blob/8ee0de51c593fe0483fbea38117c3c88e065f2ef/context/context.go#L3402) method used for serving different representations of a resource at the same URI. It returns `context.ErrContentNotSupported` when not matched mime type(s).

- The "v" can be a single [iris.N](https://github.com/kataras/iris/blob/8ee0de51c593fe0483fbea38117c3c88e065f2ef/context/context.go#L3298-L3309) struct value.
- The "v" can be any value completes the [context.ContentSelector](https://github.com/kataras/iris/blob/8ee0de51c593fe0483fbea38117c3c88e065f2ef/context/context.go#L3272) interface.
- The "v" can be any value completes the [context.ContentNegotiator](https://github.com/kataras/iris/blob/8ee0de51c593fe0483fbea38117c3c88e065f2ef/context/context.go#L3281) interface.
- The "v" can be any value of struct(JSON, JSONP, XML, YAML) or
string(TEXT, HTML) or []byte(Markdown, Binary) or []byte with any matched mime type.
- If the "v" is nil, the `Context.Negotitation()` builder's
content will be used instead, otherwise "v" overrides builder's content
(server mime types are still retrieved by its registered, supported, mime list)

- Set mime type priorities by [Negotiation().MIME.Text.JSON.XML.HTML...](https://github.com/kataras/iris/blob/8ee0de51c593fe0483fbea38117c3c88e065f2ef/context/context.go#L3500-L3621).
- Set charset priorities by [Negotiation().Charset(...)](https://github.com/kataras/iris/blob/8ee0de51c593fe0483fbea38117c3c88e065f2ef/context/context.go#L3640).
- Set encoding algorithm priorities by [Negotiation().Encoding(...)](https://github.com/kataras/iris/blob/8ee0de51c593fe0483fbea38117c3c88e065f2ef/context/context.go#L3652-L3665).
- Modify the accepted by [Negotiation().Accept./Override()/.XML().JSON().Charset(...).Encoding(...)...](https://github.com/kataras/iris/blob/8ee0de51c593fe0483fbea38117c3c88e065f2ef/context/context.go#L3774-L3877).

```go
Context.Negotiate(v interface{}) (int, error)
```

## The new Websocket package

There are times that you simply can't improve something without a breaking change. After a year and a half without breaking changes, this version of Iris introduces two breaking changes for the best. The first one is the websocket module which was fully re-written and the second has to do with how you serve system (or embedded) directories.

The new websocket package, which is selfhosted at <https://github.com/kataras/neffos>, is a work of 4 months daily designing, coding, re-designing and refactoring.

Even there, from day-zero, **users immediately started to be participated** by asking questions and making proposals. Of course, as our trandition, they are discussed (a lot) and are all available by now:

[Broadcast message to a Connection ID](https://github.com/kataras/neffos/issues/1#issuecomment-498013666)

![](https://thepracticaldev.s3.amazonaws.com/i/kzywdg6s1ku8uvsn8c0v.png)

[IDGenerator for Iris](https://github.com/kataras/neffos/issues/1#issuecomment-508689819)

![](https://thepracticaldev.s3.amazonaws.com/i/iad8w2370gh1s7d1couk.png)

[Server Ask method like Conn.Ask](https://github.com/kataras/neffos/issues/1#issuecomment-509024562)

![](https://thepracticaldev.s3.amazonaws.com/i/rhgavv5stehk8i80tuof.png)

![](https://thepracticaldev.s3.amazonaws.com/i/q2ozhqlxh9k8nz650nmt.png)

[Add a cron example](https://github.com/kataras/neffos/issues/1#issuecomment-511046580)

[Adapters support for scalability](https://github.com/kataras/neffos/issues/3)

![](https://thepracticaldev.s3.amazonaws.com/i/5j7r5unu2gu4uazaj504.png)

The new websocket implementation is far better and faster at all use cases than we had previously and without the bugs and the compromises we had to deal brecause of the no-breaking-changes rule of the previous versions. Unlike the previous one which had only a simple go client that new one provides clients for Go and Typescript/Javascript(both nodejs and browser-side) and anyone can make a client for any language, C++ for example with ease. I can say that our new websocket module is very unique but feels like home with a lot of preparation and prototyping under the hoods. The result worth the days and nights I spent on this thing -- of course, you - as community will prove that point, based on your feedback in the end of the day.

Let's see what the new version of websocket package offers that the previous v11.1.x one couldn't handle.

| Feature             |      v11.1.x          |   v11.2.x (neffos) |
|---------------------|:----------------------|---------:|
| Scale-out using Nats or Redis |  NO | YES |
| Gorilla Protocol Implementation |  YES | YES |
| Gobwas/ws Protocol Implementation |  NO | YES |
| Acknowledgements |  YES | YES |
| Namespaces |  NO | YES |
| Rooms |  YES | YES |
| Broadcast |  YES(but slow) | YES(faster than socket.io and everything else we've tested) |
| Event-Driven architecture |  YES | YES |
| Request-Response architecture |  NO | YES |
| Error Awareness |  NO | YES |
| Asynchronous Broadcast | NO | YES |
| Timeouts | YES | YES |
| Encoding | YES (only JSON) | YES |
| Native WebSocket Messages | YES | YES |
| Reconnection | NO | YES |
| Modern client for Browsers, Nodejs and Go | NO | YES |

Except the new selfhosted [neffos repository](https://github.com/kataras/neffos). The [kataras/iris/websocket](https://github.com/kataras/iris/tree/v11.2.0/websocket) subpackage now contains (only) Iris-specific migrations and helpers for the neffos websocket framework one.

For example, to gain access of the request's `Context` you can call the `websocket.GetContext(Conn)` from inside an event message handler/callback:

```go
// GetContext returns the Iris Context from a websocket connection.
func GetContext(c *neffos.Conn) Context
```

To register a websocket `neffos.Server` to a route use the `websocket.Handler` function: 

```go
// IDGenerator is an iris-specific IDGenerator for new connections.
type IDGenerator func(Context) string

// Handler returns an Iris handler to be served in a route of an Iris application.
// Accepts the neffos websocket server as its first input argument
// and optionally an Iris-specific `IDGenerator` as its second one.
func Handler(s *neffos.Server, IDGenerator ...IDGenerator) Handler
```

**Usage**

```go
import (
    "github.com/kataras/neffos"
    "github.com/kataras/iris/websocket"
)

// [...]

onChat := func(ns *neffos.NSConn, msg neffos.Message) error {
    ctx := websocket.GetContext(ns.Conn)
    // [...]
    return nil
}

app := iris.New()
ws := neffos.New(websocket.DefaultGorillaUpgrader, neffos.Namespaces{
    "default": neffos.Events {
        "chat": onChat,
    },
})
app.Get("/websocket_endpoint", websocket.Handler(ws))
```

<!-- The `iris/websocket` subpackage also contains type aliases and function shortcuts for the neffos package, which makes your coding experience easier by not importing more packages than you need in your Go source files, e.g. all `neffos.Conn, neffos.NSConn, neffos.New, neffos.Dial, neffos.Namespaces, neffos.Events, neffos.Struct ...` can be written as `websocket.Conn, websocket.NSConn, websocket.Dial, websocket.Namespaces, websocket.Events, websocket.Struct ...`, `neffos/exchange/redis.NewStackExchange` as `websocket.NewRedisStackExchange`,  `neffos/gorilla.DefaultUpgrader, Dialer` as `websocket.DefaultGorillaUpgrader, Dialer` and so on. In fact you don't even need to import the `github.com/kataras/neffos` package at all if you don't want to, just keep using the `kataras/iris/websocket` subpackage and you will be totally fine. -->

### MVC | The new Websocket Controller

The neffos package contains a feature to create events from Go struct values, its `NewStruct` package-level function. In addition, Iris has its own `iris/mvc/Application.HandleWebsocket(v interface{}) *neffos.Struct` to register controllers in existing Iris MVC applications(offering a fully featured dependency injection container for request values and static services) like any regular HTTP Controllers you are used to.

```go
// HandleWebsocket handles a websocket specific controller.
// Its exported methods are the events.
// If a "Namespace" field or method exists then namespace is set,
// otherwise empty namespace will be used for this controller.
//
// Note that a websocket controller is registered and ran under
// a connection connected to a namespace
// and it cannot send HTTP responses on that state.
// However all static and dynamic dependencies behave as expected.
func (*mvc.Application) HandleWebsocket(controller interface{}) *neffos.Struct
```

Let's see a usage example, we want to bind the `OnNamespaceConnected`, `OnNamespaceDisconnect` built-in events and a custom `"OnChat"` event with our controller's methods.

**1.** We create the controller by declaring a NSConn type field as `stateless` and write the methods we need.

```go
type websocketController struct {
    *neffos.NSConn `stateless:"true"`
    Namespace string

    Logger MyLoggerInterface
}

func (c *websocketController) OnNamespaceConnected(msg neffos.Message) error {
	return nil
}

func (c *websocketController) OnNamespaceDisconnect(msg neffos.Message) error {
	return nil
}

func (c *websocketController) OnChat(msg neffos.Message) error {
    return nil
}
```

Iris is smart enough to catch the `Namespace string` struct field to use it to register the controller's methods as events for that namespace, alternatively you can create a controller method of `Namespace() string { return "default" }` or use the `HandleWebsocket`'s return value to `.SetNamespace("default")`, it's up to you.

**2.** We inititalize our MVC application targets to a websocket endpoint, as we used to do with regular HTTP Controllers for HTTP routes.

```go
import (
    // [...]
    "github.com/kataras/iris/mvc"
)
// [app := iris.New...]

mvcApp := mvc.New(app.Party("/websocket_endpoint"))
```

**3.** We register our dependencies, if any.

```go
mvcApp.Register(
    &prefixedLogger{prefix: "DEV"},
)
```

**4.** We register one or more websocket controllers, each websocket controller maps to one namespace (just one is enough, as in most of the cases you don't need more, but that depends on your app's needs and requirements).

```go
mvcApp.HandleWebsocket(&websocketController{Namespace: "default"})
```

**5.** Next, we continue by mapping the mvc application as a connection handler to a websocket server (you may use more than one mvc applications per websocket server via `neffos.JoinConnHandlers(mvcApp1, mvcApp2)`).

```go
websocketServer := neffos.New(websocket.DefaultGorillaUpgrader, mvcApp)
```

**6.** And the last step is to register that server to our endpoint through a normal `.Get` method.

```go
mvcApp.Router.Get("/", websocket.Handler(websocketServer))
```

We will not cover the whole neffos package here, there are a lot of new features. Don't be afraid, you can still do all the things you did previously without a lot of learning process but as you going further to more advanced applications you can achieve more by reading its [wiki](https://github.com/kataras/neffos/wiki) page. In fact there are so many new things that are written in a e-book which you can request direct online access 100% free.

### Examples

* [Websocket Controller](https://github.com/kataras/iris/tree/v11.2.0/_examples/mvc/websocket)
* [Basic](https://github.com/kataras/iris/blob/v11.2.0/_examples/websocket/basic)
    * [Server](https://github.com/kataras/iris/blob/v11.2.0/_examples/websocket/basic/server.go)
    * [Go Client](https://github.com/kataras/iris/blob/v11.2.0/_examples/websocket/basic/go-client/client.go)
    * [Browser Client](https://github.com/kataras/iris/blob/v11.2.0/_examples/websocket/basic/browser/index.html)
    * [Browser NPM Client (browserify)](https://github.com/kataras/iris/blob/v11.2.0/_examples/websocket/basic/browserify/app.js)

Interesting? Continue the reading by navigating to the **[learning neffos section](https://github.com/kataras/neffos#learning-neffos)**.

## The new FileServer

We will continue by looking the new `FileServer` package-level function and `Party.HandleDir` method.

Below is a list of the functions and methods we were using so far(as of v11.1.x):

1. `Party.StaticWeb(requestPath string, systemPath string) *Route` [*](https://github.com/kataras/iris/blob/6564922661686d43954b8bcf1d4f469a3d2c3fa3/core/router/api_builder.go#L806) (the most commonly used)
2. `func NewStaticHandlerBuilder(dir string) StaticHandlerBuilder` [*](https://github.com/kataras/iris/blob/6564922661686d43954b8bcf1d4f469a3d2c3fa3/core/router/fs.go#L194)
3. `func StaticHandler(systemPath string, showList bool, gzip bool) Handler` [*](https://github.com/kataras/iris/blob/6564922661686d43954b8bcf1d4f469a3d2c3fa3/core/router/fs.go#L133)
4. `Party.StaticHandler(systemPath string, showList bool, gzip bool) Handler` [*](https://github.com/kataras/iris/blob/6564922661686d43954b8bcf1d4f469a3d2c3fa3/core/router/api_builder.go#L640)
5. `Party.StaticServe(systemPath string, requestPath ...string) *Route` [*](https://github.com/kataras/iris/blob/6564922661686d43954b8bcf1d4f469a3d2c3fa3/core/router/api_builder.go#L648)
6. `func StaticEmbeddedHandler(vdir string, assetFn func(name string) ([]byte, error), namesFn func() []string, assetsGziped bool) Handler` [*](https://github.com/kataras/iris/blob/6564922661686d43954b8bcf1d4f469a3d2c3fa3/core/router/fs.go#L28)
7. `Party.StaticEmbeddedGzip(requestPath string, vdir string, gzipAssetFn func(name string) ([]byte, error), gzipNamesFn func() []string) *Route ` [*](https://github.com/kataras/iris/blob/6564922661686d43954b8bcf1d4f469a3d2c3fa3/core/router/api_builder.go#L704)
8. `Party.StaticEmbedded(requestPath string, vdir string, assetFn func(name string) ([]byte, error), namesFn func() []string) *Route` [*](https://github.com/kataras/iris/blob/6564922661686d43954b8bcf1d4f469a3d2c3fa3/core/router/api_builder.go#L688)
9. `Application.SPA(assetHandler Handler) *router.SPABuilder` [*](https://github.com/kataras/iris/blob/6564922661686d43954b8bcf1d4f469a3d2c3fa3/iris.go#L481)

**That is a hell of functions that doing slightly differnet things but all resulting to the same functionality that an Iris-Dev wants in the end**. Also, the embedded file server was missing an important feature that a (physical) system's file server had, serve by content range (to be fair with ourselves, we weren't alone, the rest of third party tools and frameworks don't even have or think the half features that we provided to our users for embedded files, including this one).

So, I was wondering, _in the spirit that we are free of the no-breaking-changes rule for this release on the websocket level_, to bring some break changes outside of the websocket module too by not just replacing but also removing all existing static handler functions, however I came up to the decision that it's better to let them exist for one major version more ~and call the new methods under the hoods but~ with a _deprecation_  warning that will be logged to the dev's terminal. Supposedly you had a `main.go` and on its line 18 `app.StaticWeb("/static", "./assets")` exists, the error will look like that:

![deprecation_output_example](https://user-images.githubusercontent.com/22900943/59934728-e4e6b780-9454-11e9-8494-af050fbd4cb6.png)

> Note the hover, most code editors will navigate you to the source of the problem, the deprecation log takes the parameter values of the deprecated method, in that case the `StaticWeb` and suggests the new way.

 All those functions can be replaced with a single one package-level and one Party method. The package-level function gives you an `Handler` to work with and the other `Party` method will register routes on subdomain, subrouter and etc. At this point I am writing the issue I already completed this feature locally, not yet pushed but will be soon. It looks like that:

```go
FileServer(directory string, options ...DirOptions) Handler
```
```go
Party.HandleDir(requestPath string, directory string, options ...DirOptions) *Route
```

Where the `DirOptions` are:

```go
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 wasn't managed to handle it manually/by hand.
IndexName string
// Should files served under gzip compression?
Gzip 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).
DirList func(ctx Context, dirName string, dir http.File) error

// When embedded.
Asset      func(name string) ([]byte, error)   
AssetInfo  func(name string) (os.FileInfo, error)
AssetNames func() []string

// Optional validator that loops through each found requested resource.
AssetValidator func(ctx Context, name string) bool
}
```

If you used one of the above methods, **refactoring** your project's static file serving code blocks **is highly recommended**, it's quite easy in fact, here is how you can do it:

### Party.StaticWeb and Party.StaticServe

**v11.1.x**
```go
app.StaticWeb("/static", "./assets")
```
**v11.2.x**
```go
app.HandleDir("/static", "./assets")
```

> If you used the `StaticWeb/StaticServe`, just make a replace-to-all-files to `HandleDir` operation in your code editor and you're done.

### StaticHandler

**v11.1.x**
```go
handler := iris.StaticHandler("./assets", true, true)
```
**v11.2.x**
```go
handler := iris.FileServer("./assets", iris.DirOptions {ShowList: true, Gzip: true})
```

### StaticEmbeddedHandler

**v11.1.x**
```go
handler := iris.StaticEmbeddedHandler("./assets", Asset, AssetNames, true)
```
**v11.2.x**
```go
handler := iris.FileServer("./assets", iris.DirOptions {
  Asset: Asset,
  AssetInfo: AssetInfo,
  AssetNames: AssetNames,
  Gzip: true})
```

### Party.StaticEmbedded and Party.StaticEmbeddedGzip

**v11.1.x**
```go
app.StaticEmbedded("/static", "./assets", Asset, AssetNames)
```
**v11.2.x**
```go
app.HandleDir("/static", "./assets", iris.DirOptions {
  Asset: Asset,
  AssetInfo: AssetInfo,
  AssetNames: AssetNames,
  Gzip: true/false})
```

### Application.SPA

**v11.1.x**
```go
app.RegisterView(iris.HTML("./public", ".html"))

app.Get("/", func(ctx iris.Context) {
	ctx.ViewData("Page", page)
	ctx.View("index.html")
})

assetHandler := app.StaticHandler("./public", false, false)
app.SPA(assetHandler)
```
**v11.2.x**
```go
app.RegisterView(iris.HTML("./public", ".html"))

// Overrides the file server's index route. 
// Order of this route registration does not matter.
app.Get("/", func(ctx iris.Context) {
	ctx.ViewData("Page", page)
	ctx.View("index.html")
})

app.HandleDir("/", "./public")
```

The above changes are not only syntactical. Unlike the standard net/http design we give the chance and the features to the end-developer to use different handlers for index files to customize the middlewares and any other options and code that required when designing a Single Page Applications.
Previously something like `/static/index.html` -> `/static` should be manually handled by developer through `app.Get` to serve a directory's `index.html` file. Now, if a handler like this is missing then the framework will register it automatically, order of route registration does not even matter, Iris handles them on build state. Another new feature is that now the file server can handle content-range embedded files and also show a list of files in an embedded directory via the `DirOptions.ShowList` exactly like the system directories.

The above `FileServer` function and `HandleDir` method handles every case in a single spot, all previous and new features are live inside those two. 

As a result from the 9(**nine**) functions and methods we had, we **end up with** just 2(**two**) with less code, more improvements and new features. That fact gives any user, experienced or newcomer an ideal place to start working without searching and reading more than they need to. 

## New Jet View Engine

This version contains a new new `View Engine` for the `jet` template parser as requested at: https://github.com/kataras/iris/issues/1281

![](https://thepracticaldev.s3.amazonaws.com/i/c8h82fi59rc0ndxqaacm.png)

```go
tmpl := iris.Jet("./views", ".jet")
app.RegisterView(tmpl)
```

## Bugfixes and minor improvements

Let's continue by listing the minor bugfixes, improvements and new functions. For more information check the links after the function or method declaration.

**1**. `Context.FullRequestURI()` - as requested at: https://github.com/kataras/iris/issues/1167.

**2**. `NewConditionalHandler(filter func(ctx Context) bool, handlers ...Handler) Handler` as requested at: https://github.com/kataras/iris/issues/1170.

**3**. `Context.ResetRequest(newReq *http.Request)` as requested at: https://github.com/kataras/iris/issues/1180.

![](https://thepracticaldev.s3.amazonaws.com/i/io3q7d7hlxasq5jn82o4.png)

**4**. Fix `Context.StopExecution()` wasn't respect by MVC controller's methods, as reported at: https://github.com/kataras/iris/issues/1187.

**5**. Give the ability to modify the whole session's cookie on Start and `Update/ShiftExpiration` methods and add a `StartWithPath` helper as requested at: https://github.com/kataras/iris/issues/1186.

![](https://thepracticaldev.s3.amazonaws.com/i/c0vx7r2034yl7jwtrpi9.png)

**6**. Add `Context.ResponseWriter().IsHijacked() bool` to report whether the underline connection is hijacked or not.

**7**. Add the ability to intercept the default error handler by seting a custom `ErrorHandler` to MVC application-level or per controller as requested at: https://github.com/kataras/iris/issues/1244:

![](https://thepracticaldev.s3.amazonaws.com/i/y9gokutivkj8qdbecctb.png)

```go
mvcApp := mvc.New(app)
mvcApp.HandleError(func(ctx iris.Context, err error) {
    ctx.HTML(fmt.Sprintf("<b>%s</b>", err.Error()))
})
// OR

type myController struct { /* [...] */ }

// Overriddes the mvcApp.HandleError function.
func (c *myController) HandleError(ctx iris.Context, err error) {
	ctx.HTML(fmt.Sprintf("<i>%s</i>", err.Error()))
}
```

**8**. Extract the `Delim` configuration field for redis sessiondb as requested at: https://github.com/kataras/iris/issues/1256. And replace the underline redis client library to the [radix](https://github.com/mediocregopher/radix) one.

![](https://thepracticaldev.s3.amazonaws.com/i/xzgthdshrhgdr5n71s7t.png)

**9**. Fix hero/mvc when map, struct, slice return nil as null in JSON responses, as reported at: https://github.com/kataras/iris/issues/1273.

![](https://thepracticaldev.s3.amazonaws.com/i/gwynvz0shtkyljrsqzg9.png)

**10.** Enable `view.Django` pongo2 `addons` as requested at: https://github.com/kataras/iris/issues/1284.

**11.** Add `mvc#Before/AfterActivation.HandleMany` and `GetRoutes` methods as requested at: https://github.com/kataras/iris/issues/1292.

**12.** Fix `WithoutBodyConsumptionOnUnmarshal` option not be respected on `Context.ReadForm` and `Context.FormValues` as reported at: https://github.com/kataras/iris/issues/1297.

**13.** Fix [jwt](https://github.com/iris-contrib/middleware/tree/master/jwt), [casbin](https://github.com/iris-contrib/middleware/tree/master/casbin) and [go-i81n](https://github.com/iris-contrib/middleware/tree/master/go-i81n) middlewares.

**14.** Fix https://github.com/kataras/iris/issues/1298.

**15.** Add `ReadQuery` as requested at: https://github.com/kataras/iris/issues/1207.

**16.** Easy way to register a session as a middleware. Add `sessions/Sessions#Handler` and package-level `sessions.Get` function (examples below).

### Debugging

1. Warning messages for invalid registration of MVC dependencies and controllers fields or method input arguments.
2. Print information for MVC Controller's method maps to a websocket event.
3. `Context.RouteName()` which returns the current route's name.
4. `Context.HandlerFileName()` which returns the exact program's source code position of the current handler function that it's being executed (file:line).

## Examples

Iris offers more than 110 examples for both experienced and new gophers.

## New Examples

* [Serve using HTTP/3 Quic](https://github.com/kataras/iris/blob/v11.2.0/_examples/http-listening/http3-quic) as requested at: https://github.com/kataras/iris/issues/1295

![](https://thepracticaldev.s3.amazonaws.com/i/wa944fg9cka7b3zwu8t8.png)

* [Public domain address](https://github.com/kataras/iris/blob/v11.2.0/_examples/http-listening/listen-addr-public/main.go)
* [Build RESTful API with the official MongoDB Go Driver and Iris](https://github.com/kataras/iris/tree/4cfdc6418532f49ff0159045a06080db0457eb55/_examples/tutorial/mongodb)
* [Yet another dependency injection example and good practises at general](https://github.com/kataras/iris/blob/v11.2.0/_examples/hero/smart-contract/main.go)
* [MVC Regexp](https://github.com/kataras/iris/blob/v11.2.0/_examples/mvc/regexp/main.go)
* [Jet View Engine](https://github.com/kataras/iris/blob/v11.2.0/_examples/view/template_jet_0) and [Embedded Jet templates](https://github.com/kataras/iris/blob/v11.2.0/_examples/view/template_jet_1_embedded)
* [Websocket](https://github.com/kataras/iris/blob/v11.2.0/_examples/websocket/basic)
    * [Server](https://github.com/kataras/iris/blob/v11.2.0/_examples/websocket/basic/server.go)
    * [Go Client](https://github.com/kataras/iris/blob/v11.2.0/_examples/websocket/basic/go-client/client.go)
    * [Browser Client](https://github.com/kataras/iris/blob/v11.2.0/_examples/websocket/basic/browser/index.html)
    * [Browser NPM Client (browserify)](https://github.com/kataras/iris/blob/v11.2.0/_examples/websocket/basic/browserify/app.js)
* [GORM](https://github.com/kataras/iris/pull/1275)
* [ReadQuery](https://github.com/kataras/iris/tree/master/_examples/http_request/read-query)
* [Sessions Middleware](https://github.com/kataras/iris/blob/master/_examples/sessions/middleware/main.go)
* [Content Negotiation](https://github.com/kataras/iris/blob/master/_examples/http_responsewriter/content-negotiation)
* [Read YAML](https://github.com/kataras/iris/blob/master/_examples/http_request/read-yaml)

## Updated Examples

* [Custom Router Wrapper](https://github.com/kataras/iris/blob/v11.2.0/_examples/routing/custom-wrapper/main.go)
* [FileServer Basics](https://github.com/kataras/iris/blob/v11.2.0/_examples/file-server/basic/main.go)
* [Embedding Files Into App Executable File](https://github.com/kataras/iris/blob/v11.2.0/_examples/file-server/embedding-files-into-app/main.go)
* [Embedding Gziped Files Into App Executable File](https://github.com/kataras/iris/blob/v11.2.0/_examples/file-server/embedding-gziped-files-into-app/main.go)
* [Single Page Application](https://github.com/kataras/iris/blob/v11.2.0/_examples/file-server/single-page-application/basic/main.go)
* [Embedded Single Page Application](https://github.com/kataras/iris/blob/v11.2.0/_examples/file-server/single-page-application/embedded-single-page-application/main.go)
7. [Embedded Single Page Application with other routes](https://github.com/kataras/iris/blob/v11.2.0/_examples/file-server/single-page-application/embedded-single-page-application-with-other-routes/main.go)
* [Websocket Native Messages](https://github.com/kataras/iris/blob/v11.2.0/_examples/websocket/native-messages/main.go)
* [Websocket Controller](https://github.com/kataras/iris/tree/v11.2.0/_examples/mvc/websocket)
* [Using the Redis Session Database](https://github.com/kataras/iris/blob/v11.2.0/_examples/sessions/database/redis/main.go)