replace ioutil with io package and other minor improvements

This commit is contained in:
Gerasimos (Makis) Maropoulos 2022-06-17 22:03:18 +03:00
parent 20d2855a66
commit ef2643b046
No known key found for this signature in database
GPG Key ID: 3595CBE7F3B4082E
108 changed files with 1069 additions and 1021 deletions

View File

@ -28,6 +28,7 @@ The codebase for Dependency Injection, Internationalization and localization and
## Fixes and Improvements ## Fixes and Improvements
- Make the `Context.JSON` method customizable by modifying the `context.WriteJSON` package-level function.
- Add new `iris.NewGuide` which helps you build a simple and nice JSON API with services as dependencies and better design pattern. - Add new `iris.NewGuide` which helps you build a simple and nice JSON API with services as dependencies and better design pattern.
- Make `Context.Domain()` customizable by letting developers to modify the `Context.GetDomain` package-level function. - Make `Context.Domain()` customizable by letting developers to modify the `Context.GetDomain` package-level function.
- Remove Request Context-based Transaction feature as its usage can be replaced with just the Iris Context (as of go1.7+) and better [project](_examples/project) structure. - Remove Request Context-based Transaction feature as its usage can be replaced with just the Iris Context (as of go1.7+) and better [project](_examples/project) structure.

View File

@ -1,4 +1,7 @@
Copyright (c) 2017-2022 The Iris Authors. All rights reserved. Copyright (c) 2017-2022, The Iris Authors. All rights reserved.
The Iris Authors:
* Gerasimos (Makis) Maropoulos
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are modification, are permitted provided that the following conditions are

View File

