diff --git a/API-versioning.md b/API-versioning.md index 30b3744..41c6617 100644 --- a/API-versioning.md +++ b/API-versioning.md @@ -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)) } diff --git a/Automatic-public-address.md b/Automatic-public-address.md index 8693762..c6b578a 100644 --- a/Automatic-public-address.md +++ b/Automatic-public-address.md @@ -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. diff --git a/Cache.md b/Cache.md index 8503e8a..5699ec1 100644 --- a/Cache.md +++ b/Cache.md @@ -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: . +Examples can be found at: . diff --git a/Configuration.md b/Configuration.md index 9bd2491..bab9b9b 100644 --- a/Configuration.md +++ b/Configuration.md @@ -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 doesn’t 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"] +} ``` diff --git a/Content-negotiation.md b/Content-negotiation.md index 88f74f0..25d04b8 100644 --- a/Content-negotiation.md +++ b/Content-negotiation.md @@ -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 diff --git a/Cookies.md b/Cookies.md index f0bb09f..2e47452 100644 --- a/Cookies.md +++ b/Cookies.md @@ -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) diff --git a/Forms.md b/Forms.md index 3794499..65c2e2a 100644 --- a/Forms.md +++ b/Forms.md @@ -212,4 +212,4 @@ curl -X POST http://localhost:8080/upload \ -H "Content-Type: multipart/form-data" ``` -More examples can be found at . \ No newline at end of file +More examples can be found at and . \ No newline at end of file diff --git a/Grpc.md b/Grpc.md new file mode 100644 index 0000000..63ea4c3 --- /dev/null +++ b/Grpc.md @@ -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: . diff --git a/Home.md b/Home.md index 72a3b62..251a0b6 100644 --- a/Home.md +++ b/Home.md @@ -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]] diff --git a/Host.md b/Host.md index 14c011c..a1237f9 100644 --- a/Host.md +++ b/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")) ``` @@ -251,4 +261,4 @@ func main() { } ``` -Continue reading the [[Configuration]] section to learn about `app.Run`'s second variadic argument. \ No newline at end of file +Continue reading the [[Configuration]] section to learn about `app.Run`'s second variadic argument. diff --git a/MVC.md b/MVC.md index b52b131..1500600 100644 --- a/MVC.md +++ b/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... -> For websocket controller go ahead to the [[Websockets]] chapter instead. \ No newline at end of file +> 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. \ No newline at end of file diff --git a/Model-validation.md b/Model-validation.md index 0086019..577bb96 100644 --- a/Model-validation.md +++ b/Model-validation.md @@ -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: . +Example can be found at: . diff --git a/Our-users.md b/Our-users.md index 7990cbe..ac17c5a 100644 --- a/Our-users.md +++ b/Our-users.md @@ -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 \ No newline at end of file diff --git a/Publications.md b/Publications.md index 45010d0..fc152a6 100644 --- a/Publications.md +++ b/Publications.md @@ -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 | diff --git a/Response-recorder.md b/Response-recorder.md index 921fddb..af9e181 100644 --- a/Response-recorder.md +++ b/Response-recorder.md @@ -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). diff --git a/Routing-context-methods.md b/Routing-context-methods.md index 1ea266a..6a34801 100644 --- a/Routing-context-methods.md +++ b/Routing-context-methods.md @@ -4,899 +4,1091 @@ Here is a full list of methods that the `iris.Context` provides. ```go type Context interface { - // ResponseWriter returns an http.ResponseWriter compatible response writer, as expected. - ResponseWriter() ResponseWriter - // ResetResponseWriter should change or upgrade the Context's ResponseWriter. - ResetResponseWriter(ResponseWriter) + // 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) - // Request returns the original *http.Request, as expected. - Request() *http.Request - // ResetRequest sets the Context's Request, - // It is useful to store the new request created by a std *http.Request#WithContext() into Iris' Context. - // Use `ResetRequest` when for some reason you want to make a full - // override of the *http.Request. - // Note that: when you just want to change one of each fields you can use the Request() which returns a pointer to Request, - // so the changes will have affect without a full override. - // Usage: you use a native http handler which uses the standard "context" package - // to get values instead of the Iris' Context#Values(): - // r := ctx.Request() - // stdCtx := context.WithValue(r.Context(), key, val) - // ctx.ResetRequest(r.WithContext(stdCtx)). - ResetRequest(r *http.Request) + // ResponseWriter returns an http.ResponseWriter compatible response writer, as expected. + ResponseWriter() ResponseWriter + // ResetResponseWriter should change or upgrade the Context's ResponseWriter. + ResetResponseWriter(ResponseWriter) - // 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. - GetCurrentRoute() RouteReadOnly + // Request returns the original *http.Request, as expected. + Request() *http.Request + // ResetRequest sets the Context's Request, + // It is useful to store the new request created by a std *http.Request#WithContext() into Iris' Context. + // Use `ResetRequest` when for some reason you want to make a full + // override of the *http.Request. + // Note that: when you just want to change one of each fields you can use the Request() which returns a pointer to Request, + // so the changes will have affect without a full override. + // Usage: you use a native http handler which uses the standard "context" package + // to get values instead of the Iris' Context#Values(): + // r := ctx.Request() + // stdCtx := context.WithValue(r.Context(), key, val) + // ctx.ResetRequest(r.WithContext(stdCtx)). + ResetRequest(r *http.Request) - // Do calls the SetHandlers(handlers) - // and executes the first handler, - // handlers should not be empty. - // - // It's used by the router, developers may use that - // to replace and execute handlers immediately. - Do(Handlers) + // 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 - // AddHandler can add handler(s) - // to the current request in serve-time, - // these handlers are not persistenced to the router. - // - // 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) - // Handlers keeps tracking of the current handlers. - Handlers() Handlers + // Do calls the SetHandlers(handlers) + // and executes the first handler, + // handlers should not be empty. + // + // It's used by the router, developers may use that + // to replace and execute handlers immediately. + Do(Handlers) - // HandlerIndex sets the current index of the - // current context's handlers chain. - // If -1 passed then it just returns the - // current handler index without change the current index. - // - // Look Handlers(), Next() and StopExecution() too. - HandlerIndex(n int) (currentIndex int) - // Proceed is an alternative way to check if a particular handler - // has been executed and called the `ctx.Next` function inside it. - // This is useful only when you run a handler inside - // another handler. It justs checks for before index and the after index. - // - // A usecase example is when you want to execute a middleware - // inside controller's `BeginRequest` that calls the `ctx.Next` inside it. - // The Controller looks the whole flow (BeginRequest, method handler, EndRequest) - // as one handler, so `ctx.Next` will not be reflected to the method handler - // if called from the `BeginRequest`. - // - // Although `BeginRequest` should NOT be used to call other handlers, - // the `BeginRequest` has been introduced to be able to set - // common data to all method handlers before their execution. - // Controllers can accept middleware(s) from the MVC's Application's Router as normally. - // - // That said let's see an example of `ctx.Proceed`: - // - // var authMiddleware = basicauth.New(basicauth.Config{ - // Users: map[string]string{ - // "admin": "password", - // }, - // }) - // - // func (c *UsersController) BeginRequest(ctx iris.Context) { - // if !ctx.Proceed(authMiddleware) { - // ctx.StopExecution() - // } - // } - // This Get() will be executed in the same handler as `BeginRequest`, - // internally controller checks for `ctx.StopExecution`. - // So it will not be fired if BeginRequest called the `StopExecution`. - // func(c *UsersController) Get() []models.User { - // return c.Service.GetAll() - //} - // Alternative way is `!ctx.IsStopped()` if middleware make use of the `ctx.StopExecution()` on failure. - Proceed(Handler) bool - // HandlerName returns the current handler's name, helpful for debugging. - HandlerName() string - // HandlerFileLine returns the current running handler's function source file and line information. - // Useful mostly when debugging. - HandlerFileLine() (file string, line int) - // RouteName returns the route name that this handler is running on. - // Note that it will return empty on not found handlers. - RouteName() string - // Next calls all the next handler from the handlers chain, - // it should be used inside a middleware. - // - // Note: Custom context should override this method in order to be able to pass its own context.Context implementation. - Next() - // NextOr checks if chain has a next handler, if so then it executes it - // otherwise it sets a new chain assigned to this Context based on the given handler(s) - // and executes its first handler. - // - // Returns true if next handler exists and executed, otherwise false. - // - // Note that if no next handler found and handlers are missing then - // it sends a Status Not Found (404) to the client and it stops the execution. - NextOr(handlers ...Handler) bool - // NextOrNotFound checks if chain has a next handler, if so then it executes it - // otherwise it sends a Status Not Found (404) to the client and stops the execution. - // - // Returns true if next handler exists and executed, otherwise false. - NextOrNotFound() bool - // NextHandler returns (it doesn't execute) the next handler from the handlers chain. - // - // Use .Skip() to skip this handler if needed to execute the next of this returning handler. - NextHandler() Handler - // 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, - // 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() bool - // 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. - // - // This mechanism can be used to cancel long operations on the server - // if the client has disconnected before the response is ready. - // - // It depends on the `http#CloseNotify`. - // CloseNotify may wait to notify until Request.Body has been - // fully read. - // - // After the main Handler has returned, there is no guarantee - // that the channel receives a value. - // - // Finally, it reports whether the protocol supports pipelines (HTTP/1.1 with pipelines disabled is not supported). - // The "cb" will not fire for sure if the output value is false. - // - // Note that you can register only one callback for the entire request handler chain/per route. - // - // Look the `ResponseWriter#CloseNotifier` for more. - OnConnectionClose(fnGoroutine func()) bool - // 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. - // - // Look the `Context#OnConnectionClose` and `ResponseWriter#SetBeforeFlush` for more. - OnClose(cb func()) + // AddHandler can add handler(s) + // to the current request in serve-time, + // these handlers are not persistenced to the router. + // + // 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) + // Handlers keeps tracking of the current handlers. + Handlers() Handlers - // +------------------------------------------------------------+ - // | Current "user/request" storage | - // | and share information between the handlers - Values(). | - // | Save and get named path parameters - Params() | - // +------------------------------------------------------------+ + // HandlerIndex sets the current index of the + // current context's handlers chain. + // 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. + HandlerIndex(n int) (currentIndex int) + // Proceed is an alternative way to check if a particular handler + // has been executed and called the `ctx.Next` function inside it. + // This is useful only when you run a handler inside + // another handler. It justs checks for before index and the after index. + // + // A usecase example is when you want to execute a middleware + // inside controller's `BeginRequest` that calls the `ctx.Next` inside it. + // The Controller looks the whole flow (BeginRequest, method handler, EndRequest) + // as one handler, so `ctx.Next` will not be reflected to the method handler + // if called from the `BeginRequest`. + // + // Although `BeginRequest` should NOT be used to call other handlers, + // the `BeginRequest` has been introduced to be able to set + // common data to all method handlers before their execution. + // Controllers can accept middleware(s) from the MVC's Application's Router as normally. + // + // That said let's see an example of `ctx.Proceed`: + // + // var authMiddleware = basicauth.New(basicauth.Config{ + // Users: map[string]string{ + // "admin": "password", + // }, + // }) + // + // func (c *UsersController) BeginRequest(ctx iris.Context) { + // if !ctx.Proceed(authMiddleware) { + // ctx.StopExecution() + // } + // } + // This Get() will be executed in the same handler as `BeginRequest`, + // internally controller checks for `ctx.StopExecution`. + // So it will not be fired if BeginRequest called the `StopExecution`. + // func(c *UsersController) Get() []models.User { + // return c.Service.GetAll() + //} + // Alternative way is `!ctx.IsStopped()` if middleware make use of the `ctx.StopExecution()` on failure. + Proceed(Handler) bool + // HandlerName returns the current handler's name, helpful for debugging. + HandlerName() string + // HandlerFileLine returns the current running handler's function source file and line information. + // Useful mostly when debugging. + HandlerFileLine() (file string, line int) + // RouteName returns the route name that this handler is running on. + // Note that it will return empty on not found handlers. + RouteName() string + // Next calls all the next handler from the handlers chain, + // it should be used inside a middleware. + // + // Note: Custom context should override this method in order to be able to pass its own context.Context implementation. + Next() + // NextOr checks if chain has a next handler, if so then it executes it + // otherwise it sets a new chain assigned to this Context based on the given handler(s) + // and executes its first handler. + // + // Returns true if next handler exists and executed, otherwise false. + // + // Note that if no next handler found and handlers are missing then + // it sends a Status Not Found (404) to the client and it stops the execution. + NextOr(handlers ...Handler) bool + // NextOrNotFound checks if chain has a next handler, if so then it executes it + // otherwise it sends a Status Not Found (404) to the client and stops the execution. + // + // Returns true if next handler exists and executed, otherwise false. + NextOrNotFound() bool + // NextHandler returns (it doesn't execute) the next handler from the handlers chain. + // + // Use .Skip() to skip this handler if needed to execute the next of this returning handler. + NextHandler() Handler + // Skip skips/ignores the next handler from the handlers chain, + // it should be used inside a middleware. + Skip() + // 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 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) - // Params returns the current url's named parameters key-value storage. - // Named path parameters are being saved here. - // This storage, as the whole Context, is per-request lifetime. - Params() *RequestParams + // 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. + // + // This mechanism can be used to cancel long operations on the server + // if the client has disconnected before the response is ready. + // + // It depends on the `http#CloseNotify`. + // CloseNotify may wait to notify until Request.Body has been + // fully read. + // + // After the main Handler has returned, there is no guarantee + // that the channel receives a value. + // + // Finally, it reports whether the protocol supports pipelines (HTTP/1.1 with pipelines disabled is not supported). + // The "cb" will not fire for sure if the output value is false. + // + // Note that you can register only one callback for the entire request handler chain/per route. + // + // Look the `ResponseWriter#CloseNotifier` for more. + OnConnectionClose(fnGoroutine func()) bool + // 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()) - // Values returns the current "user" storage. - // Named path parameters and any optional data can be saved here. - // This storage, as the whole Context, is per-request lifetime. - // - // You can use this function to Set and Get local values - // that can be used to share information between handlers and middleware. - Values() *memstore.Store + // +------------------------------------------------------------+ + // | Current "user/request" storage | + // | and share information between the handlers - Values(). | + // | Save and get named path parameters - Params() | + // +------------------------------------------------------------+ - // +------------------------------------------------------------+ - // | Path, Host, Subdomain, IP, Headers, Localization etc... | - // +------------------------------------------------------------+ + // Params returns the current url's named parameters key-value storage. + // Named path parameters are being saved here. + // This storage, as the whole Context, is per-request lifetime. + Params() *RequestParams - // Method returns the request.Method, the client's http method to the server. - Method() string - // Path returns the full request path, - // escaped if EnablePathEscape config field is true. - Path() string - // RequestPath returns the full request path, - // based on the 'escape'. - RequestPath(escape bool) string - // Host returns the host part of the current url. - Host() string - // 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) - // IsWWW returns true if the current subdomain (if any) is www. - IsWWW() bool - // FullRqeuestURI returns the full URI, - // including the scheme, the host and the relative requested path/resource. - FullRequestURI() string - // RemoteAddr tries to parse and return the real client's request IP. - // - // Based on allowed headers names that can be modified from Configuration.RemoteAddrHeaders. - // - // If parse based on these headers fail then it will return the Request's `RemoteAddr` field - // which is filled by the server before the HTTP handler. - // - // Look `Configuration.RemoteAddrHeaders`, - // `Configuration.WithRemoteAddrHeader(...)`, - // `Configuration.WithoutRemoteAddrHeader(...)` for more. - RemoteAddr() string - // GetHeader returns the request header's value based on its name. - GetHeader(name string) 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. - // You should never trust data coming from the client, they can be easily overcome by spoofing. - // - // Note that "X-Requested-With" Header can be modified by any client(because of "X-"), - // so don't rely on IsAjax for really serious stuff, - // try to find another way of detecting the type(i.e, content type), - // there are many blogs that describe these problems and provide different kind of solutions, - // it's always depending on the application you're building, - // this is the reason why this `IsAjax`` is simple enough for general purpose use. - // - // Read more at: https://developer.mozilla.org/en-US/docs/AJAX - // and https://xhr.spec.whatwg.org/ - IsAjax() bool - // IsMobile checks if client is using a mobile device(phone or tablet) to communicate with this server. - // If the return value is true that means that the http client using a mobile - // device to communicate with the server, otherwise false. - // - // Keep note that this checks the "User-Agent" request header. - IsMobile() bool - // GetReferrer extracts and returns the information from the "Referer" header as specified - // in https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy - // or by the URL query parameter "referer". - GetReferrer() Referrer - // GetLocale returns the current request's `Locale` found by i18n middleware. - // See `Tr` too. - GetLocale() Locale - // Tr returns a i18n localized message based on format with optional arguments. - // See `GetLocale` too. - // Example: https://github.com/kataras/iris/tree/master/_examples/i18n - Tr(format string, args ...interface{}) string + // Values returns the current "user" storage. + // Named path parameters and any optional data can be saved here. + // This storage, as the whole Context, is per-request lifetime. + // + // You can use this function to Set and Get local values + // that can be used to share information between handlers and middleware. + Values() *memstore.Store - // +------------------------------------------------------------+ - // | Headers helpers | - // +------------------------------------------------------------+ + // +------------------------------------------------------------+ + // | Path, Host, Subdomain, IP, Headers, Localization etc... | + // +------------------------------------------------------------+ - // Header adds a header to the response writer. - Header(name string, value string) + // Method returns the request.Method, the client's http method to the server. + Method() string + // Path returns the full request path, + // escaped if EnablePathEscape config field is true. + Path() string + // RequestPath returns the full request path, + // based on the 'escape'. + RequestPath(escape bool) string + // Host returns the host part of the current url. + Host() string + // 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, + // including the scheme, the host and the relative requested path/resource. + FullRequestURI() string + // RemoteAddr tries to parse and return the real client's request IP. + // + // Based on allowed headers names that can be modified from Configuration.RemoteAddrHeaders. + // + // If parse based on these headers fail then it will return the Request's `RemoteAddr` field + // which is filled by the server before the HTTP handler. + // + // Look `Configuration.RemoteAddrHeaders`, + // `Configuration.WithRemoteAddrHeader(...)`, + // `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. + // You should never trust data coming from the client, they can be easily overcome by spoofing. + // + // Note that "X-Requested-With" Header can be modified by any client(because of "X-"), + // so don't rely on IsAjax for really serious stuff, + // try to find another way of detecting the type(i.e, content type), + // there are many blogs that describe these problems and provide different kind of solutions, + // it's always depending on the application you're building, + // this is the reason why this `IsAjax`` is simple enough for general purpose use. + // + // Read more at: https://developer.mozilla.org/en-US/docs/AJAX + // and https://xhr.spec.whatwg.org/ + IsAjax() bool + // IsMobile checks if client is using a mobile device(phone or tablet) to communicate with this server. + // If the return value is true that means that the http client using a mobile + // device to communicate with the server, otherwise false. + // + // Keep note that this checks the "User-Agent" request header. + IsMobile() bool + // 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 "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 + // Tr returns a i18n localized message based on format with optional arguments. + // 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 | + // +------------------------------------------------------------+ - // ContentType sets the response writer's header key "Content-Type" to the 'cType'. - ContentType(cType string) - // GetContentType returns the response writer's header value of "Content-Type" - // which may, set before with the 'ContentType'. - GetContentType() string - // GetContentType returns the request's header value of "Content-Type". - GetContentTypeRequested() string + // Header adds a header to the response writer. + Header(name string, value string) - // GetContentLength returns the request's header value of "Content-Length". - // Returns 0 if header was unable to be found or its value was not a valid number. - GetContentLength() int64 + // ContentType sets the response writer's header key "Content-Type" to the 'cType'. + ContentType(cType string) + // GetContentType returns the response writer's header value of "Content-Type" + // which may, set before with the 'ContentType'. + GetContentType() string + // GetContentType returns the request's header value of "Content-Type". + GetContentTypeRequested() string - // StatusCode sets the status code header to the response. - // Look .`GetStatusCode` too. - StatusCode(statusCode int) - // GetStatusCode returns the current status code of the response. - // Look `StatusCode` too. - GetStatusCode() int + // GetContentLength returns the request's header value of "Content-Length". + // Returns 0 if header was unable to be found or its value was not a valid number. + GetContentLength() int64 - // AbsoluteURI parses the "s" and returns its absolute URI form. - AbsoluteURI(s string) string - // Redirect sends a redirect response to the client - // to a specific url or relative path. - // accepts 2 parameters string and an optional int - // first parameter is the url to redirect - // second parameter is the http status should send, - // default is 302 (StatusFound), - // you can set it to 301 (Permant redirect) - // or 303 (StatusSeeOther) if POST method, - // or StatusTemporaryRedirect(307) if that's nessecery. - Redirect(urlToRedirect string, statusHeader ...int) - // +------------------------------------------------------------+ - // | Various Request and Post Data | - // +------------------------------------------------------------+ + // StatusCode sets the status code header to the response. + // Look .`GetStatusCode` too. + StatusCode(statusCode int) + // GetStatusCode returns the current status code of the response. + // Look `StatusCode` too. + GetStatusCode() int - // URLParam returns true if the url parameter exists, otherwise false. - URLParamExists(name string) bool - // URLParamDefault returns the get parameter from a request, - // if not found then "def" is returned. - URLParamDefault(name string, def string) string - // URLParam returns the get parameter from a request, if any. - URLParam(name string) string - // URLParamTrim returns the url query parameter with trailing white spaces removed from a request. - URLParamTrim(name string) string - // URLParamEscape returns the escaped url query parameter from a request. - URLParamEscape(name string) string - // URLParamInt returns the url query parameter as int value from a request, - // returns -1 and an error if parse failed. - URLParamInt(name string) (int, error) - // URLParamIntDefault returns the url query parameter as int value from a request, - // if not found or parse failed then "def" is returned. - URLParamIntDefault(name string, def int) int - // URLParamInt32Default returns the url query parameter as int32 value from a request, - // if not found or parse failed then "def" is returned. - URLParamInt32Default(name string, def int32) int32 - // URLParamInt64 returns the url query parameter as int64 value from a request, - // returns -1 and an error if parse failed. - URLParamInt64(name string) (int64, error) - // URLParamInt64Default returns the url query parameter as int64 value from a request, - // if not found or parse failed then "def" is returned. - URLParamInt64Default(name string, def int64) int64 - // URLParamFloat64 returns the url query parameter as float64 value from a request, - // returns -1 and an error if parse failed. - URLParamFloat64(name string) (float64, error) - // URLParamFloat64Default returns the url query parameter as float64 value from a request, - // if not found or parse failed then "def" is returned. - URLParamFloat64Default(name string, def float64) float64 - // URLParamBool returns the url query parameter as boolean value from a request, - // returns an error if parse failed or not found. - URLParamBool(name string) (bool, error) - // URLParams returns a map of GET query parameters separated by comma if more than one - // it returns an empty map if nothing found. - URLParams() map[string]string + // AbsoluteURI parses the "s" and returns its absolute URI form. + AbsoluteURI(s string) string + // Redirect sends a redirect response to the client + // to a specific url or relative path. + // accepts 2 parameters string and an optional int + // first parameter is the url to redirect + // second parameter is the http status should send, + // default is 302 (StatusFound), + // you can set it to 301 (Permant redirect) + // or 303 (StatusSeeOther) if POST method, + // or StatusTemporaryRedirect(307) if that's nessecery. + Redirect(urlToRedirect string, statusHeader ...int) + // +------------------------------------------------------------+ + // | Various Request and Post Data | + // +------------------------------------------------------------+ - // FormValueDefault returns a single parsed form value by its "name", - // including both the URL field's query parameters and the POST or PUT form data. - // - // Returns the "def" if not found. - FormValueDefault(name string, def string) string - // FormValue returns a single parsed form value by its "name", - // including both the URL field's query parameters and the POST or PUT form data. - FormValue(name string) string - // FormValues returns the parsed form data, including both the URL - // field's query parameters and the POST or PUT form data. - // - // 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. - // - // NOTE: A check for nil is necessary. - FormValues() map[string][]string + // URLParam returns true if the url parameter exists, otherwise false. + URLParamExists(name string) bool + // URLParamDefault returns the get parameter from a request, + // if not found then "def" is returned. + URLParamDefault(name string, def string) string + // URLParam returns the get parameter from a request, if any. + URLParam(name string) string + // URLParamTrim returns the url query parameter with trailing white spaces removed from a request. + URLParamTrim(name string) string + // URLParamEscape returns the escaped url query parameter from a request. + URLParamEscape(name string) string + // URLParamInt returns the url query parameter as int value from a request, + // returns -1 and an error if parse failed. + URLParamInt(name string) (int, error) + // URLParamIntDefault returns the url query parameter as int value from a request, + // if not found or parse failed then "def" is returned. + URLParamIntDefault(name string, def int) int + // URLParamInt32Default returns the url query parameter as int32 value from a request, + // if not found or parse failed then "def" is returned. + URLParamInt32Default(name string, def int32) int32 + // URLParamInt64 returns the url query parameter as int64 value from a request, + // returns -1 and an error if parse failed. + URLParamInt64(name string) (int64, error) + // URLParamInt64Default returns the url query parameter as int64 value from a request, + // if not found or parse failed then "def" is returned. + URLParamInt64Default(name string, def int64) int64 + // URLParamFloat64 returns the url query parameter as float64 value from a request, + // returns -1 and an error if parse failed. + URLParamFloat64(name string) (float64, error) + // URLParamFloat64Default returns the url query parameter as float64 value from a request, + // if not found or parse failed then "def" is returned. + URLParamFloat64Default(name string, def float64) float64 + // URLParamBool returns the url query parameter as boolean value from a request, + // returns an error if parse failed or not found. + URLParamBool(name string) (bool, error) + // URLParams returns a map of GET query parameters separated by comma if more than one + // it returns an empty map if nothing found. + URLParams() map[string]string - // PostValueDefault returns the parsed form data from POST, PATCH, - // or PUT body parameters based on a "name". - // - // If not found then "def" is returned instead. - PostValueDefault(name string, def string) string - // PostValue returns the parsed form data from POST, PATCH, - // or PUT body parameters based on a "name" - PostValue(name string) string - // PostValueTrim returns the parsed form data from POST, PATCH, - // or PUT body parameters based on a "name", without trailing spaces. - PostValueTrim(name string) string - // PostValueInt returns the parsed form data from POST, PATCH, - // or PUT body parameters based on a "name", as int. - // - // If not found returns -1 and a non-nil error. - PostValueInt(name string) (int, error) - // PostValueIntDefault returns the parsed form data from POST, PATCH, - // or PUT body parameters based on a "name", as int. - // - // If not found returns or parse errors the "def". - PostValueIntDefault(name string, def int) int - // PostValueInt64 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 no-nil error. - PostValueInt64(name string) (int64, error) - // PostValueInt64Default returns the parsed form data from POST, PATCH, - // or PUT body parameters based on a "name", as int64. - // - // If not found or parse errors returns the "def". - PostValueInt64Default(name string, def int64) int64 - // PostValueInt64Default 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, - // 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, - // or PUT body parameters based on a "name", as bool. - // - // If not found or value is false, then it returns false, otherwise true. - PostValueBool(name string) (bool, error) - // PostValues returns all the parsed form data from POST, PATCH, - // or PUT body parameters based on a "name" as a string slice. - // - // 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. - PostValues(name string) []string - // FormFile returns the first uploaded file that received from the client. - // - // 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 - FormFile(key string) (multipart.File, *multipart.FileHeader, error) - // UploadFormFiles uploads any received file(s) from the client - // to the system physical location "destDirectory". - // - // The second optional argument "before" gives caller the chance to - // modify the *miltipart.FileHeader before saving to the disk, - // it can be used to change a file's name based on the current request, - // all FileHeader's options can be changed. You can ignore it if - // you don't need to use this capability before saving a file to the disk. - // - // Note that it doesn't check if request body streamed. - // - // Returns the copied length as int64 and - // a not nil error if at least one new file - // can't be created due to the operating system's permissions or - // http.ErrMissingFile if no file received. - // - // If you want to receive & accept files and manage them manually you can use the `context#FormFile` - // instead and create a copy function that suits your needs, the below is for generic usage. - // - // 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. - // - // See `FormFile` to a more controlled to receive a file. - // - // - // Example: https://github.com/kataras/iris/tree/master/_examples/http_request/upload-files - UploadFormFiles(destDirectory string, before ...func(Context, *multipart.FileHeader)) (n int64, err error) + // FormValueDefault returns a single parsed form value by its "name", + // including both the URL field's query parameters and the POST or PUT form data. + // + // Returns the "def" if not found. + FormValueDefault(name string, def string) string + // FormValue returns a single parsed form value by its "name", + // including both the URL field's query parameters and the POST or PUT form data. + FormValue(name string) string + // FormValues returns the parsed form data, including both the URL + // field's query parameters and the POST or PUT form data. + // + // 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. + // + // NOTE: A check for nil is necessary. + FormValues() map[string][]string - // +------------------------------------------------------------+ - // | Custom HTTP Errors | - // +------------------------------------------------------------+ + // PostValueDefault returns the parsed form data from POST, PATCH, + // or PUT body parameters based on a "name". + // + // If not found then "def" is returned instead. + PostValueDefault(name string, def string) string + // PostValue returns the parsed form data from POST, PATCH, + // or PUT body parameters based on a "name" + PostValue(name string) string + // PostValueTrim returns the parsed form data from POST, PATCH, + // or PUT body parameters based on a "name", without trailing spaces. + PostValueTrim(name string) string + // PostValueInt returns the parsed form data from POST, PATCH, + // or PUT body parameters based on a "name", as int. + // + // If not found returns -1 and a non-nil error. + PostValueInt(name string) (int, error) + // PostValueIntDefault returns the parsed form data from POST, PATCH, + // or PUT body parameters based on a "name", as int. + // + // If not found returns or parse errors the "def". + PostValueIntDefault(name string, def int) int + // PostValueInt64 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 no-nil error. + PostValueInt64(name string) (int64, error) + // PostValueInt64Default returns the parsed form data from POST, PATCH, + // or PUT body parameters based on a "name", as int64. + // + // If not found or parse errors returns the "def". + PostValueInt64Default(name string, def int64) int64 + // 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) + // 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 + // 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. + PostValueBool(name string) (bool, error) + // PostValues returns all the parsed form data from POST, PATCH, + // or PUT body parameters based on a "name" as a string slice. + // + // 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. + PostValues(name string) []string + // FormFile returns the first uploaded file that received from the client. + // + // 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/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". + // + // The second optional argument "before" gives caller the chance to + // modify the *miltipart.FileHeader before saving to the disk, + // it can be used to change a file's name based on the current request, + // all FileHeader's options can be changed. You can ignore it if + // you don't need to use this capability before saving a file to the disk. + // + // Note that it doesn't check if request body streamed. + // + // Returns the copied length as int64 and + // a not nil error if at least one new file + // can't be created due to the operating system's permissions or + // http.ErrMissingFile if no file received. + // + // If you want to receive & accept files and manage them manually you can use the `context#FormFile` + // instead and create a copy function that suits your needs, the below is for generic usage. + // + // 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. + // + // See `FormFile` to a more controlled to receive a file. + // + // + // 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) - // NotFound emits an error 404 to the client, using the specific custom error error handler. - // Note that you may need to call ctx.StopExecution() if you don't want the next handlers - // to be executed. Next handlers are being executed on iris because you can alt the - // error code and change it to a more specific one, i.e - // users := app.Party("/users") - // users.Done(func(ctx context.Context){ if ctx.StatusCode() == 400 { /* custom error code for /users */ }}) - NotFound() + // +------------------------------------------------------------+ + // | Custom HTTP Errors | + // +------------------------------------------------------------+ - // +------------------------------------------------------------+ - // | Body Readers | - // +------------------------------------------------------------+ + // NotFound emits an error 404 to the client, using the specific custom error error handler. + // Note that you may need to call ctx.StopExecution() if you don't want the next handlers + // to be executed. Next handlers are being executed on iris because you can alt the + // error code and change it to a more specific one, i.e + // users := app.Party("/users") + // users.Done(func(ctx context.Context){ if ctx.StatusCode() == 400 { /* custom error code for /users */ }}) + NotFound() - // SetMaxRequestBodySize sets a limit to the request body size - // should be called before reading the request body from the client. - SetMaxRequestBodySize(limitOverBytes int64) + // +------------------------------------------------------------+ + // | Body Readers | + // +------------------------------------------------------------+ - // GetBody reads and returns the request body. - // The default behavior for the http request reader is to consume the data readen - // but you can change that behavior by passing the `WithoutBodyConsumptionOnUnmarshal` iris option. - // - // However, whenever you can use the `ctx.Request().Body` instead. - GetBody() ([]byte, error) - // 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 - // - // 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 - // However you are still free to read the `ctx.Request().Body io.Reader` manually. - 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 - 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 - 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 - ReadYAML(outPtr interface{}) error - // ReadForm binds the formObject with the form data - // it supports any kind of type, including custom structs. - // It will return nothing if request data are empty. - // The struct field tag is "form". - // - // Example: https://github.com/kataras/iris/blob/master/_examples/http_request/read-form/main.go - ReadForm(formObject interface{}) error - // ReadQuery binds the "ptr" with the url query string. The struct field tag is "url". - // - // Example: https://github.com/kataras/iris/blob/master/_examples/http_request/read-query/main.go - ReadQuery(ptr interface{}) error - // +------------------------------------------------------------+ - // | Body (raw) Writers | - // +------------------------------------------------------------+ + // SetMaxRequestBodySize sets a limit to the request body size + // should be called before reading the request body from the client. + SetMaxRequestBodySize(limitOverBytes int64) - // Write writes the data to the connection as part of an HTTP reply. - // - // If WriteHeader has not yet been called, Write calls - // WriteHeader(http.StatusOK) before writing the data. If the Header - // does not contain a Content-Type line, Write adds a Content-Type set - // to the result of passing the initial 512 bytes of written data to - // DetectContentType. - // - // Depending on the HTTP protocol version and the client, calling - // Write or WriteHeader may prevent future reads on the - // Request.Body. For HTTP/1.x requests, handlers should read any - // needed request body data before writing the response. Once the - // headers have been flushed (due to either an explicit Flusher.Flush - // call or writing enough data to trigger a flush), the request body - // may be unavailable. For HTTP/2 requests, the Go HTTP server permits - // handlers to continue to read the request body while concurrently - // writing the response. However, such behavior may not be supported - // by all HTTP/2 clients. Handlers should read before writing if - // possible to maximize compatibility. - Write(body []byte) (int, error) - // Writef formats according to a format specifier and writes to the response. - // - // Returns the number of bytes written and any write error encountered. - Writef(format string, args ...interface{}) (int, error) - // WriteString writes a simple string to the response. - // - // Returns the number of bytes written and any write error encountered. - WriteString(body string) (int, error) + // GetBody reads and returns the request body. + // The default behavior for the http request reader is to consume the data readen + // but you can change that behavior by passing the `WithoutBodyConsumptionOnUnmarshal` iris option. + // + // However, whenever you can use the `ctx.Request().Body` instead. + GetBody() ([]byte, error) + // 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/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 + // However you are still free to read the `ctx.Request().Body io.Reader` manually. + 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/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/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/request-body/read-yaml/main.go + ReadYAML(outPtr interface{}) error + // 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/request-body/read-form/main.go + ReadForm(formObject interface{}) error + // ReadQuery binds url query to "ptr". The struct field tag is "url". + // + // 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 - // SetLastModified sets the "Last-Modified" based on the "modtime" input. - // If "modtime" is zero then it does nothing. - // - // It's mostly internally on core/router and context packages. - // - // Note that modtime.UTC() is being used instead of just modtime, so - // you don't have to know the internals in order to make that works. - SetLastModified(modtime time.Time) - // CheckIfModifiedSince checks if the response is modified since the "modtime". - // Note that it has nothing to do with server-side caching. - // It does those checks by checking if the "If-Modified-Since" request header - // sent by client or a previous server response header - // (e.g with WriteWithExpiration or HandleDir or Favicon etc.) - // is a valid one and it's before the "modtime". - // - // A check for !modtime && err == nil is necessary to make sure that - // it's not modified since, because it may return false but without even - // had the chance to check the client-side (request) header due to some errors, - // like the HTTP Method is not "GET" or "HEAD" or if the "modtime" is zero - // or if parsing time from the header failed. - // - // It's mostly used internally, e.g. `context#WriteWithExpiration`. See `ErrPreconditionFailed` too. - // - // Note that modtime.UTC() is being used instead of just modtime, so - // you don't have to know the internals in order to make that works. - CheckIfModifiedSince(modtime time.Time) (bool, error) - // WriteNotModified sends a 304 "Not Modified" status code to the client, - // it makes sure that the content type, the content length headers - // and any "ETag" are removed before the response sent. - // - // It's mostly used internally on core/router/fs.go and context methods. - WriteNotModified() - // WriteWithExpiration works like `Write` but it will check if a resource is modified, - // based on the "modtime" input argument, - // otherwise sends a 304 status code in order to let the client-side render the cached content. - WriteWithExpiration(body []byte, modtime time.Time) (int, error) - // StreamWriter registers the given stream writer for populating - // response body. - // - // Access to context's and/or its' members is forbidden from writer. - // - // This function may be used in the following cases: - // - // * if response body is too big (more than iris.LimitRequestBodySize(if set)). - // * if response body is streamed from slow external sources. - // * if response body must be streamed to the client in chunks. - // (aka `http server push`). - // - // receives a function which receives the response writer - // and returns false when it should stop writing, otherwise true in order to continue - StreamWriter(writer func(w io.Writer) bool) + // +------------------------------------------------------------+ + // | Body (raw) Writers | + // +------------------------------------------------------------+ - // +------------------------------------------------------------+ - // | Body Writers with compression | - // +------------------------------------------------------------+ - // ClientSupportsGzip retruns true if the client supports gzip compression. - ClientSupportsGzip() bool - // WriteGzip accepts bytes, which are compressed to gzip format and sent to the client. - // returns the number of bytes written and an error ( if the client doesn' supports gzip compression) - // You may re-use this function in the same handler - // to write more data many times without any troubles. - WriteGzip(b []byte) (int, error) - // TryWriteGzip accepts bytes, which are compressed to gzip format and sent to the client. - // If client does not supprots gzip then the contents are written as they are, uncompressed. - TryWriteGzip(b []byte) (int, error) - // GzipResponseWriter converts the current response writer into a response writer - // which when its .Write called it compress the data to gzip and writes them to the client. - // - // Can be also disabled with its .Disable and .ResetBody to rollback to the usual response writer. - GzipResponseWriter() *GzipResponseWriter - // Gzip enables or disables (if enabled before) the gzip response writer,if the client - // supports gzip compression, so the following response data will - // be sent as compressed gzip data to the client. - Gzip(enable bool) + // Write writes the data to the connection as part of an HTTP reply. + // + // If WriteHeader has not yet been called, Write calls + // WriteHeader(http.StatusOK) before writing the data. If the Header + // does not contain a Content-Type line, Write adds a Content-Type set + // to the result of passing the initial 512 bytes of written data to + // DetectContentType. + // + // Depending on the HTTP protocol version and the client, calling + // Write or WriteHeader may prevent future reads on the + // Request.Body. For HTTP/1.x requests, handlers should read any + // needed request body data before writing the response. Once the + // headers have been flushed (due to either an explicit Flusher.Flush + // call or writing enough data to trigger a flush), the request body + // may be unavailable. For HTTP/2 requests, the Go HTTP server permits + // handlers to continue to read the request body while concurrently + // writing the response. However, such behavior may not be supported + // by all HTTP/2 clients. Handlers should read before writing if + // possible to maximize compatibility. + Write(body []byte) (int, error) + // Writef formats according to a format specifier and writes to the response. + // + // Returns the number of bytes written and any write error encountered. + Writef(format string, args ...interface{}) (int, error) + // WriteString writes a simple string to the response. + // + // Returns the number of bytes written and any write error encountered. + WriteString(body string) (int, error) - // +------------------------------------------------------------+ - // | Rich Body Content Writers/Renderers | - // +------------------------------------------------------------+ + // SetLastModified sets the "Last-Modified" based on the "modtime" input. + // If "modtime" is zero then it does nothing. + // + // It's mostly internally on core/router and context packages. + // + // Note that modtime.UTC() is being used instead of just modtime, so + // you don't have to know the internals in order to make that works. + SetLastModified(modtime time.Time) + // CheckIfModifiedSince checks if the response is modified since the "modtime". + // Note that it has nothing to do with server-side caching. + // It does those checks by checking if the "If-Modified-Since" request header + // sent by client or a previous server response header + // (e.g with WriteWithExpiration or HandleDir or Favicon etc.) + // is a valid one and it's before the "modtime". + // + // A check for !modtime && err == nil is necessary to make sure that + // it's not modified since, because it may return false but without even + // had the chance to check the client-side (request) header due to some errors, + // like the HTTP Method is not "GET" or "HEAD" or if the "modtime" is zero + // or if parsing time from the header failed. + // + // It's mostly used internally, e.g. `context#WriteWithExpiration`. See `ErrPreconditionFailed` too. + // + // Note that modtime.UTC() is being used instead of just modtime, so + // you don't have to know the internals in order to make that works. + CheckIfModifiedSince(modtime time.Time) (bool, error) + // WriteNotModified sends a 304 "Not Modified" status code to the client, + // it makes sure that the content type, the content length headers + // and any "ETag" are removed before the response sent. + // + // It's mostly used internally on core/router/fs.go and context methods. + WriteNotModified() + // WriteWithExpiration works like `Write` but it will check if a resource is modified, + // based on the "modtime" input argument, + // otherwise sends a 304 status code in order to let the client-side render the cached content. + WriteWithExpiration(body []byte, modtime time.Time) (int, error) + // StreamWriter registers the given stream writer for populating + // response body. + // + // Access to context's and/or its' members is forbidden from writer. + // + // This function may be used in the following cases: + // + // * if response body is too big (more than iris.LimitRequestBodySize(if set)). + // * if response body is streamed from slow external sources. + // * if response body must be streamed to the client in chunks. + // (aka `http server push`). + // + // receives a function which receives the response writer + // and returns false when it should stop writing, otherwise true in order to continue + StreamWriter(writer func(w io.Writer) bool) - // ViewLayout sets the "layout" option if and when .View - // is being called afterwards, in the same request. - // Useful when need to set or/and change a layout based on the previous handlers in the chain. - // - // Note that the 'layoutTmplFile' argument can be set to iris.NoLayout || view.NoLayout - // to disable the layout for a specific view render action, - // it disables the engine's configuration's layout property. - // - // Look .ViewData and .View too. - // - // Example: https://github.com/kataras/iris/tree/master/_examples/view/context-view-data/ - ViewLayout(layoutTmplFile string) - // ViewData saves one or more key-value pair in order to be passed if and when .View - // is being called afterwards, in the same request. - // Useful when need to set or/and change template data from previous hanadlers in the chain. - // - // If .View's "binding" argument is not nil and it's not a type of map - // then these data are being ignored, binding has the priority, so the main route's handler can still decide. - // If binding is a map or context.Map then these data are being added to the view data - // and passed to the template. - // - // After .View, the data are not destroyed, in order to be re-used if needed (again, in the same request as everything else), - // to clear the view data, developers can call: - // ctx.Set(ctx.Application().ConfigurationReadOnly().GetViewDataContextKey(), nil) - // - // If 'key' is empty then the value is added as it's (struct or map) and developer is unable to add other value. - // - // Look .ViewLayout and .View too. - // - // Example: https://github.com/kataras/iris/tree/master/_examples/view/context-view-data/ - ViewData(key string, value interface{}) - // GetViewData returns the values registered by `context#ViewData`. - // The return value is `map[string]interface{}`, this means that - // if a custom struct registered to ViewData then this function - // will try to parse it to map, if failed then the return value is nil - // A check for nil is always a good practise if different - // kind of values or no data are registered via `ViewData`. - // - // Similarly to `viewData := ctx.Values().Get("iris.viewData")` or - // `viewData := ctx.Values().Get(ctx.Application().ConfigurationReadOnly().GetViewDataContextKey())`. - GetViewData() map[string]interface{} - // View renders a template based on the registered view engine(s). - // First argument accepts the filename, relative to the view engine's Directory and Extension, - // i.e: if directory is "./templates" and want to render the "./templates/users/index.html" - // then you pass the "users/index.html" as the filename argument. - // - // The second optional argument can receive a single "view model" - // that will be binded to the view template if it's not nil, - // otherwise it will check for previous view data stored by the `ViewData` - // even if stored at any previous handler(middleware) for the same request. - // - // Look .ViewData` and .ViewLayout too. - // - // Examples: https://github.com/kataras/iris/tree/master/_examples/view - View(filename string, optionalViewModel ...interface{}) error + // +------------------------------------------------------------+ + // | Body Writers with compression | + // +------------------------------------------------------------+ + // ClientSupportsGzip retruns true if the client supports gzip compression. + ClientSupportsGzip() bool + // WriteGzip accepts bytes, which are compressed to gzip format and sent to the client. + // returns the number of bytes written and an error ( if the client doesn' supports gzip compression) + // You may re-use this function in the same handler + // to write more data many times without any troubles. + WriteGzip(b []byte) (int, error) + // TryWriteGzip accepts bytes, which are compressed to gzip format and sent to the client. + // If client does not supprots gzip then the contents are written as they are, uncompressed. + TryWriteGzip(b []byte) (int, error) + // GzipResponseWriter converts the current response writer into a response writer + // which when its .Write called it compress the data to gzip and writes them to the client. + // + // Can be also disabled with its .Disable and .ResetBody to rollback to the usual response writer. + GzipResponseWriter() *GzipResponseWriter + // Gzip enables or disables (if enabled before) the gzip response writer,if the client + // 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 - // Binary writes out the raw bytes as binary data. - Binary(data []byte) (int, error) - // Text writes out a string as plain text. - Text(format string, args ...interface{}) (int, error) - // HTML writes out a string as text/html. - HTML(format string, args ...interface{}) (int, error) - // JSON marshals the given interface object and writes the JSON response. - JSON(v interface{}, options ...JSON) (int, error) - // JSONP marshals the given interface object and writes the JSON response. - JSONP(v interface{}, options ...JSONP) (int, error) - // XML marshals the given interface object and writes the XML response. - // To render maps as XML see the `XMLMap` package-level function. - XML(v interface{}, options ...XML) (int, error) - // Problem writes a JSON or XML problem response. - // Order of Problem fields are not always rendered the same. - // - // Behaves exactly like `Context.JSON` - // but with default ProblemOptions.JSON indent of " " and - // a response content type of "application/problem+json" instead. - // - // Use the options.RenderXML and XML fields to change this behavior and - // send a response of content type "application/problem+xml" instead. - // - // Read more at: https://github.com/kataras/iris/wiki/Routing-error-handlers - Problem(v interface{}, opts ...ProblemOptions) (int, error) - // Markdown parses the markdown to html and renders its result to the client. - 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) + // +------------------------------------------------------------+ + // | Rich Body Content Writers/Renderers | + // +------------------------------------------------------------+ - // +-----------------------------------------------------------------------+ - // | Content Νegotiation | - // | https://developer.mozilla.org/en-US/docs/Web/HTTP/Content_negotiation | | - // +-----------------------------------------------------------------------+ + // ViewLayout sets the "layout" option if and when .View + // is being called afterwards, in the same request. + // Useful when need to set or/and change a layout based on the previous handlers in the chain. + // + // Note that the 'layoutTmplFile' argument can be set to iris.NoLayout || view.NoLayout + // to disable the layout for a specific view render action, + // it disables the engine's configuration's layout property. + // + // Look .ViewData and .View too. + // + // Example: https://github.com/kataras/iris/tree/master/_examples/view/context-view-data/ + ViewLayout(layoutTmplFile string) + // ViewData saves one or more key-value pair in order to be passed if and when .View + // is being called afterwards, in the same request. + // Useful when need to set or/and change template data from previous hanadlers in the chain. + // + // If .View's "binding" argument is not nil and it's not a type of map + // then these data are being ignored, binding has the priority, so the main route's handler can still decide. + // If binding is a map or context.Map then these data are being added to the view data + // and passed to the template. + // + // After .View, the data are not destroyed, in order to be re-used if needed (again, in the same request as everything else), + // to clear the view data, developers can call: + // ctx.Set(ctx.Application().ConfigurationReadOnly().GetViewDataContextKey(), nil) + // + // If 'key' is empty then the value is added as it's (struct or map) and developer is unable to add other value. + // + // Look .ViewLayout and .View too. + // + // Example: https://github.com/kataras/iris/tree/master/_examples/view/context-view-data/ + ViewData(key string, value interface{}) + // GetViewData returns the values registered by `context#ViewData`. + // The return value is `map[string]interface{}`, this means that + // if a custom struct registered to ViewData then this function + // will try to parse it to map, if failed then the return value is nil + // A check for nil is always a good practise if different + // kind of values or no data are registered via `ViewData`. + // + // Similarly to `viewData := ctx.Values().Get("iris.viewData")` or + // `viewData := ctx.Values().Get(ctx.Application().ConfigurationReadOnly().GetViewDataContextKey())`. + GetViewData() map[string]interface{} + // View renders a template based on the registered view engine(s). + // First argument accepts the filename, relative to the view engine's Directory and Extension, + // i.e: if directory is "./templates" and want to render the "./templates/users/index.html" + // then you pass the "users/index.html" as the filename argument. + // + // The second optional argument can receive a single "view model" + // that will be binded to the view template if it's not nil, + // otherwise it will check for previous view data stored by the `ViewData` + // even if stored at any previous handler(middleware) for the same request. + // + // Look .ViewData` and .ViewLayout too. + // + // Examples: https://github.com/kataras/iris/tree/master/_examples/view + View(filename string, optionalViewModel ...interface{}) error - // Negotiation creates once and returns the negotiation builder - // to build server-side available content for specific mime type(s) - // and charset(s). - // - // See `Negotiate` method too. - Negotiation() *NegotiationBuilder - // Negotiate used for serving different representations of a resource at the same URI. - // - // The "v" can be a single `N` struct value. - // The "v" can be any value completes the `ContentSelector` interface. - // The "v" can be any value completes the `ContentNegotiator` interface. - // The "v" can be any value of struct(JSON, JSONP, XML, YAML) or - // string(TEXT, HTML) or []byte(Markdown, Binary) or []byte with any matched mime type. - // - // If the "v" is nil, the `Context.Negotitation()` builder's - // content will be used instead, otherwise "v" overrides builder's content - // (server mime types are still retrieved by its registered, supported, mime list) - // - // Set mime type priorities by `Negotiation().JSON().XML().HTML()...`. - // Set charset priorities by `Negotiation().Charset(...)`. - // Set encoding algorithm priorities by `Negotiation().Encoding(...)`. - // Modify the accepted by - // `Negotiation().Accept./Override()/.XML().JSON().Charset(...).Encoding(...)...`. - // - // It returns `ErrContentNotSupported` when not matched mime type(s). - // - // Resources: - // https://developer.mozilla.org/en-US/docs/Web/HTTP/Content_negotiation - // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept - // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Charset - // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding - // - // Supports the above without quality values. - // - // Read more at: https://github.com/kataras/iris/wiki/Content-negotiation - Negotiate(v interface{}) (int, error) + // Binary writes out the raw bytes as binary data. + Binary(data []byte) (int, error) + // Text writes out a string as plain text. + Text(format string, args ...interface{}) (int, error) + // HTML writes out a string as text/html. + HTML(format string, args ...interface{}) (int, error) + // JSON marshals the given interface object and writes the JSON response. + JSON(v interface{}, options ...JSON) (int, error) + // JSONP marshals the given interface object and writes the JSON response. + JSONP(v interface{}, options ...JSONP) (int, error) + // XML marshals the given interface object and writes the XML response. + // To render maps as XML see the `XMLMap` package-level function. + XML(v interface{}, options ...XML) (int, error) + // Problem writes a JSON or XML problem response. + // Order of Problem fields are not always rendered the same. + // + // Behaves exactly like `Context.JSON` + // but with default ProblemOptions.JSON indent of " " and + // a response content type of "application/problem+json" instead. + // + // Use the options.RenderXML and XML fields to change this behavior and + // send a response of content type "application/problem+xml" instead. + // + // Read more at: https://github.com/kataras/iris/wiki/Routing-error-handlers + Problem(v interface{}, opts ...ProblemOptions) (int, error) + // Markdown parses the markdown to html and renders its result to the client. + 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) - // +------------------------------------------------------------+ - // | Serve files | - // +------------------------------------------------------------+ + // +-----------------------------------------------------------------------+ + // | Content Νegotiation | + // | https://developer.mozilla.org/en-US/docs/Web/HTTP/Content_negotiation | | + // +-----------------------------------------------------------------------+ - // ServeContent serves content, headers are autoset - // receives three parameters, it's low-level function, instead you can use .ServeFile(string,bool)/SendFile(string,string) - // - // - // You can define your own "Content-Type" with `context#ContentType`, before this function call. - // - // 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) - // - // You can define your own "Content-Type" with `context#ContentType`, before this function call. - // - // This function doesn't support resuming (by range), - // use ctx.SendFile or router's `HandleDir` instead. - // - // 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 - // - // Use this instead of ServeFile to 'force-download' bigger files to the client. - SendFile(filename string, destinationName string) error + // Negotiation creates once and returns the negotiation builder + // to build server-side available content for specific mime type(s) + // and charset(s). + // + // See `Negotiate` method too. + Negotiation() *NegotiationBuilder + // Negotiate used for serving different representations of a resource at the same URI. + // + // The "v" can be a single `N` struct value. + // The "v" can be any value completes the `ContentSelector` interface. + // The "v" can be any value completes the `ContentNegotiator` interface. + // The "v" can be any value of struct(JSON, JSONP, XML, YAML) or + // string(TEXT, HTML) or []byte(Markdown, Binary) or []byte with any matched mime type. + // + // If the "v" is nil, the `Context.Negotitation()` builder's + // content will be used instead, otherwise "v" overrides builder's content + // (server mime types are still retrieved by its registered, supported, mime list) + // + // Set mime type priorities by `Negotiation().JSON().XML().HTML()...`. + // Set charset priorities by `Negotiation().Charset(...)`. + // Set encoding algorithm priorities by `Negotiation().Encoding(...)`. + // Modify the accepted by + // `Negotiation().Accept./Override()/.XML().JSON().Charset(...).Encoding(...)...`. + // + // It returns `ErrContentNotSupported` when not matched mime type(s). + // + // Resources: + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Content_negotiation + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Charset + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding + // + // Supports the above without quality values. + // + // Read more at: https://github.com/kataras/iris/wiki/Content-negotiation + Negotiate(v interface{}) (int, error) - // +------------------------------------------------------------+ - // | Cookies | - // +------------------------------------------------------------+ + // +------------------------------------------------------------+ + // | Serve files | + // +------------------------------------------------------------+ - // 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) - // 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, - // use the `CookieExpires` and `CookiePath` to modify them. - // Alternatively: ctx.SetCookie(&http.Cookie{...}) - // - // If you want to set custom the path: - // ctx.SetCookieKV(name, value, iris.CookiePath("/custom/path/cookie/will/be/stored")) - // - // If you want to be visible only to current request path: - // ctx.SetCookieKV(name, value, iris.CookieCleanPath/iris.CookiePath("")) - // More: - // iris.CookieExpires(time.Duration) - // iris.CookieHTTPOnly(false) - // - // Example: https://github.com/kataras/iris/tree/master/_examples/cookies/basic - SetCookieKV(name, value string, options ...CookieOption) - // GetCookie returns cookie's value by its name - // returns empty string if nothing was found. - // - // If you want more than the value then: - // cookie, err := ctx.Request().Cookie("name") - // - // Example: https://github.com/kataras/iris/tree/master/_examples/cookies/basic - GetCookie(name string, options ...CookieOption) string - // RemoveCookie deletes a cookie by its name and path = "/". - // Tip: change the cookie's path to the current one by: RemoveCookie("name", iris.CookieCleanPath) - // - // Example: https://github.com/kataras/iris/tree/master/_examples/cookies/basic - RemoveCookie(name string, options ...CookieOption) - // VisitAllCookies accepts a visitor function which is called - // on each (request's) cookies' name and value. - VisitAllCookies(visitor func(name string, value 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. + // + // The name is otherwise unused; in particular it can be empty and is + // never sent in the response. + // + // 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. + // + // The content's Seek method must work: ServeContent uses + // a seek to the end of the content to determine its size. + // + // 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. + // + // 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. + // + // 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 - // MaxAge returns the "cache-control" request header's value - // seconds as int64 - // if header not found or parse failed then it returns -1. - MaxAge() int64 + // +------------------------------------------------------------+ + // | Cookies | + // +------------------------------------------------------------+ - // +------------------------------------------------------------+ - // | Advanced: Response Recorder and Transactions | - // +------------------------------------------------------------+ + // 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, + // use the `CookieExpires` and `CookiePath` to modify them. + // Alternatively: ctx.SetCookie(&http.Cookie{...}) + // + // If you want to set custom the path: + // ctx.SetCookieKV(name, value, iris.CookiePath("/custom/path/cookie/will/be/stored")) + // + // If you want to be visible only to current request path: + // ctx.SetCookieKV(name, value, iris.CookieCleanPath/iris.CookiePath("")) + // More: + // iris.CookieExpires(time.Duration) + // iris.CookieHTTPOnly(false) + // + // Example: https://github.com/kataras/iris/tree/master/_examples/cookies/basic + SetCookieKV(name, value string, options ...CookieOption) + // GetCookie returns cookie's value by its name + // returns empty string if nothing was found. + // + // If you want more than the value then: + // cookie, err := ctx.Request().Cookie("name") + // + // Example: https://github.com/kataras/iris/tree/master/_examples/cookies/basic + GetCookie(name string, options ...CookieOption) string + // RemoveCookie deletes a cookie by its name and path = "/". + // Tip: change the cookie's path to the current one by: RemoveCookie("name", iris.CookieCleanPath) + // + // Example: https://github.com/kataras/iris/tree/master/_examples/cookies/basic + RemoveCookie(name string, options ...CookieOption) + // VisitAllCookies accepts a visitor function which is called + // on each (request's) cookies' name and value. + VisitAllCookies(visitor func(name string, value string)) - // Record transforms the context's basic and direct responseWriter to a ResponseRecorder - // which can be used to reset the body, reset headers, get the body, - // get & set the status code at any time and more. - Record() - // Recorder returns the context's ResponseRecorder - // if not recording then it starts recording and returns the new context's ResponseRecorder - Recorder() *ResponseRecorder - // IsRecording returns the response recorder and a true value - // when the response writer is recording the status code, body, headers and so on, - // else returns nil and false. - IsRecording() (*ResponseRecorder, bool) + // MaxAge returns the "cache-control" request header's value + // seconds as int64 + // if header not found or parse failed then it returns -1. + MaxAge() int64 - // BeginTransaction starts a scoped transaction. - // - // You can search third-party articles or books on how Business Transaction works (it's quite simple, especially here). - // - // Note that this is unique and new - // (=I haver never seen any other examples or code in Golang on this subject, so far, as with the most of iris features...) - // it's not covers all paths, - // such as databases, this should be managed by the libraries you use to make your database connection, - // this transaction scope is only for context's response. - // Transactions have their own middleware ecosystem also, look iris.go:UseTransaction. - // - // See https://github.com/kataras/iris/tree/master/_examples/ for more - BeginTransaction(pipe func(t *Transaction)) - // SkipTransactions if called then skip the rest of the transactions - // or all of them if called before the first transaction - SkipTransactions() - // TransactionsSkipped returns true if the transactions skipped or canceled at all. - TransactionsSkipped() bool + // +------------------------------------------------------------+ + // | Advanced: Response Recorder and Transactions | + // +------------------------------------------------------------+ - // Exec calls the `context/Application#ServeCtx` - // based on this context but with a changed method and path - // like it was requested by the user, but it is not. - // - // Offline means that the route is registered to the iris and have all features that a normal route has - // BUT it isn't available by browsing, its handlers executed only when other handler's context call them - // it can validate paths, has sessions, path parameters and all. - // - // You can find the Route by app.GetRoute("theRouteName") - // you can set a route name as: myRoute := app.Get("/mypath", handler)("theRouteName") - // that will set a name to the route and returns its RouteInfo instance for further usage. - // - // It doesn't changes the global state, if a route was "offline" it remains offline. - // - // app.None(...) and app.GetRoutes().Offline(route)/.Online(route, method) - // - // Example: https://github.com/kataras/iris/tree/master/_examples/routing/route-state - // - // User can get the response by simple using rec := ctx.Recorder(); rec.Body()/rec.StatusCode()/rec.Header(). - // - // Context's Values and the Session are kept in order to be able to communicate via the result route. - // - // It's for extreme use cases, 99% of the times will never be useful for you. - Exec(method, path string) + // Record transforms the context's basic and direct responseWriter to a ResponseRecorder + // which can be used to reset the body, reset headers, get the body, + // get & set the status code at any time and more. + Record() + // Recorder returns the context's ResponseRecorder + // if not recording then it starts recording and returns the new context's ResponseRecorder + Recorder() *ResponseRecorder + // IsRecording returns the response recorder and a true value + // when the response writer is recording the status code, body, headers and so on, + // else returns nil and false. + IsRecording() (*ResponseRecorder, bool) - // RouteExists reports whether a particular route exists - // It will search from the current subdomain of context's host, if not inside the root domain. - RouteExists(method, path string) bool + // BeginTransaction starts a scoped transaction. + // + // You can search third-party articles or books on how Business Transaction works (it's quite simple, especially here). + // + // Note that this is unique and new + // (=I haver never seen any other examples or code in Golang on this subject, so far, as with the most of iris features...) + // it's not covers all paths, + // such as databases, this should be managed by the libraries you use to make your database connection, + // this transaction scope is only for context's response. + // Transactions have their own middleware ecosystem also, look iris.go:UseTransaction. + // + // See https://github.com/kataras/iris/tree/master/_examples/ for more + BeginTransaction(pipe func(t *Transaction)) + // SkipTransactions if called then skip the rest of the transactions + // or all of them if called before the first transaction + SkipTransactions() + // TransactionsSkipped returns true if the transactions skipped or canceled at all. + TransactionsSkipped() 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 - // to be executed at serve-time. The full app's fields - // and methods are not available here for the developer's safety. - Application() Application + // Exec calls the `context/Application#ServeCtx` + // based on this context but with a changed method and path + // like it was requested by the user, but it is not. + // + // Offline means that the route is registered to the iris and have all features that a normal route has + // BUT it isn't available by browsing, its handlers executed only when other handler's context call them + // it can validate paths, has sessions, path parameters and all. + // + // You can find the Route by app.GetRoute("theRouteName") + // you can set a route name as: myRoute := app.Get("/mypath", handler)("theRouteName") + // that will set a name to the route and returns its RouteInfo instance for further usage. + // + // It doesn't changes the global state, if a route was "offline" it remains offline. + // + // app.None(...) and app.GetRoutes().Offline(route)/.Online(route, method) + // + // Example: https://github.com/kataras/iris/tree/master/_examples/routing/route-state + // + // User can get the response by simple using rec := ctx.Recorder(); rec.Body()/rec.StatusCode()/rec.Header(). + // + // Context's Values and the Session are kept in order to be able to communicate via the result route. + // + // It's for extreme use cases, 99% of the times will never be useful for you. + Exec(method, path string) - // 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. - // - // 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. - String() string + // RouteExists reports whether a particular route exists + // 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 + // to be executed at serve-time. The full app's fields + // and methods are not available here for the developer's safety. + Application() Application + + // 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. + // + // 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 } ``` diff --git a/Routing-error-handlers.md b/Routing-error-handlers.md index dea2cff..baa5e8f 100644 --- a/Routing-error-handlers.md +++ b/Routing-error-handlers.md @@ -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). diff --git a/Routing-middleware.md b/Routing-middleware.md index 4420a35..6c94fe5 100644 --- a/Routing-middleware.md +++ b/Routing-middleware.md @@ -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! \ No newline at end of file diff --git a/Routing-wrap-the-router.md b/Routing-wrap-the-router.md index 5df9243..984f14d 100644 --- a/Routing-wrap-the-router.md +++ b/Routing-wrap-the-router.md @@ -49,12 +49,12 @@ func newApp() *iris.Application { ctx.HTML("Resource Not found") }) + 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) @@ -104,4 +104,4 @@ func main() { } ``` -There is not much to say here, it's just a function wrapper which accepts the native response writer and request and the next handler which is the Iris' Router itself, it's being or not executed whether is called or not, **it's a middleware for the whole Router**. \ No newline at end of file +There is not much to say here, it's just a function wrapper which accepts the native response writer and request and the next handler which is the Iris' Router itself, it's being or not executed whether is called or not, **it's a middleware for the whole Router**. diff --git a/Sitemap.md b/Sitemap.md index 2e69db2..8f472cb 100644 --- a/Sitemap.md +++ b/Sitemap.md @@ -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. diff --git a/Starter-kits.md b/Starter-kits.md index 8f3099f..4d74342 100644 --- a/Starter-kits.md +++ b/Starter-kits.md @@ -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 | diff --git a/_Footer.md b/_Footer.md index 9f367db..51718a7 100644 --- a/_Footer.md +++ b/_Footer.md @@ -1,4 +1,4 @@ [[Home]] *|* [[About]] *|* [[Project|Project-and-community]] *|* [[Getting Started]] *|* -[Technical Docs](https://godoc.org/github.com/kataras/iris) *|* Copyright © 2019 Gerasimos Maropoulos. [[Documentation terms of use|Documentation-terms-and-conditions]]. +[Technical Docs](https://godoc.org/github.com/kataras/iris) *|* Copyright © 2019-2020 Gerasimos Maropoulos. [[Documentation terms of use|Documentation-terms-and-conditions]]. diff --git a/_Sidebar.md b/_Sidebar.md index 82753c2..b0f6ecc 100644 --- a/_Sidebar.md +++ b/_Sidebar.md @@ -35,6 +35,7 @@ * [[Websockets]] * [[Dependency Injection|Dependency-Injection]] * [[MVC]] +* [[gRPC]] * [[Sitemap]] * [[Localization]] * [[Testing]] diff --git a/_assets/grpc-compatible-question.png b/_assets/grpc-compatible-question.png new file mode 100644 index 0000000..c7ad8bb Binary files /dev/null and b/_assets/grpc-compatible-question.png differ diff --git a/_assets/hero-1-monokai.png b/_assets/hero-1-monokai.png deleted file mode 100644 index fcc6696..0000000 Binary files a/_assets/hero-1-monokai.png and /dev/null differ diff --git a/_assets/hero-2-monokai.png b/_assets/hero-2-monokai.png deleted file mode 100644 index 975ffe1..0000000 Binary files a/_assets/hero-2-monokai.png and /dev/null differ diff --git a/_assets/hero-3-monokai.png b/_assets/hero-3-monokai.png deleted file mode 100644 index 178e6ee..0000000 Binary files a/_assets/hero-3-monokai.png and /dev/null differ diff --git a/dependency-injection.md b/dependency-injection.md index 77395f3..00bcbdf 100644 --- a/dependency-injection.md +++ b/dependency-injection.md @@ -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: +// * {structValue} +// * func(accepts ) returns or (, error) +// * func(accepts iris.Context) returns or (, error) +// +// A Dependency can accept a previous registered dependency and return a new one or the same updated. +// * func(accepts1 , accepts2 ) returns or (, error) or error +// * func(acceptsPathParameter1 string, id uint64) returns or (, 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 + +```go +type ResultHandler func(ctx iris.Context, v interface{}) error +``` +
+ +```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 , 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 `` 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.