update some contents for the upcoming release (ebook too)

Gerasimos (Makis) Maropoulos 2020-06-07 18:48:01 +03:00
parent 388df26430
commit 6ad0e5ab5b
No known key found for this signature in database
GPG Key ID: 5DBE766BD26A54E7
28 changed files with 1864 additions and 987 deletions

@ -54,7 +54,7 @@ myMiddleware := func(ctx iris.Context) {
}
myCustomNotVersionFound := func(ctx iris.Context) {
ctx.StatusCode(404)
ctx.StatusCode(iris.StatusNotFound)
ctx.Writef("%s version not found", versioning.GetVersion(ctx))
}

@ -12,7 +12,7 @@ Follow the steps below to, temporarily, convert your local Iris web server to a
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)
![tunneling_screenshot](https://user-images.githubusercontent.com/22900943/81442996-42731800-917d-11ea-90da-7d6475a6b365.png)
- `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.

@ -84,4 +84,4 @@ similar to the `HandleDir`(which sends status OK(200) and browser disk caching i
func Cache304(expiresEvery time.Duration) Handler
```
Examples can be found at: <https://github.com/kataras/iris/tree/master/_examples/cache>.
Examples can be found at: <https://github.com/kataras/iris/tree/master/_examples/response-writer/cache>.

@ -2,17 +2,15 @@ At the [[previous|Host]] section we've learnt about the first input argument of
Let's start from basics. The `iris.New` function returns an `iris.Application`. This Application value can be configured through its `Configure(...iris.Configurator)` and `Run` methods.
The second optional, varadiac argument of `app.Run` method accepts one or more `iris.Configurator`. An `iris.Configurator` is just a type of: `func(app *iris.Application)`. Custom `iris.Configurator` can be passed to modify yours `*iris.Application` as well.
The second optional, varadiac argument of `app.Run/Listen` method accepts one or more `iris.Configurator`. An `iris.Configurator` is just a type of: `func(app *iris.Application)`. Custom `iris.Configurator` can be passed to modify yours `*iris.Application` as well.
There are built-in`iris.Configurator`s for each of the core [Configuration](https://godoc.org/github.com/kataras/iris#Configuration)'s fields, such as `iris.WithoutStartupLog`, `iris.WithCharset("UTF-8")`, `iris.WithOptimizations` and `iris.WithConfiguration(iris.Configuration{...})` functions.
Each “module” like the iris view engine, websockets, sessions and each middleware has its own configurations and options, most of them, separately from the core configuration.
Each "module" like the iris view engine, websockets, sessions and each middleware has its own configurations and options.
## Using the [Configuration](https://godoc.org/github.com/kataras/iris#Configuration)
The only one configuration structure is the `iris.Configuration`. Let's start by that one which can be passed on the `iris.WithConfiguration` function to make it an `iris.Configurator`.
All of the `iris.Configuration` fields are defaulted to the most common use cases. Iris doesnt need any configuration before its `app.Run` but if you want to make use of a custom `iris.Configurator` before the server runs then you can use the `app.Configure` method to pass the configurator(s) there.
All of the `iris.Configuration` fields are defaulted to the most common use cases. If you want to make use of a custom `iris.Configurator` at any point call the `app.Configure(accepts iris.Configurator)` method to pass your configurator(s) there.
```go
config := iris.WithConfiguration(iris.Configuration {
@ -70,13 +68,14 @@ app.Listen(":8080", config)
## Using the functional way
As we already mention, you can pass any number of `iris.Configurator` in the `app.Run`s second argument. Iris provides an option for each of its `iris.Configuration`s fields.
As we already mention, you can pass any number of `iris.Configurator` in the `app.Run/Listen`s second argument. Iris provides an option for each of its `iris.Configuration`s fields.
```go
app.Listen(":8080", iris.WithoutInterruptHandler,
iris.WithoutServerError(iris.ErrServerClosed),
iris.WithoutBodyConsumptionOnUnmarshal,
iris.WithoutAutoFireStatusCode,
iris.WithLowercaseRouting,
iris.WithPathIntelligence,
iris.WithOptimizations,
iris.WithTimeFormat("Mon, 01 Jan 2006 15:04:05 GMT"),
)
@ -86,9 +85,181 @@ Good when you want to change some of the configuration's field.
Prefix: "With" or "Without", code editors will help you navigate through all
configuration options without even a glitch to the documentation.
List of functional options:
```go
// WithGlobalConfiguration will load the global yaml configuration file
// from the home directory and it will set/override the whole app's configuration
// to that file's contents. The global configuration file can be modified by user
// and be used by multiple iris instances.
//
// This is useful when we run multiple iris servers that share the same
// configuration, even with custom values at its "Other" field.
//
// Usage: `app.Configure(iris.WithGlobalConfiguration)` or `app.Run([iris.Runner], iris.WithGlobalConfiguration)`.
var WithGlobalConfiguration Configurator
// WithLogLevel sets the `Configuration.LogLevel` field.
func WithLogLevel(level string) Configurator
// WithoutServerError will cause to ignore the matched "errors"
// from the main application's `Run/Listen` function.
//
// Usage:
// err := app.Listen(":8080", iris.WithoutServerError(iris.ErrServerClosed))
// will return `nil` if the server's error was `http/iris#ErrServerClosed`.
//
// See `Configuration#IgnoreServerErrors []string` too.
//
// Example: https://github.com/kataras/iris/tree/master/_examples/http-server/listen-addr/omit-server-errors
func WithoutServerError(errors ...error)Configurator
// WithoutStartupLog turns off the information send, once, to the terminal when the main server is open.
var WithoutStartupLog Configurator
// WithoutBanner is a conversion for the `WithoutStartupLog` option.
//
// Turns off the information send, once, to the terminal when the main server is open.
var WithoutBanner = WithoutStartupLog
// WithoutInterruptHandler disables the automatic graceful server shutdown
// when control/cmd+C pressed.
var WithoutInterruptHandler Configurator
// WithoutPathCorrection disables the PathCorrection setting.
//
// See `Configuration`.
var WithoutPathCorrection Configurator
// WithPathIntelligence enables the EnablePathIntelligence setting.
//
// See `Configuration`.
var WithPathIntelligence Configurator
// WithoutPathCorrectionRedirection disables the PathCorrectionRedirection setting.
//
// See `Configuration`.
var WithoutPathCorrectionRedirection Configurator
// WithoutBodyConsumptionOnUnmarshal disables BodyConsumptionOnUnmarshal setting.
//
// See `Configuration`.
var WithoutBodyConsumptionOnUnmarshal Configurator
// WithEmptyFormError enables the setting `FireEmptyFormError`.
//
// See `Configuration`.
var WithEmptyFormError Configurator
// WithoutAutoFireStatusCode disables the AutoFireStatusCode setting.
//
// See `Configuration`.
var WithoutAutoFireStatusCode Configurator
// WithPathEscape sets the EnablePathEscape setting to true.
//
// See `Configuration`.
var WithPathEscape Configurator
// WithLowercaseRouting enables for lowercase routing by
// setting the `ForceLowercaseRoutes` to true.
//
// See `Configuration`.
var WithLowercaseRouting Configurator
// WithOptimizations can force the application to optimize for the best performance where is possible.
//
// See `Configuration`.
var WithOptimizations Configurator
// WithFireMethodNotAllowed enables the FireMethodNotAllowed setting.
//
// See `Configuration`.
func WithFireMethodNotAllowed Configurator
// WithTimeFormat sets the TimeFormat setting.
//
// See `Configuration`.
func WithTimeFormat(timeformat string) Configurator
// WithCharset sets the Charset setting.
//
// See `Configuration`.
func WithCharset(charset string) Configurator
// WithPostMaxMemory sets the maximum post data size
// that a client can send to the server, this differs
// from the overral request body size which can be modified
// by the `context#SetMaxRequestBodySize` or `iris#LimitRequestBodySize`.
//
// Defaults to 32MB or 32 << 20 or 32*iris.MB if you prefer.
func WithPostMaxMemory(limit int64) Configurator
// WithRemoteAddrHeader enables or adds a new or existing request header name
// that can be used to validate the client's real IP.
//
// By-default no "X-" header is consired safe to be used for retrieving the
// client's IP address, because those headers can manually change by
// the client. But sometimes are useful e.g., when behind a proxy
// you want to enable the "X-Forwarded-For" or when cloudflare
// you want to enable the "CF-Connecting-IP", inneed you
// can allow the `ctx.RemoteAddr()` to use any header
// that the client may sent.
//
// Defaults to an empty map but an example usage is:
// WithRemoteAddrHeader("X-Forwarded-For")
//
// Look `context.RemoteAddr()` for more.
func WithRemoteAddrHeader(headerName string) Configurator
// WithoutRemoteAddrHeader disables an existing request header name
// that can be used to validate and parse the client's real IP.
//
//
// Keep note that RemoteAddrHeaders is already defaults to an empty map
// so you don't have to call this Configurator if you didn't
// add allowed headers via configuration or via `WithRemoteAddrHeader` before.
//
// Look `context.RemoteAddr()` for more.
func WithoutRemoteAddrHeader(headerName string) Configurator
// WithRemoteAddrPrivateSubnet adds a new private sub-net to be excluded from `context.RemoteAddr`.
// See `WithRemoteAddrHeader` too.
func WithRemoteAddrPrivateSubnet(startIP, endIP string) Configurator
// WithOtherValue adds a value based on a key to the Other setting.
//
// See `Configuration.Other`.
func WithOtherValue(key string, val interface{}) Configurator
// WithSitemap enables the sitemap generator.
// Use the Route's `SetLastMod`, `SetChangeFreq` and `SetPriority` to modify
// the sitemap's URL child element properties.
//
// It accepts a "startURL" input argument which
// is the prefix for the registered routes that will be included in the sitemap.
//
// If more than 50,000 static routes are registered then sitemaps will be splitted and a sitemap index will be served in
// /sitemap.xml.
//
// If `Application.I18n.Load/LoadAssets` is called then the sitemap will contain translated links for each static route.
//
// If the result does not complete your needs you can take control
// and use the github.com/kataras/sitemap package to generate a customized one instead.
//
// Example: https://github.com/kataras/iris/tree/master/_examples/sitemap.
func WithSitemap(startURL string) Configurator
// WithTunneling is the `iris.Configurator` for the `iris.Configuration.Tunneling` field.
// It's used to enable http tunneling for an Iris Application, per registered host
//
// Alternatively use the `iris.WithConfiguration(iris.Configuration{Tunneling: iris.TunnelingConfiguration{ ...}}}`.
var WithTunneling Configuartor
```
## Custom values
The `iris.Configuration` contains a field called `Other map[string]interface{}` which accepts any custom `key:value` option, therefore you can use that field to pass specific values that your app expects based on the custom requirements.
The `iris.Configuration` contains a field named `Other map[string]interface{}` which accepts any custom `key:value` option, therefore you can use that field to pass specific values that your app expects based on the custom requirements.
```go
app.Listen(":8080",
@ -99,15 +270,12 @@ app.Listen(":8080",
You can access those fields via `app.ConfigurationReadOnly`.
```go
serverName := app.ConfigurationReadOnly().Other["MyServerName"]
serverOwner := app.ConfigurationReadOnly().Other["ServerOwner"]
```
## Access Configuration from Context
Inside a handler, retrieve those fields using the following:
```go
ctx.Application().ConfigurationReadOnly()
func (ctx iris.Context) {
cfg := app.Application().ConfigurationReadOnly().GetOther()
srvName := cfg["MyServerName"]
srvOwner := cfg["ServerOwner"]
}
```

@ -75,8 +75,7 @@ app.Get("/resource2", func(ctx iris.Context) {
})
```
[Read the full example](https://github.com/kataras/iris/blob/8ee0de51c593fe0483fbea38117c3c88e065f2ef/_examples/http_responsewriter/content-negotiation/main.go#L22).
[Read the full example](https://github.com/kataras/iris/blob/master/_examples/response-writer/content-negotiation/main.go#L22).
## Documentation

@ -4,9 +4,11 @@ However the Iris `Context` provides some helpers to make the most common use cas
## Set a Cookie
The `SetCookie` method adds a cookie.
The `SetCookie` and `UpsertCookie` methods add or set (replaces if necessary) a cookie.
Use of the "options" is not required, they can be used to modify the "cookie". You'll see later on what the available options are, custom ones can be added depending on your web application requirements, this also helps to not repeat yourself in a codebase.
The last argument of "options" is optional. a `CookieOption` can be used to modify the "cookie". You'll see later on what the available options are, custom ones can be added depending on your web application requirements, this also helps to not repeat yourself in a codebase.
Before we continue, the `Context` has a `SetSameSite(http.SameSite)` method too. It sets the ["SameSite"](https://web.dev/samesite-cookies-explained/) cookie field for all cookies to be sent.
```go
SetCookie(cookie *http.Cookie, options ...CookieOption)

@ -212,4 +212,4 @@ curl -X POST http://localhost:8080/upload \
-H "Content-Type: multipart/form-data"
```
More examples can be found at <https://github.com/kataras/iris/tree/master/_examples/http_request>.
More examples can be found at <https://github.com/kataras/iris/tree/master/_examples/request-body> and <https://github.com/kataras/iris/tree/master/_examples/file-server>.

142
Grpc.md Normal file

@ -0,0 +1,142 @@
gRPC[*](https://grpc.io/) is a modern open source high performance Remote Procedure Call framework that can run in any environment. It can efficiently connect services in and across data centers with pluggable support for load balancing, tracing, health checking and authentication. It is also applicable in last mile of distributed computing to connect devices, mobile applications and browsers to backend services.
## gRPC
Iris and gRPC integration lives inside the [mvc](https://github.com/kataras/iris/tree/master/mvc) package.
Have you ever have difficulties converting your app or parts of it from HTTP to gGRPC or did you ever wish you had decent HTTP framework support as well for your gRPC services? Now, with Iris you have the best of two worlds. Without change a bit of your existing gRPC services code, you can register them as Iris HTTP routes through a Controller (your service struct value).
[[_assets/grpc-compatible-question.png]]
> Learn more about our conversation at: https://github.com/kataras/iris/issues/1449#issuecomment-623260695
## Step by step
We will follow the [official helloworld gRPC example](https://github.com/grpc/grpc-go/tree/master/examples/helloworld). If you had already work with gRPC services you can skip 1-5.
**1.** Let's write our proto schema for request and response.
```protobuf
syntax = "proto3";
package helloworld;
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
```
**2.** Install the protoc Go plugin
```sh
$ go get -u github.com/golang/protobuf/protoc-gen-go
```
**3.** Generate Go file from the helloworld.proto file above
```sh
$ protoc -I helloworld/ helloworld/helloworld.proto --go_out=plugins=grpc:helloworld
```
**4.** Implement a gRPC service as usually, with or without Iris
```go
import (
// [...]
pb "myapp/helloworld"
"context"
)
```
```go
type Greeter struct { }
// SayHello implements helloworld.GreeterServer.
func (c *Greeter) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
return &pb.HelloReply{Message: "Hello " + in.GetName()}, nil
}
```
Iris automatically binds the standard "context" `context.Context` to `iris.Context.Request().Context()`
and any other structure that is not mapping to a registered dependency
as a payload (depending on the request), e.g XML, YAML, Query, Form, JSON, Protobuf.
**5.** Register your service to the gRPC server
```go
import (
// [...]
pb "myapp/helloworld"
"google.golang.org/grpc"
)
```
```go
grpcServer := grpc.NewServer()
myService := &Greeter{}
pb.RegisterGreeterServer(grpcServer, myService)
```
**6.** Register this `myService` to Iris
The `mvc.New(party).Handle(ctrl, mvc.GRPC{...})` option allows to register gRPC services per-party (without the requirement of a full wrapper) and optionally strict access to gRPC-only clients.
Register MVC application controller for gRPC services.
You can bind as many mvc gRpc services in the same Party or app,
as the `ServiceName` differs.
```go
import (
// [...]
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/mvc"
)
```
```go
app := iris.New()
rootApp := mvc.New(app)
rootApp.Handle(myService, mvc.GRPC{
Server: grpcServer, // Required.
ServiceName: "helloworld.Greeter", // Required.
Strict: false,
})
```
**7.** Generate TLS Keys
The Iris server **should ran under TLS** (it's a gRPC requirement).
```sh
$ openssl genrsa -out server.key 2048
$ openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650
```
**8.** Listen and Serve
```go
app.Run(iris.TLS(":443", "server.crt", "server.key"))
```
POST: `https://localhost:443/helloworld.Greeter/SayHello` with request data: `{"name": "John"}`
xpected output: `{"message": "Hello John"}`.
Both HTTP Client and gRPC client will be able to communicate with our Iris+gRPC service.
### Exercise files
Full Server, Clients and Testing Code can be found at: <https://github.com/kataras/iris/tree/master/_examples/mvc/grpc-compatible>.

@ -40,6 +40,7 @@ This wiki is the main source of documentation for **developers** working with (o
* [[Websockets]]
* [[Dependency Injection|Dependency-Injection]]
* [[MVC]]
* [[gRPC]]
* [[Sitemap]]
* [[Localization]]
* [[Testing]]

28
Host.md

@ -8,20 +8,30 @@ by passing a network address with form of "hostname:ip". With Iris
we use the `iris.Addr` which is an `iris.Runner` type
```go
import "github.com/kataras/iris/v12"
```
```go
app := iris.New()
// Listening on tcp with network address 0.0.0.0:8080.
//
// Listen is a shortcut for app.Run(iris.Addr(":8080")).
app.Listen(":8080")
```
Sometimes you have created a standard net/http server somewhere else in your app and want to use that to serve the Iris web app
Sometimes you choose to have full control over the `http.Server` instance.
```go
import "net/http"
```
```go
// Same as before but using a custom http.Server which may being used somewhere else too
app.Run(iris.Server(&http.Server{Addr:":8080"}))
server := &http.Server{Addr:":8080" /* other fields here */}
app.Run(iris.Server(server))
```
The most advanced usage is to create a custom or a standard `net.Listener` and pass that to `app.Run`
The most advanced usage is to create a custom `net.Listener` and pass that to `app.Run`.
```go
// Using a custom net.Listener
@ -32,7 +42,7 @@ if err != nil {
app.Run(iris.Listener(l))
```
A more complete example, using the unix-only socket files feature
A more complete example, using the unix-only socket files feature.
```go
package main
@ -66,7 +76,7 @@ func main() {
}
```
UNIX and BSD hosts can take advandage of the reuse port feature
UNIX and BSD hosts can take advandage of the reuse port feature.
```go
package main
@ -115,17 +125,17 @@ func main() {
### HTTP/2 and Secure
If you have signed file keys you can use the `iris.TLS` to serve `https` based on those certification keys
If you have local certification and server key files you can use the `iris.TLS` to serve `https://`.
```go
// TLS using files
// TLS using files or raw contents.
app.Run(iris.TLS("127.0.0.1:443", "mycert.cert", "mykey.key"))
```
The method you should use when your app is ready for **production** is the `iris.AutoTLS` which starts a secure server with automated certifications provided by https://letsencrypt.org for **free**
The method you should use when your app is ready for the outside world is the `iris.AutoTLS` which starts a secure server with certifications provided by https://letsencrypt.org for **free**.
```go
// Automatic TLS
// Automatic TLS.
app.Run(iris.AutoTLS(":443", "example.com", "admin@example.com"))
```

16
MVC.md

@ -16,6 +16,8 @@ with the fastest possible execution.
If you're new to back-end web development read about the MVC architectural pattern first, a good start is that [wikipedia article](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller).
**Note:** Read the [[Dependency Injection]] section before continue.
**Characteristics**
All HTTP Methods are supported, for example if want to serve `GET`
@ -88,8 +90,7 @@ useful to call middlewares or when many methods use the same collection of data.
Optional `EndRequest(ctx)` function to perform any finalization after any method executed.
Inheritance, recursively, e.g. our [mvc session-controller example](https://github.com/kataras/iris/tree/master/_examples/mvc/session-controller), has the `Session *sessions.Session` as struct field which is filled by the sessions manager's `Start` as a dynamic dependency to the MVC Application:
`mvcApp.Register(sessions.New(sessions.Config{Cookie: "iris_session_id"}).Start)`.
Inheritance, recursively, e.g. our [mvc session-controller example](https://github.com/kataras/iris/tree/master/_examples/mvc/session-controller), has the `Session *sessions.Session` as struct field which is filled by the sessions manager.
Access to the dynamic path parameters via the controller's methods' input arguments, no binding is needed.
When you use the Iris' default syntax to parse handlers from a controller, you need to suffix the methods
@ -135,7 +136,7 @@ func(c *ExampleController) Get() string |
mvc.Result or (mvc.Result, error)
```
Where `mvc.Result` is a `hero.Result` type alias, which is this interface one:
Where `mvc.Result` is just a type alias of `hero.Result`:
```go
type Result interface {
@ -146,8 +147,7 @@ type Result interface {
## Example
This example is equivalent to the
https://github.com/kataras/iris/blob/master/_examples/hello-world/main.go
This example is equivalent to a simple hello world application.
It seems that additional code you have to write doesn't worth it but remember that, this example
does not make use of iris mvc features like
@ -164,7 +164,7 @@ but you can choose
what suits you best with Iris, low-level handlers: performance
or high-level controllers: easier to maintain and smaller codebase on large applications.
**Read the comments very careful**
**Read the comments carefully**
```go
package main
@ -312,6 +312,8 @@ The first comment states this is an [HTTP GET](https://www.w3schools.com/tags/re
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](https://www.w3schools.com/tags/ref_httpmethods.asp) 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](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) will be sent to the client instead.
> The [_examples/mvc](https://github.com/kataras/iris/tree/master/_examples/mvc) and [mvc/controller_test.go](https://github.com/kataras/iris/blob/master/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...
> The [_examples/mvc](https://github.com/kataras/iris/tree/master/_examples/mvc) and [mvc/controller_test.go](https://github.com/kataras/iris/blob/master/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 more...
> For websocket controller go ahead to the [[Websockets]] chapter instead.
> For gRPC controller go ahead to the [[gRPC]] chapter instead.

@ -1,9 +1,9 @@
Iris, wisely, not features a builtin data validation. However, you are not limited. In this example we will learn how to use the [**go-playground/validator/v10**](https://github.com/go-playground/validator) for request values validation.
Iris, wisely, not features a builtin data validation. However, it does allow you to attach a validator which will automatically called on methods like `Context.ReadJSON, ReadXML...`. In this example we will learn how to use the [**go-playground/validator/v10**](https://github.com/go-playground/validator) for request body validation.
Check the full docs on tags usage [here](https://pkg.go.dev/github.com/go-playground/validator/v10@v10.2.0?tab=doc).
Check the full docs and the struct tags usage [here](https://pkg.go.dev/github.com/go-playground/validator/v10@v10.2.0?tab=doc).
```sh
$ go get github.com/go-playground/validator/v10
$ go get github.com/go-playground/validator/v10@latest
```
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"`.
@ -37,29 +37,26 @@ type Address struct {
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()
// Use a single instance of Validate, it caches struct info.
v := validator.New()
// Register validation for 'User'
// Register a custom struct validation for 'User'
// NOTE: only have to register a non-pointer type for 'User', validator
// internally dereferences during it's type checks.
validate.RegisterStructValidation(UserStructLevelValidation, User{})
v.RegisterStructValidation(UserStructLevelValidation, User{})
app := iris.New()
// Register the validator to the Iris Application.
app.Validator = v
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)
err := vctx.ReadJSON(&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.
@ -122,4 +119,4 @@ Example request of JSON form:
}
```
Example can be found at: <https://github.com/kataras/iris/tree/master/_examples/http_request/read-json-struct-validation/main.go>.
Example can be found at: <https://github.com/kataras/iris/tree/master/_examples/request-body/read-json-struct-validation/main.go>.

@ -4,7 +4,11 @@ Simply edit this wiki page and add your own in a list-based form.
---------------
- https://www.cedato.com/
- https://www.cedato.com
- https://github.com/peterq/pan-light
- https://mlog.club
- https://elta.ee
- https://wzd.dev
- https://anuncia.do
- PUT YOUR PROJECT OR COMPANY HERE
https://github.com/eltaline/ctrl

@ -1,5 +1,6 @@
Prepare a cup of coffee or tea, whatever pleases you the most, and read some articles we found for you.
* [Go lang: MongoDB, Iris API](https://bit.ly/2TTtbYx)
* [Golang Iris Web Course 2019](https://bit.ly/web-course-2019)
* [How to write a Go API Part 1: A Webserver With Iris](https://bit.ly/32xmf4Q)
* [How to write a Go API Part 2: Database Integration With GORM](https://bit.ly/34PvKxR)
@ -21,9 +22,8 @@ Prepare a cup of coffee or tea, whatever pleases you the most, and read some art
| Description | Link | Author | Year |
| -----------|-------------|-------------|-----|
| Web Course | https://bit.ly/web-course-2019 | TechMaster | **2019** |
| Quick Start with Iris | https://bit.ly/2wQIrJw | J-Secur1ty | **2019** |
| Installing Iris | https://bit.ly/2KhgB1J | WarnabiruTV | 2018 |
| Web Course | https://bit.ly/web-course-2019 | TechMaster | 2019 |
| Quick Start with Iris | https://bit.ly/2wQIrJw | J-Secur1ty | 2019 |
| Iris & Mongo DB Complete | https://bit.ly/2IcXZOu | Musobar Media | 2018 |
| Getting Started with Iris | https://bit.ly/2XGafMv | stephgdesign | 2018 |

@ -103,4 +103,4 @@ app.Get("/save", func(ctx iris.Context) {
})
```
In addition to that, Iris provides a comprehensive API for **Transactions**. Learn more about it by running an [example](https://github.com/kataras/iris/blob/master/_examples/http_responsewriter/transactions/main.go).
In addition to that, Iris provides a comprehensive API for **Transactions**. Learn more about it by running an [example](https://github.com/kataras/iris/blob/master/_examples/response-writer/transactions/main.go).

@ -4,6 +4,29 @@ Here is a full list of methods that the `iris.Context` provides.
```go
type Context interface {
// BeginRequest is executing once for each request
// it should prepare the (new or acquired from pool) context's fields for the new request.
// Do NOT call it manually. Framework calls it automatically.
//
// Resets
// 1. handlers to nil.
// 2. values to empty.
// 3. the defer function.
// 4. response writer to the http.ResponseWriter.
// 5. request to the *http.Request.
BeginRequest(http.ResponseWriter, *http.Request)
// EndRequest is executing once after a response to the request was sent and this context is useless or released.
// Do NOT call it manually. Framework calls it automatically.
//
// 1. executes the Defer function (if any).
// 2. flushes the response writer's result or fire any error handler.
// 3. releases the response writer.
EndRequest()
// Defer executes a handler on this Context right before the request ends.
// The `StopExecution` does not effect the execution of this defer handler.
// The "h" runs before `FireErrorCode` (when response status code is not successful).
Defer(Handler)
// ResponseWriter returns an http.ResponseWriter compatible response writer, as expected.
ResponseWriter() ResponseWriter
// ResetResponseWriter should change or upgrade the Context's ResponseWriter.
@ -24,18 +47,13 @@ type Context interface {
// ctx.ResetRequest(r.WithContext(stdCtx)).
ResetRequest(r *http.Request)
// SetCurrentRouteName sets the route's name internally,
// in order to be able to find the correct current "read-only" Route when
// end-developer calls the `GetCurrentRoute()` function.
// It's being initialized by the Router, if you change that name
// manually nothing really happens except that you'll get other
// route via `GetCurrentRoute()`.
// Instead, to execute a different path
// from this context you should use the `Exec` function
// or change the handlers via `SetHandlers/AddHandler` functions.
SetCurrentRouteName(currentRouteName string)
// GetCurrentRoute returns the current registered "read-only" route that
// was being registered to this request's path.
// SetCurrentRoutes sets the route internally,
// See `GetCurrentRoute()` method too.
// It's being initialized by the Router.
// See `Exec` or `SetHandlers/AddHandler` methods to simulate a request.
SetCurrentRoute(route RouteReadOnly)
// GetCurrentRoute returns the current "read-only" route that
// was registered to this request's path.
GetCurrentRoute() RouteReadOnly
// Do calls the SetHandlers(handlers)
@ -53,7 +71,6 @@ type Context interface {
// Router is calling this function to add the route's handler.
// If AddHandler called then the handlers will be inserted
// to the end of the already-defined route's handler.
//
AddHandler(...Handler)
// SetHandlers replaces all handlers with the new.
SetHandlers(Handlers)
@ -62,7 +79,7 @@ type Context interface {
// HandlerIndex sets the current index of the
// current context's handlers chain.
// If -1 passed then it just returns the
// If n < 0 or the current handlers length is 0 then it just returns the
// current handler index without change the current index.
//
// Look Handlers(), Next() and StopExecution() too.
@ -138,12 +155,44 @@ type Context interface {
// Skip skips/ignores the next handler from the handlers chain,
// it should be used inside a middleware.
Skip()
// StopExecution if called then the following .Next calls are ignored,
// StopExecution stops the handlers chain of this request.
// Meaning that any following `Next` calls are ignored,
// as a result the next handlers in the chain will not be fire.
StopExecution()
// IsStopped checks and returns true if the current position of the Context is 255,
// means that the StopExecution() was called.
// IsStopped reports whether the current position of the context's handlers is -1,
// means that the StopExecution() was called at least once.
IsStopped() bool
// StopWithStatus stops the handlers chain and writes the "statusCode".
//
// If the status code is a failure one then
// it will also fire the specified error code handler.
StopWithStatus(statusCode int)
// StopWithText stops the handlers chain and writes the "statusCode"
// among with a message "plainText".
//
// If the status code is a failure one then
// it will also fire the specified error code handler.
StopWithText(statusCode int, plainText string)
// StopWithError stops the handlers chain and writes the "statusCode"
// among with the error "err".
//
// If the status code is a failure one then
// it will also fire the specified error code handler.
StopWithError(statusCode int, err error)
// StopWithJSON stops the handlers chain, writes the status code
// and sends a JSON response.
//
// If the status code is a failure one then
// it will also fire the specified error code handler.
StopWithJSON(statusCode int, jsonObject interface{})
// StopWithProblem stops the handlers chain, writes the status code
// and sends an application/problem+json response.
// See `iris.NewProblem` to build a "problem" value correctly.
//
// If the status code is a failure one then
// it will also fire the specified error code handler.
StopWithProblem(statusCode int, problem Problem)
// OnConnectionClose registers the "cb" function which will fire (on its own goroutine, no need to be registered goroutine by the end-dev)
// when the underlying connection has gone away.
//
@ -167,6 +216,7 @@ type Context interface {
// OnClose registers the callback function "cb" to the underline connection closing event using the `Context#OnConnectionClose`
// and also in the end of the request handler using the `ResponseWriter#SetBeforeFlush`.
// Note that you can register only one callback for the entire request handler chain/per route.
// Note that the "cb" will only be called once.
//
// Look the `Context#OnConnectionClose` and `ResponseWriter#SetBeforeFlush` for more.
OnClose(cb func())
@ -207,6 +257,12 @@ type Context interface {
// Subdomain returns the subdomain of this request, if any.
// Note that this is a fast method which does not cover all cases.
Subdomain() (subdomain string)
// FindClosest returns a list of "n" paths close to
// this request based on subdomain and request path.
//
// Order may change.
// Example: https://github.com/kataras/iris/tree/master/_examples/routing/intelligence/manual
FindClosest(n int) []string
// IsWWW returns true if the current subdomain (if any) is www.
IsWWW() bool
// FullRqeuestURI returns the full URI,
@ -221,10 +277,13 @@ type Context interface {
//
// Look `Configuration.RemoteAddrHeaders`,
// `Configuration.WithRemoteAddrHeader(...)`,
// `Configuration.WithoutRemoteAddrHeader(...)` for more.
// `Configuration.WithoutRemoteAddrHeader(...)`and
// `Configuration.RemoteAddrPrivateSubnets` for more.
RemoteAddr() string
// GetHeader returns the request header's value based on its name.
GetHeader(name string) string
// GetDomain resolves and returns the server's domain.
GetDomain() string
// IsAjax returns true if this request is an 'ajax request'( XMLHttpRequest)
//
// There is no a 100% way of knowing that a request was made via Ajax.
@ -246,10 +305,25 @@ type Context interface {
//
// Keep note that this checks the "User-Agent" request header.
IsMobile() bool
// GetReferrer extracts and returns the information from the "Referer" header as specified
// IsScript reports whether a client is a script.
IsScript() bool
// IsHTTP2 reports whether the protocol version for incoming request was HTTP/2.
// The client code always uses either HTTP/1.1 or HTTP/2.
IsHTTP2() bool
// IsGRPC reports whether the request came from a gRPC client.
IsGRPC() bool
// GetReferrer extracts and returns the information from the "Referrer" header as specified
// in https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy
// or by the URL query parameter "referer".
// or by the URL query parameter "referrer".
GetReferrer() Referrer
// SetLanguage force-sets the language for i18n, can be used inside a middleare.
// It has the highest priority over the rest and if it is empty then it is ignored,
// if it set to a static string of "default" or to the default language's code
// then the rest of the language extractors will not be called at all and
// the default language will be set instead.
//
// See `app.I18n.ExtractFunc` for a more organised way of the same feature.
SetLanguage(langCode string)
// GetLocale returns the current request's `Locale` found by i18n middleware.
// See `Tr` too.
GetLocale() Locale
@ -257,7 +331,9 @@ type Context interface {
// See `GetLocale` too.
// Example: https://github.com/kataras/iris/tree/master/_examples/i18n
Tr(format string, args ...interface{}) string
// SetVersion force-sets the API Version integrated with the "iris/versioning" subpackage.
// It can be used inside a middleare.
SetVersion(constraint string)
// +------------------------------------------------------------+
// | Headers helpers |
// +------------------------------------------------------------+
@ -387,17 +463,17 @@ type Context interface {
//
// If not found or parse errors returns the "def".
PostValueInt64Default(name string, def int64) int64
// PostValueInt64Default returns the parsed form data from POST, PATCH,
// PostValueFloat64 returns the parsed form data from POST, PATCH,
// or PUT body parameters based on a "name", as float64.
//
// If not found returns -1 and a non-nil error.
PostValueFloat64(name string) (float64, error)
// PostValueInt64Default returns the parsed form data from POST, PATCH,
// PostValueFloat64Default returns the parsed form data from POST, PATCH,
// or PUT body parameters based on a "name", as float64.
//
// If not found or parse errors returns the "def".
PostValueFloat64Default(name string, def float64) float64
// PostValueInt64Default returns the parsed form data from POST, PATCH,
// PostValueBool returns the parsed form data from POST, PATCH,
// or PUT body parameters based on a "name", as bool.
//
// If not found or value is false, then it returns false, otherwise true.
@ -413,7 +489,7 @@ type Context interface {
// The default form's memory maximum size is 32MB, it can be changed by the
// `iris#WithPostMaxMemory` configurator at main configuration passed on `app.Run`'s second argument.
//
// Example: https://github.com/kataras/iris/tree/master/_examples/http_request/upload-file
// Example: https://github.com/kataras/iris/tree/master/_examples/file-server/upload-file
FormFile(key string) (multipart.File, *multipart.FileHeader, error)
// UploadFormFiles uploads any received file(s) from the client
// to the system physical location "destDirectory".
@ -440,7 +516,7 @@ type Context interface {
// See `FormFile` to a more controlled to receive a file.
//
//
// Example: https://github.com/kataras/iris/tree/master/_examples/http_request/upload-files
// Example: https://github.com/kataras/iris/tree/master/_examples/file-server/upload-files
UploadFormFiles(destDirectory string, before ...func(Context, *multipart.FileHeader)) (n int64, err error)
// +------------------------------------------------------------+
@ -472,7 +548,7 @@ type Context interface {
// UnmarshalBody reads the request's body and binds it to a value or pointer of any type.
// Examples of usage: context.ReadJSON, context.ReadXML.
//
// Example: https://github.com/kataras/iris/blob/master/_examples/http_request/read-custom-via-unmarshaler/main.go
// Example: https://github.com/kataras/iris/blob/master/_examples/request-body/read-custom-via-unmarshaler/main.go
//
// UnmarshalBody does not check about gzipped data.
// Do not rely on compressed data incoming to your server. The main reason is: https://en.wikipedia.org/wiki/Zip_bomb
@ -480,27 +556,40 @@ type Context interface {
UnmarshalBody(outPtr interface{}, unmarshaler Unmarshaler) error
// ReadJSON reads JSON from request's body and binds it to a pointer of a value of any json-valid type.
//
// Example: https://github.com/kataras/iris/blob/master/_examples/http_request/read-json/main.go
// Example: https://github.com/kataras/iris/blob/master/_examples/request-body/read-json/main.go
ReadJSON(jsonObjectPtr interface{}) error
// ReadXML reads XML from request's body and binds it to a pointer of a value of any xml-valid type.
//
// Example: https://github.com/kataras/iris/blob/master/_examples/http_request/read-xml/main.go
// Example: https://github.com/kataras/iris/blob/master/_examples/request-body/read-xml/main.go
ReadXML(xmlObjectPtr interface{}) error
// ReadYAML reads YAML from request's body and binds it to the "outPtr" value.
//
// Example: https://github.com/kataras/iris/blob/master/_examples/http_request/read-yaml/main.go
// Example: https://github.com/kataras/iris/blob/master/_examples/request-body/read-yaml/main.go
ReadYAML(outPtr interface{}) error
// ReadForm binds the formObject with the form data
// it supports any kind of type, including custom structs.
// ReadForm binds the request body of a form to the "formObject".
// It supports any kind of type, including custom structs.
// It will return nothing if request data are empty.
// The struct field tag is "form".
// Note that it will return nil error on empty form data if `Configuration.FireEmptyFormError`
// is false (as defaulted) in this case the caller should check the pointer to
// see if something was actually binded.
//
// Example: https://github.com/kataras/iris/blob/master/_examples/http_request/read-form/main.go
// Example: https://github.com/kataras/iris/blob/master/_examples/request-body/read-form/main.go
ReadForm(formObject interface{}) error
// ReadQuery binds the "ptr" with the url query string. The struct field tag is "url".
// ReadQuery binds url query to "ptr". The struct field tag is "url".
//
// Example: https://github.com/kataras/iris/blob/master/_examples/http_request/read-query/main.go
// Example: https://github.com/kataras/iris/blob/master/_examples/request-body/read-query/main.go
ReadQuery(ptr interface{}) error
// ReadProtobuf binds the body to the "ptr" of a proto Message and returns any error.
ReadProtobuf(ptr proto.Message) error
// ReadMsgPack binds the request body of msgpack format to the "ptr" and returns any error.
ReadMsgPack(ptr interface{}) error
// ReadBody binds the request body to the "ptr" depending on the HTTP Method and the Request's Content-Type.
// If a GET method request then it reads from a form (or URL Query), otherwise
// it tries to match (depending on the request content-type) the data format e.g.
// JSON, Protobuf, MsgPack, XML, YAML, MultipartForm and binds the result to the "ptr".
ReadBody(ptr interface{}) error
// +------------------------------------------------------------+
// | Body (raw) Writers |
// +------------------------------------------------------------+
@ -608,6 +697,24 @@ type Context interface {
// supports gzip compression, so the following response data will
// be sent as compressed gzip data to the client.
Gzip(enable bool)
// GzipReader accepts a boolean, which, if set to true
// it wraps the request body reader with a gzip reader one (decompress request data on read).
// If the "enable" input argument is false then the request body will reset to the default one.
//
// Useful when incoming request data are gzip compressed.
// All future calls of `ctx.GetBody/ReadXXX/UnmarshalBody` methods will respect this option.
//
// Usage:
// app.Use(func(ctx iris.Context){
// ctx.GzipReader(true)
// ctx.Next()
// })
//
// If a client request's body is not gzip compressed then
// it returns with a `ErrGzipNotSupported` error, which can be safety ignored.
//
// See `GzipReader` package-level middleware too.
GzipReader(enable bool) error
// +------------------------------------------------------------+
// | Rich Body Content Writers/Renderers |
@ -698,6 +805,10 @@ type Context interface {
Markdown(markdownB []byte, options ...Markdown) (int, error)
// YAML parses the "v" using the yaml parser and renders its result to the client.
YAML(v interface{}) (int, error)
// Protobuf parses the "v" of proto Message and renders its result to the client.
Protobuf(v proto.Message) (int, error)
// MsgPack parses the "v" of msgpack format and renders its result to the client.
MsgPack(v interface{}) (int, error)
// +-----------------------------------------------------------------------+
// | Content Νegotiation |
@ -745,41 +856,94 @@ type Context interface {
// | Serve files |
// +------------------------------------------------------------+
// ServeContent serves content, headers are autoset
// receives three parameters, it's low-level function, instead you can use .ServeFile(string,bool)/SendFile(string,string)
// ServeContent replies to the request using the content in the
// provided ReadSeeker. The main benefit of ServeContent over io.Copy
// is that it handles Range requests properly, sets the MIME type, and
// handles If-Match, If-Unmodified-Since, If-None-Match, If-Modified-Since,
// and If-Range requests.
//
// If the response's Content-Type header is not set, ServeContent
// first tries to deduce the type from name's file extension.
//
// You can define your own "Content-Type" with `context#ContentType`, before this function call.
// The name is otherwise unused; in particular it can be empty and is
// never sent in the response.
//
// This function doesn't support resuming (by range),
// use ctx.SendFile or router's `HandleDir` instead.
ServeContent(content io.ReadSeeker, filename string, modtime time.Time, gzipCompression bool) error
// ServeFile serves a file (to send a file, a zip for example to the client you should use the `SendFile` instead)
// receives two parameters
// filename/path (string)
// gzipCompression (bool)
// If modtime is not the zero time or Unix epoch, ServeContent
// includes it in a Last-Modified header in the response. If the
// request includes an If-Modified-Since header, ServeContent uses
// modtime to decide whether the content needs to be sent at all.
//
// You can define your own "Content-Type" with `context#ContentType`, before this function call.
// The content's Seek method must work: ServeContent uses
// a seek to the end of the content to determine its size.
//
// This function doesn't support resuming (by range),
// use ctx.SendFile or router's `HandleDir` instead.
// If the caller has set w's ETag header formatted per RFC 7232, section 2.3,
// ServeContent uses it to handle requests using If-Match, If-None-Match, or If-Range.
//
// Use it when you want to serve dynamic files to the client.
ServeFile(filename string, gzipCompression bool) error
// SendFile sends file for force-download to the client
// Note that *os.File implements the io.ReadSeeker interface.
// Note that gzip compression can be registered through `ctx.Gzip(true)` or `app.Use(iris.Gzip)`.
ServeContent(content io.ReadSeeker, filename string, modtime time.Time)
// ServeContentWithRate same as `ServeContent` but it can throttle the speed of reading
// and though writing the "content" to the client.
ServeContentWithRate(content io.ReadSeeker, filename string, modtime time.Time, limit float64, burst int)
// ServeFile replies to the request with the contents of the named
// file or directory.
//
// Use this instead of ServeFile to 'force-download' bigger files to the client.
// If the provided file or directory name is a relative path, it is
// interpreted relative to the current directory and may ascend to
// parent directories. If the provided name is constructed from user
// input, it should be sanitized before calling `ServeFile`.
//
// Use it when you want to serve assets like css and javascript files.
// If client should confirm and save the file use the `SendFile` instead.
// Note that gzip compression can be registered through `ctx.Gzip(true)` or `app.Use(iris.Gzip)`.
ServeFile(filename string) error
// ServeFileWithRate same as `ServeFile` but it can throttle the speed of reading
// and though writing the file to the client.
ServeFileWithRate(filename string, limit float64, burst int) error
// SendFile sends a file as an attachment, that is downloaded and saved locally from client.
// Note that gzip compression can be registered through `ctx.Gzip(true)` or `app.Use(iris.Gzip)`.
// Use `ServeFile` if a file should be served as a page asset instead.
SendFile(filename string, destinationName string) error
// SendFileWithRate same as `SendFile` but it can throttle the speed of reading
// and though writing the file to the client.
SendFileWithRate(src, destName string, limit float64, burst int) error
// +------------------------------------------------------------+
// | Cookies |
// +------------------------------------------------------------+
// AddCookieOptions adds cookie options for `SetCookie`,
// `SetCookieKV, UpsertCookie` and `RemoveCookie` methods
// for the current request. It can be called from a middleware before
// cookies sent or received from the next Handler in the chain.
// See `ClearCookieOptions` too.
//
// Available builtin Cookie options are:
// * CookieAllowReclaim
// * CookieAllowSubdomains
// * CookieSecure
// * CookieHTTPOnly
// * CookieSameSite
// * CookiePath
// * CookieCleanPath
// * CookieExpires
// * CookieEncoding
//
// Example at: https://github.com/kataras/iris/tree/master/_examples/cookies/securecookie
AddCookieOptions(options ...CookieOption)
// ClearCookieOptions clears any previously registered cookie options.
// See `AddCookieOptions` too.
ClearCookieOptions()
// SetCookie adds a cookie.
// Use of the "options" is not required, they can be used to amend the "cookie".
//
// Example: https://github.com/kataras/iris/tree/master/_examples/cookies/basic
SetCookie(cookie *http.Cookie, options ...CookieOption)
// UpsertCookie adds a cookie to the response like `SetCookie` does
// but it will also perform a replacement of the cookie
// if already set by a previous `SetCookie` call.
// It reports whether the cookie is new (true) or an existing one was updated (false).
UpsertCookie(cookie *http.Cookie, options ...CookieOption) bool
// SetCookieKV adds a cookie, requires the name(string) and the value(string).
//
// By default it expires at 2 hours and it's added to the root path,
@ -883,6 +1047,27 @@ type Context interface {
// It will search from the current subdomain of context's host, if not inside the root domain.
RouteExists(method, path string) bool
// ReflectValue caches and returns a []reflect.Value{reflect.ValueOf(ctx)}.
// It's just a helper to maintain variable inside the context itself.
ReflectValue() []reflect.Value
// Controller returns a reflect Value of the custom Controller from which this handler executed.
// It will return a Kind() == reflect.Invalid if the handler was not executed from within a controller.
Controller() reflect.Value
// RegisterDependency registers a struct dependency at serve-time
// for the next handler in the chain. One value per type.
// Note that it's highly recommended to register
// your dependencies before server ran
// through APIContainer(app.ConfigureContainer) or MVC(mvc.New)
// in sake of minimum performance cost.
//
// See `UnRegisterDependency` too.
RegisterDependency(v interface{})
// UnRegisterDependency removes a dependency based on its type.
// Reports whether a dependency with that type was found and removed successfully.
//
// See `RegisterDependency` too.
UnRegisterDependency(typ reflect.Type) bool
// Application returns the iris app instance which belongs to this context.
// Worth to notice that this function returns an interface
// of the Application, which contains methods that are safe
@ -890,13 +1075,20 @@ type Context interface {
// and methods are not available here for the developer's safety.
Application() Application
// String returns the string representation of this request.
// Each context has a unique string representation.
// It can be used for simple debugging scenarios, i.e print context as string.
// SetID sets an ID, any value, to the Request Context.
// If possible the "id" should implement a `String() string` method
// so it can be rendered on `Context.String` method.
//
// What it returns? A number which declares the length of the
// total `String` calls per executable application, followed
// by the remote IP (the client) and finally the method:url.
// See `GetID` and `middleware/requestid` too.
SetID(id interface{})
// GetID returns the Request Context's ID.
// It returns nil if not given by a prior `SetID` call.
// See `middleware/requestid` too.
GetID() interface{}
// String returns the string representation of this request.
//
// It returns the Context's ID given by a `SetID`call,
// followed by the client's IP and the method:uri.
String() string
}
```

@ -1,6 +1,6 @@
# Error handlers
You can define your own handlers when a specific http error code occurs.
You can define your own handlers when a specific http error code occurs. Error handlers can be registered per `Party`.
Error codes are the http status codes that are bigger than or equal to 400, like 404 not found and 500 internal server error.
@ -13,13 +13,15 @@ import "github.com/kataras/iris/v12"
func main(){
app := iris.New()
app.RegisterView(iris.HTML("./views", ".html"))
app.OnErrorCode(iris.StatusNotFound, notFound)
app.OnErrorCode(iris.StatusInternalServerError, internalServerError)
// to register a handler for all "error"
// status codes(kataras/iris/context.StatusCodeNotSuccessful)
// defaults to < 200 || >= 400:
// to register a handler for all error codes:
// app.OnAnyErrorCode(handler)
app.Get("/", index)
app.Listen(":8080")
}
@ -54,6 +56,18 @@ func index(ctx iris.Context) {
> Learn more about [[View]].
Iris has the `EnablePathIntelligence` option which can be passed to `app.Run/Listen` method to auto-redirect a not found path to its closest match one, e.g `"http://localhost:8080/contac"` to `"http://localhost:8080/contact"`. Enable it:
```go
app.Listen(":8080", iris.WithPathIntelligence)
```
You can also force all paths to be lowercased by setting the `ForceLowercaseRouting` option:
```go
app.Listen(":8080", iris.WithLowercaseRouting, iris.WithPathIntelligence)
```
## The Problem type
Iris has builtin support for the [Problem Details for HTTP APIs](https://tools.ietf.org/html/rfc7807).

@ -124,9 +124,9 @@ Iris, unlike others, is 100% compatible with the standards and that's why the ma
Any third-party middleware that written for `net/http` is compatible with Iris using the `iris.FromStd(aThirdPartyMiddleware)`. Remember, `ctx.ResponseWriter()` and `ctx.Request()` returns the same `net/http` input arguments of an [http.Handler](https://golang.org/pkg/net/http/#Handler).
- [From func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc)](convert-handlers/negroni-like/main.go)
- [From http.Handler or http.HandlerFunc](convert-handlers/nethttp/main.go)
- [From func(http.HandlerFunc) http.HandlerFunc](convert-handlers/real-usecase-raven/writing-middleware/main.go)
- [From func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc)](https://github.com/kataras/iris/tree/master/_examples/convert-handlers/negroni-like/main.go)
- [From http.Handler or http.HandlerFunc](https://github.com/kataras/iris/tree/master/_examplesconvert-handlers/nethttp/main.go)
- [From func(http.HandlerFunc) http.HandlerFunc](https://github.com/kataras/iris/tree/master/_examplesconvert-handlers/real-usecase-raven/writing-middleware/main.go)
---------------------
Here is a list of some handlers made specifically for Iris:
@ -135,26 +135,50 @@ Here is a list of some handlers made specifically for Iris:
| Middleware | Example |
| -----------|-------------|
| [basic authentication](https://github.com/kataras/iris/tree/master/middleware/basicauth) | [iris/_examples/authentication/basicauth](https://github.com/kataras/iris/tree/master/_examples/authentication/basicauth) |
| [request logger](https://github.com/kataras/iris/tree/master/middleware/logger) | [iris/_examples/http_request/request-logger](https://github.com/kataras/iris/tree/master/_examples/http_request/request-logger) |
| [basic authentication](https://github.com/kataras/iris/tree/master/middleware/basicauth) | [iris/_examples/auth/basicauth](https://github.com/kataras/iris/tree/master/_examples/auth/basicauth) |
| [request logger](https://github.com/kataras/iris/tree/master/middleware/logger) | [iris/_examples/logging/request-logger](https://github.com/kataras/iris/tree/master/_examples/logging/request-logger) |
| [HTTP method override](https://github.com/kataras/iris/tree/master/middleware/methodoverride) | [iris/middleware/methodoverride/methodoverride_test.go](https://github.com/kataras/iris/blob/master/middleware/methodoverride/methodoverride_test.go) |
| [profiling (pprof)](https://github.com/kataras/iris/tree/master/middleware/pprof) | [iris/_examples/miscellaneous/pprof](https://github.com/kataras/iris/tree/master/_examples/miscellaneous/pprof) |
| [Google reCAPTCHA](https://github.com/kataras/iris/tree/master/middleware/recaptcha) | [iris/_examples/miscellaneous/recaptcha](https://github.com/kataras/iris/tree/master/_examples/miscellaneous/recaptcha) |
| [recovery](https://github.com/kataras/iris/tree/master/middleware/recover) | [iris/_examples/miscellaneous/recover](https://github.com/kataras/iris/tree/master/_examples/miscellaneous/recover) |
| [profiling (pprof)](https://github.com/kataras/iris/tree/master/middleware/pprof) | [iris/_examples/pprof](https://github.com/kataras/iris/tree/master/_examples/pprof) |
| [Google reCAPTCHA](https://github.com/kataras/iris/tree/master/middleware/recaptcha) | [iris/_examples/auth/recaptcha](https://github.com/kataras/iris/tree/master/_examples/auth/recaptcha) |
| [hCaptcha](https://github.com/kataras/iris/tree/master/middleware/hcaptcha) | [iris/_examples/auth/recaptcha](https://github.com/kataras/iris/tree/master/_examples/auth/hcaptcha) |
| [recovery](https://github.com/kataras/iris/tree/master/middleware/recover) | [iris/_examples/recover](https://github.com/kataras/iris/tree/master/_examples/recover) |
| [rate](https://github.com/kataras/iris/tree/master/middleware/rate) | [iris/_examples/request-ratelimit](https://github.com/kataras/iris/tree/master/_examples/request-ratelimit) |
| [jwt](https://github.com/kataras/iris/tree/master/middleware/jwt) | [iris/_examples/auth/jwt](https://github.com/kataras/iris/tree/master/_examples/auth/jwt) |
| [requestid](https://github.com/kataras/iris/tree/master/middleware/requestid) | [iris/middleware/requestid/requestid_test.go](https://github.com/kataras/iris/blob/master/_examples/middleware/requestid/requestid_test.go) |
## Community made
Most of the experimental handlers are ported to work with _iris_'s handler form, from third-party sources.
## Community
| Middleware | Description | Example |
| -----------|--------|-------------|
| [jwt](https://github.com/iris-contrib/middleware/tree/master/jwt) | Middleware checks for a JWT on the `Authorization` header on incoming requests and decodes it. | [iris-contrib/middleware/jwt/_example](https://github.com/iris-contrib/middleware/tree/master/jwt/_example) |
| [cors](https://github.com/iris-contrib/middleware/tree/master/cors) | HTTP Access Control. | [iris-contrib/middleware/cors/_example](https://github.com/iris-contrib/middleware/tree/master/cors/_example) |
| [secure](https://github.com/iris-contrib/middleware/tree/master/secure) | Middleware that implements a few quick security wins. | [iris-contrib/middleware/secure/_example](https://github.com/iris-contrib/middleware/tree/master/secure/_example/main.go) |
| [tollbooth](https://github.com/iris-contrib/middleware/tree/master/tollboothic) | Generic middleware to rate-limit HTTP requests. | [iris-contrib/middleware/tollbooth/_examples/limit-handler](https://github.com/iris-contrib/middleware/tree/master/tollbooth/_examples/limit-handler) |
| [cloudwatch](https://github.com/iris-contrib/middleware/tree/master/cloudwatch) | AWS cloudwatch metrics middleware. |[iris-contrib/middleware/cloudwatch/_example](https://github.com/iris-contrib/middleware/tree/master/cloudwatch/_example) |
| [new relic](https://github.com/iris-contrib/middleware/tree/master/newrelic) | Official [New Relic Go Agent](https://github.com/newrelic/go-agent). | [iris-contrib/middleware/newrelic/_example](https://github.com/iris-contrib/middleware/tree/master/newrelic/_example) |
| [jwt](https://github.com/iris-contrib/middleware/tree/master/jwt) | Middleware checks for a JWT on the `Authorization` header on incoming requests and decodes it | [iris-contrib/middleware/jwt/_example](https://github.com/iris-contrib/middleware/tree/master/jwt/_example) |
| [cors](https://github.com/iris-contrib/middleware/tree/master/cors) | HTTP Access Control | [iris-contrib/middleware/cors/_example](https://github.com/iris-contrib/middleware/tree/master/cors/_example) |
| [secure](https://github.com/iris-contrib/middleware/tree/master/secure) | Middleware that implements a few quick security wins | [iris-contrib/middleware/secure/_example](https://github.com/iris-contrib/middleware/tree/master/secure/_example/main.go) |
| [tollbooth](https://github.com/iris-contrib/middleware/tree/master/tollboothic) | Generic middleware to rate-limit HTTP requests | [iris-contrib/middleware/tollboothic/_examples/limit-handler](https://github.com/iris-contrib/middleware/tree/master/tollboothic/_examples/limit-handler) |
| [cloudwatch](https://github.com/iris-contrib/middleware/tree/master/cloudwatch) | AWS cloudwatch metrics middleware |[iris-contrib/middleware/cloudwatch/_example](https://github.com/iris-contrib/middleware/tree/master/cloudwatch/_example) |
| [new relic](https://github.com/iris-contrib/middleware/tree/master/newrelic) | Official [New Relic Go Agent](https://github.com/newrelic/go-agent) | [iris-contrib/middleware/newrelic/_example](https://github.com/iris-contrib/middleware/tree/master/newrelic/_example) |
| [prometheus](https://github.com/iris-contrib/middleware/tree/master/prometheus)| Easily create metrics endpoint for the [prometheus](http://prometheus.io) instrumentation tool | [iris-contrib/middleware/prometheus/_example](https://github.com/iris-contrib/middleware/tree/master/prometheus/_example) |
| [casbin](https://github.com/iris-contrib/middleware/tree/master/casbin)| An authorization library that supports access control models like ACL, RBAC, ABAC | [iris-contrib/middleware/casbin/_examples](https://github.com/iris-contrib/middleware/tree/master/casbin/_examples) |
| [raven](https://github.com/iris-contrib/middleware/tree/master/raven)| Sentry client in Go | [iris-contrib/middleware/raven/_example](https://github.com/iris-contrib/middleware/blob/master/raven/_example/main.go) |
| [csrf](https://github.com/iris-contrib/middleware/tree/master/csrf)| Cross-Site Request Forgery Protection | [iris-contrib/middleware/csrf/_example](https://github.com/iris-contrib/middleware/blob/master/csrf/_example/main.go) |
| [go-i18n](https://github.com/iris-contrib/middleware/tree/master/go-i18n)| i18n Iris Loader for nicksnyder/go-i18n | [iris-contrib/middleware/go-i18n/_example](https://github.com/iris-contrib/middleware/blob/master/go-i18n/_example/main.go) |
| [throttler](https://github.com/iris-contrib/middleware/tree/master/throttler)| Rate limiting access to HTTP endpoints | [iris-contrib/middleware/throttler/_example](https://github.com/iris-contrib/middleware/blob/master/throttler/_example/main.go) |
## Third-Party
Iris has its own middleware form of `func(ctx iris.Context)` but it's also compatible with all `net/http` middleware forms. See [here](https://github.com/kataras/iris/tree/master/_examples/convert-handlers).
Here's a small list of useful third-party handlers:
| Middleware | Description |
| -----------|-------------|
| [goth](https://github.com/markbates/goth) | OAuth, OAuth2 authentication. [Example](https://github.com/kataras/iris/tree/master/_examples/auth/goth) |
| [permissions2](https://github.com/xyproto/permissions2) | Cookies, users and permissions. [Example](https://github.com/kataras/iris/tree/master/_examples/auth/permissions) |
| [csp](https://github.com/awakenetworks/csp) | [Content Security Policy](https://www.w3.org/TR/CSP2/) (CSP) support |
| [delay](https://github.com/jeffbmartinez/delay) | Add delays/latency to endpoints. Useful when testing effects of high latency |
| [onthefly](https://github.com/xyproto/onthefly) | Generate TinySVG, HTML and CSS on the fly |
| [RestGate](https://github.com/pjebs/restgate) | Secure authentication for REST API endpoints |
| [stats](https://github.com/thoas/stats) | Store information about your web application (response time, etc.) |
| [VanGoH](https://github.com/auroratechnologies/vangoh) | Configurable [AWS-Style](http://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html) HMAC authentication middleware |
| [digits](https://github.com/bamarni/digits) | Middleware that handles [Twitter Digits](https://get.digits.com/) authentication |
> Feel free to put up your own middleware in this list!

@ -49,12 +49,12 @@ func newApp() *iris.Application {
ctx.HTML("<b>Resource Not found</b>")
})
app.HandleDir("/", "./public")
app.Get("/profile/{username}", func(ctx iris.Context) {
ctx.Writef("Hello %s", ctx.Params().Get("username"))
})
app.HandleDir("/", "./public")
myOtherHandler := func(ctx iris.Context) {
ctx.Writef("inside a handler which is fired manually by our custom router wrapper")
}
@ -71,8 +71,8 @@ func newApp() *iris.Application {
if strings.HasPrefix(path, "/other") {
// acquire and release a context in order to use it to execute
// our custom handler
// remember: we use net/http.Handler because here
// we are in the "low-level", before the router itself.
// remember: if you are going to call just a net/http middleware
// then you DONT have to acquire and release the context here.
ctx := app.ContextPool.Acquire(w, r)
myOtherHandler(ctx)
app.ContextPool.Release(ctx)

@ -17,7 +17,7 @@ app.Get("/home", handler).SetLastMod(time.Now()).SetChangeFreq("hourly").SetPrio
> A static route is exposed on GET HTTP Method and its path does not contain a dynamic parameter. e.g. /home, /about but not /user/{id:uint64} and e.t.c.
See it in action, download and run the **example** from: https://github.com/kataras/iris/tree/master/_examples/sitemap
See it in action, download and run the **example** from: https://github.com/kataras/iris/tree/master/_examples/routing/sitemap
If the application **is localized** then `iris.WithSitemap` will add `xhtml:link` XML elements to the sitemap file for each translation language as recommended at: https://support.google.com/webmasters/answer/189077?hl=en. Read more about it at the [[Localization]] section.

@ -1,6 +1,8 @@
| Project | Description | Stars | Author |
| --------|-------------|-------|--------|
| [peterq/pan-light](https://bit.ly/33qfKlt) | Baidu network disk unlimited speed client, golang + qt5, cross-platform graphical interface | 9936 | @peterq |
| [peterq/pan-light](https://bit.ly/33qfKlt) | Baidu network disk unlimited speed client, golang + qt5, cross-platform graphical interface | 10460 | @peterq |
| [eltaline/wzd](https://github.com/eltaline/wzd) | wZD is a powerful storage and database server, designed for big data storage systems with small and large files for mixed use and dramatically reduces count of small files for extend abilities any normal or clustered POSIX compatible file systems | 68 | @eltaline |
| [eltaline/ctrl](https://github.com/eltaline/ctrl) | cTRL is a server for remote execution of pending tasks and commands in real time, supporting a queue with continuous thread limiting and throttling | 2 | @eltaline |
| [mlogclub/bbs-go](https://bit.ly/2PXcgmp) | Golang-based community system | 527 | @mlogclub |
| [snowlyg/IrisApiProject](https://bit.ly/2IaL1R6) | Iris + gorm + JWT + sqlite3 | 359 | @snowlyg |
| [mohuishou/scuplus-go](https://bit.ly/34H6Jol) | WeChat applet Backend API | 62 | @mohuishou |

@ -1,4 +1,4 @@
[[Home]] *|* [[About]] *|*
[[Project|Project-and-community]] *|*
[[Getting Started]] *|*
[Technical Docs](https://godoc.org/github.com/kataras/iris) *|* Copyright &copy; 2019 Gerasimos Maropoulos. [[Documentation terms of use|Documentation-terms-and-conditions]].
[Technical Docs](https://godoc.org/github.com/kataras/iris) *|* Copyright &copy; 2019-2020 Gerasimos Maropoulos. [[Documentation terms of use|Documentation-terms-and-conditions]].

@ -35,6 +35,7 @@
* [[Websockets]]
* [[Dependency Injection|Dependency-Injection]]
* [[MVC]]
* [[gRPC]]
* [[Sitemap]]
* [[Localization]]
* [[Testing]]

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 208 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 257 KiB

@ -1,46 +1,350 @@
The subpackage [hero](https://github.com/kataras/iris/tree/master/hero) contains features for binding any object or function that handlers can accept on their input arguments, these are called dependencies.
Iris provides first-class support for dependency injection through request handlers and server replies based on return value(s).
A dependency can be either `Static` for things like Services or `Dynamic` for values that depend on the incoming request.
## Dependency Injection
With Iris you get truly safe bindings. It is blazing-fast, near to raw handlers performance because Iris tries to calculates everything before the server even goes online!
With Iris you get truly safe bindings. It is blazing-fast, near to raw handlers performance because we pre-allocates necessary information before the server even goes online!
Iris provides built-in dependencies to match route's parameters with func input arguments that you can use right away.
A dependency can be either a function or a static value. A function dependency can accept a previously registered dependency as its input argument too.
To use this feature you should import the hero subpackage:
Example Code:
```go
import (
// [...]
"github.com/kataras/iris/v12/hero"
func printFromTo(from, to string) string { return "message" }
// [...]
app.ConfigureContainer(func (api *iris.APIContainer){
api.Get("/{from}/{to}", printFromTo)
})
```
As you've seen above the `iris.Context` input argument is totally optional. Iris is smart enough to bind it as well without any hassle.
### Overview
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 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':
```go
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:
```go
// 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
```
```go
// 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))
```
```go
// 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)
```
<details><summary>ResultHandler</summary>
```go
type ResultHandler func(ctx iris.Context, v interface{}) error
```
</details>
```go
// 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{})
```
```go
// 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...
```
Here is a list of the built-in dependencies that can be used right away as input parameters:
| Type | Maps To |
|------|:---------|
| [*mvc.Application](https://pkg.go.dev/github.com/kataras/iris/v12/mvc?tab=doc#Application) | Current MVC Application |
| [iris.Context](https://pkg.go.dev/github.com/kataras/iris/v12/context?tab=doc#Context) | Current Iris Context |
| [*sessions.Session](https://pkg.go.dev/github.com/kataras/iris/v12/sessions?tab=doc#Session) | Current Iris Session |
| [context.Context](https://golang.org/pkg/context/#Context) | [ctx.Request().Context()](https://golang.org/pkg/net/http/#Request.Context) |
| [*http.Request](https://golang.org/pkg/net/http/#Request) | `ctx.Request()` |
| [http.ResponseWriter](https://golang.org/pkg/net/http/#ResponseWriter) | `ctx.ResponseWriter()` |
| [http.Header](https://golang.org/pkg/net/http/#Header) | `ctx.Request().Header` |
| [time.Time](https://golang.org/pkg/time/#Time) | `time.Now()` |
| `string`, | |
| `int, int8, int16, int32, int64`, | |
| `uint, uint8, uint16, uint32, uint64`, | |
| `float, float32, float64`, | |
| `bool`, | |
| `slice` | [Path Parameter](https://github.com/kataras/iris/wiki/Routing-path-parameter-types) |
| Struct | [Request Body](https://github.com/kataras/iris/tree/master/_examples/request-body) of `JSON`, `XML`, `YAML`, `Form`, `URL Query`, `Protobuf`, `MsgPack` |
### Request & Response & Path Parameters
**1.** Declare Go types for client's request body and a server's response.
```go
type (
request struct {
Firstname string `json:"firstname"`
Lastname string `json:"lastname"`
}
response struct {
ID uint64 `json:"id"`
Message string `json:"message"`
}
)
```
And use its `hero.Handler` package-level function to build a handler from a function that can accept dependencies and send response from its output, like this:
**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
```go
func printFromTo(from, to string) string { /* [...]*/ }
// [...]
app.Get("/{from}/{to}", hero.Handler(printFromTo))
func updateUser(id uint64, input request) response {
return response{
ID: id,
Message: "User updated successfully",
}
}
```
As you've seen above the `iris.Context` input argument is totally optional. Of course you can still declare it as **first input argument** - Iris is smart enough to bind it as well without any hassle.
**3.** Configure the container per group and register the route.
Below you will see some screenshots designed to facilitate your understanding:
```go
app.Party("/user").ConfigureContainer(container)
## 1. Path Parameters - Built-in Dependencies
func container(api *iris.APIContainer) {
api.Put("/{id:uint64}", updateUser)
}
```
[[_assets/hero-1-monokai.png]]
**4.** Simulate a [client](https://curl.haxx.se/download.html) request which sends data to the server and displays the response.
## 2. Services - Static Dependencies
```sh
curl --request PUT -d '{"firstanme":"John","lastname":"Doe"}' http://localhost:8080/user/42
```
[[_assets/hero-2-monokai.png]]
```json
{
"id": 42,
"message": "User updated successfully"
}
```
## 3. Per-Request - Dynamic Dependencies
### Custom Preflight
[[_assets/hero-3-monokai.png]]
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.
In addition the hero subpackage adds support to send responses through the **output values** of a function, for example:
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.
```go
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.
```go
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).
```json
{
"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](https://www.sqlite.org/index.html).
```go
import "database/sql"
import _ "github.com/mattn/go-sqlite3"
```
**2.** Configure the container ([see above](#request--response--path-parameters)), register your dependencies. Handler expects an *sql.DB instance.
```go
localDB, _ := sql.Open("sqlite3", "./foo.db")
api.RegisterDependency(localDB)
```
**3.** Register a route to create a user.
```go
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.
```go
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](https://curl.haxx.se/download.html) to create a user.
```sh
# JSON
curl --request POST -d '{"firstname":"John","lastname":"Doe"}' \
--header 'Content-Type: application/json' \
http://localhost:8080/user
```
```sh
# Form (multipart)
curl --request POST 'http://localhost:8080/users' \
--header 'Content-Type: multipart/form-data' \
--form 'firstname=John' \
--form 'lastname=Doe'
```
```sh
# 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'
```
```sh
# URL Query
curl --request POST 'http://localhost:8080/users?firstname=John&lastname=Doe'
```
Response:
```json
{
"id": 42,
"message": "User created",
"code": 201,
"timestamp": 1583313026
}
```
## Return values
- if the return value is `string` then it will send that string as the response's body.
- If it's an `int` then it will send it as a status code.
@ -49,34 +353,49 @@ In addition the hero subpackage adds support to send responses through the **out
- If it's a custom `struct` then it sent as a JSON, when a Content-Type header is not already set.
- If it's a custom `struct` and a `string` then the second output value, string, it will be the Content-Type and so on.
```go
func myHandler(...dependencies) string |
(string, string) |
(string, int) |
int |
(int, string) |
(string, error) |
error |
(int, error) |
(any, bool) |
(customStruct, error) |
customStruct |
(customStruct, int) |
(customStruct, string) |
hero.Result |
(hero.Result, error) {
return a_response
}
```
| Type | Replies to |
|------|:---------|
| string | body |
| string, string | content-type, body |
| string, int | body, status code |
| int | status code |
| int, string | status code, body |
| error | if not nil, bad request |
| any, bool | if false then fires not found |
| <Τ> | JSON body |
| <Τ>, string | body, content-type |
| <Τ>, error | JSON body or bad request |
| <Τ>, int | JSON body, status code |
| [Result](https://godoc.org/github.com/kataras/iris/hero#Result) | calls its `Dispatch` method |
| [PreflightResult](https://godoc.org/github.com/kataras/iris/hero#PreflightResult) | calls its `Preflight` method |
The `hero.Result` is an interface which help custom structs to be rendered using custom logic through their `Dispatch(ctx iris.Context)`.
> Where `<T>` means any struct value.
```go
type Result interface {
Dispatch(ctx iris.Context)
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
}
func deleteUser(db *sql.DB, id uint64) *response {
// [...custom logic]
return &response{
Message: "User has been marked for deletion",
Code: iris.StatusAccepted,
}
}
```
Honestly, `hero funcs` are very easy to understand and when you start using them **you never go back**.
Later on you'll see how this knowledge will help you to craft an application using the MVC architectural pattern which Iris provides wonderful API for it.