@ -19,7 +19,8 @@
Iris is a fast, simple yet fully featured and very efficient web framework for Go. Iris is a fast, simple yet fully featured and very efficient web framework for Go.
It provides a [beautifully](iris_guide.go#L31-L44) expressive and easy to use foundation for your next website or API. It provides a beautifully expressive and easy to use foundation for your next website or API.
```go ```go
package main package main
@ -38,7 +39,7 @@ func main() {
} }
``` ```
<details><summary>More with simple Handler</summary> <!-- <details><summary>More with simple Handler</summary>
```go ```go
package main package main
@ -178,6 +179,34 @@ func main() {
<br/> <br/>
-->
As one [Go developer](https://twitter.com/dkuye/status/1532087942696554497) once said, **Iris got you covered all-round and standing strong over the years**.
Some of the features Iris offers:
* HTTP/2 (Push, even Embedded data)
* Middleware (Accesslog, Basicauth, CORS, gRPC, Anti-Bot hCaptcha, JWT, MethodOverride, ModRevision, Monitor, PPROF, Ratelimit, Anti-Bot reCaptcha, Recovery, RequestID, Rewrite)
* API Versioning
* Model-View-Controller
* Websockets
* gRPC
* Auto-HTTPS
* Builtin support for ngrok to put your app on the internet, the fastest way
* Unique Router with dynamic path as parameter with standard types like :uuid, :string, :int... and the ability to create your own
* Compression
* View Engines (HTML, Django, Amber, Handlebars, Pug/Jade and more)
* Create your own File Server and host your own WebDAV server
* Cache
* Localization (i18n, sitemap)
* Sessions
* Rich Responses (HTML, Text, Markdown, XML, YAML, Binary, JSON, JSONP, Protocol Buffers, MessagePack, Content Negotiation, Streaming, Server-Sent Events and more)
* Response Compression (gzip, deflate, brotli, snappy, s2)
* Rich Requests (Bind URL Query, Headers, Form, Text, XML, YAML, Binary, JSON, Validation, Protocol Buffers, MessagePack and more)
* Dependency Injection (MVC, Handlers, API Routers)
* Testing Suite
* And the most important... you get fast answers and support from the 1st day until now - that's six full years!
Learn what [others saying about Iris](https://www.iris-go.com/#review) and **[star](https://github.com/kataras/iris/stargazers)** this open-source project to support its potentials. Learn what [others saying about Iris](https://www.iris-go.com/#review) and **[star](https://github.com/kataras/iris/stargazers)** this open-source project to support its potentials.
[![](https://iris-go.com/images/reviews.gif)](https://iris-go.com/testimonials/) [![](https://iris-go.com/images/reviews.gif)](https://iris-go.com/testimonials/)

View File

@ -5,7 +5,6 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net/http" "net/http"
"net/url" "net/url"
"strconv" "strconv"
@ -105,5 +104,5 @@ func BindResponse(resp *http.Response, dest interface{}) error {
func RawResponse(resp *http.Response) ([]byte, error) { func RawResponse(resp *http.Response) ([]byte, error) {
defer resp.Body.Close() defer resp.Body.Close()
return ioutil.ReadAll(resp.Body) return io.ReadAll(resp.Body)
} }

View File

@ -4,7 +4,7 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io"
"net/http" "net/http"
"github.com/kataras/iris/v12/context" "github.com/kataras/iris/v12/context"
@ -51,7 +51,7 @@ func getExample() {
} }
defer cr.Close() defer cr.Close()
body, err := ioutil.ReadAll(cr) body, err := io.ReadAll(cr)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -103,7 +103,7 @@ func postExample() {
} }
defer cr.Close() defer cr.Close()
body, err := ioutil.ReadAll(cr) body, err := io.ReadAll(cr)
if err != nil { if err != nil {
panic(err) panic(err)
} }

View File

@ -5,7 +5,7 @@ import (
"compress/gzip" "compress/gzip"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io"
"net/http" "net/http"
) )
@ -42,7 +42,7 @@ func getExample() {
} }
defer r.Close() defer r.Close()
body, err := ioutil.ReadAll(r) body, err := io.ReadAll(r)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -93,7 +93,7 @@ func postExample() {
} }
defer r.Close() defer r.Close()
body, err := ioutil.ReadAll(r) body, err := io.ReadAll(r)
if err != nil { if err != nil {
panic(err) panic(err)
} }

View File

@ -11,8 +11,8 @@ import (
const addr = "127.0.0.1:8080" const addr = "127.0.0.1:8080"
/* /*
$ go build -mod=mod -ldflags -H=windowsgui -o myapp.exe $ go build -mod=mod -ldflags -H=windowsgui -o myapp.exe
$ ./myapp.exe # run the app $ ./myapp.exe # run the app
*/ */
func main() { func main() {
go runServer() go runServer()

View File

@ -8,8 +8,8 @@ import (
const addr = "127.0.0.1:8080" const addr = "127.0.0.1:8080"
/* /*
$ go build -mod=mod -ldflags="-H windowsgui" -o myapp.exe # build for windows $ go build -mod=mod -ldflags="-H windowsgui" -o myapp.exe # build for windows
$ ./myapp.exe # run $ ./myapp.exe # run
*/ */
func main() { func main() {
go runServer() go runServer()

View File

@ -8,21 +8,21 @@ import (
const addr = "127.0.0.1:8080" const addr = "127.0.0.1:8080"
/* /*
# Windows requires special linker flags for GUI apps. # Windows requires special linker flags for GUI apps.
# It's also recommended to use TDM-GCC-64 compiler for CGo. # It's also recommended to use TDM-GCC-64 compiler for CGo.
# http://tdm-gcc.tdragon.net/download # http://tdm-gcc.tdragon.net/download
# #
# #
$ go build -mod=mod -ldflags="-H windowsgui" -o myapp.exe # build for windows $ go build -mod=mod -ldflags="-H windowsgui" -o myapp.exe # build for windows
$ ./myapp.exe # run $ ./myapp.exe # run
# #
# MacOS uses app bundles for GUI apps # MacOS uses app bundles for GUI apps
$ mkdir -p example.app/Contents/MacOS $ mkdir -p example.app/Contents/MacOS
$ go build -o example.app/Contents/MacOS/example $ go build -o example.app/Contents/MacOS/example
$ open example.app # Or click on the app in Finder $ open example.app # Or click on the app in Finder
# #
# Note: if you see "use option -std=c99 or -std=gnu99 to compile your code" # Note: if you see "use option -std=c99 or -std=gnu99 to compile your code"
# please refer to: https://github.com/webview/webview/issues/188 # please refer to: https://github.com/webview/webview/issues/188
*/ */
func main() { func main() {
go runServer() go runServer()

View File

@ -1,7 +1,7 @@
package main package main
import ( import (
"io/ioutil" "os"
"path/filepath" "path/filepath"
"strings" "strings"
"testing" "testing"
@ -47,7 +47,7 @@ func (r resource) loadFromBase(dir string, strip string) string {
fullpath := filepath.Join(dir, filename) fullpath := filepath.Join(dir, filename)
b, err := ioutil.ReadFile(fullpath) b, err := os.ReadFile(fullpath)
if err != nil { if err != nil {
panic(fullpath + " failed with error: " + err.Error()) panic(fullpath + " failed with error: " + err.Error())
} }

View File

@ -1,6 +1,6 @@
// Code generated by go-bindata. (@generated) DO NOT EDIT. // Code generated by go-bindata. (@generated) DO NOT EDIT.
//Package main generated by go-bindata.// sources: // Package main generated by go-bindata.// sources:
// assets/css/main.css // assets/css/main.css
// assets/favicon.ico // assets/favicon.ico
// assets/js/main.js // assets/js/main.js
@ -11,7 +11,6 @@ import (
"compress/gzip" "compress/gzip"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
@ -288,11 +287,13 @@ var _bindata = map[string]func() (*asset, error){
// directory embedded in the file by go-bindata. // directory embedded in the file by go-bindata.
// For example if you run go-bindata on data/... and data contains the // For example if you run go-bindata on data/... and data contains the
// following hierarchy: // following hierarchy:
//
// data/ // data/
// foo.txt // foo.txt
// img/ // img/
// a.png // a.png
// b.png // b.png
//
// then AssetDir("data") would return []string{"foo.txt", "img"} // then AssetDir("data") would return []string{"foo.txt", "img"}
// AssetDir("data/img") would return []string{"a.png", "b.png"} // AssetDir("data/img") would return []string{"a.png", "b.png"}
// AssetDir("foo.txt") and AssetDir("notexist") would return an error // AssetDir("foo.txt") and AssetDir("notexist") would return an error
@ -348,7 +349,7 @@ func RestoreAsset(dir, name string) error {
if err != nil { if err != nil {
return err return err
} }
err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) err = os.WriteFile(_filePath(dir, name), data, info.Mode())
if err != nil { if err != nil {
return err return err
} }

View File

@ -1,7 +1,7 @@
package main package main
import ( import (
"io/ioutil" "os"
"path/filepath" "path/filepath"
"runtime" "runtime"
"strings" "strings"
@ -46,7 +46,7 @@ func (r resource) loadFromBase(dir string) string {
fullpath := filepath.Join(dir, filename) fullpath := filepath.Join(dir, filename)
b, err := ioutil.ReadFile(fullpath) b, err := os.ReadFile(fullpath)
if err != nil { if err != nil {
panic(fullpath + " failed with error: " + err.Error()) panic(fullpath + " failed with error: " + err.Error())
} }

View File

@ -1,6 +1,6 @@
// Code generated by go-bindata. (@generated) DO NOT EDIT. // Code generated by go-bindata. (@generated) DO NOT EDIT.
//Package main generated by go-bindata.// sources: // Package main generated by go-bindata.// sources:
// ../embedding-files-into-app/assets/css/main.css // ../embedding-files-into-app/assets/css/main.css
// ../embedding-files-into-app/assets/favicon.ico // ../embedding-files-into-app/assets/favicon.ico
// ../embedding-files-into-app/assets/js/main.js // ../embedding-files-into-app/assets/js/main.js
@ -11,7 +11,6 @@ import (
"compress/gzip" "compress/gzip"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
@ -288,11 +287,13 @@ var _bindata = map[string]func() (*asset, error){
// directory embedded in the file by go-bindata. // directory embedded in the file by go-bindata.
// For example if you run go-bindata on data/... and data contains the // For example if you run go-bindata on data/... and data contains the
// following hierarchy: // following hierarchy:
//
// data/ // data/
// foo.txt // foo.txt
// img/ // img/
// a.png // a.png
// b.png // b.png
//
// then AssetDir("data") would return []string{"foo.txt", "img"} // then AssetDir("data") would return []string{"foo.txt", "img"}
// AssetDir("data/img") would return []string{"a.png", "b.png"} // AssetDir("data/img") would return []string{"a.png", "b.png"}
// AssetDir("foo.txt") and AssetDir("notexist") would return an error // AssetDir("foo.txt") and AssetDir("notexist") would return an error
@ -348,7 +349,7 @@ func RestoreAsset(dir, name string) error {
if err != nil { if err != nil {
return err return err
} }
err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) err = os.WriteFile(_filePath(dir, name), data, info.Mode())
if err != nil { if err != nil {
return err return err
} }

View File

@ -2,7 +2,7 @@ package main
import ( import (
"bytes" "bytes"
"io/ioutil" "os"
"path/filepath" "path/filepath"
"runtime" "runtime"
"strings" "strings"
@ -48,7 +48,7 @@ func (r resource) loadFromBase(dir string) string {
fullpath := filepath.Join(dir, filename) fullpath := filepath.Join(dir, filename)
b, err := ioutil.ReadFile(fullpath) b, err := os.ReadFile(fullpath)
if err != nil { if err != nil {
panic(fullpath + " failed with error: " + err.Error()) panic(fullpath + " failed with error: " + err.Error())
} }

View File

@ -1,6 +1,6 @@
// Code generated by go-bindata. (@generated) DO NOT EDIT. // Code generated by go-bindata. (@generated) DO NOT EDIT.
//Package main generated by go-bindata.// sources: // Package main generated by go-bindata.// sources:
// ../http2push/assets/app2/app2app3/css/main.css // ../http2push/assets/app2/app2app3/css/main.css
// ../http2push/assets/app2/app2app3/dirs/dir1/text.txt // ../http2push/assets/app2/app2app3/dirs/dir1/text.txt
// ../http2push/assets/app2/app2app3/dirs/dir2/text.txt // ../http2push/assets/app2/app2app3/dirs/dir2/text.txt
@ -19,7 +19,6 @@ import (
"compress/gzip" "compress/gzip"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
@ -464,11 +463,13 @@ var _bindata = map[string]func() (*asset, error){
// directory embedded in the file by go-bindata. // directory embedded in the file by go-bindata.
// For example if you run go-bindata on data/... and data contains the // For example if you run go-bindata on data/... and data contains the
// following hierarchy: // following hierarchy:
//
// data/ // data/
// foo.txt // foo.txt
// img/ // img/
// a.png // a.png
// b.png // b.png
//
// then AssetDir("data") would return []string{"foo.txt", "img"} // then AssetDir("data") would return []string{"foo.txt", "img"}
// AssetDir("data/img") would return []string{"a.png", "b.png"} // AssetDir("data/img") would return []string{"a.png", "b.png"}
// AssetDir("foo.txt") and AssetDir("notexist") would return an error // AssetDir("foo.txt") and AssetDir("notexist") would return an error
@ -546,7 +547,7 @@ func RestoreAsset(dir, name string) error {
if err != nil { if err != nil {
return err return err
} }
err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) err = os.WriteFile(_filePath(dir, name), data, info.Mode())
if err != nil { if err != nil {
return err return err
} }

View File

@ -1,6 +1,6 @@
// Code generated by go-bindata. (@generated) DO NOT EDIT. // Code generated by go-bindata. (@generated) DO NOT EDIT.
//Package main generated by go-bindata.// sources: // Package main generated by go-bindata.// sources:
// ../http2push/assets/app2/app2app3/css/main.css // ../http2push/assets/app2/app2app3/css/main.css
// ../http2push/assets/app2/app2app3/dirs/dir1/text.txt // ../http2push/assets/app2/app2app3/dirs/dir1/text.txt
// ../http2push/assets/app2/app2app3/dirs/dir2/text.txt // ../http2push/assets/app2/app2app3/dirs/dir2/text.txt
@ -19,7 +19,6 @@ import (
"compress/gzip" "compress/gzip"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
@ -464,11 +463,13 @@ var _bindata = map[string]func() (*asset, error){
// directory embedded in the file by go-bindata. // directory embedded in the file by go-bindata.
// For example if you run go-bindata on data/... and data contains the // For example if you run go-bindata on data/... and data contains the
// following hierarchy: // following hierarchy:
//
// data/ // data/
// foo.txt // foo.txt
// img/ // img/
// a.png // a.png
// b.png // b.png
//
// then AssetDir("data") would return []string{"foo.txt", "img"} // then AssetDir("data") would return []string{"foo.txt", "img"}
// AssetDir("data/img") would return []string{"a.png", "b.png"} // AssetDir("data/img") would return []string{"a.png", "b.png"}
// AssetDir("foo.txt") and AssetDir("notexist") would return an error // AssetDir("foo.txt") and AssetDir("notexist") would return an error
@ -546,7 +547,7 @@ func RestoreAsset(dir, name string) error {
if err != nil { if err != nil {
return err return err
} }
err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) err = os.WriteFile(_filePath(dir, name), data, info.Mode())
if err != nil { if err != nil {
return err return err
} }

View File

@ -1,6 +1,6 @@
// Code generated by go-bindata. (@generated) DO NOT EDIT. // Code generated by go-bindata. (@generated) DO NOT EDIT.
//Package main generated by go-bindata.// sources: // Package main generated by go-bindata.// sources:
// public/app.js // public/app.js
// public/css/main.css // public/css/main.css
// public/index.html // public/index.html
@ -11,7 +11,6 @@ import (
"compress/gzip" "compress/gzip"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
@ -288,11 +287,13 @@ var _bindata = map[string]func() (*asset, error){
// directory embedded in the file by go-bindata. // directory embedded in the file by go-bindata.
// For example if you run go-bindata on data/... and data contains the // For example if you run go-bindata on data/... and data contains the
// following hierarchy: // following hierarchy:
//
// data/ // data/
// foo.txt // foo.txt
// img/ // img/
// a.png // a.png
// b.png // b.png
//
// then AssetDir("data") would return []string{"foo.txt", "img"} // then AssetDir("data") would return []string{"foo.txt", "img"}
// AssetDir("data/img") would return []string{"a.png", "b.png"} // AssetDir("data/img") would return []string{"a.png", "b.png"}
// AssetDir("foo.txt") and AssetDir("notexist") would return an error // AssetDir("foo.txt") and AssetDir("notexist") would return an error
@ -346,7 +347,7 @@ func RestoreAsset(dir, name string) error {
if err != nil { if err != nil {
return err return err
} }
err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) err = os.WriteFile(_filePath(dir, name), data, info.Mode())
if err != nil { if err != nil {
return err return err
} }

View File

@ -12,7 +12,6 @@ import (
"compress/gzip" "compress/gzip"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
@ -310,11 +309,13 @@ var _bindata = map[string]func() (*asset, error){
// directory embedded in the file by go-bindata. // directory embedded in the file by go-bindata.
// For example if you run go-bindata on data/... and data contains the // For example if you run go-bindata on data/... and data contains the
// following hierarchy: // following hierarchy:
//
// data/ // data/
// foo.txt // foo.txt
// img/ // img/
// a.png // a.png
// b.png // b.png
//
// then AssetDir("data") would return []string{"foo.txt", "img"} // then AssetDir("data") would return []string{"foo.txt", "img"}
// AssetDir("data/img") would return []string{"a.png", "b.png"} // AssetDir("data/img") would return []string{"a.png", "b.png"}
// AssetDir("foo.txt") and AssetDir("nonexistent") would return an error // AssetDir("foo.txt") and AssetDir("nonexistent") would return an error
@ -377,7 +378,7 @@ func RestoreAsset(dir, name string) error {
if err != nil { if err != nil {
return err return err
} }
err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) err = os.WriteFile(_filePath(dir, name), data, info.Mode())
if err != nil { if err != nil {
return err return err
} }

View File

@ -1,7 +1,7 @@
package main package main
import ( import (
"io/ioutil" "os"
"path/filepath" "path/filepath"
"runtime" "runtime"
"strings" "strings"
@ -41,7 +41,7 @@ func (r resource) loadFromBase(dir string) string {
fullpath := filepath.Join(dir, filename) fullpath := filepath.Join(dir, filename)
b, err := ioutil.ReadFile(fullpath) b, err := os.ReadFile(fullpath)
if err != nil { if err != nil {
panic(fullpath + " failed with error: " + err.Error()) panic(fullpath + " failed with error: " + err.Error())
} }

View File

@ -1,8 +1,8 @@
package main package main
import ( import (
"io/ioutil"
"net" "net"
"os"
"path/filepath" "path/filepath"
"testing" "testing"
@ -40,7 +40,7 @@ func (r resource) loadFromBase(dir string) string {
fullpath := filepath.Join(dir, filename) fullpath := filepath.Join(dir, filename)
b, err := ioutil.ReadFile(fullpath) b, err := os.ReadFile(fullpath)
if err != nil { if err != nil {
panic(fullpath + " failed with error: " + err.Error()) panic(fullpath + " failed with error: " + err.Error())
} }

View File

@ -1,10 +1,12 @@
/*Package main is a proxy + accesslog example. /*
Package main is a proxy + accesslog example.
In this example we will make a small proxy which listens requests on "/proxy/+path". In this example we will make a small proxy which listens requests on "/proxy/+path".
With two accesslog instances, one for the main application and one for the /proxy/ requests. With two accesslog instances, one for the main application and one for the /proxy/ requests.
Of cource, you could a single accesslog for the whole application, but for the sake of the example Of cource, you could a single accesslog for the whole application, but for the sake of the example
let's log them separately. let's log them separately.
We will make use of iris.StripPrefix and host.ProxyHandler.*/ We will make use of iris.StripPrefix and host.ProxyHandler.
*/
package main package main
import ( import (

View File

@ -5,15 +5,15 @@ import (
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"encoding/json" "encoding/json"
"io/ioutil"
"log" "log"
"net/http" "net/http"
"os"
pb "github.com/kataras/iris/v12/_examples/mvc/grpc-compatible/helloworld" pb "github.com/kataras/iris/v12/_examples/mvc/grpc-compatible/helloworld"
) )
func main() { func main() {
b, err := ioutil.ReadFile("../server.crt") b, err := os.ReadFile("../server.crt")
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }

View File

@ -30,6 +30,7 @@ type UsersController struct {
// curl -i -u admin:password http://localhost:8080/users // curl -i -u admin:password http://localhost:8080/users
// //
// The correct way if you have sensitive data: // The correct way if you have sensitive data:
//
// func (c *UsersController) Get() (results []viewmodels.User) { // func (c *UsersController) Get() (results []viewmodels.User) {
// data := c.Service.GetAll() // data := c.Service.GetAll()
// //
@ -38,6 +39,7 @@ type UsersController struct {
// } // }
// return // return
// } // }
//
// otherwise just return the datamodels. // otherwise just return the datamodels.
func (c *UsersController) Get() (results []datamodels.User) { func (c *UsersController) Get() (results []datamodels.User) {
return c.Service.GetAll() return c.Service.GetAll()

View File

@ -9,9 +9,11 @@ else as well, otherwise I am going with the first one:
```go ```go
// 1 // 1
mvc.Configure(app.Party("/user"), func(m *mvc.Application) {
mvc.Configure(app.Party("/user"), func(m *mvc.Application) {
m.Router.Use(cache.Handler(10*time.Second)) m.Router.Use(cache.Handler(10*time.Second))
}) })
``` ```
```go ```go
@ -32,10 +34,12 @@ mvc.Configure(userRouter, ...)
```go ```go
// 4 // 4
// same: // same:
app.PartyFunc("/user", func(r iris.Party){
app.PartyFunc("/user", func(r iris.Party){
r.Use(cache.Handler(10*time.Second)) r.Use(cache.Handler(10*time.Second))
mvc.Configure(r, ...) mvc.Configure(r, ...)
}) })
``` ```
If you want to use a middleware for a single route, If you want to use a middleware for a single route,
@ -48,7 +52,8 @@ then you just call it on the method:
var myMiddleware := myMiddleware.New(...) // this should return an iris/context.Handler var myMiddleware := myMiddleware.New(...) // this should return an iris/context.Handler
type UserController struct{} type UserController struct{}
func (c *UserController) GetSomething(ctx iris.Context) {
func (c *UserController) GetSomething(ctx iris.Context) {
// ctx.Proceed checks if myMiddleware called `ctx.Next()` // ctx.Proceed checks if myMiddleware called `ctx.Next()`
// inside it and returns true if so, otherwise false. // inside it and returns true if so, otherwise false.
nextCalled := ctx.Proceed(myMiddleware) nextCalled := ctx.Proceed(myMiddleware)
@ -57,7 +62,8 @@ func (c *UserController) GetSomething(ctx iris.Context) {
} }
// else do the job here, it's allowed // else do the job here, it's allowed
} }
``` ```
And last, if you want to add a middleware on a specific method And last, if you want to add a middleware on a specific method

View File

@ -1,8 +1,10 @@
/*Package main shows how to add done handlers in an MVC application without /*
Package main shows how to add done handlers in an MVC application without
the necessity of `ctx.Next()` inside the controller's methods. the necessity of `ctx.Next()` inside the controller's methods.
When we want the `Done` handlers of that specific mvc app's `Party` When we want the `Done` handlers of that specific mvc app's `Party`
to be executed but we don't want to add `ctx.Next()` on the `exampleController#EndRequest`*/ to be executed but we don't want to add `ctx.Next()` on the `exampleController#EndRequest`
*/
package main package main
import ( import (

View File

@ -35,7 +35,9 @@ func pong(ctx iris.Context) {
| | | | | |
| | | | | |
DEV | | | PROD DEV | | | PROD
-------------------+---------------------+ | +----------------------+------------------- -------------------+---------------------+ | +----------------------+-------------------
| | | | | |
| | | | | |
+---+-----+ +----------------v------------------+ +----+----+ +---+-----+ +----------------v------------------+ +----+----+
@ -64,6 +66,7 @@ func pong(ctx iris.Context) {
| | /greet?name=kataras | | | | /greet?name=kataras | |
| +-----------+-----------+ | | +-----------+-----------+ |
| | | | | |
+------------------+--------+ +------------+------------+ +-------+------------------+ +------------------+--------+ +------------+------------+ +-------+------------------+
| model.Response (JSON) | | Response (JSON, error) | | Bad Request | | model.Response (JSON) | | Response (JSON, error) | | Bad Request |
+---------------------------+ +-------------------------+ +--------------------------+ +---------------------------+ +-------------------------+ +--------------------------+

View File

@ -23,6 +23,7 @@ type MovieController struct {
// curl -i http://localhost:8080/movies // curl -i http://localhost:8080/movies
// //
// The correct way if you have sensitive data: // The correct way if you have sensitive data:
//
// func (c *MovieController) Get() (results []viewmodels.Movie) { // func (c *MovieController) Get() (results []viewmodels.Movie) {
// data := c.Service.GetAll() // data := c.Service.GetAll()
// //
@ -31,6 +32,7 @@ type MovieController struct {
// } // }
// return // return
// } // }
//
// otherwise just return the datamodels. // otherwise just return the datamodels.
func (c *MovieController) Get() (results []datamodels.Movie) { func (c *MovieController) Get() (results []datamodels.Movie) {
return c.Service.GetAll() return c.Service.GetAll()

View File

@ -18,7 +18,7 @@ func (srv *Server) buildRouter() {
ServerName: srv.config.ServerName, ServerName: srv.config.ServerName,
Env: srv.config.Env, Env: srv.config.Env,
Developer: "kataras", Developer: "kataras",
TimeLocation: time.FixedZone("Greece/Athens", 10800), TimeLocation: time.FixedZone("Greece/Athens", 7200),
})) }))
api := srv.Party("/api") api := srv.Party("/api")

View File

@ -8,7 +8,7 @@ func main() {
app := iris.New() app := iris.New()
app.Post("/", logAllBody, logJSON, logFormValues, func(ctx iris.Context) { app.Post("/", logAllBody, logJSON, logFormValues, func(ctx iris.Context) {
// body, err := ioutil.ReadAll(ctx.Request().Body) once or // body, err := io.ReadAll(ctx.Request().Body) once or
body, err := ctx.GetBody() // as many times as you need. body, err := ctx.GetBody() // as many times as you need.
if err != nil { if err != nil {
ctx.StopWithError(iris.StatusInternalServerError, err) ctx.StopWithError(iris.StatusInternalServerError, err)

View File

@ -4,7 +4,7 @@ import (
"bytes" "bytes"
"compress/gzip" "compress/gzip"
"encoding/xml" "encoding/xml"
"io/ioutil" "io"
"testing" "testing"
"github.com/kataras/iris/v12" "github.com/kataras/iris/v12"
@ -69,7 +69,7 @@ func TestContentNegotiation(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
rawResponse, err := ioutil.ReadAll(zr) rawResponse, err := io.ReadAll(zr)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -7,7 +7,8 @@ import (
"github.com/kataras/iris/v12/core/router" "github.com/kataras/iris/v12/core/router"
) )
/* A Router should contain all three of the following methods: /*
A Router should contain all three of the following methods:
- HandleRequest should handle the request based on the Context. - HandleRequest should handle the request based on the Context.
HandleRequest(ctx iris.Context) HandleRequest(ctx iris.Context)
- Build should builds the handler, it's being called on router's BuildRouter. - Build should builds the handler, it's being called on router's BuildRouter.

View File

@ -1,7 +1,7 @@
package main package main
import ( import (
"io/ioutil" "os"
"path/filepath" "path/filepath"
"strings" "strings"
"testing" "testing"
@ -29,7 +29,7 @@ func (r resource) loadFromBase(dir string) string {
fullpath := filepath.Join(dir, filename) fullpath := filepath.Join(dir, filename)
b, err := ioutil.ReadFile(fullpath) b, err := os.ReadFile(fullpath)
if err != nil { if err != nil {
panic(fullpath + " failed with error: " + err.Error()) panic(fullpath + " failed with error: " + err.Error())
} }

View File

@ -1,4 +1,5 @@
/*Package main is a simple example of the behavior change of the execution flow of the handlers, /*
Package main is a simple example of the behavior change of the execution flow of the handlers,
normally we need the `ctx.Next()` to call the next handler in a route's handler chain, normally we need the `ctx.Next()` to call the next handler in a route's handler chain,
but with the `ExecutionRules` we can change this default behavior. but with the `ExecutionRules` we can change this default behavior.
Please read below before continue. Please read below before continue.
@ -8,11 +9,11 @@ The `Party#SetExecutionRules` alters the execution flow of the route handlers.
For example, if for some reason the desired result is the (done or all) handlers For example, if for some reason the desired result is the (done or all) handlers
to be executed no matter what, even if no `ctx.Next()` is called in the previous handlers: to be executed no matter what, even if no `ctx.Next()` is called in the previous handlers:
app.SetExecutionRules(iris.ExecutionRules { app.SetExecutionRules(iris.ExecutionRules {
Begin: iris.ExecutionOptions{Force: true}, # begin handlers(.Use) Begin: iris.ExecutionOptions{Force: true}, # begin handlers(.Use)
Main: iris.ExecutionOptions{Force: true}, # main handler (.Handle/Get...) Main: iris.ExecutionOptions{Force: true}, # main handler (.Handle/Get...)
Done: iris.ExecutionOptions{Force: true}, # done handlers (.Done) Done: iris.ExecutionOptions{Force: true}, # done handlers (.Done)
}) })
Note that if `true` then the only remained way to "break" the handler chain Note that if `true` then the only remained way to "break" the handler chain
is by calling the `ctx.StopExecution()` (now that `ctx.Next()` doesn't even matter). is by calling the `ctx.StopExecution()` (now that `ctx.Next()` doesn't even matter).
@ -22,7 +23,6 @@ the same rules will be applied to that as well.
Reset of these rules to their defaults (before `Party#Handle`) can be done Reset of these rules to their defaults (before `Party#Handle`) can be done
with `Party#SetExecutionRules(iris.ExecutionRules{})`. with `Party#SetExecutionRules(iris.ExecutionRules{})`.
*/ */
package main package main

View File

@ -1,7 +1,6 @@
package main package main
import ( import (
"io/ioutil"
"os" "os"
"testing" "testing"
"time" "time"
@ -14,7 +13,7 @@ import (
// The rest possible checks is up to you, take it as as an exercise! // The rest possible checks is up to you, take it as as an exercise!
func TestURLShortener(t *testing.T) { func TestURLShortener(t *testing.T) {
// temp db file // temp db file
f, err := ioutil.TempFile("", "shortener") f, err := os.CreateTemp("", "shortener")
if err != nil { if err != nil {
t.Fatalf("creating temp file for database failed: %v", err) t.Fatalf("creating temp file for database failed: %v", err)
} }

View File

@ -1,6 +1,6 @@
// Code generated by go-bindata. (@generated) DO NOT EDIT. // Code generated by go-bindata. (@generated) DO NOT EDIT.
//Package main generated by go-bindata.// sources: // Package main generated by go-bindata.// sources:
// templates/layouts/layout.html // templates/layouts/layout.html
// templates/layouts/mylayout.html // templates/layouts/mylayout.html
// templates/page1.html // templates/page1.html
@ -12,7 +12,6 @@ import (
"compress/gzip" "compress/gzip"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
@ -310,11 +309,13 @@ var _bindata = map[string]func() (*asset, error){
// directory embedded in the file by go-bindata. // directory embedded in the file by go-bindata.
// For example if you run go-bindata on data/... and data contains the // For example if you run go-bindata on data/... and data contains the
// following hierarchy: // following hierarchy:
//
// data/ // data/
// foo.txt // foo.txt
// img/ // img/
// a.png // a.png
// b.png // b.png
//
// then AssetDir("data") would return []string{"foo.txt", "img"} // then AssetDir("data") would return []string{"foo.txt", "img"}
// AssetDir("data/img") would return []string{"a.png", "b.png"} // AssetDir("data/img") would return []string{"a.png", "b.png"}
// AssetDir("foo.txt") and AssetDir("notexist") would return an error // AssetDir("foo.txt") and AssetDir("notexist") would return an error
@ -371,7 +372,7 @@ func RestoreAsset(dir, name string) error {
if err != nil { if err != nil {
return err return err
} }
err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) err = os.WriteFile(_filePath(dir, name), data, info.Mode())
if err != nil { if err != nil {
return err return err
} }

View File

@ -10,7 +10,6 @@ import (
"compress/gzip" "compress/gzip"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
@ -266,11 +265,13 @@ var _bindata = map[string]func() (*asset, error){
// directory embedded in the file by go-bindata. // directory embedded in the file by go-bindata.
// For example if you run go-bindata on data/... and data contains the // For example if you run go-bindata on data/... and data contains the
// following hierarchy: // following hierarchy:
//
// data/ // data/
// foo.txt // foo.txt
// img/ // img/
// a.png // a.png
// b.png // b.png
//
// then AssetDir("data") would return []string{"foo.txt", "img"} // then AssetDir("data") would return []string{"foo.txt", "img"}
// AssetDir("data/img") would return []string{"a.png", "b.png"} // AssetDir("data/img") would return []string{"a.png", "b.png"}
// AssetDir("foo.txt") and AssetDir("nonexistent") would return an error // AssetDir("foo.txt") and AssetDir("nonexistent") would return an error
@ -323,7 +324,7 @@ func RestoreAsset(dir, name string) error {
if err != nil { if err != nil {
return err return err
} }
err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) err = os.WriteFile(_filePath(dir, name), data, info.Mode())
if err != nil { if err != nil {
return err return err
} }

View File

@ -13,7 +13,6 @@ import (
"compress/gzip" "compress/gzip"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
@ -332,11 +331,13 @@ var _bindata = map[string]func() (*asset, error){
// directory embedded in the file by go-bindata. // directory embedded in the file by go-bindata.
// For example if you run go-bindata on data/... and data contains the // For example if you run go-bindata on data/... and data contains the
// following hierarchy: // following hierarchy:
//
// data/ // data/
// foo.txt // foo.txt
// img/ // img/
// a.png // a.png
// b.png // b.png
//
// then AssetDir("data") would return []string{"foo.txt", "img"} // then AssetDir("data") would return []string{"foo.txt", "img"}
// AssetDir("data/img") would return []string{"a.png", "b.png"} // AssetDir("data/img") would return []string{"a.png", "b.png"}
// AssetDir("foo.txt") and AssetDir("nonexistent") would return an error // AssetDir("foo.txt") and AssetDir("nonexistent") would return an error
@ -394,7 +395,7 @@ func RestoreAsset(dir, name string) error {
if err != nil { if err != nil {
return err return err
} }
err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) err = os.WriteFile(_filePath(dir, name), data, info.Mode())
if err != nil { if err != nil {
return err return err
} }

View File

@ -12,7 +12,6 @@ import (
"compress/gzip" "compress/gzip"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
@ -310,11 +309,13 @@ var _bindata = map[string]func() (*asset, error){
// directory embedded in the file by go-bindata. // directory embedded in the file by go-bindata.
// For example if you run go-bindata on data/... and data contains the // For example if you run go-bindata on data/... and data contains the
// following hierarchy: // following hierarchy:
//
// data/ // data/
// foo.txt // foo.txt
// img/ // img/
// a.png // a.png
// b.png // b.png
//
// then AssetDir("data") would return []string{"foo.txt", "img"} // then AssetDir("data") would return []string{"foo.txt", "img"}
// AssetDir("data/img") would return []string{"a.png", "b.png"} // AssetDir("data/img") would return []string{"a.png", "b.png"}
// AssetDir("foo.txt") and AssetDir("nonexistent") would return an error // AssetDir("foo.txt") and AssetDir("nonexistent") would return an error
@ -371,7 +372,7 @@ func RestoreAsset(dir, name string) error {
if err != nil { if err != nil {
return err return err
} }
err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) err = os.WriteFile(_filePath(dir, name), data, info.Mode())
if err != nil { if err != nil {
return err return err
} }

View File

@ -10,7 +10,6 @@ import (
"compress/gzip" "compress/gzip"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
@ -266,11 +265,13 @@ var _bindata = map[string]func() (*asset, error){
// directory embedded in the file by go-bindata. // directory embedded in the file by go-bindata.
// For example if you run go-bindata on data/... and data contains the // For example if you run go-bindata on data/... and data contains the
// following hierarchy: // following hierarchy:
//
// data/ // data/
// foo.txt // foo.txt
// img/ // img/
// a.png // a.png
// b.png // b.png
//
// then AssetDir("data") would return []string{"foo.txt", "img"} // then AssetDir("data") would return []string{"foo.txt", "img"}
// AssetDir("data/img") would return []string{"a.png", "b.png"} // AssetDir("data/img") would return []string{"a.png", "b.png"}
// AssetDir("foo.txt") and AssetDir("nonexistent") would return an error // AssetDir("foo.txt") and AssetDir("nonexistent") would return an error
@ -321,7 +322,7 @@ func RestoreAsset(dir, name string) error {
if err != nil { if err != nil {
return err return err
} }
err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) err = os.WriteFile(_filePath(dir, name), data, info.Mode())
if err != nil { if err != nil {
return err return err
} }

View File

@ -12,7 +12,6 @@ package main
import ( import (
"flag" "flag"
"io/ioutil"
"log" "log"
"os" "os"
"strconv" "strconv"
@ -53,7 +52,7 @@ func readFileIfModified(lastMod time.Time) ([]byte, time.Time, error) {
if !fi.ModTime().After(lastMod) { if !fi.ModTime().After(lastMod) {
return nil, lastMod, nil return nil, lastMod, nil
} }
p, err := ioutil.ReadFile(filename) p, err := os.ReadFile(filename)
if err != nil { if err != nil {
return nil, fi.ModTime(), err return nil, fi.ModTime(), err
} }

View File

@ -13,6 +13,7 @@ import (
// The cases are filtered in order of their registration. // The cases are filtered in order of their registration.
// //
// Example Code: // Example Code:
//
// switcher := Switch(Hosts{ // switcher := Switch(Hosts{
// "mydomain.com": app, // "mydomain.com": app,
// "test.mydomain.com": testSubdomainApp, // "test.mydomain.com": testSubdomainApp,

View File

@ -99,6 +99,7 @@ func MustLoad[T User](filename string) *Auth[T] {
// Must is a helper that wraps a call to a function returning (*Auth[T], error) // Must is a helper that wraps a call to a function returning (*Auth[T], error)
// and panics if the error is non-nil. It is intended for use in variable // and panics if the error is non-nil. It is intended for use in variable
// initializations such as // initializations such as
//
// var s = auth.Must(auth.New[MyUser](config)) // var s = auth.Must(auth.New[MyUser](config))
func Must[T User](s *Auth[T], err error) *Auth[T] { func Must[T User](s *Auth[T], err error) *Auth[T] {
if err != nil { if err != nil {
@ -142,6 +143,7 @@ func New[T User](config Configuration) (*Auth[T], error) {
// method when a Provider of T and ErrorHandler is available through the registered Party's dependencies. // method when a Provider of T and ErrorHandler is available through the registered Party's dependencies.
// //
// Usage Example: // Usage Example:
//
// api := app.Party("/api") // api := app.Party("/api")
// api.EnsureStaticBindings().RegisterDependency( // api.EnsureStaticBindings().RegisterDependency(
// NewAuthProviderErrorHandler(), // NewAuthProviderErrorHandler(),

1
cache/browser.go vendored
View File

@ -49,6 +49,7 @@ var NoCache = func(ctx *context.Context) {
// Usage: `app.Use(cache.StaticCache(24 * time.Hour))` or `app.Use(cache.Staticcache(-1))`. // Usage: `app.Use(cache.StaticCache(24 * time.Hour))` or `app.Use(cache.Staticcache(-1))`.
// A middleware, which is a simple Handler can be called inside another handler as well, example: // A middleware, which is a simple Handler can be called inside another handler as well, example:
// cacheMiddleware := cache.StaticCache(...) // cacheMiddleware := cache.StaticCache(...)
//
// func(ctx iris.Context){ // func(ctx iris.Context){
// cacheMiddleware(ctx) // cacheMiddleware(ctx)
// [...] // [...]

View File

@ -2,7 +2,7 @@ package client
import ( import (
"bytes" "bytes"
"io/ioutil" "io"
"net/http" "net/http"
"time" "time"
@ -17,8 +17,8 @@ import (
// register one client handler per route. // register one client handler per route.
// //
// it's just calls a remote cache service server/handler, // it's just calls a remote cache service server/handler,
// which lives on other, external machine.
// //
// which lives on other, external machine.
type ClientHandler struct { type ClientHandler struct {
// bodyHandler the original route's handler // bodyHandler the original route's handler
bodyHandler context.Handler bodyHandler context.Handler
@ -162,7 +162,7 @@ func (h *ClientHandler) ServeHTTP(ctx *context.Context) {
// get the status code , content type and the write the response body // get the status code , content type and the write the response body
ctx.ContentType(response.Header.Get(cfg.ContentTypeHeader)) ctx.ContentType(response.Header.Get(cfg.ContentTypeHeader))
ctx.StatusCode(response.StatusCode) ctx.StatusCode(response.StatusCode)
responseBody, err := ioutil.ReadAll(response.Body) responseBody, err := io.ReadAll(response.Body)
response.Body.Close() response.Body.Close()
if err != nil { if err != nil {
return return

View File

@ -22,6 +22,7 @@ type PreValidator func(*context.Context) bool
// //
// Q: What's the difference between this and a PreValidator? // Q: What's the difference between this and a PreValidator?
// A: PreValidator runs BEFORE trying to get the cache, it cares only for the request // A: PreValidator runs BEFORE trying to get the cache, it cares only for the request
//
// and if at least one PreValidator returns false then it just runs the original handler and stop there, at the other hand // and if at least one PreValidator returns false then it just runs the original handler and stop there, at the other hand
// a PostValidator runs if all PreValidators returns true and original handler is executed but with a response recorder, // a PostValidator runs if all PreValidators returns true and original handler is executed but with a response recorder,
// also the PostValidator should return true to store the cached response. // also the PostValidator should return true to store the cached response.

View File

@ -2,7 +2,6 @@ package iris
import ( import (
"fmt" "fmt"
"io/ioutil"
"os" "os"
"os/user" "os/user"
"path/filepath" "path/filepath"
@ -65,7 +64,7 @@ func parseYAML(filename string) (Configuration, error) {
} }
// read the raw contents of the file // read the raw contents of the file
data, err := ioutil.ReadFile(yamlAbsPath) data, err := os.ReadFile(yamlAbsPath)
if err != nil { if err != nil {
return c, fmt.Errorf("parse yaml: %w", err) return c, fmt.Errorf("parse yaml: %w", err)
} }
@ -112,7 +111,6 @@ func YAML(filename string) Configuration {
// Read more about toml's implementation at: // Read more about toml's implementation at:
// https://github.com/toml-lang/toml // https://github.com/toml-lang/toml
// //
//
// Accepts the absolute path of the configuration file. // Accepts the absolute path of the configuration file.
// An error will be shown to the user via panic with the error message. // An error will be shown to the user via panic with the error message.
// Error may occur when the file does not exist or is not formatted correctly. // Error may occur when the file does not exist or is not formatted correctly.
@ -144,7 +142,7 @@ func TOML(filename string) Configuration {
} }
// read the raw contents of the file // read the raw contents of the file
data, err := ioutil.ReadFile(tomlAbsPath) data, err := os.ReadFile(tomlAbsPath)
if err != nil { if err != nil {
panic(fmt.Errorf("toml :%w", err)) panic(fmt.Errorf("toml :%w", err))
} }
@ -754,6 +752,7 @@ type Configuration struct {
// then the application tries to optimize for the best performance where is possible. // then the application tries to optimize for the best performance where is possible.
// //
// Defaults to false. // Defaults to false.
// Deprecated. As of version 12.2.x this field does nothing.
EnableOptimizations bool `ini:"enable_optimizations" json:"enableOptimizations,omitempty" yaml:"EnableOptimizations" toml:"EnableOptimizations"` EnableOptimizations bool `ini:"enable_optimizations" json:"enableOptimizations,omitempty" yaml:"EnableOptimizations" toml:"EnableOptimizations"`
// EnableProtoJSON when this field is true // EnableProtoJSON when this field is true
// enables the proto marshaler on given proto messages when calling the Context.JSON method. // enables the proto marshaler on given proto messages when calling the Context.JSON method.

View File

@ -1,7 +1,6 @@
package iris package iris
import ( import (
"io/ioutil"
"os" "os"
"reflect" "reflect"
"testing" "testing"
@ -103,7 +102,7 @@ func createGlobalConfiguration(t *testing.T) {
out, err := yaml.Marshal(&c) out, err := yaml.Marshal(&c)
if err == nil { if err == nil {
err = ioutil.WriteFile(filename, out, os.FileMode(0666)) err = os.WriteFile(filename, out, os.FileMode(0666))
} }
if err != nil { if err != nil {
t.Fatalf("error while writing the global configuration field at: %s. Trace: %v\n", filename, err) t.Fatalf("error while writing the global configuration field at: %s. Trace: %v\n", filename, err)
@ -131,7 +130,7 @@ func testConfigurationGlobal(t *testing.T, c Configurator) {
} }
func TestConfigurationYAML(t *testing.T) { func TestConfigurationYAML(t *testing.T) {
yamlFile, ferr := ioutil.TempFile("", "configuration.yml") yamlFile, ferr := os.CreateTemp("", "configuration.yml")
if ferr != nil { if ferr != nil {
t.Fatal(ferr) t.Fatal(ferr)
@ -264,7 +263,7 @@ Other:
} }
func TestConfigurationTOML(t *testing.T) { func TestConfigurationTOML(t *testing.T) {
tomlFile, ferr := ioutil.TempFile("", "configuration.toml") tomlFile, ferr := os.CreateTemp("", "configuration.toml")
if ferr != nil { if ferr != nil {
t.Fatal(ferr) t.Fatal(ferr)

View File

@ -8,7 +8,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"mime" "mime"
"mime/multipart" "mime/multipart"
"net" "net"
@ -31,7 +30,6 @@ import (
"github.com/Shopify/goreferrer" "github.com/Shopify/goreferrer"
"github.com/fatih/structs" "github.com/fatih/structs"
gojson "github.com/goccy/go-json"
"github.com/iris-contrib/schema" "github.com/iris-contrib/schema"
"github.com/mailru/easyjson" "github.com/mailru/easyjson"
"github.com/mailru/easyjson/jwriter" "github.com/mailru/easyjson/jwriter"
@ -100,7 +98,7 @@ type (
// is terminated and the error is received by the ReadJSONStream method, // is terminated and the error is received by the ReadJSONStream method,
// otherwise it continues to read the next available object. // otherwise it continues to read the next available object.
// Look the `Context.ReadJSONStream` method. // Look the `Context.ReadJSONStream` method.
DecodeFunc func(ctx stdContext.Context, outPtr interface{}) error DecodeFunc func(outPtr interface{}) error
) )
// Unmarshal parses the X-encoded data and stores the result in the value pointed to by v. // Unmarshal parses the X-encoded data and stores the result in the value pointed to by v.
@ -539,7 +537,6 @@ func (ctx *Context) Do(handlers Handlers) {
// Router is calling this function to add the route's handler. // Router is calling this function to add the route's handler.
// If AddHandler called then the handlers will be inserted // If AddHandler called then the handlers will be inserted
// to the end of the already-defined route's handler. // to the end of the already-defined route's handler.
//
func (ctx *Context) AddHandler(handlers ...Handler) { func (ctx *Context) AddHandler(handlers ...Handler) {
ctx.handlers = append(ctx.handlers, handlers...) ctx.handlers = append(ctx.handlers, handlers...)
} }
@ -601,12 +598,15 @@ func (ctx *Context) HandlerIndex(n int) (currentIndex int) {
// ctx.StopExecution() // ctx.StopExecution()
// } // }
// } // }
//
// This Get() will be executed in the same handler as `BeginRequest`, // This Get() will be executed in the same handler as `BeginRequest`,
// internally controller checks for `ctx.StopExecution`. // internally controller checks for `ctx.StopExecution`.
// So it will not be fired if BeginRequest called the `StopExecution`. // So it will not be fired if BeginRequest called the `StopExecution`.
//
// func(c *UsersController) Get() []models.User { // func(c *UsersController) Get() []models.User {
// return c.Service.GetAll() // return c.Service.GetAll()
//} // }
//
// Alternative way is `!ctx.IsStopped()` if middleware make use of the `ctx.StopExecution()` on failure. // Alternative way is `!ctx.IsStopped()` if middleware make use of the `ctx.StopExecution()` on failure.
func (ctx *Context) Proceed(h Handler) bool { func (ctx *Context) Proceed(h Handler) bool {
_, ok := ctx.ProceedAndReportIfStopped(h) _, ok := ctx.ProceedAndReportIfStopped(h)
@ -841,8 +841,7 @@ func (ctx *Context) StopWithPlainError(statusCode int, err error) {
// it will also fire the specified error code handler. // it will also fire the specified error code handler.
func (ctx *Context) StopWithJSON(statusCode int, jsonObject interface{}) error { func (ctx *Context) StopWithJSON(statusCode int, jsonObject interface{}) error {
ctx.StopWithStatus(statusCode) ctx.StopWithStatus(statusCode)
_, err := ctx.writeJSON(jsonObject, nil) // do not modify - see errors.DefaultContextErrorHandler. return ctx.writeJSON(jsonObject, nil) // do not modify - see errors.DefaultContextErrorHandler.
return err
} }
// StopWithProblem stops the handlers chain, writes the status code // StopWithProblem stops the handlers chain, writes the status code
@ -854,8 +853,7 @@ func (ctx *Context) StopWithJSON(statusCode int, jsonObject interface{}) error {
func (ctx *Context) StopWithProblem(statusCode int, problem Problem) error { func (ctx *Context) StopWithProblem(statusCode int, problem Problem) error {
ctx.StopWithStatus(statusCode) ctx.StopWithStatus(statusCode)
problem.Status(statusCode) problem.Status(statusCode)
_, err := ctx.Problem(problem) return ctx.Problem(problem)
return err
} }
// +------------------------------------------------------------+ // +------------------------------------------------------------+
@ -939,7 +937,7 @@ func (ctx *Context) RequestPath(escape bool) string {
const sufscheme = "://" const sufscheme = "://"
// GetScheme returns the full scheme of the request URL (https://, http:// or ws:// and e.t.c.``). // GetScheme returns the full scheme of the request URL (https://, http:// or ws:// and e.t.c.).
func GetScheme(r *http.Request) string { func GetScheme(r *http.Request) string {
scheme := r.URL.Scheme scheme := r.URL.Scheme
@ -1111,10 +1109,11 @@ func (ctx *Context) FullRequestURI() string {
// even if it's part of a private network. // even if it's part of a private network.
// //
// Look `Configuration.RemoteAddrHeaders`, // Look `Configuration.RemoteAddrHeaders`,
// `Configuration.RemoteAddrHeadersForce`, //
// `Configuration.WithRemoteAddrHeader(...)`, // Configuration.RemoteAddrHeadersForce,
// `Configuration.WithoutRemoteAddrHeader(...)` and // Configuration.WithRemoteAddrHeader(...),
// `Configuration.RemoteAddrPrivateSubnets` for more. // Configuration.WithoutRemoteAddrHeader(...) and
// Configuration.RemoteAddrPrivateSubnetsW for more.
func (ctx *Context) RemoteAddr() string { func (ctx *Context) RemoteAddr() string {
if remoteHeaders := ctx.app.ConfigurationReadOnly().GetRemoteAddrHeaders(); len(remoteHeaders) > 0 { if remoteHeaders := ctx.app.ConfigurationReadOnly().GetRemoteAddrHeaders(); len(remoteHeaders) > 0 {
privateSubnets := ctx.app.ConfigurationReadOnly().GetRemoteAddrPrivateSubnets() privateSubnets := ctx.app.ConfigurationReadOnly().GetRemoteAddrPrivateSubnets()
@ -1176,7 +1175,7 @@ func (ctx *Context) GetHeader(name string) string {
// try to find another way of detecting the type(i.e, content type), // 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, // there are many blogs that describe these problems and provide different kind of solutions,
// it's always depending on the application you're building, // it's always depending on the application you're building,
// this is the reason why this `IsAjax`` is simple enough for general purpose use. // 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 // Read more at: https://developer.mozilla.org/en-US/docs/AJAX
// and https://xhr.spec.whatwg.org/ // and https://xhr.spec.whatwg.org/
@ -1557,9 +1556,11 @@ func (ctx *Context) URLParamEscape(name string) string {
// Example: // Example:
// //
// n, err := context.URLParamInt("url_query_param_name") // n, err := context.URLParamInt("url_query_param_name")
//
// if errors.Is(err, context.ErrNotFound) { // if errors.Is(err, context.ErrNotFound) {
// // [handle error...] // // [handle error...]
// } // }
//
// Another usage would be `err == context.ErrNotFound` // Another usage would be `err == context.ErrNotFound`
// HOWEVER prefer use the new `errors.Is` as API details may change in the future. // HOWEVER prefer use the new `errors.Is` as API details may change in the future.
var ErrNotFound = errors.New("not found") var ErrNotFound = errors.New("not found")
@ -1816,7 +1817,7 @@ func GetForm(r *http.Request, postMaxMemory int64, resetBody bool) (form map[str
setBody(r, body) // so the ctx.request.Body works setBody(r, body) // so the ctx.request.Body works
defer restoreBody() // so the next GetForm calls work. defer restoreBody() // so the next GetForm calls work.
// r.Body = ioutil.NopCloser(io.TeeReader(r.Body, buf)) // r.Body = io.NopCloser(io.TeeReader(r.Body, buf))
} else { } else {
resetBody = false resetBody = false
} }
@ -1828,7 +1829,7 @@ func GetForm(r *http.Request, postMaxMemory int64, resetBody bool) (form map[str
// subsequent calls have no effect, are idempotent. // subsequent calls have no effect, are idempotent.
err := r.ParseMultipartForm(postMaxMemory) err := r.ParseMultipartForm(postMaxMemory)
// if resetBody { // if resetBody {
// r.Body = ioutil.NopCloser(bytes.NewBuffer(bodyCopy)) // r.Body = io.NopCloser(bytes.NewBuffer(bodyCopy))
// } // }
if err != nil && err != http.ErrNotMultipart { if err != nil && err != http.ErrNotMultipart {
return nil, false return nil, false
@ -2037,7 +2038,6 @@ func (ctx *Context) PostValueBool(name string) (bool, error) {
// FormFile returns the first uploaded file that received from the client. // 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 // 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. // `iris#WithPostMaxMemory` configurator at main configuration passed on `app.Run`'s second argument.
// //
@ -2284,7 +2284,7 @@ var emptyFunc = func() {}
// GetBody reads and returns the request body. // GetBody reads and returns the request body.
func GetBody(r *http.Request, resetBody bool) ([]byte, func(), error) { func GetBody(r *http.Request, resetBody bool) ([]byte, func(), error) {
data, err := ioutil.ReadAll(r.Body) data, err := io.ReadAll(r.Body)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -2301,7 +2301,7 @@ func GetBody(r *http.Request, resetBody bool) ([]byte, func(), error) {
} }
func setBody(r *http.Request, data []byte) { func setBody(r *http.Request, data []byte) {
r.Body = ioutil.NopCloser(bytes.NewBuffer(data)) r.Body = io.NopCloser(bytes.NewBuffer(data))
} }
const disableRequestBodyConsumptionContextKey = "iris.request.body.record" const disableRequestBodyConsumptionContextKey = "iris.request.body.record"
@ -2385,10 +2385,6 @@ func (ctx *Context) UnmarshalBody(outPtr interface{}, unmarshaler Unmarshaler) e
return ctx.app.Validate(outPtr) return ctx.app.Validate(outPtr)
} }
func (ctx *Context) shouldOptimize() bool {
return ctx.app.ConfigurationReadOnly().GetEnableOptimizations()
}
// JSONReader holds the JSON decode options of the `Context.ReadJSON, ReadBody` methods. // JSONReader holds the JSON decode options of the `Context.ReadJSON, ReadBody` methods.
type JSONReader struct { // Note(@kataras): struct instead of optional funcs to keep consistently with the encoder options. type JSONReader struct { // Note(@kataras): struct instead of optional funcs to keep consistently with the encoder options.
// DisallowUnknownFields causes the json decoder to return an error when the destination // DisallowUnknownFields causes the json decoder to return an error when the destination
@ -2413,123 +2409,31 @@ type JSONReader struct { // Note(@kataras): struct instead of optional funcs to
// {"username": "makis"} // {"username": "makis"}
// {"username": "george"} // {"username": "george"}
ArrayStream bool ArrayStream bool
// Optional context cancelation of decoder when Optimize field is enabled.
// On ReadJSON method this is automatically binded to the request context.
Context stdContext.Context
} }
type internalJSONDecoder interface { var ReadJSON = func(ctx *Context, outPtr interface{}, opts ...JSONReader) error {
Token() (json.Token, error) // gojson.Token is an alias of this, so we are ok. decoder := json.NewDecoder(ctx.request.Body)
More() bool // decoder := gojson.NewDecoder(ctx.Request().Body)
DisallowUnknownFields() if len(opts) > 0 {
} options := opts[0]
type unmarshalerContext interface {
// UnmarshalJSON unmarshal json with context support.
UnmarshalJSON(stdContext.Context, []byte) error //lint:ignore stdmethods external pkg.
}
func wrapDecodeFunc(decodeFunc func(interface{}) error) DecodeFunc {
return func(_ stdContext.Context, outPtr interface{}) error {
return decodeFunc(outPtr)
}
}
func (options JSONReader) unmarshal(ctx stdContext.Context, body []byte, outPtr interface{}) error {
if options.Optimize {
if outPtr != nil {
if _, supportsContext := outPtr.(unmarshalerContext); !supportsContext {
return gojson.Unmarshal(body, outPtr)
}
}
return gojson.UnmarshalContext(ctx, body, outPtr)
}
return json.Unmarshal(body, outPtr)
}
func (options JSONReader) getDecoder(r io.Reader, outPtr interface{}) (internalJSONDecoder, DecodeFunc) {
var (
decoder internalJSONDecoder
decodeFunc DecodeFunc
)
if options.Optimize {
dec := gojson.NewDecoder(r)
if outPtr != nil {
// If a custom type does not implement the unnmarshal json with context interface
// that is REQUIRED by the gojson, then fallback to the normal gojson decode without context support,
// so we protect compatibility against existing objects.
if _, supportsContext := outPtr.(unmarshalerContext); supportsContext {
decodeFunc = dec.DecodeContext
} else {
decodeFunc = wrapDecodeFunc(dec.Decode)
}
} else {
decodeFunc = dec.DecodeContext
}
decoder = dec
} else {
dec := json.NewDecoder(r)
decodeFunc = wrapDecodeFunc(dec.Decode)
decoder = dec
}
if options.DisallowUnknownFields { if options.DisallowUnknownFields {
decoder.DisallowUnknownFields() decoder.DisallowUnknownFields()
} }
}
return decoder, decodeFunc if err := decoder.Decode(&outPtr); err != nil {
return err
}
return ctx.app.Validate(outPtr)
} }
// ReadJSON reads JSON from request's body and binds it to a value of any json-valid type. // ReadJSON reads JSON from request's body and binds it to a value of any json-valid type.
// //
// Example: https://github.com/kataras/iris/blob/master/_examples/request-body/read-json/main.go // Example: https://github.com/kataras/iris/blob/master/_examples/request-body/read-json/main.go
func (ctx *Context) ReadJSON(outPtr interface{}, opts ...JSONReader) error { func (ctx *Context) ReadJSON(outPtr interface{}, opts ...JSONReader) error {
var options JSONReader return ReadJSON(ctx, outPtr, opts...)
options.Optimize = ctx.shouldOptimize()
if len(opts) > 0 {
options = opts[0]
}
if ctx.IsRecordingBody() {
body, restoreBody, err := GetBody(ctx.request, true)
if err != nil {
return err
}
restoreBody()
err = options.unmarshal(ctx.request.Context(), body, outPtr)
if err != nil {
return err
}
} else {
_, decodeFunc := options.getDecoder(ctx.request.Body, outPtr)
err := decodeFunc(ctx.request.Context(), outPtr)
if err != nil {
return err
}
}
return ctx.app.Validate(outPtr)
/*
b, err := ctx.GetBody()
if err != nil {
return err
}
if options.Optimize {
return gojson.UnmarshalContext(ctx.request.Context(), b, outPtr)
} else {
return json.Unmarshal(b, outPtr)
}
*/
} }
// ReadJSONStream is an alternative of ReadJSON which can reduce the memory load // ReadJSONStream is an alternative of ReadJSON which can reduce the memory load
@ -2543,21 +2447,16 @@ func (ctx *Context) ReadJSON(outPtr interface{}, opts ...JSONReader) error {
// //
// Example: https://github.com/kataras/iris/blob/master/_examples/request-body/read-json-stream/main.go // Example: https://github.com/kataras/iris/blob/master/_examples/request-body/read-json-stream/main.go
func (ctx *Context) ReadJSONStream(onDecode func(DecodeFunc) error, opts ...JSONReader) error { func (ctx *Context) ReadJSONStream(onDecode func(DecodeFunc) error, opts ...JSONReader) error {
var options JSONReader decoder := json.NewDecoder(ctx.request.Body)
if len(opts) > 0 {
options = opts[0]
}
decoder, decodeFunc := options.getDecoder(ctx.request.Body, nil) if len(opts) > 0 && opts[0].ArrayStream {
if options.ArrayStream {
_, err := decoder.Token() // read open bracket. _, err := decoder.Token() // read open bracket.
if err != nil { if err != nil {
return err return err
} }
for decoder.More() { // hile the array contains values. for decoder.More() { // hile the array contains values.
if err = onDecode(decodeFunc); err != nil { if err = onDecode(decoder.Decode); err != nil {
return err return err
} }
} }
@ -2568,7 +2467,7 @@ func (ctx *Context) ReadJSONStream(onDecode func(DecodeFunc) error, opts ...JSON
// while the array contains values // while the array contains values
for decoder.More() { for decoder.More() {
if err := onDecode(decodeFunc); err != nil { if err := onDecode(decoder.Decode); err != nil {
return err return err
} }
} }
@ -2788,7 +2687,7 @@ func (ctx *Context) ReadMultipartRelated() (MultipartRelated, error) {
} }
defer part.Close() defer part.Close()
b, err := ioutil.ReadAll(part) b, err := io.ReadAll(part)
if err != nil { if err != nil {
return MultipartRelated{}, fmt.Errorf("multipart related: next part: read: %w", err) return MultipartRelated{}, fmt.Errorf("multipart related: next part: read: %w", err)
} }
@ -3149,6 +3048,7 @@ func (ctx *Context) SetLastModified(modtime time.Time) {
// that has to perform one or more client side preconditions before the actual check, e.g. `CheckIfModifiedSince`. // that has to perform one or more client side preconditions before the actual check, e.g. `CheckIfModifiedSince`.
// Usage: // Usage:
// ok, err := context.CheckIfModifiedSince(modTime) // ok, err := context.CheckIfModifiedSince(modTime)
//
// if err != nil { // if err != nil {
// if errors.Is(err, context.ErrPreconditionFailed) { // if errors.Is(err, context.ErrPreconditionFailed) {
// [handle missing client conditions,such as not valid request method...] // [handle missing client conditions,such as not valid request method...]
@ -3232,9 +3132,9 @@ func (ctx *Context) WriteWithExpiration(body []byte, modtime time.Time) (int, er
// //
// This function may be used in the following cases: // 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 too big (more than iris.LimitRequestBodySize(if set)).
// * if response body is streamed from slow external sources. // - if response body is streamed from slow external sources.
// * if response body must be streamed to the client in chunks. // - if response body must be streamed to the client in chunks.
// (aka `http server push`). // (aka `http server push`).
func (ctx *Context) StreamWriter(writer func(w io.Writer) error) error { func (ctx *Context) StreamWriter(writer func(w io.Writer) error) error {
cancelCtx := ctx.Request().Context() cancelCtx := ctx.Request().Context()
@ -3287,10 +3187,12 @@ func (ctx *Context) ClientSupportsEncoding(encodings ...string) bool {
// will change the response writer to a compress writer instead. // will change the response writer to a compress writer instead.
// All future write and rich write methods will respect this option. // All future write and rich write methods will respect this option.
// Usage: // Usage:
//
// app.Use(func(ctx iris.Context){ // app.Use(func(ctx iris.Context){
// err := ctx.CompressWriter(true) // err := ctx.CompressWriter(true)
// ctx.Next() // ctx.Next()
// }) // })
//
// The recommendation is to compress data as much as possible and therefore to use this field, // The recommendation is to compress data as much as possible and therefore to use this field,
// but some types of resources, such as jpeg images, are already compressed. // but some types of resources, such as jpeg images, are already compressed.
// Sometimes, using additional compression doesn't reduce payload size and // Sometimes, using additional compression doesn't reduce payload size and
@ -3350,11 +3252,14 @@ func (ctx *Context) CompressWriter(enable bool) error {
// All future calls of `ctx.GetBody/ReadXXX/UnmarshalBody` methods will respect this option. // All future calls of `ctx.GetBody/ReadXXX/UnmarshalBody` methods will respect this option.
// //
// Usage: // Usage:
//
// app.Use(func(ctx iris.Context){ // app.Use(func(ctx iris.Context){
// err := ctx.CompressReader(true) // err := ctx.CompressReader(true)
// ctx.Next() // ctx.Next()
// }) // })
//
// More: // More:
//
// if cr, ok := ctx.Request().Body.(*CompressReader); ok { // if cr, ok := ctx.Request().Body.(*CompressReader); ok {
// cr.Src // the original request body // cr.Src // the original request body
// cr.Encoding // the compression algorithm. // cr.Encoding // the compression algorithm.
@ -3592,6 +3497,7 @@ func (ctx *Context) fireFallbackViewOnce(err ErrViewNotExist) error {
// is responsible to handle the error or render a different view. // is responsible to handle the error or render a different view.
// //
// Usage: // Usage:
//
// FallbackView(iris.FallbackView("fallback.html")) // FallbackView(iris.FallbackView("fallback.html"))
// FallbackView(iris.FallbackViewLayout("layouts/fallback.html")) // FallbackView(iris.FallbackViewLayout("layouts/fallback.html"))
// OR // OR
@ -3749,8 +3655,6 @@ type ProtoMarshalOptions = protojson.MarshalOptions
// JSON contains the options for the JSON (Context's) Renderer. // JSON contains the options for the JSON (Context's) Renderer.
type JSON struct { type JSON struct {
// http-specific
StreamingJSON bool `yaml:"StreamingJSON"`
// content-specific // content-specific
UnescapeHTML bool `yaml:"UnescapeHTML"` UnescapeHTML bool `yaml:"UnescapeHTML"`
Indent string `yaml:"Indent"` Indent string `yaml:"Indent"`
@ -3772,8 +3676,7 @@ var DefaultJSONOptions = JSON{}
// IsDefault reports whether this JSON options structure holds the default values. // IsDefault reports whether this JSON options structure holds the default values.
func (j *JSON) IsDefault() bool { func (j *JSON) IsDefault() bool {
return j.StreamingJSON == DefaultJSONOptions.StreamingJSON && return j.UnescapeHTML == DefaultJSONOptions.UnescapeHTML &&
j.UnescapeHTML == DefaultJSONOptions.UnescapeHTML &&
j.Indent == DefaultJSONOptions.Indent && j.Indent == DefaultJSONOptions.Indent &&
j.Prefix == DefaultJSONOptions.Prefix && j.Prefix == DefaultJSONOptions.Prefix &&
j.ASCII == DefaultJSONOptions.ASCII && j.ASCII == DefaultJSONOptions.ASCII &&
@ -3858,28 +3761,24 @@ func (ctx *Context) handleSpecialJSONResponseValue(v interface{}, options *JSON)
} }
// WriteJSON marshals the given interface object and writes the JSON response to the 'writer'. // WriteJSON marshals the given interface object and writes the JSON response to the 'writer'.
func WriteJSON(ctx stdContext.Context, writer io.Writer, v interface{}, options *JSON, shouldOptimize bool) (int, error) { var WriteJSON = func(ctx *Context, v interface{}, options *JSON) error {
if options.StreamingJSON { if !options.Secure && !options.ASCII && options.Prefix == "" {
var err error
if shouldOptimize {
// jsoniterConfig := jsoniter.Config{ // jsoniterConfig := jsoniter.Config{
// EscapeHTML: !options.UnescapeHTML, // EscapeHTML: !options.UnescapeHTML,
// IndentionStep: 4, // IndentionStep: 4,
// }.Froze() // }.Froze()
// enc := jsoniterConfig.NewEncoder(ctx.writer) // enc := jsoniterConfig.NewEncoder(ctx.writer)
// err = enc.Encode(v) // err = enc.Encode(v)
enc := gojson.NewEncoder(writer) //
// enc := gojson.NewEncoder(ctx.writer)
// enc.SetEscapeHTML(!options.UnescapeHTML)
// enc.SetIndent(options.Prefix, options.Indent)
// err = enc.EncodeContext(ctx, v)
enc := json.NewEncoder(ctx.writer)
enc.SetEscapeHTML(!options.UnescapeHTML) enc.SetEscapeHTML(!options.UnescapeHTML)
enc.SetIndent(options.Prefix, options.Indent) enc.SetIndent(options.Prefix, options.Indent)
err = enc.EncodeContext(ctx, v)
} else {
enc := json.NewEncoder(writer)
enc.SetEscapeHTML(!options.UnescapeHTML)
enc.SetIndent(options.Prefix, options.Indent)
err = enc.Encode(v)
}
return 0, err return enc.Encode(v)
} }
var ( var (
@ -3887,35 +3786,15 @@ func WriteJSON(ctx stdContext.Context, writer io.Writer, v interface{}, options
err error err error
) )
// Let's keep it as it is.
// if !shouldOptimize && options.Indent == "" {
// options.Indent = " "
// }
if indent := options.Indent; indent != "" { if indent := options.Indent; indent != "" {
if shouldOptimize {
// result,err = jsoniter.ConfigCompatibleWithStandardLibrary.MarshalIndent
result, err = gojson.MarshalIndent(v, "", indent)
} else {
result, err = json.MarshalIndent(v, "", indent) result, err = json.MarshalIndent(v, "", indent)
}
result = append(result, newLineB...) result = append(result, newLineB...)
} else {
if shouldOptimize {
// result, err = jsoniter.ConfigCompatibleWithStandardLibrary.Marshal
if ctx != nil {
result, err = gojson.MarshalContext(ctx, v)
} else {
result, err = gojson.Marshal(v)
}
} else { } else {
result, err = json.Marshal(v) result, err = json.Marshal(v)
} }
}
if err != nil { if err != nil {
return 0, err return err
} }
prependSecure := false prependSecure := false
@ -3960,7 +3839,8 @@ func WriteJSON(ctx stdContext.Context, writer io.Writer, v interface{}, options
result = append(stringToBytes(prefix), result...) result = append(stringToBytes(prefix), result...)
} }
return writer.Write(result) _, err = ctx.Write(result)
return err
} }
// See https://golang.org/src/strings/builder.go#L45 // See https://golang.org/src/strings/builder.go#L45
@ -4007,25 +3887,27 @@ func (ctx *Context) handleContextError(err error) {
} }
// JSON marshals the given "v" value to JSON and writes the response to the client. // JSON marshals the given "v" value to JSON and writes the response to the client.
// Look the Configuration.EnableProtoJSON/EnableEasyJSON and EnableOptimizations too. // Look the Configuration.EnableProtoJSON and EnableEasyJSON too.
// //
// It reports any JSON parser or write errors back to the caller. // It reports any JSON parser or write errors back to the caller.
// Look the Application.SetContextErrorHandler to override the // Look the Application.SetContextErrorHandler to override the
// default status code 500 with a custom error response. // default status code 500 with a custom error response.
// //
// It can, optionally, accept the JSON structure which may hold customizations over the // Customize the behavior of every `Context.JSON“ can be achieved
// final JSON response but keep in mind that the caller should NOT modify that JSON options // by modifying the package-level `WriteJSON` function on program initilization.
// value in another goroutine while JSON method is still running. func (ctx *Context) JSON(v interface{}, opts ...JSON) (err error) {
func (ctx *Context) JSON(v interface{}, opts ...JSON) (n int, err error) {
var options *JSON var options *JSON
if len(opts) > 0 { if len(opts) > 0 {
options = &opts[0] options = &opts[0]
} else {
// If no options are given safely read the already-initialized value.
options = &DefaultJSONOptions
} }
if n, err = ctx.writeJSON(v, options); err != nil { if err = ctx.writeJSON(v, options); err != nil {
// if no options are given or OmitErrorHandler is true // if no options are given or OmitErrorHandler is true
// then do call the error handler (which may lead to a cycle). // then do call the error handler (which may lead to a cycle).
if options == nil || !options.OmitErrorHandler { if !options.OmitErrorHandler {
ctx.handleContextError(err) ctx.handleContextError(err)
} }
} }
@ -4033,76 +3915,38 @@ func (ctx *Context) JSON(v interface{}, opts ...JSON) (n int, err error) {
return return
} }
func (ctx *Context) writeJSON(v interface{}, options *JSON) (int, error) { func (ctx *Context) writeJSON(v interface{}, options *JSON) error {
ctx.ContentType(ContentJSONHeaderValue) ctx.ContentType(ContentJSONHeaderValue)
// After content type given and before everything else, try handle proto or easyjson, no matter the performance mode. // After content type given and before everything else, try handle proto or easyjson, no matter the performance mode.
if handled, n, err := ctx.handleSpecialJSONResponseValue(v, options); handled { if handled, _, err := ctx.handleSpecialJSONResponseValue(v, options); handled {
return n, err return err
} }
shouldOptimize := ctx.shouldOptimize() return WriteJSON(ctx, v, options)
if options == nil {
if shouldOptimize {
// If no options given and optimizations are enabled.
// write using the fast json marshaler with the http request context as soon as possible.
result, err := gojson.MarshalContext(ctx.request.Context(), v)
if err != nil {
return 0, err
}
return ctx.Write(result)
}
// Else if no options given neither optimizations are enabled, then safely read the already-initialized object.
options = &DefaultJSONOptions
}
return WriteJSON(ctx, ctx.writer, v, options, shouldOptimize)
} }
var finishCallbackB = []byte(");") var finishCallbackB = []byte(");")
// WriteJSONP marshals the given interface object and writes the JSON response to the writer. // WriteJSONP marshals the given interface object and writes the JSONP response to the writer.
func WriteJSONP(writer io.Writer, v interface{}, options JSONP, optimize bool) (int, error) { var WriteJSONP = func(ctx *Context, v interface{}, options *JSONP) (err error) {
if callback := options.Callback; callback != "" { if callback := options.Callback; callback != "" {
n, err := writer.Write(stringToBytes(callback + "(")) _, err = ctx.Write(stringToBytes(callback + "("))
if err != nil { if err != nil {
return n, err return err
} }
defer writer.Write(finishCallbackB) defer func() {
if err == nil {
ctx.Write(finishCallbackB)
}
}()
} }
if !optimize && options.Indent == "" { err = WriteJSON(ctx, v, &JSON{
options.Indent = " " Indent: options.Indent,
} OmitErrorHandler: options.OmitErrorHandler,
})
if indent := options.Indent; indent != "" { return err
marshalIndent := json.MarshalIndent
if optimize {
// marshalIndent = jsoniter.ConfigCompatibleWithStandardLibrary.MarshalIndent
marshalIndent = gojson.MarshalIndent
}
result, err := marshalIndent(v, "", indent)
if err != nil {
return 0, err
}
result = append(result, newLineB...)
return writer.Write(result)
}
marshal := json.Marshal
if optimize {
// marshal = jsoniter.ConfigCompatibleWithStandardLibrary.Marshal
marshal = gojson.Marshal
}
result, err := marshal(v)
if err != nil {
return 0, err
}
return writer.Write(result)
} }
// DefaultJSONPOptions is the optional settings that are being used // DefaultJSONPOptions is the optional settings that are being used
@ -4114,14 +3958,16 @@ var DefaultJSONPOptions = JSONP{}
// It reports any JSON parser or write errors back to the caller. // It reports any JSON parser or write errors back to the caller.
// Look the Application.SetContextErrorHandler to override the // Look the Application.SetContextErrorHandler to override the
// default status code 500 with a custom error response. // default status code 500 with a custom error response.
func (ctx *Context) JSONP(v interface{}, opts ...JSONP) (n int, err error) { func (ctx *Context) JSONP(v interface{}, opts ...JSONP) (err error) {
options := DefaultJSONPOptions var options *JSONP
if len(opts) > 0 { if len(opts) > 0 {
options = opts[0] options = &opts[0]
} else {
options = &DefaultJSONPOptions
} }
ctx.ContentType(ContentJavascriptHeaderValue) ctx.ContentType(ContentJavascriptHeaderValue)
if n, err = WriteJSONP(ctx.writer, v, options, ctx.shouldOptimize()); err != nil { if err = WriteJSONP(ctx, v, options); err != nil {
if !options.OmitErrorHandler { if !options.OmitErrorHandler {
ctx.handleContextError(err) ctx.handleContextError(err)
} }
@ -4174,32 +4020,21 @@ func (m xmlMap) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
} }
// WriteXML marshals the given interface object and writes the XML response to the writer. // WriteXML marshals the given interface object and writes the XML response to the writer.
func WriteXML(writer io.Writer, v interface{}, options XML, optimize bool) (int, error) { var WriteXML = func(ctx *Context, v interface{}, options *XML) error {
if prefix := options.Prefix; prefix != "" { if prefix := options.Prefix; prefix != "" {
n, err := writer.Write(stringToBytes(prefix)) _, err := ctx.Write(stringToBytes(prefix))
if err != nil { if err != nil {
return n, err return err
} }
} }
if !optimize && options.Indent == "" { encoder := xml.NewEncoder(ctx.writer)
options.Indent = " " // Two spaces for XML is the default indentation when not optimized. encoder.Indent("", options.Indent)
if err := encoder.Encode(v); err != nil {
return err
} }
if indent := options.Indent; indent != "" { return encoder.Flush()
result, err := xml.MarshalIndent(v, "", indent)
if err != nil {
return 0, err
}
result = append(result, newLineB...)
return writer.Write(result)
}
result, err := xml.Marshal(v)
if err != nil {
return 0, err
}
return writer.Write(result)
} }
// DefaultXMLOptions is the optional settings that are being used // DefaultXMLOptions is the optional settings that are being used
@ -4212,14 +4047,16 @@ var DefaultXMLOptions = XML{}
// It reports any XML parser or write errors back to the caller. // It reports any XML parser or write errors back to the caller.
// Look the Application.SetContextErrorHandler to override the // Look the Application.SetContextErrorHandler to override the
// default status code 500 with a custom error response. // default status code 500 with a custom error response.
func (ctx *Context) XML(v interface{}, opts ...XML) (n int, err error) { func (ctx *Context) XML(v interface{}, opts ...XML) (err error) {
options := DefaultXMLOptions var options *XML
if len(opts) > 0 { if len(opts) > 0 {
options = opts[0] options = &opts[0]
} else {
options = &DefaultXMLOptions
} }
ctx.ContentType(ContentXMLHeaderValue) ctx.ContentType(ContentXMLHeaderValue)
if n, err = WriteXML(ctx.writer, v, options, ctx.shouldOptimize()); err != nil { if err = WriteXML(ctx, v, options); err != nil {
if !options.OmitErrorHandler { if !options.OmitErrorHandler {
ctx.handleContextError(err) ctx.handleContextError(err)
} }
@ -4239,7 +4076,7 @@ func (ctx *Context) XML(v interface{}, opts ...XML) (n int, err error) {
// send a response of content type "application/problem+xml" instead. // send a response of content type "application/problem+xml" instead.
// //
// Read more at: https://github.com/kataras/iris/wiki/Routing-error-handlers // Read more at: https://github.com/kataras/iris/wiki/Routing-error-handlers
func (ctx *Context) Problem(v interface{}, opts ...ProblemOptions) (int, error) { func (ctx *Context) Problem(v interface{}, opts ...ProblemOptions) error {
options := DefaultProblemOptions options := DefaultProblemOptions
if len(opts) > 0 { if len(opts) > 0 {
options = opts[0] options = opts[0]
@ -4276,13 +4113,14 @@ func (ctx *Context) Problem(v interface{}, opts ...ProblemOptions) (int, error)
} }
// WriteMarkdown parses the markdown to html and writes these contents to the writer. // WriteMarkdown parses the markdown to html and writes these contents to the writer.
func WriteMarkdown(writer io.Writer, markdownB []byte, options Markdown) (n int, err error) { var WriteMarkdown = func(ctx *Context, markdownB []byte, options *Markdown) error {
buf := blackfriday.Run(markdownB) buf := blackfriday.Run(markdownB)
if options.Sanitize { if options.Sanitize {
buf = bluemonday.UGCPolicy().SanitizeBytes(buf) buf = bluemonday.UGCPolicy().SanitizeBytes(buf)
} }
return writer.Write(buf) _, err := ctx.Write(buf)
return err
} }
// DefaultMarkdownOptions is the optional settings that are being used // DefaultMarkdownOptions is the optional settings that are being used
@ -4294,14 +4132,16 @@ var DefaultMarkdownOptions = Markdown{}
// It reports any Markdown parser or write errors back to the caller. // It reports any Markdown parser or write errors back to the caller.
// Look the Application.SetContextErrorHandler to override the // Look the Application.SetContextErrorHandler to override the
// default status code 500 with a custom error response. // default status code 500 with a custom error response.
func (ctx *Context) Markdown(markdownB []byte, opts ...Markdown) (n int, err error) { func (ctx *Context) Markdown(markdownB []byte, opts ...Markdown) (err error) {
options := DefaultMarkdownOptions var options *Markdown
if len(opts) > 0 { if len(opts) > 0 {
options = opts[0] options = &opts[0]
} else {
options = &DefaultMarkdownOptions
} }
ctx.ContentType(ContentHTMLHeaderValue) ctx.ContentType(ContentHTMLHeaderValue)
if n, err = WriteMarkdown(ctx.writer, markdownB, options); err != nil { if err = WriteMarkdown(ctx, markdownB, options); err != nil {
if !options.OmitErrorHandler { if !options.OmitErrorHandler {
ctx.handleContextError(err) ctx.handleContextError(err)
} }
@ -4310,31 +4150,46 @@ func (ctx *Context) Markdown(markdownB []byte, opts ...Markdown) (n int, err err
return return
} }
// WriteYAML sends YAML response to the client.
var WriteYAML = func(ctx *Context, v interface{}, indentSpace int) error {
encoder := yaml.NewEncoder(ctx.writer)
encoder.SetIndent(indentSpace)
if err := encoder.Encode(v); err != nil {
return err
}
return encoder.Close()
}
// YAML marshals the given "v" value using the yaml marshaler and writes the result to the client. // YAML marshals the given "v" value using the yaml marshaler and writes the result to the client.
// //
// It reports any YAML parser or write errors back to the caller. // It reports any YAML parser or write errors back to the caller.
// Look the Application.SetContextErrorHandler to override the // Look the Application.SetContextErrorHandler to override the
// default status code 500 with a custom error response. // default status code 500 with a custom error response.
func (ctx *Context) YAML(v interface{}) (int, error) { func (ctx *Context) YAML(v interface{}) error {
out, err := yaml.Marshal(v)
if err != nil {
ctx.handleContextError(err)
return 0, err
}
ctx.ContentType(ContentYAMLHeaderValue) ctx.ContentType(ContentYAMLHeaderValue)
n, err := ctx.Write(out)
err := WriteYAML(ctx, v, 0)
if err != nil { if err != nil {
ctx.handleContextError(err) ctx.handleContextError(err)
return err
} }
return n, err return nil
} }
// TextYAML calls the Context.YAML method but with the text/yaml content type instead. // TextYAML calls the Context.YAML method but with the text/yaml content type instead.
func (ctx *Context) TextYAML(v interface{}) (int, error) { func (ctx *Context) TextYAML(v interface{}) error {
ctx.contentTypeOnce(ContentYAMLTextHeaderValue, "") ctx.ContentType(ContentYAMLTextHeaderValue)
return ctx.YAML(v)
err := WriteYAML(ctx, v, 4)
if err != nil {
ctx.handleContextError(err)
return err
}
return nil
} }
// Protobuf marshals the given "v" value of proto Message and writes its result to the client. // Protobuf marshals the given "v" value of proto Message and writes its result to the client.
@ -4583,19 +4438,54 @@ func (ctx *Context) Negotiate(v interface{}) (int, error) {
case ContentTextHeaderValue, ContentHTMLHeaderValue: case ContentTextHeaderValue, ContentHTMLHeaderValue:
return ctx.WriteString(v.(string)) return ctx.WriteString(v.(string))
case ContentMarkdownHeaderValue: case ContentMarkdownHeaderValue:
return ctx.Markdown(v.([]byte)) err := ctx.Markdown(v.([]byte))
if err != nil {
return 0, err
}
return ctx.writer.Written(), nil
case ContentJSONHeaderValue: case ContentJSONHeaderValue:
return ctx.JSON(v) err := ctx.JSON(v)
if err != nil {
return 0, err
}
return ctx.writer.Written(), nil
case ContentJSONProblemHeaderValue, ContentXMLProblemHeaderValue: case ContentJSONProblemHeaderValue, ContentXMLProblemHeaderValue:
return ctx.Problem(v) err := ctx.Problem(v)
if err != nil {
return 0, err
}
return ctx.writer.Written(), nil
case ContentJavascriptHeaderValue: case ContentJavascriptHeaderValue:
return ctx.JSONP(v) err := ctx.JSONP(v)
if err != nil {
return 0, err
}
return ctx.writer.Written(), nil
case ContentXMLHeaderValue, ContentXMLUnreadableHeaderValue: case ContentXMLHeaderValue, ContentXMLUnreadableHeaderValue:
return ctx.XML(v) err := ctx.XML(v)
if err != nil {
return 0, err
}
return ctx.writer.Written(), nil
case ContentYAMLHeaderValue: case ContentYAMLHeaderValue:
return ctx.YAML(v) err := ctx.YAML(v)
if err != nil {
return 0, err
}
return ctx.writer.Written(), nil
case ContentYAMLTextHeaderValue: case ContentYAMLTextHeaderValue:
return ctx.TextYAML(v) err := ctx.TextYAML(v)
if err != nil {
return 0, err
}
return ctx.writer.Written(), nil
case ContentProtobufHeaderValue: case ContentProtobufHeaderValue:
msg, ok := v.(proto.Message) msg, ok := v.(proto.Message)
if !ok { if !ok {
@ -5438,15 +5328,15 @@ const cookieOptionsContextKey = "iris.cookie.options"
// cookies sent or received from the next Handler in the chain. // cookies sent or received from the next Handler in the chain.
// //
// Available builtin Cookie options are: // Available builtin Cookie options are:
// * CookieAllowReclaim // - CookieAllowReclaim
// * CookieAllowSubdomains // - CookieAllowSubdomains
// * CookieSecure // - CookieSecure
// * CookieHTTPOnly // - CookieHTTPOnly
// * CookieSameSite // - CookieSameSite
// * CookiePath // - CookiePath
// * CookieCleanPath // - CookieCleanPath
// * CookieExpires // - CookieExpires
// * CookieEncoding // - CookieEncoding
// //
// Example at: https://github.com/kataras/iris/tree/master/_examples/cookies/securecookie // Example at: https://github.com/kataras/iris/tree/master/_examples/cookies/securecookie
func (ctx *Context) AddCookieOptions(options ...CookieOption) { func (ctx *Context) AddCookieOptions(options ...CookieOption) {
@ -5554,6 +5444,7 @@ var SetCookieKVExpiration = 8760 * time.Hour
// (note that client should be responsible for that if server sent an empty cookie's path, all browsers are compatible) // (note that client should be responsible for that if server sent an empty cookie's path, all browsers are compatible)
// ctx.SetCookieKV(name, value, iris.CookieCleanPath/iris.CookiePath("")) // ctx.SetCookieKV(name, value, iris.CookieCleanPath/iris.CookiePath(""))
// More: // More:
//
// iris.CookieExpires(time.Duration) // iris.CookieExpires(time.Duration)
// iris.CookieHTTPOnly(false) // iris.CookieHTTPOnly(false)
// //

View File

@ -166,10 +166,12 @@ func (u *SimpleUser) GetField(key string) (interface{}, error) {
// UserMap can be used to convert a common map[string]interface{} to a User. // UserMap can be used to convert a common map[string]interface{} to a User.
// Usage: // Usage:
//
// user := map[string]interface{}{ // user := map[string]interface{}{
// "username": "kataras", // "username": "kataras",
// "age" : 27, // "age" : 27,
// } // }
//
// ctx.SetUser(user) // ctx.SetUser(user)
// OR // OR
// user := UserStruct{....} // user := UserStruct{....}

View File

@ -275,7 +275,6 @@ type Filter func(*Context) bool
// Handlers here should act like middleware, they should contain `ctx.Next` to proceed // Handlers here should act like middleware, they should contain `ctx.Next` to proceed
// to the next handler of the chain. Those "handlers" are registered to the per-request context. // to the next handler of the chain. Those "handlers" are registered to the per-request context.
// //
//
// It checks the "filter" and if passed then // It checks the "filter" and if passed then
// it, correctly, executes the "handlers". // it, correctly, executes the "handlers".
// //

View File

@ -131,6 +131,7 @@ func (r *RequestParams) GetIntUnslashed(key string) (int, bool) {
// Key is the specific type, which should be unique. // Key is the specific type, which should be unique.
// The value is a function which accepts the parameter index // The value is a function which accepts the parameter index
// and it should return the value as the parameter type evaluator expects it. // and it should return the value as the parameter type evaluator expects it.
//
// i.e [reflect.TypeOf("string")] = func(paramIndex int) interface{} { // i.e [reflect.TypeOf("string")] = func(paramIndex int) interface{} {
// return func(ctx *Context) <T> { // return func(ctx *Context) <T> {
// return ctx.Params().GetEntryAt(paramIndex).ValueRaw.(<T>) // return ctx.Params().GetEntryAt(paramIndex).ValueRaw.(<T>)
@ -261,7 +262,9 @@ var ParamResolvers = map[reflect.Type]func(paramIndex int) interface{}{
// and the parameter's index based on the registered path. // and the parameter's index based on the registered path.
// Usage: nameResolver := ParamResolverByKindAndKey(reflect.TypeOf(""), 0) // Usage: nameResolver := ParamResolverByKindAndKey(reflect.TypeOf(""), 0)
// Inside a Handler: nameResolver.Call(ctx)[0] // Inside a Handler: nameResolver.Call(ctx)[0]
//
// it will return the reflect.Value Of the exact type of the parameter(based on the path parameters and macros). // it will return the reflect.Value Of the exact type of the parameter(based on the path parameters and macros).
//
// It is only useful for dynamic binding of the parameter, it is used on "hero" package and it should be modified // It is only useful for dynamic binding of the parameter, it is used on "hero" package and it should be modified
// only when Macros are modified in such way that the default selections for the available go std types are not enough. // only when Macros are modified in such way that the default selections for the available go std types are not enough.
// //

View File

@ -4,7 +4,7 @@ import (
"bytes" "bytes"
"errors" "errors"
"fmt" "fmt"
"io/ioutil" "io"
"net/http" "net/http"
"net/textproto" "net/textproto"
"strconv" "strconv"
@ -32,7 +32,7 @@ func releaseResponseRecorder(w *ResponseRecorder) {
// A ResponseRecorder is used mostly for testing // A ResponseRecorder is used mostly for testing
// in order to record and modify, if necessary, the body and status code and headers. // in order to record and modify, if necessary, the body and status code and headers.
// //
// See `context.Recorder`` method too. // See `context.Recorder method too.
type ResponseRecorder struct { type ResponseRecorder struct {
ResponseWriter ResponseWriter
@ -366,7 +366,7 @@ func (w *ResponseRecorder) Result() *http.Response { // a modified copy of net/h
} }
res.Status = fmt.Sprintf("%03d %s", res.StatusCode, http.StatusText(res.StatusCode)) res.Status = fmt.Sprintf("%03d %s", res.StatusCode, http.StatusText(res.StatusCode))
if w.chunks != nil { if w.chunks != nil {
res.Body = ioutil.NopCloser(bytes.NewReader(w.chunks)) res.Body = io.NopCloser(bytes.NewReader(w.chunks))
} else { } else {
res.Body = http.NoBody res.Body = http.NoBody
} }

View File

@ -111,7 +111,6 @@ func StatusText(code int) string {
// Read more at `iris/Configuration#DisableAutoFireStatusCode` and // Read more at `iris/Configuration#DisableAutoFireStatusCode` and
// `iris/core/router/Party#OnAnyErrorCode` for relative information. // `iris/core/router/Party#OnAnyErrorCode` for relative information.
// //
//
// Modify this variable when your Iris server or/and client // Modify this variable when your Iris server or/and client
// not follows the RFC: https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html // not follows the RFC: https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
var StatusCodeNotSuccessful = func(statusCode int) bool { return statusCode >= 400 } var StatusCodeNotSuccessful = func(statusCode int) bool { return statusCode >= 400 }

View File

@ -10,6 +10,7 @@ import (
// FromStd converts native http.Handler & http.HandlerFunc to context.Handler. // FromStd converts native http.Handler & http.HandlerFunc to context.Handler.
// //
// Supported form types: // Supported form types:
//
// .FromStd(h http.Handler) // .FromStd(h http.Handler)
// .FromStd(func(w http.ResponseWriter, r *http.Request)) // .FromStd(func(w http.ResponseWriter, r *http.Request))
// .FromStd(func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc)) // .FromStd(func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc))

View File

@ -730,7 +730,6 @@ func (e Entry) Value() interface{} {
// Save same as `Set` // Save same as `Set`
// However, if "immutable" is true then saves it as immutable (same as `SetImmutable`). // However, if "immutable" is true then saves it as immutable (same as `SetImmutable`).
// //
//
// Returns the entry and true if it was just inserted, meaning that // Returns the entry and true if it was just inserted, meaning that
// it will return the entry and a false boolean if the entry exists and it has been updated. // it will return the entry and a false boolean if the entry exists and it has been updated.
func (r *Store) Save(key string, value interface{}, immutable bool) (Entry, bool) { func (r *Store) Save(key string, value interface{}, immutable bool) (Entry, bool) {

View File

@ -377,6 +377,7 @@ func (api *APIBuilder) RegisterDependency(dependencies ...interface{}) {
// (see ConfigureContainer().RegisterDependency) // (see ConfigureContainer().RegisterDependency)
// or leave the framework to parse the request and fill the values accordingly. // or leave the framework to parse the request and fill the values accordingly.
// The output of the "handlersFn" can be any output result: // The output of the "handlersFn" can be any output result:
//
// custom structs <T>, string, []byte, int, error, // custom structs <T>, string, []byte, int, error,
// a combination of the above, hero.Result(hero.View | hero.Response) and more. // a combination of the above, hero.Result(hero.View | hero.Response) and more.
// //
@ -474,6 +475,7 @@ func (api *APIBuilder) AllowMethods(methods ...string) Party {
// For example, if for some reason the desired result is the (done or all) handlers to be executed no matter what // For example, if for some reason the desired result is the (done or all) handlers to be executed no matter what
// even if no `ctx.Next()` is called in the previous handlers, including the begin(`Use`), // even if no `ctx.Next()` is called in the previous handlers, including the begin(`Use`),
// the main(`Handle`) and the done(`Done`) handlers themselves, then: // the main(`Handle`) and the done(`Done`) handlers themselves, then:
//
// Party#SetExecutionRules(iris.ExecutionRules { // Party#SetExecutionRules(iris.ExecutionRules {
// Begin: iris.ExecutionOptions{Force: true}, // Begin: iris.ExecutionOptions{Force: true},
// Main: iris.ExecutionOptions{Force: true}, // Main: iris.ExecutionOptions{Force: true},
@ -567,8 +569,11 @@ func (api *APIBuilder) handle(errorCode int, method string, relativePath string,
// otherwise use `Party` which can handle many paths with different handlers and middlewares. // otherwise use `Party` which can handle many paths with different handlers and middlewares.
// //
// Usage: // Usage:
//
// app.HandleMany("GET", "/user /user/{id:uint64} /user/me", genericUserHandler) // app.HandleMany("GET", "/user /user/{id:uint64} /user/me", genericUserHandler)
//
// At the other side, with `Handle` we've had to write: // At the other side, with `Handle` we've had to write:
//
// app.Handle("GET", "/user", userHandler) // app.Handle("GET", "/user", userHandler)
// app.Handle("GET", "/user/{id:uint64}", userByIDHandler) // app.Handle("GET", "/user/{id:uint64}", userByIDHandler)
// app.Handle("GET", "/user/me", userMeHandler) // app.Handle("GET", "/user/me", userMeHandler)
@ -904,6 +909,7 @@ func (api *APIBuilder) Party(relativePath string, handlers ...context.Handler) P
// Note: `iris#Party` and `core/router#Party` describes the exactly same interface. // Note: `iris#Party` and `core/router#Party` describes the exactly same interface.
// //
// Usage: // Usage:
//
// app.PartyFunc("/users", func(u iris.Party){ // app.PartyFunc("/users", func(u iris.Party){
// u.Use(authMiddleware, logMiddleware) // u.Use(authMiddleware, logMiddleware)
// u.Get("/", getAllUsers) // u.Get("/", getAllUsers)
@ -949,14 +955,19 @@ type (
// Useful when the api's dependencies amount are too much to pass on a function. // Useful when the api's dependencies amount are too much to pass on a function.
// //
// Usage: // Usage:
//
// app.PartyConfigure("/users", &api.UsersAPI{UserRepository: ..., ...}) // app.PartyConfigure("/users", &api.UsersAPI{UserRepository: ..., ...})
//
// Where UsersAPI looks like: // Where UsersAPI looks like:
//
// type UsersAPI struct { [...] } // type UsersAPI struct { [...] }
// func(api *UsersAPI) Configure(router iris.Party) { // func(api *UsersAPI) Configure(router iris.Party) {
// router.Get("/{id:uuid}", api.getUser) // router.Get("/{id:uuid}", api.getUser)
// [...] // [...]
// } // }
//
// Usage with (static) dependencies: // Usage with (static) dependencies:
//
// app.RegisterDependency(userRepo, ...) // app.RegisterDependency(userRepo, ...)
// app.PartyConfigure("/users", new(api.UsersAPI)) // app.PartyConfigure("/users", new(api.UsersAPI))
func (api *APIBuilder) PartyConfigure(relativePath string, partyReg ...PartyConfigurator) Party { func (api *APIBuilder) PartyConfigure(relativePath string, partyReg ...PartyConfigurator) Party {
@ -1626,6 +1637,7 @@ func (api *APIBuilder) RegisterView(viewEngine context.ViewEngine) {
// FallbackView registers one or more fallback views for a template or a template layout. // FallbackView registers one or more fallback views for a template or a template layout.
// Usage: // Usage:
//
// FallbackView(iris.FallbackView("fallback.html")) // FallbackView(iris.FallbackView("fallback.html"))
// FallbackView(iris.FallbackViewLayout("layouts/fallback.html")) // FallbackView(iris.FallbackViewLayout("layouts/fallback.html"))
// OR // OR
@ -1653,6 +1665,7 @@ func (api *APIBuilder) FallbackView(provider context.FallbackViewProvider) {
// app := iris.New() // app := iris.New()
// app.RegisterView(iris.$VIEW_ENGINE("./views", ".$extension")) // app.RegisterView(iris.$VIEW_ENGINE("./views", ".$extension"))
// my := app.Party("/my").Layout("layouts/mylayout.html") // my := app.Party("/my").Layout("layouts/mylayout.html")
//
// my.Get("/", func(ctx iris.Context) { // my.Get("/", func(ctx iris.Context) {
// ctx.View("page1.html") // ctx.View("page1.html")
// }) // })

View File

@ -12,7 +12,6 @@ import (
"github.com/kataras/golog" "github.com/kataras/golog"
) )
//
// randStringBytesMaskImprSrc helps us to generate random paths for the test, // randStringBytesMaskImprSrc helps us to generate random paths for the test,
// the below piece of code is external, as an answer to a stackoverflow question. // the below piece of code is external, as an answer to a stackoverflow question.
// //

View File

@ -7,7 +7,6 @@ import (
"html" "html"
"html/template" "html/template"
"io" "io"
"io/ioutil"
"net/http" "net/http"
"net/url" "net/url"
"os" "os"
@ -1041,7 +1040,7 @@ func cacheFiles(ctx stdContext.Context, fs http.FileSystem, names []string, comp
fi := newFileInfo(path.Base(name), inf.Mode(), inf.ModTime()) fi := newFileInfo(path.Base(name), inf.Mode(), inf.ModTime())
contents, err := ioutil.ReadAll(f) contents, err := io.ReadAll(f)
f.Close() f.Close()
if err != nil { if err != nil {
return err return err

View File

@ -6,6 +6,7 @@ import (
// ExecutionRules gives control to the execution of the route handlers outside of the handlers themselves. // ExecutionRules gives control to the execution of the route handlers outside of the handlers themselves.
// Usage: // Usage:
//
// Party#SetExecutionRules(ExecutionRules { // Party#SetExecutionRules(ExecutionRules {
// Done: ExecutionOptions{Force: true}, // Done: ExecutionOptions{Force: true},
// }) // })

View File

@ -491,8 +491,9 @@ func (r *Route) GetTitle() string {
// Should be called after `Build` state. // Should be called after `Build` state.
// //
// It prints the @method: @path (@description) (@route_rel_location) // It prints the @method: @path (@description) (@route_rel_location)
// * @handler_name (@handler_rel_location) // - @handler_name (@handler_rel_location)
// * @second_handler ... // - @second_handler ...
//
// If route and handler line:number locations are equal then the second is ignored. // If route and handler line:number locations are equal then the second is ignored.
func (r *Route) Trace(w io.Writer, stoppedIndex int) { func (r *Route) Trace(w io.Writer, stoppedIndex int) {
title := r.GetTitle() title := r.GetTitle()

7
doc.go
View File

@ -36,11 +36,11 @@ Source code and other details for the project are available at GitHub:
https://github.com/kataras/iris https://github.com/kataras/iris
Current Version # Current Version
12.2.0-beta3 12.2.0-beta4
Installation # Installation
The only requirement is the Go Programming Language, at least version 1.18. The only requirement is the Go Programming Language, at least version 1.18.
@ -62,6 +62,5 @@ Middleware:
Home Page: Home Page:
https://iris-go.com https://iris-go.com
*/ */
package iris package iris

1
go.mod
View File

@ -15,7 +15,6 @@ require (
github.com/fatih/structs v1.1.0 github.com/fatih/structs v1.1.0
github.com/flosch/pongo2/v4 v4.0.2 github.com/flosch/pongo2/v4 v4.0.2
github.com/go-redis/redis/v8 v8.11.5 github.com/go-redis/redis/v8 v8.11.5
github.com/goccy/go-json v0.9.8-0.20220506185958-23bd66f4c0d5
github.com/golang/snappy v0.0.4 github.com/golang/snappy v0.0.4
github.com/google/uuid v1.3.0 github.com/google/uuid v1.3.0
github.com/gorilla/securecookie v1.1.1 github.com/gorilla/securecookie v1.1.1

2
go.sum generated
View File

@ -62,8 +62,6 @@ github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.1.0 h1:7RFti/xnNkMJnrK7D1yQ/iCIB5OrrY/54/H930kIbHA= github.com/gobwas/ws v1.1.0 h1:7RFti/xnNkMJnrK7D1yQ/iCIB5OrrY/54/H930kIbHA=
github.com/gobwas/ws v1.1.0/go.mod h1:nzvNcVha5eUziGrbxFCo6qFIojQHjJV5cLYIbezhfL0= github.com/gobwas/ws v1.1.0/go.mod h1:nzvNcVha5eUziGrbxFCo6qFIojQHjJV5cLYIbezhfL0=
github.com/goccy/go-json v0.9.8-0.20220506185958-23bd66f4c0d5 h1:aeyOtISssR4sP36FAC9LV96PQqxzcbhz54EWv9U+ZGc=
github.com/goccy/go-json v0.9.8-0.20220506185958-23bd66f4c0d5/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=

View File

@ -327,22 +327,27 @@ func Handler(fn interface{}) context.Handler {
// as middleware or as simple route handler or subdomain's handler. // as middleware or as simple route handler or subdomain's handler.
// //
// func(...<T>) iris.Handler // func(...<T>) iris.Handler
//
// - if <T> are all static dependencies then // - if <T> are all static dependencies then
// there is no reflection involved at serve-time. // there is no reflection involved at serve-time.
// //
// func(pathParameter string, ...<T>) // func(pathParameter string, ...<T>)
//
// - one or more path parameters (e.g. :uid, :string, :int, :path, :uint64) // - one or more path parameters (e.g. :uid, :string, :int, :path, :uint64)
// are automatically binded to the first input Go standard types (string, int, uint64 and e.t.c.) // are automatically binded to the first input Go standard types (string, int, uint64 and e.t.c.)
// //
// func(<T>) error // func(<T>) error
//
// - if a function returns an error then this error's text is sent to the client automatically. // - if a function returns an error then this error's text is sent to the client automatically.
// //
// func(<T>) <R> // func(<T>) <R>
//
// - The result of the function is a dependency too. // - The result of the function is a dependency too.
// If <R> is a request-scope dependency (dynamic) then // If <R> is a request-scope dependency (dynamic) then
// this function will be called at every request. // this function will be called at every request.
// //
// func(<T>) <R> // func(<T>) <R>
//
// - If <R> is static dependency (e.g. a database or a service) then its result // - If <R> is static dependency (e.g. a database or a service) then its result
// can be used as a static dependency to the next dependencies or to the controller/function itself. // can be used as a static dependency to the next dependencies or to the controller/function itself.
func (c *Container) Handler(fn interface{}) context.Handler { func (c *Container) Handler(fn interface{}) context.Handler {

View File

@ -27,11 +27,9 @@ func defaultResultHandler(ctx *context.Context, v interface{}) error {
switch context.TrimHeaderValue(ctx.GetContentType()) { switch context.TrimHeaderValue(ctx.GetContentType()) {
case context.ContentXMLHeaderValue, context.ContentXMLUnreadableHeaderValue: case context.ContentXMLHeaderValue, context.ContentXMLUnreadableHeaderValue:
_, err := ctx.XML(v) return ctx.XML(v)
return err
case context.ContentYAMLHeaderValue: case context.ContentYAMLHeaderValue:
_, err := ctx.YAML(v) return ctx.YAML(v)
return err
case context.ContentProtobufHeaderValue: case context.ContentProtobufHeaderValue:
msg, ok := v.(proto.Message) msg, ok := v.(proto.Message)
if !ok { if !ok {
@ -45,8 +43,7 @@ func defaultResultHandler(ctx *context.Context, v interface{}) error {
return err return err
default: default:
// otherwise default to JSON. // otherwise default to JSON.
_, err := ctx.JSON(v) return ctx.JSON(v)
return err
} }
} }

View File

@ -92,7 +92,7 @@ func (e err) Dispatch(ctx iris.Context) {
// write the status code based on the err's StatusCode. // write the status code based on the err's StatusCode.
ctx.StatusCode(e.Status) ctx.StatusCode(e.Status)
// send to the client the whole object as json // send to the client the whole object as json
_, _ = ctx.JSON(e) _ = ctx.JSON(e)
} }
func GetCustomErrorAsDispatcher() err { func GetCustomErrorAsDispatcher() err {

View File

@ -98,8 +98,11 @@ func DefaultConfiguration() *Configuration {
// New Prepares and returns a new test framework based on the "app". // New Prepares and returns a new test framework based on the "app".
// Usage: // Usage:
//
// httptest.New(t, app) // httptest.New(t, app)
//
// With options: // With options:
//
// httptest.New(t, app, httptest.URL(...), httptest.Debug(true), httptest.LogLevel("debug"), httptest.Strict(true)) // httptest.New(t, app, httptest.URL(...), httptest.Debug(true), httptest.LogLevel("debug"), httptest.Strict(true))
// //
// Example at: https://github.com/kataras/iris/tree/master/_examples/testing/httptest. // Example at: https://github.com/kataras/iris/tree/master/_examples/testing/httptest.

View File

@ -152,7 +152,7 @@ func (loc *Locale) Tag() *language.Tag {
} }
// Language should return the exact languagecode of this `Locale` // Language should return the exact languagecode of this `Locale`
//that the user provided on `New` function. // that the user provided on `New` function.
// //
// Same as `Tag().String()` but it's static. // Same as `Tag().String()` but it's static.
func (loc *Locale) Language() string { func (loc *Locale) Language() string {

View File

@ -3,7 +3,7 @@ package i18n
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "os"
"path/filepath" "path/filepath"
"strings" "strings"
@ -33,7 +33,7 @@ func Glob(globPattern string, options LoaderConfig) Loader {
panic(err) panic(err)
} }
return load(assetNames, ioutil.ReadFile, options) return load(assetNames, os.ReadFile, options)
} }
// Assets accepts a function that returns a list of filenames (physical or virtual), // Assets accepts a function that returns a list of filenames (physical or virtual),

View File

@ -299,6 +299,7 @@ func (app *Application) ConfigurationReadOnly() context.ConfigurationReadOnly {
// Adding one or more outputs : app.Logger().AddOutput(io.Writer...) // Adding one or more outputs : app.Logger().AddOutput(io.Writer...)
// //
// Adding custom levels requires import of the `github.com/kataras/golog` package: // Adding custom levels requires import of the `github.com/kataras/golog` package:
//
// First we create our level to a golog.Level // First we create our level to a golog.Level
// in order to be used in the Log functions. // in order to be used in the Log functions.
// var SuccessLevel golog.Level = 6 // var SuccessLevel golog.Level = 6
@ -309,6 +310,7 @@ func (app *Application) ConfigurationReadOnly() context.ConfigurationReadOnly {
// // ColorfulText (Green Color[SUCC]) // // ColorfulText (Green Color[SUCC])
// ColorfulText: "\x1b[32m[SUCC]\x1b[0m", // ColorfulText: "\x1b[32m[SUCC]\x1b[0m",
// } // }
//
// Usage: // Usage:
// app.Logger().SetLevel("success") // app.Logger().SetLevel("success")
// app.Logger().Logf(SuccessLevel, "a custom leveled log message") // app.Logger().Logf(SuccessLevel, "a custom leveled log message")

View File

@ -237,7 +237,7 @@ type (
Step3 interface { Step3 interface {
// Health enables the /health route. // Health enables the /health route.
// If "env" and "developer" are given, these fields will be populated to the client // If "env" and "developer" are given, these fields will be populated to the client
// through headers and environemnt on health route. // through headers and environment on health route.
Health(b bool, env, developer string) Step4 Health(b bool, env, developer string) Step4
} }

View File

@ -18,7 +18,9 @@ import (
// Note that the builtin macros return error too, but they're handled // Note that the builtin macros return error too, but they're handled
// by the `else` literal (error code). To change this behavior // by the `else` literal (error code). To change this behavior
// and send a custom error response you have to register it: // and send a custom error response you have to register it:
//
// app.Macros().Get("uuid").HandleError(func(ctx iris.Context, paramIndex int, err error)). // app.Macros().Get("uuid").HandleError(func(ctx iris.Context, paramIndex int, err error)).
//
// You can also set custom macros by `app.Macros().Register`. // You can also set custom macros by `app.Macros().Register`.
// //
// See macro.HandleError to set it. // See macro.HandleError to set it.

View File

@ -4,7 +4,6 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"strings" "strings"
@ -324,7 +323,7 @@ func BenchmarkAccessLogAfterPrint(b *testing.B) {
} }
func benchmarkAccessLogAfter(b *testing.B, withLogStruct, async bool) { func benchmarkAccessLogAfter(b *testing.B, withLogStruct, async bool) {
ac := New(ioutil.Discard) ac := New(io.Discard)
ac.Clock = TClock(time.Time{}) ac.Clock = TClock(time.Time{})
ac.BytesReceived = false ac.BytesReceived = false
ac.BytesReceivedBody = false ac.BytesReceivedBody = false

View File

@ -11,6 +11,7 @@ import (
) )
// Log represents the log data specifically for the accesslog middleware. // Log represents the log data specifically for the accesslog middleware.
//
//easyjson:json //easyjson:json
type Log struct { type Log struct {
// The AccessLog instance this Log was created of. // The AccessLog instance this Log was created of.

View File

@ -52,6 +52,7 @@ type ErrorHandler func(ctx *context.Context, err error)
// The only required value is the Allow field. // The only required value is the Allow field.
// //
// Usage: // Usage:
//
// opts := Options { ... } // opts := Options { ... }
// auth := New(opts) // auth := New(opts)
type Options struct { type Options struct {
@ -171,6 +172,7 @@ type BasicAuth struct {
// The result should be used to wrap an existing handler or the HTTP application's root router. // The result should be used to wrap an existing handler or the HTTP application's root router.
// //
// Example Code: // Example Code:
//
// opts := basicauth.Options{ // opts := basicauth.Options{
// Realm: basicauth.DefaultRealm, // Realm: basicauth.DefaultRealm,
// ErrorHandler: basicauth.DefaultErrorHandler, // ErrorHandler: basicauth.DefaultErrorHandler,
@ -238,12 +240,14 @@ func New(opts Options) context.Handler {
// are required as they are compared against the user input // are required as they are compared against the user input
// when access to protected resource is requested. // when access to protected resource is requested.
// A user list can defined with one of the following values: // A user list can defined with one of the following values:
//
// map[string]string form of: {username:password, ...} // map[string]string form of: {username:password, ...}
// map[string]interface{} form of: {"username": {"password": "...", "other_field": ...}, ...} // map[string]interface{} form of: {"username": {"password": "...", "other_field": ...}, ...}
// []T which T completes the User interface, where T is a struct value // []T which T completes the User interface, where T is a struct value
// []T which T contains at least Username and Password fields. // []T which T contains at least Username and Password fields.
// //
// Usage: // Usage:
//
// auth := Default(map[string]string{ // auth := Default(map[string]string{
// "admin": "admin", // "admin": "admin",
// "john": "p@ss", // "john": "p@ss",
@ -260,6 +264,7 @@ func Default(users interface{}, userOpts ...UserAuthOption) context.Handler {
// a filename to load the users from. // a filename to load the users from.
// //
// Usage: // Usage:
//
// auth := Load("users.yml") // auth := Load("users.yml")
func Load(jsonOrYamlFilename string, userOpts ...UserAuthOption) context.Handler { func Load(jsonOrYamlFilename string, userOpts ...UserAuthOption) context.Handler {
opts := Options{ opts := Options{

View File

@ -3,7 +3,7 @@ package basicauth
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "os"
"reflect" "reflect"
"strings" "strings"
@ -16,8 +16,8 @@ import (
// ReadFile can be used to customize the way the // ReadFile can be used to customize the way the
// AllowUsersFile function is loading the filename from. // AllowUsersFile function is loading the filename from.
// Example of usage: embedded users.yml file. // Example of usage: embedded users.yml file.
// Defaults to the `ioutil.ReadFile` which reads the file from the physical disk. // Defaults to the `os.ReadFile` which reads the file from the physical disk.
var ReadFile = ioutil.ReadFile var ReadFile = os.ReadFile
// User is a partial part of the iris.User interface. // User is a partial part of the iris.User interface.
// It's used to declare a static slice of registered User for authentication. // It's used to declare a static slice of registered User for authentication.
@ -48,6 +48,7 @@ type UserAuthOption func(*UserAuthOptions)
// See https://www.usenix.org/legacy/event/usenix99/provos/provos.pdf. // See https://www.usenix.org/legacy/event/usenix99/provos/provos.pdf.
// //
// Usage: // Usage:
//
// Default(..., BCRYPT) OR // Default(..., BCRYPT) OR
// Load(..., BCRYPT) OR // Load(..., BCRYPT) OR
// Options.Allow = AllowUsers(..., BCRYPT) OR // Options.Allow = AllowUsers(..., BCRYPT) OR
@ -75,6 +76,7 @@ func toUserAuthOptions(opts []UserAuthOption) (options UserAuthOptions) {
// AllowUsers is an AuthFunc which authenticates user input based on a (static) user list. // AllowUsers is an AuthFunc which authenticates user input based on a (static) user list.
// The "users" input parameter can be one of the following forms: // The "users" input parameter can be one of the following forms:
//
// map[string]string e.g. {username: password, username: password...}. // map[string]string e.g. {username: password, username: password...}.
// []map[string]interface{} e.g. []{"username": "...", "password": "...", "other_field": ...}, ...}. // []map[string]interface{} e.g. []{"username": "...", "password": "...", "other_field": ...}, ...}.
// []T which T completes the User interface. // []T which T completes the User interface.
@ -155,7 +157,9 @@ func userMap(usernamePassword map[string]string, opts ...UserAuthOption) AuthFun
// loaded from a file on initialization. // loaded from a file on initialization.
// //
// Example Code: // Example Code:
//
// New(Options{Allow: AllowUsersFile("users.yml", BCRYPT)}) // New(Options{Allow: AllowUsersFile("users.yml", BCRYPT)})
//
// The users.yml file looks like the following: // The users.yml file looks like the following:
// - username: kataras // - username: kataras
// password: kataras_pass // password: kataras_pass

View File

@ -2,7 +2,6 @@ package basicauth
import ( import (
"errors" "errors"
"io/ioutil"
"os" "os"
"reflect" "reflect"
"testing" "testing"
@ -164,7 +163,7 @@ func TestAllowUsers(t *testing.T) {
// Test YAML user loading with b-encrypted passwords. // Test YAML user loading with b-encrypted passwords.
func TestAllowUsersFile(t *testing.T) { func TestAllowUsersFile(t *testing.T) {
f, err := ioutil.TempFile("", "*users.yml") f, err := os.CreateTemp("", "*users.yml")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -85,6 +85,7 @@ type (
// please refer to: https://github.com/iris-contrib/middleware repository instead. // please refer to: https://github.com/iris-contrib/middleware repository instead.
// //
// Example Code: // Example Code:
//
// import "github.com/kataras/iris/v12/middleware/cors" // import "github.com/kataras/iris/v12/middleware/cors"
// import "github.com/kataras/iris/v12/x/errors" // import "github.com/kataras/iris/v12/x/errors"
// //

View File

@ -13,6 +13,7 @@ import (
// The Iris server SHOULD run under HTTP/2 and clients too. // The Iris server SHOULD run under HTTP/2 and clients too.
// //
// Usage: // Usage:
//
// import grpcWrapper "github.com/kataras/iris/v12/middleware/grpc" // import grpcWrapper "github.com/kataras/iris/v12/middleware/grpc"
// [...] // [...]
// app := iris.New() // app := iris.New()

View File

@ -3,7 +3,7 @@ package hcaptcha
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io"
"net/http" "net/http"
"net/url" "net/url"
@ -70,7 +70,9 @@ func New(secret string, options ...Option) context.Handler {
// Handler is the HTTP route middleware featured hcaptcha validation. // Handler is the HTTP route middleware featured hcaptcha validation.
// It calls the `SiteVerify` method and fires the "next" when user completed the hcaptcha successfully, // It calls the `SiteVerify` method and fires the "next" when user completed the hcaptcha successfully,
//
// otherwise it calls the Client's `FailureHandler`. // otherwise it calls the Client's `FailureHandler`.
//
// The hcaptcha's `Response` (which contains any `ErrorCodes`) // The hcaptcha's `Response` (which contains any `ErrorCodes`)
// is saved on the Request's Context (see `GetResponseFromContext`). // is saved on the Request's Context (see `GetResponseFromContext`).
func (c *Client) Handler(ctx *context.Context) { func (c *Client) Handler(ctx *context.Context) {
@ -113,7 +115,7 @@ func SiteVerify(ctx *context.Context, secret string) (response Response) {
return return
} }
body, err := ioutil.ReadAll(resp.Body) body, err := io.ReadAll(resp.Body)
resp.Body.Close() resp.Body.Close()
if err != nil { if err != nil {
response.ErrorCodes = append(response.ErrorCodes, err.Error()) response.ErrorCodes = append(response.ErrorCodes, err.Error())

View File

@ -62,22 +62,29 @@ type Verifier struct {
// Usage: // Usage:
// //
// verifier := NewVerifier(HS256, secret) // verifier := NewVerifier(HS256, secret)
//
// OR // OR
//
// verifier := NewVerifier(HS256, secret, Expected{Issuer: "my-app"}) // verifier := NewVerifier(HS256, secret, Expected{Issuer: "my-app"})
// //
// claimsGetter := func() interface{} { return new(userClaims) } // claimsGetter := func() interface{} { return new(userClaims) }
// middleware := verifier.Verify(claimsGetter) // middleware := verifier.Verify(claimsGetter)
//
// OR // OR
//
// middleware := verifier.Verify(claimsGetter, Expected{Issuer: "my-app"}) // middleware := verifier.Verify(claimsGetter, Expected{Issuer: "my-app"})
// //
// Register the middleware, e.g. // Register the middleware, e.g.
//
// app.Use(middleware) // app.Use(middleware)
// //
// Get the claims: // Get the claims:
//
// claims := jwt.Get(ctx).(*userClaims) // claims := jwt.Get(ctx).(*userClaims)
// username := claims.Username // username := claims.Username
// //
// Get the context user: // Get the context user:
//
// username, err := ctx.User().GetUsername() // username, err := ctx.User().GetUsername()
func NewVerifier(signatureAlg Alg, signatureKey interface{}, validators ...TokenValidator) *Verifier { func NewVerifier(signatureAlg Alg, signatureKey interface{}, validators ...TokenValidator) *Verifier {
if signatureAlg == HS256 { if signatureAlg == HS256 {

View File

@ -166,6 +166,7 @@ func Query(paramName string) Option {
// to determinate the method to override with. // to determinate the method to override with.
// //
// Use cases: // Use cases:
//
// 1. When need to check only for headers and ignore other fields: // 1. When need to check only for headers and ignore other fields:
// New(Only(Headers("X-Custom-Header"))) // New(Only(Headers("X-Custom-Header")))
// //
@ -185,7 +186,6 @@ func Only(o ...Option) Option {
// that do not support certain HTTP operations such as DELETE or PUT for security reasons. // that do not support certain HTTP operations such as DELETE or PUT for security reasons.
// This wrapper will accept a method, based on criteria, to override the POST method with. // This wrapper will accept a method, based on criteria, to override the POST method with.
// //
//
// Read more at: // Read more at:
// https://github.com/kataras/iris/issues/1325 // https://github.com/kataras/iris/issues/1325
func New(opt ...Option) router.WrapperFunc { func New(opt ...Option) router.WrapperFunc {

View File

@ -34,11 +34,12 @@ type Options struct {
// for security reasons. // for security reasons.
// //
// Example Code: // Example Code:
//
// app.Get("/health", modrevision.New(modrevision.Options{ // app.Get("/health", modrevision.New(modrevision.Options{
// ServerName: "Iris Server", // ServerName: "Iris Server",
// Env: "development", // Env: "development",
// Developer: "kataras", // Developer: "kataras",
// TimeLocation: time.FixedZone("Greece/Athens", 10800), // TimeLocation: time.FixedZone("Greece/Athens", 7200),
// })) // }))
func New(opts Options) context.Handler { func New(opts Options) context.Handler {
buildTime, buildRevision := context.BuildTime, context.BuildRevision buildTime, buildRevision := context.BuildTime, context.BuildRevision

View File

@ -41,6 +41,7 @@ var profileDescriptions = map[string]string{
// New returns a new pprof (profile, cmdline, symbol, goroutine, heap, threadcreate, debug/block) Middleware. // New returns a new pprof (profile, cmdline, symbol, goroutine, heap, threadcreate, debug/block) Middleware.
// Note: Route MUST have the last named parameter wildcard named '{action:path}'. // Note: Route MUST have the last named parameter wildcard named '{action:path}'.
// Example: // Example:
//
// app.HandleMany("GET", "/debug/pprof /debug/pprof/{action:path}", pprof.New()) // app.HandleMany("GET", "/debug/pprof /debug/pprof/{action:path}", pprof.New())
func New() context.Handler { func New() context.Handler {
return func(ctx *context.Context) { return func(ctx *context.Context) {

View File

@ -3,7 +3,7 @@ package recaptcha
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io"
"net/url" "net/url"
"time" "time"
@ -50,7 +50,9 @@ func New(secret string) context.Handler {
} }
// SiteVerify accepts context and the secret key(https://www.google.com/recaptcha) // SiteVerify accepts context and the secret key(https://www.google.com/recaptcha)
//
// and returns the google's recaptcha response, if `response.Success` is true // and returns the google's recaptcha response, if `response.Success` is true
//
// then validation passed. // then validation passed.
// //
// Use `New` for middleware use instead. // Use `New` for middleware use instead.
@ -74,7 +76,7 @@ func SiteVerify(ctx *context.Context, secret string) (response Response) {
return return
} }
body, err := ioutil.ReadAll(r.Body) body, err := io.ReadAll(r.Body)
r.Body.Close() r.Body.Close()
if err != nil { if err != nil {
response.ErrorCodes = append(response.ErrorCodes, err.Error()) response.ErrorCodes = append(response.ErrorCodes, err.Error())
@ -113,6 +115,7 @@ var recaptchaForm = `<form action="%s" method="POST">
// Example Code: // Example Code:
// //
// Method: "POST" | Path: "/contact" // Method: "POST" | Path: "/contact"
//
// func postContact(ctx *context.Context) { // func postContact(ctx *context.Context) {
// // [...] // // [...]
// response := recaptcha.SiteVerify(ctx, recaptchaSecret) // response := recaptcha.SiteVerify(ctx, recaptchaSecret)
@ -125,6 +128,7 @@ var recaptchaForm = `<form action="%s" method="POST">
// } // }
// //
// Method: "GET" | Path: "/contact" // Method: "GET" | Path: "/contact"
//
// func getContact(ctx *context.Context) { // func getContact(ctx *context.Context) {
// // render the recaptcha form // // render the recaptcha form
// ctx.HTML(recaptcha.GetFormHTML(recaptchaPublic, "/contact")) // ctx.HTML(recaptcha.GetFormHTML(recaptchaPublic, "/contact"))

View File

@ -384,9 +384,11 @@ func (c *ControllerActivator) handleHTTPError(funcName string) *router.Route {
// //
// Just like `Party#HandleMany`:, it returns the `[]*router.Routes`. // Just like `Party#HandleMany`:, it returns the `[]*router.Routes`.
// Usage: // Usage:
//
// func (*Controller) BeforeActivation(b mvc.BeforeActivation) { // func (*Controller) BeforeActivation(b mvc.BeforeActivation) {
// b.HandleMany("GET", "/path /path1" /path2", "HandlePath") // b.HandleMany("GET", "/path /path1" /path2", "HandlePath")
// } // }
//
// HandleMany will override any routes of this "funcName". // HandleMany will override any routes of this "funcName".
func (c *ControllerActivator) HandleMany(method, path, funcName string, middleware ...context.Handler) []*router.Route { func (c *ControllerActivator) HandleMany(method, path, funcName string, middleware ...context.Handler) []*router.Route {
return c.handleMany(method, path, funcName, true, middleware...) return c.handleMany(method, path, funcName, true, middleware...)

View File

@ -86,6 +86,7 @@ func New(party router.Party) *Application {
// this function simply calls the `New(party)` and its `.Configure(configurators...)`. // this function simply calls the `New(party)` and its `.Configure(configurators...)`.
// //
// A call of `mvc.New(app.Party("/path").Configure(buildMyMVC)` is equal to // A call of `mvc.New(app.Party("/path").Configure(buildMyMVC)` is equal to
//
// `mvc.Configure(app.Party("/path"), buildMyMVC)`. // `mvc.Configure(app.Party("/path"), buildMyMVC)`.
// //
// Read more at `New() Application` and `Application#Configure` methods. // Read more at `New() Application` and `Application#Configure` methods.

View File

@ -10,8 +10,8 @@ import (
// It requires a specific "version" constraint for a Controller, // It requires a specific "version" constraint for a Controller,
// e.g. ">1.0.0 <=2.0.0". // e.g. ">1.0.0 <=2.0.0".
// //
//
// Usage: // Usage:
//
// m := mvc.New(dataRouter) // m := mvc.New(dataRouter)
// m.Handle(new(v1Controller), mvc.Version("1.0.0"), mvc.Deprecated(mvc.DeprecationOptions{})) // m.Handle(new(v1Controller), mvc.Version("1.0.0"), mvc.Deprecated(mvc.DeprecationOptions{}))
// m.Handle(new(v2Controller), mvc.Version("2.3.0")) // m.Handle(new(v2Controller), mvc.Version("2.3.0"))

View File

@ -78,8 +78,13 @@ func (c Config) Validate() Config {
} }
if c.SessionIDGenerator == nil { if c.SessionIDGenerator == nil {
c.SessionIDGenerator = func(*context.Context) string { c.SessionIDGenerator = func(ctx *context.Context) string {
id, _ := uuid.NewRandom() id, err := uuid.NewRandom()
if err != nil {
ctx.StopWithError(400, err)
return ""
}
return id.String() return id.String()
} }
} }

View File

@ -94,24 +94,28 @@ func (s *mem) OnUpdateExpiration(string, time.Duration) error { return nil }
// immutable depends on the store, it may not implement it at all. // immutable depends on the store, it may not implement it at all.
func (s *mem) Set(sid string, key string, value interface{}, _ time.Duration, immutable bool) error { func (s *mem) Set(sid string, key string, value interface{}, _ time.Duration, immutable bool) error {
s.mu.RLock() s.mu.RLock()
s.values[sid].Save(key, value, immutable) store, ok := s.values[sid]
s.mu.RUnlock() s.mu.RUnlock()
if ok {
store.Save(key, value, immutable)
}
return nil return nil
} }
func (s *mem) Get(sid string, key string) interface{} { func (s *mem) Get(sid string, key string) interface{} {
s.mu.RLock() s.mu.RLock()
v := s.values[sid].Get(key) store, ok := s.values[sid]
s.mu.RUnlock() s.mu.RUnlock()
if ok {
return store.Get(key)
}
return v return nil
} }
func (s *mem) Decode(sid string, key string, outPtr interface{}) error { func (s *mem) Decode(sid string, key string, outPtr interface{}) error {
s.mu.RLock() v := s.Get(sid, key)
v := s.values[sid].Get(key)
s.mu.RUnlock()
if v != nil { if v != nil {
reflect.ValueOf(outPtr).Set(reflect.ValueOf(v)) reflect.ValueOf(outPtr).Set(reflect.ValueOf(v))
} }
@ -119,29 +123,45 @@ func (s *mem) Decode(sid string, key string, outPtr interface{}) error {
} }
func (s *mem) Visit(sid string, cb func(key string, value interface{})) error { func (s *mem) Visit(sid string, cb func(key string, value interface{})) error {
s.values[sid].Visit(cb) s.mu.RLock()
store, ok := s.values[sid]
s.mu.RUnlock()
if ok {
store.Visit(cb)
}
return nil return nil
} }
func (s *mem) Len(sid string) int { func (s *mem) Len(sid string) int {
s.mu.RLock() s.mu.RLock()
n := s.values[sid].Len() store, ok := s.values[sid]
s.mu.RUnlock() s.mu.RUnlock()
if ok {
return store.Len()
}
return n return 0
} }
func (s *mem) Delete(sid string, key string) (deleted bool) { func (s *mem) Delete(sid string, key string) (deleted bool) {
s.mu.RLock() s.mu.RLock()
deleted = s.values[sid].Remove(key) store, ok := s.values[sid]
s.mu.RUnlock() s.mu.RUnlock()
if ok {
deleted = store.Remove(key)
}
return return
} }
func (s *mem) Clear(sid string) error { func (s *mem) Clear(sid string) error {
s.mu.Lock() s.mu.RLock()
s.values[sid].Reset() store, ok := s.values[sid]
s.mu.Unlock() s.mu.RUnlock()
if ok {
store.Reset()
}
return nil return nil
} }
@ -150,7 +170,6 @@ func (s *mem) Release(sid string) error {
s.mu.Lock() s.mu.Lock()
delete(s.values, sid) delete(s.values, sid)
s.mu.Unlock() s.mu.Unlock()
return nil return nil
} }

View File

@ -50,7 +50,6 @@ func (p *provider) newSession(man *Sessions, sid string, expires time.Duration)
sid: sid, sid: sid,
Man: man, Man: man,
provider: p, provider: p,
flashes: make(map[string]*flashMessage),
} }
onExpire := func() { onExpire := func() {
@ -99,6 +98,7 @@ func (p *provider) EndRequest(ctx *context.Context, session *Session) {
// ErrNotFound may be returned from `UpdateExpiration` of a non-existing or // ErrNotFound may be returned from `UpdateExpiration` of a non-existing or
// invalid session entry from memory storage or databases. // invalid session entry from memory storage or databases.
// Usage: // Usage:
//
// if err != nil && err.Is(err, sessions.ErrNotFound) { // if err != nil && err.Is(err, sessions.ErrNotFound) {
// [handle error...] // [handle error...]
// } // }

View File

@ -76,6 +76,7 @@ func (s *Session) runFlashGC() {
delete(s.flashes, key) delete(s.flashes, key)
} }
} }
s.mu.Unlock() s.mu.Unlock()
} }
@ -558,6 +559,10 @@ func (s *Session) SetImmutable(key string, value interface{}) {
// If you want to define more than one flash messages, you will have to use different keys. // If you want to define more than one flash messages, you will have to use different keys.
func (s *Session) SetFlash(key string, value interface{}) { func (s *Session) SetFlash(key string, value interface{}) {
s.mu.Lock() s.mu.Lock()
if s.flashes == nil {
s.flashes = make(map[string]*flashMessage)
}
s.flashes[key] = &flashMessage{value: value} s.flashes[key] = &flashMessage{value: value}
s.mu.Unlock() s.mu.Unlock()
} }

View File

@ -35,7 +35,7 @@ func testSessions(t *testing.T, app *iris.Application) {
s := sessions.Get(ctx) s := sessions.Get(ctx)
sessValues := s.GetAll() sessValues := s.GetAll()
_, err := ctx.JSON(sessValues) err := ctx.JSON(sessValues)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -141,8 +141,7 @@ func TestFlashMessages(t *testing.T) {
} }
writeValues := func(ctx *context.Context, values map[string]interface{}) error { writeValues := func(ctx *context.Context, values map[string]interface{}) error {
_, err := ctx.JSON(values) return ctx.JSON(values)
return err
} }
app.Post("/set", func(ctx *context.Context) { app.Post("/set", func(ctx *context.Context) {

View File

@ -30,9 +30,12 @@ type Group struct {
// any changes to its parent won't affect this one (e.g. register global middlewares afterwards). // any changes to its parent won't affect this one (e.g. register global middlewares afterwards).
// //
// A version is extracted through the versioning.GetVersion function: // A version is extracted through the versioning.GetVersion function:
//
// Accept-Version: 1.0.0 // Accept-Version: 1.0.0
// Accept: application/json; version=1.0.0 // Accept: application/json; version=1.0.0
//
// You can customize it by setting a version based on the request context: // You can customize it by setting a version based on the request context:
//
// api.Use(func(ctx *context.Context) { // api.Use(func(ctx *context.Context) {
// if version := ctx.URLParam("version"); version != "" { // if version := ctx.URLParam("version"); version != "" {
// SetVersion(ctx, version) // SetVersion(ctx, version)
@ -40,11 +43,14 @@ type Group struct {
// //
// ctx.Next() // ctx.Next()
// }) // })
//
// OR: // OR:
//
// api.Use(versioning.FromQuery("version", "")) // api.Use(versioning.FromQuery("version", ""))
// //
// Examples at: _examples/routing/versioning // Examples at: _examples/routing/versioning
// Usage: // Usage:
//
// app := iris.New() // app := iris.New()
// api := app.Party("/api") // api := app.Party("/api")
// v1 := versioning.NewGroup(api, ">=1.0.0 <2.0.0") // v1 := versioning.NewGroup(api, ">=1.0.0 <2.0.0")
@ -61,8 +67,10 @@ type Group struct {
// A Range can consist of multiple ranges separated by space: // A Range can consist of multiple ranges separated by space:
// Ranges can be linked by logical AND: // Ranges can be linked by logical AND:
// - ">1.0.0 <2.0.0" would match between both ranges, so "1.1.1" and "1.8.7" // - ">1.0.0 <2.0.0" would match between both ranges, so "1.1.1" and "1.8.7"
//
// but not "1.0.0" or "2.0.0" // but not "1.0.0" or "2.0.0"
// - ">1.0.0 <3.0.0 !2.0.3-beta.2" would match every version between 1.0.0 and 3.0.0 // - ">1.0.0 <3.0.0 !2.0.3-beta.2" would match every version between 1.0.0 and 3.0.0
//
// except 2.0.3-beta.2 // except 2.0.3-beta.2
// //
// Ranges can also be linked by logical OR: // Ranges can also be linked by logical OR:
@ -73,6 +81,7 @@ type Group struct {
// Ranges can be combined by both AND and OR // Ranges can be combined by both AND and OR
// //
// - `>1.0.0 <2.0.0 || >3.0.0 !4.2.1` would match `1.2.3`, `1.9.9`, `3.1.1`, // - `>1.0.0 <2.0.0 || >3.0.0 !4.2.1` would match `1.2.3`, `1.9.9`, `3.1.1`,
//
// but not `4.2.1`, `2.1.1` // but not `4.2.1`, `2.1.1`
func NewGroup(r API, version string) *Group { func NewGroup(r API, version string) *Group {
version = strings.ReplaceAll(version, ",", " ") version = strings.ReplaceAll(version, ",", " ")

View File

@ -187,11 +187,13 @@ func GetVersion(ctx *context.Context) string {
// It can be used inside a middleware. // It can be used inside a middleware.
// Example of how you can change the default behavior to extract a requested version (which is by headers) // Example of how you can change the default behavior to extract a requested version (which is by headers)
// from a "version" url parameter instead: // from a "version" url parameter instead:
//
// func(ctx iris.Context) { // &version=1 // func(ctx iris.Context) { // &version=1
// version := ctx.URLParamDefault("version", "1.0.0") // version := ctx.URLParamDefault("version", "1.0.0")
// versioning.SetVersion(ctx, version) // versioning.SetVersion(ctx, version)
// ctx.Next() // ctx.Next()
// } // }
//
// See `GetVersion` too. // See `GetVersion` too.
func SetVersion(ctx *context.Context, constraint string) { func SetVersion(ctx *context.Context, constraint string) {
ctx.Values().Set(ctx.Application().ConfigurationReadOnly().GetVersionContextKey(), constraint) ctx.Values().Set(ctx.Application().ConfigurationReadOnly().GetVersionContextKey(), constraint)
@ -205,6 +207,7 @@ type AliasMap = map[string]string
// for the children Parties(routers). It's respected by versioning Groups. // for the children Parties(routers). It's respected by versioning Groups.
// //
// Example Code: // Example Code:
//
// app := iris.New() // app := iris.New()
// //
// api := app.Party("/api") // api := app.Party("/api")

View File

@ -50,6 +50,7 @@ type (
// through a Context or within filter functions. // through a Context or within filter functions.
// //
// Example: // Example:
//
// AsValue("my string") // AsValue("my string")
// //
// Shortcut for `pongo2.AsValue`. // Shortcut for `pongo2.AsValue`.

View File

@ -2,7 +2,7 @@ package view
import ( import (
"fmt" "fmt"
"io/ioutil" "io"
"net/http" "net/http"
"path" "path"
"path/filepath" "path/filepath"
@ -80,7 +80,7 @@ func asset(fs http.FileSystem, name string) ([]byte, error) {
return nil, err return nil, err
} }
contents, err := ioutil.ReadAll(f) contents, err := io.ReadAll(f)
f.Close() f.Close()
return contents, err return contents, err
} }

View File

@ -140,6 +140,7 @@ func (s *HTMLEngine) Reload(developmentMode bool) *HTMLEngine {
// //
// missingkey: Control the behavior during execution if a map is // missingkey: Control the behavior during execution if a map is
// indexed with a key that is not present in the map. // indexed with a key that is not present in the map.
//
// "missingkey=default" or "missingkey=invalid" // "missingkey=default" or "missingkey=invalid"
// The default behavior: Do nothing and continue execution. // The default behavior: Do nothing and continue execution.
// If printed, the result of the index operation is the string // If printed, the result of the index operation is the string
@ -148,7 +149,6 @@ func (s *HTMLEngine) Reload(developmentMode bool) *HTMLEngine {
// The operation returns the zero value for the map type's element. // The operation returns the zero value for the map type's element.
// "missingkey=error" // "missingkey=error"
// Execution stops immediately with an error. // Execution stops immediately with an error.
//
func (s *HTMLEngine) Option(opt ...string) *HTMLEngine { func (s *HTMLEngine) Option(opt ...string) *HTMLEngine {
s.rmu.Lock() s.rmu.Lock()
s.options = append(s.options, opt...) s.options = append(s.options, opt...)
@ -172,6 +172,7 @@ func (s *HTMLEngine) Delims(left, right string) *HTMLEngine {
// for the template file with its extension. // for the template file with its extension.
// //
// Example: HTML("./templates", ".html").Layout("layouts/mainLayout.html") // Example: HTML("./templates", ".html").Layout("layouts/mainLayout.html")
//
// // mainLayout.html is inside: "./templates/layouts/". // // mainLayout.html is inside: "./templates/layouts/".
// //
// Note: Layout can be changed for a specific call // Note: Layout can be changed for a specific call

View File

@ -7,7 +7,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"mime/multipart" "mime/multipart"
"net/http" "net/http"
"net/url" "net/url"
@ -281,7 +280,7 @@ func (c *Client) Do(ctx context.Context, method, urlpath string, payload interfa
// DrainResponseBody drains response body and close it, allowing the transport to reuse TCP connections. // DrainResponseBody drains response body and close it, allowing the transport to reuse TCP connections.
// It's automatically called on Client.ReadXXX methods on the end. // It's automatically called on Client.ReadXXX methods on the end.
func (c *Client) DrainResponseBody(resp *http.Response) { func (c *Client) DrainResponseBody(resp *http.Response) {
_, _ = io.Copy(ioutil.Discard, resp.Body) _, _ = io.Copy(io.Discard, resp.Body)
resp.Body.Close() resp.Body.Close()
} }
@ -398,7 +397,7 @@ func (c *Client) ReadJSON(ctx context.Context, dest interface{}, method, urlpath
} }
// DBUG // DBUG
// b, _ := ioutil.ReadAll(resp.Body) // b, _ := io.ReadAll(resp.Body)
// println(string(b)) // println(string(b))
// return json.Unmarshal(b, &dest) // return json.Unmarshal(b, &dest)
@ -418,7 +417,7 @@ func (c *Client) ReadPlain(ctx context.Context, dest interface{}, method, urlpat
return ExtractError(resp) return ExtractError(resp)
} }
body, err := ioutil.ReadAll(resp.Body) body, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return err return err
} }
@ -495,7 +494,7 @@ func BindResponse(resp *http.Response, dest interface{}) (err error) {
case contentTypeJSON: // the most common scenario on successful responses. case contentTypeJSON: // the most common scenario on successful responses.
return json.NewDecoder(resp.Body).Decode(&dest) return json.NewDecoder(resp.Body).Decode(&dest)
case contentTypePlainText: case contentTypePlainText:
b, err := ioutil.ReadAll(resp.Body) b, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return err return err
} }

View File

@ -2,7 +2,7 @@ package client
import ( import (
"encoding/json" "encoding/json"
"io/ioutil" "io"
"net/http" "net/http"
"strings" "strings"
) )
@ -39,7 +39,7 @@ func (e APIError) Error() string {
// ExtractError returns the response wrapped inside an APIError. // ExtractError returns the response wrapped inside an APIError.
func ExtractError(resp *http.Response) APIError { func ExtractError(resp *http.Response) APIError {
body, _ := ioutil.ReadAll(resp.Body) body, _ := io.ReadAll(resp.Body)
return APIError{ return APIError{
Response: resp, Response: resp,

Some files were not shown because too many files have changed in this diff Show More