iris/HISTORY.md
Gerasimos (Makis) Maropoulos f667bc5ff3 HISTORY.md: example of the new Dependency Injection features
Former-commit-id: 94294ffa96fafeb133b129f6f59c813d73ed05f1
2020-04-30 22:50:49 +03:00

30 KiB

Changelog

Looking for free and real-time support?

https://github.com/kataras/iris/issues
https://chat.iris-go.com

Looking for previous versions?

https://github.com/kataras/iris/releases

Want to be hired?

https://facebook.com/iris.framework

Should I upgrade my Iris?

Developers are not forced to upgrade if they don't really need it. Upgrade whenever you feel ready.

How to upgrade: Open your command-line and execute this command: go get github.com/kataras/iris/v12@latest.

Next

This release introduces new features and some breaking changes inside the mvc and hero packages. The codebase for dependency injection has been simplified a lot (fewer LOCs and easier to read and follow up).

The new release contains a fresh new and awesome feature....a function dependency can accept previous registered dependencies and update or return a new value of any type.

The new implementation is faster on both design and serve-time.

The most common scenario from a route to handle is to:

  • accept one or more path parameters and request data, a payload
  • send back a response, a payload (JSON, XML,...)

The new Iris Dependency Injection feature is about 33.2% faster than its predecessor on the above case. This drops down even more the performance cost between native handlers and dynamic handlers with dependencies. This reason itself brings us, with safety and performance-wise, to the new Party.ConfigureContainer(builder ...func(*iris.APIContainer)) *APIContainer method which returns methods such as Handle(method, relativePath string, handlersFn ...interface{}) *Route and RegisterDependency.

Look how clean your codebase can be when using Iris':

package main

import "github.com/kataras/iris/v12"

type (
    testInput struct {
        Email string `json:"email"`
    }

    testOutput struct {
        ID   int    `json:"id"`
        Name string `json:"name"`
    }
)

func handler(id int, in testInput) testOutput {
    return testOutput{
        ID:   id,
        Name: in.Email,
    }
}

func main() {
    app := iris.New()
    app.ConfigureContainer(func(api *iris.APIContainer) {
        api.Post("/{id:int}", handler)
    })
    app.Listen(":5000", iris.WithOptimizations)
}

Your eyes don't lie you. You read well, no ctx.ReadJSON(&v) and ctx.JSON(send) neither error handling are presented. It is a huge relief but if you ever need, you still have the control over those, even errors from dependencies. Here is a quick list of the new Party.ConfigureContainer()'s fields and methods:

// Container holds the DI Container of this Party featured Dependency Injection.
// Use it to manually convert functions or structs(controllers) to a Handler.
Container *hero.Container
// OnError adds an error handler for this Party's DI Hero Container and its handlers (or controllers).
// The "errorHandler" handles any error may occurred and returned
// during dependencies injection of the Party's hero handlers or from the handlers themselves.
OnError(errorHandler func(iris.Context, error))
// RegisterDependency adds a dependency.
// The value can be a single struct value or a function.
// Follow the rules:
// * <T> {structValue}
// * func(accepts <T>)                                 returns <D> or (<D>, error)
// * func(accepts iris.Context)                        returns <D> or (<D>, error)
//
// A Dependency can accept a previous registered dependency and return a new one or the same updated.
// * func(accepts1 <D>, accepts2 <T>)                  returns <E> or (<E>, error) or error
// * func(acceptsPathParameter1 string, id uint64)     returns <T> or (<T>, error)
//
// Usage:
//
// - RegisterDependency(loggerService{prefix: "dev"})
// - RegisterDependency(func(ctx iris.Context) User {...})
// - RegisterDependency(func(User) OtherResponse {...})
RegisterDependency(dependency interface{})

// UseResultHandler adds a result handler to the Container.
// A result handler can be used to inject the returned struct value
// from a request handler or to replace the default renderer.
UseResultHandler(handler func(next iris.ResultHandler) iris.ResultHandler)
ResultHandler
type ResultHandler func(ctx iris.Context, v interface{}) error
// Use same as a common Party's "Use" but it accepts dynamic functions as its "handlersFn" input.
Use(handlersFn ...interface{})
// Done same as a common Party's but it accepts dynamic functions as its "handlersFn" input.
Done(handlersFn ...interface{})
// Handle same as a common Party's `Handle` but it accepts one or more "handlersFn" functions which each one of them
// can accept any input arguments that match with the Party's registered Container's `Dependencies` and
// any output result; like custom structs <T>, string, []byte, int, error,
// a combination of the above, hero.Result(hero.View | hero.Response) and more.
//
// It's common from a hero handler to not even need to accept a `Context`, for that reason,
// the "handlersFn" will call `ctx.Next()` automatically when not called manually.
// To stop the execution and not continue to the next "handlersFn"
// the end-developer should output an error and return `iris.ErrStopExecution`.
Handle(method, relativePath string, handlersFn ...interface{}) *Route

// Get registers a GET route, same as `Handle("GET", relativePath, handlersFn....)`.
Get(relativePath string, handlersFn ...interface{}) *Route
// and so on...

Prior to this version the iris.Context was the only one dependency that has been automatically binded to the handler's input or a controller's fields and methods, read below to see what types are automatically binded:

Type Maps To
iris.Context Current Iris Context
*sessions.Session Current Iris Session
context.Context ctx.Request().Context()
*http.Request ctx.Request()
http.ResponseWriter ctx.ResponseWriter()
http.Header ctx.Request().Header
time.Time time.Now()
string,
int, int8, int16, int32, int64,
uint, uint8, uint16, uint32, uint64,
float, float32, float64,
bool,
slice Path Parameter
Struct Request Body of JSON, XML, YAML, Form, URL Query, Protobuf, MsgPack

Here is a preview of what the new Hero handlers look like:

Request & Response & Path Parameters

1. Declare Go types for client's request body and a server's response.

type (
	request struct {
		Firstname string `json:"firstname"`
		Lastname  string `json:"lastname"`
	}

	response struct {
		ID      uint64 `json:"id"`
		Message string `json:"message"`
	}
)

2. Create the route handler.

Path parameters and request body are binded automatically.

  • id uint64 binds to "id:uint64"
  • input request binds to client request data such as JSON
func updateUser(id uint64, input request) response {
	return response{
		ID:      id,
		Message: "User updated successfully",
	}
}

3. Configure the container per group and register the route.

app.Party("/user").ConfigureContainer(container)

func container(api *iris.APIContainer) {
    api.Put("/{id:uint64}", updateUser)
}

4. Simulate a client request which sends data to the server and displays the response.

curl --request PUT -d '{"firstanme":"John","lastname":"Doe"}' http://localhost:8080/user/42
{
    "id": 42,
    "message": "User updated successfully"
}

Custom Preflight

Before we continue to the next section, register dependencies, you may want to learn how a response can be customized through the iris.Context right before sent to the client.

The server will automatically execute the Preflight(iris.Context) error method of a function's output struct value right before send the response to the client.

Take for example that you want to fire different HTTP status codes depending on the custom logic inside your handler and also modify the value(response body) itself before sent to the client. Your response type should contain a Preflight method like below.

type response struct {
	ID      uint64 `json:"id,omitempty"`
	Message string `json:"message"`
	Code    int    `json:"code"`
	Timestamp int64 `json:"timestamp,omitempty"`
}

func (r *response) Preflight(ctx iris.Context) error {
	if r.ID > 0 {
		r.Timestamp = time.Now().Unix()
	}

	ctx.StatusCode(r.Code)
	return nil
}

Now, each handler that returns a *response value will call the response.Preflight method automatically.

func deleteUser(db *sql.DB, id uint64) *response {
    // [...custom logic]

    return &response{
        Message: "User has been marked for deletion",
        Code: iris.StatusAccepted,
    }
}

If you register the route and fire a request you should see an output like this, the timestamp is filled and the HTTP status code of the response that the client will receive is 202 (Status Accepted).

{
  "message": "User has been marked for deletion",
  "code": 202,
  "timestamp": 1583313026
}

Register Dependencies

1. Import packages to interact with a database. The go-sqlite3 package is a database driver for SQLite.

import "database/sql"
import _ "github.com/mattn/go-sqlite3"

2. Configure the container (see above), register your dependencies. Handler expects an *sql.DB instance.

localDB, _ := sql.Open("sqlite3", "./foo.db")
api.RegisterDependency(localDB)

3. Register a route to create a user.

api.Post("/{id:uint64}", createUser)

4. The create user Handler.

The handler accepts a database and some client request data such as JSON, Protobuf, Form, URL Query and e.t.c. It Returns a response.

func createUser(db *sql.DB, user request) *response {
    // [custom logic using the db]
    userID, err := db.CreateUser(user)
    if err != nil {
        return &response{
            Message: err.Error(),
            Code: iris.StatusInternalServerError,
        }
    }

	return &response{
		ID:      userID,
		Message: "User created",
		Code:    iris.StatusCreated,
	}
}

5. Simulate a client to create a user.

# JSON
curl --request POST -d '{"firstname":"John","lastname":"Doe"}' \
--header 'Content-Type: application/json' \
http://localhost:8080/user
# Form (multipart)
curl --request POST 'http://localhost:8080/users' \
--header 'Content-Type: multipart/form-data' \
--form 'firstname=John' \
--form 'lastname=Doe'
# Form (URL-encoded)
curl --request POST 'http://localhost:8080/users' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'firstname=John' \
--data-urlencode 'lastname=Doe'
# URL Query
curl --request POST 'http://localhost:8080/users?firstname=John&lastname=Doe'

Response:

{
    "id": 42,
    "message": "User created",
    "code": 201,
    "timestamp": 1583313026
}

Other Improvements:

  • gRPC features:

    • New Router Wrapper.
    • New MVC .Handle(ctrl, mvc.GRPC{...}) option which allows to register gRPC services per-party (without the requirement of a full wrapper) and optionally strict access to gRPC clients only, see the example here.
  • Improved tracing (with app.Logger().SetLevel("debug")) for routes. Example:

DBUG Routes (1)

DBUG routes

DBUG Routes (2)

DBUG routes

  • Fix an issue about i18n loading from path which contains potential language code.

  • Server will not return neither log the ErrServerClosed if app.Shutdown was called manually via interrupt signal(CTRL/CMD+C), note that if the server closed by any other reason the error will be fired as previously (unless iris.WithoutServerError(iris.ErrServerClosed)).

  • Finally, Log level's and Route debug information colorization is respected across outputs. Previously if the application used more than one output destination (e.g. a file through app.Logger().AddOutput) the color support was automatically disabled from all, including the terminal one, this problem is fixed now. Developers can now see colors in their terminals while log files are kept with clear text.

  • New iris.WithLowercaseRouting option which forces all routes' paths to be lowercase and converts request paths to their lowercase for matching.

  • New app.Validator { Struct(interface{}) error } field and app.Validate method were added. The app.Validator = can be used to integrate a 3rd-party package such as go-playground/validator. If set-ed then Iris Context's ReadJSON, ReadXML, ReadMsgPack, ReadYAML, ReadForm, ReadQuery, ReadBody methods will return the validation error on data validation failures. The read-json-struct-validation example was updated.

  • A result of can implement the new hero.PreflightResult interface which contains a single method of Preflight(iris.Context) error. If this method exists on a custom struct value which is returned from a handler then it will fire that Preflight first and if not errored then it will cotninue by sending the struct value as JSON(by-default) response body.

  • ctx.JSON, JSONP, XML: if iris.WithOptimizations is NOT passed on app.Run/Listen then the indentation defaults to " " (four spaces) and " " respectfully otherwise it is empty or the provided value.

  • Hero Handlers (and app.ConfigureContainer().Handle) do not have to require iris.Context just to call ctx.Next() anymore, this is done automatically now.

  • Improve Remote Address parsing as requested at: #1453. Add Configuration.RemoteAddrPrivateSubnets to exclude those addresses when fetched by Configuration.RemoteAddrHeaders through context.RemoteAddr() string.

  • Fix #1487.

  • Fix #1473.

New Context Methods:

  • context.IsHTTP2() bool reports whether the protocol version for incoming request was HTTP/2
  • context.IsGRPC() bool reports whether the request came from a gRPC client
  • context.UpsertCookie(*http.Cookie, cookieOptions ...context.CookieOption) upserts a cookie, fixes #1485 too
  • context.StopWithStatus(int) stops the handlers chain and writes the status code
  • context.StopWithText(int, string) stops the handlers chain, writes thre status code and a plain text message
  • context.StopWithError(int, error) stops the handlers chain, writes thre status code and the error's message
  • context.StopWithJSON(int, interface{}) stops the handlers chain, writes the status code and sends a JSON response
  • context.StopWithProblem(int, iris.Problem) stops the handlers, writes the status code and sends an application/problem+json response
  • context.Protobuf(proto.Message) sends protobuf to the client
  • context.MsgPack(interface{}) sends msgpack format data to the client
  • context.ReadProtobuf(ptr) binds request body to a proto message
  • context.ReadMsgPack(ptr) binds request body of a msgpack format to a struct
  • context.ReadBody(ptr) binds the request body to the "ptr" depending on the request's Method and Content-Type
  • context.SetSameSite(http.SameSite) to set cookie "SameSite" option (respectful by sessions too)
  • context.Defer(Handler) works like Party.Done but for the request life-cycle instead
  • context.ReflectValue() []reflect.Value stores and returns the []reflect.ValueOf(context)
  • context.Controller() reflect.Value returns the current MVC Controller value.

Breaking Changes:

Change the MIME type of Javascript .js and JSONP as the HTML specification now recommends to "text/javascript" instead of the obselete "application/javascript". This change was pushed to the Go language itself as well. See https://go-review.googlesource.com/c/go/+/186927/.

  • route.Trace() string changed to route.Trace(w io.Writer), to achieve the same result just pass a bytes.Buffer
  • var mvc.AutoBinding removed as the default behavior now resolves such dependencies automatically (see [FEATURE REQUEST] MVC serving gRPC-compatible controller)
  • mvc#Application.SortByNumMethods() removed as the default behavior now binds the "thinnest" empty interface{} automatically (see MVC: service injecting fails)
  • mvc#BeforeActivation.Dependencies().Add should be replaced with mvc#BeforeActivation.Dependencies().Register instead
  • REMOVE the kataras/iris/v12/typescript package in favor of the new iris-cli. Also, the alm typescript online editor was removed as it is deprecated by its author, please consider using the designtsx instead.

Su, 16 February 2020 | v12.1.8

New Features:

Fixes:

New Examples:

Mo, 10 February 2020 | v12.1.7

Implement new SetRegisterRule(iris.RouteOverride, RouteSkip, RouteError) to resolve: https://github.com/kataras/iris/issues/1448

New Examples:

We, 05 February 2020 | v12.1.6

Fixes:

Su, 02 February 2020 | v12.1.5

Various improvements and linting.

Su, 29 December 2019 | v12.1.4

Minor fix on serving embedded files.

We, 25 December 2019 | v12.1.3

Fix [BUG] [iris.Default] RegisterView

Th, 19 December 2019 | v12.1.2

Fix [BUG]Session works incorrectly when meets the multi-level TLDs.

Mo, 16 December 2019 | v12.1.1

Add Context.FindClosest(n int) []string

app := iris.New()
app.OnErrorCode(iris.StatusNotFound, notFound)
func notFound(ctx iris.Context) {
    suggestPaths := ctx.FindClosest(3)
    if len(suggestPaths) == 0 {
        ctx.WriteString("404 not found")
        return
    }

    ctx.HTML("Did you mean?<ul>")
    for _, s := range suggestPaths {
        ctx.HTML(`<li><a href="%s">%s</a></li>`, s, s)
    }
    ctx.HTML("</ul>")
}

Fr, 13 December 2019 | v12.1.0

Breaking Changes

Minor as many of you don't even use them but, indeed, they need to be covered here.

  • Old i18n middleware(iris/middleware/i18n) was replaced by the i18n sub-package which lives as field at your application: app.I18n.Load(globPathPattern string, languages ...string) (see below)
  • Community-driven i18n middleware(iris-contrib/middleware/go-i18n) has a NewLoader function which returns a loader which can be passed at app.I18n.Reset(loader i18n.Loader, languages ...string) to change the locales parser
  • The Configuration's TranslateFunctionContextKey was replaced by LocaleContextKey which Context store's value (if i18n is used) returns the current Locale which contains the translate function, the language code, the language tag and the index position of it
  • The context.Translate method was replaced by context.Tr as a shortcut for the new context.GetLocale().GetMessage(format, args...) method and it matches the view's function {{tr format args}} too
  • If you used Iris Django view engine with import _ github.com/flosch/pongo2-addons you must change the import path to _ github.com/iris-contrib/pongo2-addons or add a go mod replace to your go.mod file, e.g. replace github.com/flosch/pongo2-addons => github.com/iris-contrib/pongo2-addons v0.0.1.

Fixes

All known issues.

  1. #1395
  2. #1369
  3. #1399 with PR #1400
  4. #1401
  5. #1406
  6. neffos/#20
  7. pio/#5

New Features

Internationalization and localization

Support for i18n is now a builtin feature and is being respected across your entire application, per say sitemap and views.

Refer to the wiki section: https://github.com/kataras/iris/wiki/Sitemap for details.

Sitemaps

Iris generates and serves one or more sitemap.xml for your static routes.

Navigate through: https://github.com/kataras/iris/wiki/Sitemap for more.

New Examples

  1. _examples/i18n
  2. _examples/sitemap
  3. _examples/desktop-app/blink
  4. _examples/desktop-app/lorca
  5. _examples/desktop-app/webview

Sa, 26 October 2019 | v12.0.0

  • Add version suffix of the import path, learn why and see what people voted at issue #1370

  • All errors are now compatible with go1.13 errors.Is, errors.As and fmt.Errorf and a new core/errgroup package created
  • Fix #1383
  • Report whether system couldn't find the directory of view templates
  • Remove the Party#GetReport method, keep Party#GetReporter which is an error and an errgroup.Group.
  • Remove the router's deprecated methods such as StaticWeb and StaticEmbedded_XXX
  • The Context#CheckIfModifiedSince now returns an context.ErrPreconditionFailed type of error when client conditions are not met. Usage: if errors.Is(err, context.ErrPreconditionFailed) { ... }
  • Add SourceFileName and SourceLineNumber to the Route, reports the exact position of its registration inside your project's source code.
  • Fix a bug about the MVC package route binding, see PR #1364
  • Add mvc/Application#SortByNumMethods as requested at #1343
  • Add status code 103 Early Hints
  • Fix performance of session.UpdateExpiration on 200 thousands+ keys with new radix as reported at issue #1328
  • New redis session database configuration field: Driver: redis.Redigo() or redis.Radix(), see updated examples
  • Add Clusters support for redis:radix session database (Driver: redis:Radix()) as requested at issue #1339
  • Create Iranian README_FA translation with PR #1360
  • Create Korean README_KO translation with PR #1356
  • Create Spanish README_ES and HISTORY_ES translations with PR #1344.

The iris-contrib/middleare and examples are updated to use the new github.com/kataras/iris/v12 import path.

Fr, 16 August 2019 | v11.2.8

  • Set Cookie.SameSite to Lax when subdomains sessions share is enabled*
  • Add and update all experimental handlers
  • New XMLMap function which wraps a map[string]interface{} and converts it to a valid xml content to render through Context.XML method
  • Add new ProblemOptions.XML and RenderXML fields to render the Problem as XML(application/problem+xml) instead of JSON("application/problem+json) and enrich the Negotiate to easily accept the application/problem+xml mime.

Commit log: https://github.com/kataras/iris/compare/v11.2.7...v11.2.8

Th, 15 August 2019 | v11.2.7

This minor version contains improvements on the Problem Details for HTTP APIs implemented on v11.2.5.

Example and wikis updated.

References:

Commit log: https://github.com/kataras/iris/compare/v11.2.6...v11.2.7

We, 14 August 2019 | v11.2.6

Allow handle more than one route with the same paths and parameter types but different macro validation functions.

app.Get("/{alias:string regexp(^[a-z0-9]{1,10}\\.xml$)}", PanoXML)
app.Get("/{alias:string regexp(^[a-z0-9]{1,10}$)}", Tour)

Commit log: https://github.com/kataras/iris/compare/v11.2.5...v11.2.6

Mo, 12 August 2019 | v11.2.5

Commit log: https://github.com/kataras/iris/compare/v11.2.4...v11.2.5

Fr, 09 August 2019 | v11.2.4

Commit log: https://github.com/kataras/iris/compare/v11.2.3...v11.2.4

Tu, 30 July 2019 | v11.2.3

We, 24 July 2019 | v11.2.2

Sessions as middleware:

import "github.com/kataras/iris/v12/sessions"
// [...]

app := iris.New()
sess := sessions.New(sessions.Config{...})

app.Get("/path", func(ctx iris.Context){
    session := sessions.Get(ctx)
    // [work with session...]
})
  • Add Session.Len() int to return the total number of stored values/entries.
  • Make Context.HTML and Context.Text to accept an optional, variadic, args ...interface{} input arg(s) too.

v11.1.1

Tu, 23 July 2019 | v11.2.0

Read about the new release at: https://www.facebook.com/iris.framework/posts/3276606095684693