add the stale release

This commit is contained in:
Gerasimos (Makis) Maropoulos 2020-08-12 23:41:20 +03:00
parent 535fb6dc0d
commit 2a4ce876b6
No known key found for this signature in database
GPG Key ID: 5DBE766BD26A54E7
793 changed files with 188 additions and 52415 deletions

View File

@ -1,11 +0,0 @@
version: 2
cli:
server: https://app.fossa.com
fetcher: custom
project: https://github.com/kataras/iris.git
analyze:
modules:
- name: iris
type: go
target: .
path: .

View File

@ -2,7 +2,7 @@ Examples for the Iris project can be found at
<https://github.com/kataras/iris/tree/master/_examples>.
Documentation for the Iris project can be found at
<https://pkg.go.dev/github.com/kataras/iris/v12@v12.2.0>.
<https://pkg.go.dev/github.com/kataras/iris@v12.2.0>.
Love iris? Please consider supporting the project:
👉 https://iris-go.com/donate

View File

@ -30,7 +30,7 @@ Please make sure the bug is reproducible over the `master` branch:
```sh
$ cd PROJECT
$ go get -u github.com/kataras/iris/v12@master
$ go get -u github.com/kataras/iris@master
$ go run .
```

View File

@ -1,24 +0,0 @@
sudo: false
language: go
os:
- linux
- osx
go:
- 1.14.x
- 1.15.x
- master
go_import_path: github.com/kataras/iris/v12
env:
global:
- GO111MODULE=on
install:
- go get ./...
script:
- go test -count=1 -v -cover -race ./...
after_script:
# examples
- cd ./_examples
- go get ./...
- go test -count=1 -v -cover -race ./...
- cd ../

View File

@ -1,74 +0,0 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at kataras2006@hotmail.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

View File

@ -1,33 +0,0 @@
# Contributing
First of all read our [Code of Conduct](https://github.com/kataras/iris/blob/master/CODE_OF_CONDUCT.md).
## PR
1. Open a new [issue](https://github.com/kataras/iris/issues/new)
* Write version of your local Iris.
* Write version of your local Go programming language.
* Describe your problem, what did you expect to see and what you see instead.
* If it's a feature request, describe your idea as better as you can
* optionally, navigate to the [chat](https://chat.iris-go.com) to push other members to participate and share their thoughts about your brilliant idea.
2. Fork the [repository](https://github.com/kataras/iris).
3. Make your changes.
4. Compare & Push the PR from [here](https://github.com/kataras/iris/compare).
## Translate
We need your help with translations into your native language.
Iris needs your help, please think about contributing to the translation of the [README](README.md) and https://iris-go.com, you will be rewarded.
Instructions can be found at: https://github.com/kataras/iris/issues/796
## Share
### Writing
Write an article about Iris in https://medium.com , https://dev.to or if you're being a hackathon at https://hackernoon.com, some [examples](https://github.com/kataras/iris/wiki/Publications).
### Social networks
If you're part of any social network, do a post(or tweet if twitter) about Iris and what you love about it, many examples can be found, the most recent one is [that](https://twitter.com/DorMoshe/status/1154486477247508480).

56
FAQ.md
View File

@ -1,56 +0,0 @@
# FAQ
## [![iris](https://img.shields.io/badge/iris-powered-2196f3.svg?style=for-the-badge)](https://github.com/kataras/iris)
Add a `badge` to your open-source projects powered by [Iris](https://iris-go.com) by pasting the below code snippet to the project repo's README.md:
```md
[![iris](https://img.shields.io/badge/iris-powered-2196f3.svg?style=for-the-badge)](https://github.com/kataras/iris)
```
## Editors & IDEs Extensions
### Visual Studio Code <a href="https://marketplace.visualstudio.com/items?itemName=kataras2006.iris"><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/2/2d/Visual_Studio_Code_1.18_icon.svg/2000px-Visual_Studio_Code_1.18_icon.svg.png" height="20px" width="20px" /></a>
<https://marketplace.visualstudio.com/items?itemName=kataras2006.iris>
> Please feel free to list your own Iris extension(s) here by [PR](https://github.com/kataras/iris/pulls)
## How to upgrade
```sh
go get -u github.com/kataras/iris/v12@master
```
Go version 1.13 and above is required.
## Learning
More than 180 practical examples, tutorials and articles at:
- https://iris-go.com/start
- https://bit.ly/iris-req-book
- https://github.com/kataras/iris/wiki/Starter-kits
- https://github.com/kataras/iris/tree/master/_examples
- https://godoc.org/github.com/kataras/iris <!--https://pkg.go.dev/github.com/kataras/iris/v12@v12.2.0-->
## Active development mode
Iris may have reached version 12, but we're not stopping there. We have many feature ideas on our board that we're anxious to add and other innovative web development solutions that we're planning to build into Iris.
## Can I find a job if I learn how to use Iris?
Yes, not only because you will learn Golang in the same time, but there are some positions
open for Iris-specific developers the time we speak.
Go to our facebook page, like it and receive notifications about new job offers, we already have couple of them stay at the top of the page: https://www.facebook.com/iris.framework
## Do we have a Community chat?
Yes, https://chat.iris-go.com
## How is the development of Iris economically supported?
By people like you, who help us by donating small or large amounts of money.
Help this project deliver awesome and unique features with the highest possible code quality by donating any amount via [PayPal](https://iris-go.com/donate). Your name will be published [here](https://iris-go.com) after your approval via e-mail.

View File

@ -1,829 +0,0 @@
<!-- # History/Changelog <a href="HISTORY_ZH.md"> <img width="20px" src="https://iris-go.com/images/flag-china.svg?v=10" /></a><a href="HISTORY_ID.md"> <img width="20px" src="https://iris-go.com/images/flag-indonesia.svg?v=10" /></a><a href="HISTORY_GR.md"> <img width="20px" src="https://iris-go.com/images/flag-greece.svg?v=10" /></a> -->
# Changelog
### Looking for free and real-time support?
https://github.com/kataras/iris/issues
https://chat.iris-go.com
### Looking for previous versions?
https://github.com/kataras/iris/releases
### Want to be hired?
https://facebook.com/iris.framework
### Should I upgrade my Iris?
Developers are not forced to upgrade if they don't really need it. Upgrade whenever you feel ready.
**How to upgrade**: Open your command-line and execute this command: `go get github.com/kataras/iris/v12@latest`.
# Next
This release introduces new features and some breaking changes inside the `mvc` and `hero` packages.
The codebase for dependency injection has been simplified a lot (fewer LOCs and easier to read and follow up).
The new release contains a fresh new and awesome feature....**a function dependency can accept previous registered dependencies and update or return a new value of any type**.
The new implementation is **faster** on both design and serve-time.
The most common scenario from a route to handle is to:
- accept one or more path parameters and request data, a payload
- send back a response, a payload (JSON, XML,...)
The new Iris Dependency Injection feature is about **33.2% faster** than its predecessor on the above case. This drops down even more the performance cost between native handlers and dynamic handlers with dependencies. This reason itself brings us, with safety and performance-wise, to the new `Party.ConfigureContainer(builder ...func(*iris.APIContainer)) *APIContainer` method which returns methods such as `Handle(method, relativePath string, handlersFn ...interface{}) *Route` and `RegisterDependency`.
Look how clean your codebase can be when using Iris':
```go
package main
import "github.com/kataras/iris/v12"
type (
testInput struct {
Email string `json:"email"`
}
testOutput struct {
ID int `json:"id"`
Name string `json:"name"`
}
)
func handler(id int, in testInput) testOutput {
return testOutput{
ID: id,
Name: in.Email,
}
}
func main() {
app := iris.New()
app.ConfigureContainer(func(api *iris.APIContainer) {
api.Post("/{id:int}", handler)
})
app.Listen(":5000", iris.WithOptimizations)
}
```
Your eyes don't lie you. You read well, no `ctx.ReadJSON(&v)` and `ctx.JSON(send)` neither `error` handling are presented. It is a huge relief but if you ever need, you still have the control over those, even errors from dependencies. Here is a quick list of the new Party.ConfigureContainer()'s fields and methods:
```go
// Container holds the DI Container of this Party featured Dependency Injection.
// Use it to manually convert functions or structs(controllers) to a Handler.
Container *hero.Container
```
```go
// OnError adds an error handler for this Party's DI Hero Container and its handlers (or controllers).
// The "errorHandler" handles any error may occurred and returned
// during dependencies injection of the Party's hero handlers or from the handlers themselves.
OnError(errorHandler func(iris.Context, error))
```
```go
// RegisterDependency adds a dependency.
// The value can be a single struct value or a function.
// Follow the rules:
// * <T> {structValue}
// * func(accepts <T>) returns <D> or (<D>, error)
// * func(accepts iris.Context) returns <D> or (<D>, error)
//
// A Dependency can accept a previous registered dependency and return a new one or the same updated.
// * func(accepts1 <D>, accepts2 <T>) returns <E> or (<E>, error) or error
// * func(acceptsPathParameter1 string, id uint64) returns <T> or (<T>, error)
//
// Usage:
//
// - RegisterDependency(loggerService{prefix: "dev"})
// - RegisterDependency(func(ctx iris.Context) User {...})
// - RegisterDependency(func(User) OtherResponse {...})
RegisterDependency(dependency interface{})
// UseResultHandler adds a result handler to the Container.
// A result handler can be used to inject the returned struct value
// from a request handler or to replace the default renderer.
UseResultHandler(handler func(next iris.ResultHandler) iris.ResultHandler)
```
<details><summary>ResultHandler</summary>
```go
type ResultHandler func(ctx iris.Context, v interface{}) error
```
</details>
```go
// Use same as a common Party's "Use" but it accepts dynamic functions as its "handlersFn" input.
Use(handlersFn ...interface{})
// Done same as a common Party's but it accepts dynamic functions as its "handlersFn" input.
Done(handlersFn ...interface{})
```
```go
// Handle same as a common Party's `Handle` but it accepts one or more "handlersFn" functions which each one of them
// can accept any input arguments that match with the Party's registered Container's `Dependencies` and
// any output result; like custom structs <T>, string, []byte, int, error,
// a combination of the above, hero.Result(hero.View | hero.Response) and more.
//
// It's common from a hero handler to not even need to accept a `Context`, for that reason,
// the "handlersFn" will call `ctx.Next()` automatically when not called manually.
// To stop the execution and not continue to the next "handlersFn"
// the end-developer should output an error and return `iris.ErrStopExecution`.
Handle(method, relativePath string, handlersFn ...interface{}) *Route
// Get registers a GET route, same as `Handle("GET", relativePath, handlersFn....)`.
Get(relativePath string, handlersFn ...interface{}) *Route
// and so on...
```
Prior to this version the `iris.Context` was the only one dependency that has been automatically binded to the handler's input or a controller's fields and methods, read below to see what types are automatically binded:
| Type | Maps To |
|------|:---------|
| [*mvc.Application](https://pkg.go.dev/github.com/kataras/iris/v12/mvc?tab=doc#Application) | Current MVC Application |
| [iris.Context](https://pkg.go.dev/github.com/kataras/iris/v12/context?tab=doc#Context) | Current Iris Context |
| [*sessions.Session](https://pkg.go.dev/github.com/kataras/iris/v12/sessions?tab=doc#Session) | Current Iris Session |
| [context.Context](https://golang.org/pkg/context/#Context) | [ctx.Request().Context()](https://golang.org/pkg/net/http/#Request.Context) |
| [*http.Request](https://golang.org/pkg/net/http/#Request) | `ctx.Request()` |
| [http.ResponseWriter](https://golang.org/pkg/net/http/#ResponseWriter) | `ctx.ResponseWriter()` |
| [http.Header](https://golang.org/pkg/net/http/#Header) | `ctx.Request().Header` |
| [time.Time](https://golang.org/pkg/time/#Time) | `time.Now()` |
| [*golog.Logger](https://pkg.go.dev/github.com/kataras/golog) | Iris Logger |
| [net.IP](https://golang.org/pkg/net/#IP) | `net.ParseIP(ctx.RemoteAddr())` |
| `string`, | |
| `int, int8, int16, int32, int64`, | |
| `uint, uint8, uint16, uint32, uint64`, | |
| `float, float32, float64`, | |
| `bool`, | |
| `slice` | [Path Parameter](https://github.com/kataras/iris/wiki/Routing-path-parameter-types) |
| Struct | [Request Body](https://github.com/kataras/iris/tree/master/_examples/request-body) of `JSON`, `XML`, `YAML`, `Form`, `URL Query`, `Protobuf`, `MsgPack` |
Here is a preview of what the new Hero handlers look like:
### Request & Response & Path Parameters
**1.** Declare Go types for client's request body and a server's response.
```go
type (
request struct {
Firstname string `json:"firstname"`
Lastname string `json:"lastname"`
}
response struct {
ID uint64 `json:"id"`
Message string `json:"message"`
}
)
```
**2.** Create the route handler.
Path parameters and request body are binded automatically.
- **id uint64** binds to "id:uint64"
- **input request** binds to client request data such as JSON
```go
func updateUser(id uint64, input request) response {
return response{
ID: id,
Message: "User updated successfully",
}
}
```
**3.** Configure the container per group and register the route.
```go
app.Party("/user").ConfigureContainer(container)
func container(api *iris.APIContainer) {
api.Put("/{id:uint64}", updateUser)
}
```
**4.** Simulate a [client](https://curl.haxx.se/download.html) request which sends data to the server and displays the response.
```sh
curl --request PUT -d '{"firstanme":"John","lastname":"Doe"}' http://localhost:8080/user/42
```
```json
{
"id": 42,
"message": "User updated successfully"
}
```
### Custom Preflight
Before we continue to the next section, register dependencies, you may want to learn how a response can be customized through the `iris.Context` right before sent to the client.
The server will automatically execute the `Preflight(iris.Context) error` method of a function's output struct value right before send the response to the client.
Take for example that you want to fire different HTTP status codes depending on the custom logic inside your handler and also modify the value(response body) itself before sent to the client. Your response type should contain a `Preflight` method like below.
```go
type response struct {
ID uint64 `json:"id,omitempty"`
Message string `json:"message"`
Code int `json:"code"`
Timestamp int64 `json:"timestamp,omitempty"`
}
func (r *response) Preflight(ctx iris.Context) error {
if r.ID > 0 {
r.Timestamp = time.Now().Unix()
}
ctx.StatusCode(r.Code)
return nil
}
```
Now, each handler that returns a `*response` value will call the `response.Preflight` method automatically.
```go
func deleteUser(db *sql.DB, id uint64) *response {
// [...custom logic]
return &response{
Message: "User has been marked for deletion",
Code: iris.StatusAccepted,
}
}
```
If you register the route and fire a request you should see an output like this, the timestamp is filled and the HTTP status code of the response that the client will receive is 202 (Status Accepted).
```json
{
"message": "User has been marked for deletion",
"code": 202,
"timestamp": 1583313026
}
```
### Register Dependencies
**1.** Import packages to interact with a database.
The go-sqlite3 package is a database driver for [SQLite](https://www.sqlite.org/index.html).
```go
import "database/sql"
import _ "github.com/mattn/go-sqlite3"
```
**2.** Configure the container ([see above](#request--response--path-parameters)), register your dependencies. Handler expects an *sql.DB instance.
```go
localDB, _ := sql.Open("sqlite3", "./foo.db")
api.RegisterDependency(localDB)
```
**3.** Register a route to create a user.
```go
api.Post("/{id:uint64}", createUser)
```
**4.** The create user Handler.
The handler accepts a database and some client request data such as JSON, Protobuf, Form, URL Query and e.t.c. It Returns a response.
```go
func createUser(db *sql.DB, user request) *response {
// [custom logic using the db]
userID, err := db.CreateUser(user)
if err != nil {
return &response{
Message: err.Error(),
Code: iris.StatusInternalServerError,
}
}
return &response{
ID: userID,
Message: "User created",
Code: iris.StatusCreated,
}
}
```
**5.** Simulate a [client](https://curl.haxx.se/download.html) to create a user.
```sh
# JSON
curl --request POST -d '{"firstname":"John","lastname":"Doe"}' \
--header 'Content-Type: application/json' \
http://localhost:8080/user
```
```sh
# Form (multipart)
curl --request POST 'http://localhost:8080/users' \
--header 'Content-Type: multipart/form-data' \
--form 'firstname=John' \
--form 'lastname=Doe'
```
```sh
# Form (URL-encoded)
curl --request POST 'http://localhost:8080/users' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'firstname=John' \
--data-urlencode 'lastname=Doe'
```
```sh
# URL Query
curl --request POST 'http://localhost:8080/users?firstname=John&lastname=Doe'
```
Response:
```json
{
"id": 42,
"message": "User created",
"code": 201,
"timestamp": 1583313026
}
```
Other Improvements:
- Fix `AutoTLS` when used with `iris.TLSNoRedirect` [*](https://github.com/kataras/iris/issues/1577). The `AutoTLS` runner can be customized through the new `iris.AutoTLSNoRedirect` instead, read its go documentation. Example of having both TLS and non-TLS versions of the same application without conflicts with letsencrypt `./well-known` path:
![](https://iris-go.com/images/github/autotls-1.png)
```go
package main
import (
"net/http"
"time"
"github.com/kataras/iris/v12"
)
func main() {
app := iris.New()
app.Logger().SetLevel("debug")
app.Get("/", func(ctx iris.Context) {
ctx.JSON(iris.Map{
"time": time.Now().Unix(),
"tls": ctx.Request().TLS != nil,
})
})
var fallbackServer = func(acme func(http.Handler) http.Handler) *http.Server {
srv := &http.Server{Handler: acme(app)}
go srv.ListenAndServe()
return srv
}
app.Run(iris.AutoTLS(":443", "example.com", "mail@example.com",
iris.AutoTLSNoRedirect(fallbackServer)))
}
```
- `Application.UseRouter(...Handler)` - per party to register handlers before the main router, useful on handlers that should control whether the router itself should ran or not. Independently of the incoming request's method and path values. These handlers will be executed ALWAYS against ALL incoming matched requests. Example of use-case: CORS.
- `*versioning.Group` type is a full `Party` now.
- `Party.UseOnce` - either inserts a middleware, or on the basis of the middleware already existing, replace that existing middleware instead.
- Ability to register a view engine per group of routes or for the current chain of handlers through `Party.RegisterView` and `Context.ViewEngine` respectfully.
- Add [Blocks](_examples/view/template_blocks_0) template engine. <!-- Reminder for @kataras: follow https://github.com/flosch/pongo2/pull/236#issuecomment-668950566 discussion so we can get back on using the original pongo2 repository as they fixed the issue about an incompatible 3rd party package (although they need more fixes, that's why I commented there) -->
- Add [Ace](_examples/view/template_ace_0) template parser to the view engine and other minor improvements.
- Fix huge repo size of 55.7MB, which slows down the overall Iris installation experience. Now, go-get performs ~3 times faster. I 've managed it using the [bfg-repo-cleaner](https://github.com/rtyley/bfg-repo-cleaner) tool - an alternative to git-filter-branch command. Watch the small gif below to learn how:
[![](https://media.giphy.com/media/U8560aiWTurW4iAOLn/giphy.gif)](https://media.giphy.com/media/U8560aiWTurW4iAOLn/giphy.gif)
- [gRPC](https://grpc.io/) features:
- New Router [Wrapper](middleware/grpc).
- New MVC `.Handle(ctrl, mvc.GRPC{...})` option which allows to register gRPC services per-party (without the requirement of a full wrapper) and optionally strict access to gRPC clients only, see the [example here](_examples/mvc/grpc-compatible).
- Improved tracing (with `app.Logger().SetLevel("debug")`) for routes. Example:
#### DBUG Routes (1)
![DBUG routes](https://iris-go.com/images/v12.2.0-dbug.png?v=0)
#### DBUG Routes (2)
![DBUG routes](https://iris-go.com/images/v12.2.0-dbug2.png?v=0)
- Add `Configuration.RemoteAddrHeadersForce bool` to force `Context.RemoteAddr() string` to return the first entry of request headers as a fallback instead of the `Request.RemoteAddr` one, as requested at: [1567#issuecomment-663972620](https://github.com/kataras/iris/issues/1567#issuecomment-663972620).
- Fix [#1569#issuecomment-663739177](https://github.com/kataras/iris/issues/1569#issuecomment-663739177).
- Fix [#1564](https://github.com/kataras/iris/issues/1564).
- Fix [#1553](https://github.com/kataras/iris/issues/1553).
- New `DirOptions.Cache` to cache assets in-memory among with their compressed contents (in order to be ready to served if client ask). Learn more about this feature by reading [all #1556 comments](https://github.com/kataras/iris/issues/1556#issuecomment-661057446). Usage:
```go
var dirOpts = DirOptions{
// [...other options]
Cache: DirCacheOptions{
Enable: true,
// Don't compress files smaller than 300 bytes.
CompressMinSize: 300,
// Ignore compress already compressed file types
// (some images and pdf).
CompressIgnore: iris.MatchImagesAssets,
// Gzip, deflate, br(brotli), snappy.
Encodings: []string{"gzip", "deflate", "br", "snappy"},
// Log to the stdout the total reduced file size.
Verbose: 1,
},
}
```
- New `DirOptions.PushTargets` and `PushTargetsRegexp` to push index' assets to the client without additional requests. Inspirated by issue [#1562](https://github.com/kataras/iris/issues/1562). Example matching all `.js, .css and .ico` files (recursively):
```go
var dirOpts = iris.DirOptions{
// [...other options]
IndexName: "/index.html",
PushTargetsRegexp: map[string]*regexp.Regexp{
"/": regexp.MustCompile("((.*).js|(.*).css|(.*).ico)$"),
// OR:
// "/": iris.MatchCommonAssets,
},
Compress: true,
}
```
- Update jet parser to v4.0.2, closes [#1551](https://github.com/kataras/iris/issues/1551). It contains two breaking changes by its author:
- Relative paths on `extends, import, include...` tmpl functions, e.g. `{{extends "../layouts/application.jet"}}` instead of `layouts/application.jet`
- the new [jet.Ranger](https://github.com/CloudyKit/jet/pull/165) interface now requires a `ProvidesIndex() bool` method too
- Example has been [updated](https://github.com/kataras/iris/tree/master/_examples/view/template_jet_0)
- Fix [#1552](https://github.com/kataras/iris/issues/1552).
- Proper listing of root directories on `Party.HandleDir` when its `DirOptions.ShowList` was set to true.
- Customize the file/directory listing page through views, see [example](https://github.com/kataras/iris/tree/master/_examples/file-server/file-server).
- Socket Sharding as requested at [#1544](https://github.com/kataras/iris/issues/1544). New `iris.WithSocketSharding` Configurator and `SocketSharding bool` setting.
- Versioned Controllers feature through the new `mvc.Version` option. See [_examples/mvc/versioned-controller](https://github.com/kataras/iris/blob/master/_examples/mvc/versioned-controller/main.go).
- Fix [#1539](https://github.com/kataras/iris/issues/1539).
- New [rollbar example](https://github.com/kataras/iris/blob/master/_examples/logging/rollbar/main.go).
- New builtin [requestid](https://github.com/kataras/iris/tree/master/middleware/requestid) middleware.
- New builtin [JWT](https://github.com/kataras/iris/tree/master/middleware/jwt) middleware based on [square/go-jose](https://github.com/square/go-jose) featured with optional encryption to set claims with sensitive data when necessary.
- New `iris.RouteOverlap` route registration rule. `Party.SetRegisterRule(iris.RouteOverlap)` to allow overlapping across multiple routes for the same request subdomain, method, path. See [1536#issuecomment-643719922](https://github.com/kataras/iris/issues/1536#issuecomment-643719922). This allows two or more **MVC Controllers** to listen on the same path based on one or more registered dependencies (see [_examples/mvc/authenticated-controller](https://github.com/kataras/iris/tree/master/_examples/mvc/authenticated-controller)).
- `Context.ReadForm` now can return an `iris.ErrEmptyForm` instead of `nil` when the new `Configuration.FireEmptyFormError` is true (when `iris.WithEmptyFormError` is set) on missing form body to read from.
- `Configuration.EnablePathIntelligence | iris.WithPathIntelligence` to enable path intelligence automatic path redirection on the most closest path (if any), [example]((https://github.com/kataras/iris/blob/master/_examples/routing/intelligence/main.go)
- Enhanced cookie security and management through new `Context.AddCookieOptions` method and new cookie options (look on New Package-level functions section below), [securecookie](https://github.com/kataras/iris/tree/master/_examples/cookies/securecookie) example has been updated.
- `Context.RemoveCookie` removes also the Request's specific cookie of the same request lifecycle when `iris.CookieAllowReclaim` is set to cookie options, [example](https://github.com/kataras/iris/tree/master/_examples/cookies/options).
- `iris.TLS` can now accept certificates in form of raw `[]byte` contents too.
- `iris.TLS` registers a secondary http server which redirects "http://" to their "https://" equivalent requests, unless the new `iris.TLSNoRedirect` host Configurator is provided on `iris.TLS`, e.g. `app.Run(iris.TLS("127.0.0.1:443", "mycert.cert", "mykey.key", iris.TLSNoRedirect))`. There is `iris.AutoTLSNoRedirect` option for `AutoTLS` too.
- Fix an [issue](https://github.com/kataras/i18n/issues/1) about i18n loading from path which contains potential language code.
- Server will not return neither log the `ErrServerClosed` error if `app.Shutdown` was called manually via interrupt signal(CTRL/CMD+C), note that if the server closed by any other reason the error will be fired as previously (unless `iris.WithoutServerError(iris.ErrServerClosed)`).
- Finally, Log level's and Route debug information colorization is respected across outputs. Previously if the application used more than one output destination (e.g. a file through `app.Logger().AddOutput`) the color support was automatically disabled from all, including the terminal one, this problem is fixed now. Developers can now see colors in their terminals while log files are kept with clear text.
- New `iris.WithLowercaseRouting` option which forces all routes' paths to be lowercase and converts request paths to their lowercase for matching.
- New `app.Validator { Struct(interface{}) error }` field and `app.Validate` method were added. The `app.Validator = ` can be used to integrate a 3rd-party package such as [go-playground/validator](https://github.com/go-playground/validator). If set-ed then Iris `Context`'s `ReadJSON`, `ReadXML`, `ReadMsgPack`, `ReadYAML`, `ReadForm`, `ReadQuery`, `ReadBody` methods will return the validation error on data validation failures. The [read-json-struct-validation](_examples/request-body/read-json-struct-validation) example was updated.
- A result of <T> can implement the new `hero.PreflightResult` interface which contains a single method of `Preflight(iris.Context) error`. If this method exists on a custom struct value which is returned from a handler then it will fire that `Preflight` first and if not errored then it will cotninue by sending the struct value as JSON(by-default) response body.
- `ctx.JSON, JSONP, XML`: if `iris.WithOptimizations` is NOT passed on `app.Run/Listen` then the indentation defaults to `" "` (four spaces) and `" "` respectfully otherwise it is empty or the provided value.
- Hero Handlers (and `app.ConfigureContainer().Handle`) do not have to require `iris.Context` just to call `ctx.Next()` anymore, this is done automatically now.
- Improve Remote Address parsing as requested at: [#1453](https://github.com/kataras/iris/issues/1453). Add `Configuration.RemoteAddrPrivateSubnets` to exclude those addresses when fetched by `Configuration.RemoteAddrHeaders` through `context.RemoteAddr() string`.
- Fix [#1487](https://github.com/kataras/iris/issues/1487).
- Fix [#1473](https://github.com/kataras/iris/issues/1473).
New Package-level Variables:
- `iris.DirListRichOptions` to pass on `iris.DirListRich` method.
- `iris.DirListRich` to override the default look and feel if the `DirOptions.ShowList` was set to true, can be passed to `DirOptions.DirList` field.
- `DirOptions.PushTargets` for http/2 push on index [*](https://github.com/kataras/iris/tree/master/_examples/file-server/http2push/main.go).
- `iris.Compression` middleware to compress responses and decode compressed request data respectfully.
- `iris.B, KB, MB, GB, TB, PB, EB` for byte units.
- `TLSNoRedirect` to disable automatic "http://" to "https://" redirections (see below)
- `CookieAllowReclaim`, `CookieAllowSubdomains`, `CookieSameSite`, `CookieSecure` and `CookieEncoding` to bring previously sessions-only features to all cookies in the request.
New Context Methods:
- `Context.ViewEngine(ViewEngine)` to set a view engine on-fly for the current chain of handlers, responsible to render templates through `ctx.View`. [Example](_examples/view/context-view-engine).
- `Context.SetErr(error)` and `Context.GetErr() error` helpers.
- `Context.CompressWriter(bool) error` and `Context.CompressReader(bool) error`.
- `Context.Clone() Context` returns a copy of the Context.
- `Context.IsCanceled() bool` reports whether the request has been canceled by the client.
- `Context.IsSSL() bool` reports whether the request is under HTTPS SSL (New `Configuration.SSLProxyHeaders` and `HostProxyHeaders` fields too).
- `Context.CompressReader(enable bool)` method and `iris.CompressReader` middleware to enable future request read body calls to decompress data, [example](_examples/compression/main.go).
- `Context.RegisterDependency(v interface{})` and `Context.UnregisterDependency(typ reflect.Type)` to register/remove struct dependencies on serve-time through a middleware.
- `Context.SetID(id interface{})` and `Context.GetID() interface{}` added to register a custom unique indetifier to the Context, if necessary.
- `Context.GetDomain() string` returns the domain.
- `Context.AddCookieOptions(...CookieOption)` adds options for `SetCookie`, `SetCookieKV, UpsertCookie` and `RemoveCookie` methods for the current request.
- `Context.ClearCookieOptions()` clears any cookie options registered through `AddCookieOptions`.
- `Context.SetLanguage(langCode string)` force-sets a language code from inside a middleare, similar to the `app.I18n.ExtractFunc`
- `Context.ServeContentWithRate`, `ServeFileWithRate` and `SendFileWithRate` methods to throttle the "download" speed of the client
- `Context.IsHTTP2() bool` reports whether the protocol version for incoming request was HTTP/2
- `Context.IsGRPC() bool` reports whether the request came from a gRPC client
- `Context.UpsertCookie(*http.Cookie, cookieOptions ...context.CookieOption)` upserts a cookie, fixes [#1485](https://github.com/kataras/iris/issues/1485) too
- `Context.StopWithStatus(int)` stops the handlers chain and writes the status code
- `Context.StopWithText(int, string)` stops the handlers chain, writes thre status code and a plain text message
- `Context.StopWithError(int, error)` stops the handlers chain, writes thre status code and the error's message
- `Context.StopWithJSON(int, interface{})` stops the handlers chain, writes the status code and sends a JSON response
- `Context.StopWithProblem(int, iris.Problem)` stops the handlers, writes the status code and sends an `application/problem+json` response
- `Context.Protobuf(proto.Message)` sends protobuf to the client (note that the `Context.JSON` is able to send protobuf as JSON)
- `Context.MsgPack(interface{})` sends msgpack format data to the client
- `Context.ReadProtobuf(ptr)` binds request body to a proto message
- `Context.ReadJSONProtobuf(ptr, ...options)` binds JSON request body to a proto message
- `Context.ReadMsgPack(ptr)` binds request body of a msgpack format to a struct
- `Context.ReadBody(ptr)` binds the request body to the "ptr" depending on the request's Method and Content-Type
- `Context.ReflectValue() []reflect.Value` stores and returns the `[]reflect.ValueOf(ctx)`
- `Context.Controller() reflect.Value` returns the current MVC Controller value.
Breaking Changes:
- `versioning.NewGroup(string)` now accepts a `Party` as its first input argument: `NewGroup(Party, string)`.
- `versioning.RegisterGroups` is **removed** as it is no longer necessary.
- `Configuration.RemoteAddrHeaders` from `map[string]bool` to `[]string`. If you used `With(out)RemoteAddrHeader` then you are ready to proceed without any code changes for that one.
- `ctx.Gzip(boolean)` replaced with `ctx.CompressWriter(boolean) error`.
- `ctx.GzipReader(boolean) error` replaced with `ctx.CompressReader(boolean) error`.
- `iris.Gzip` and `iris.GzipReader` replaced with `iris.Compression` (middleware).
- `ctx.ClientSupportsGzip() bool` replaced with `ctx.ClientSupportsEncoding("gzip", "br" ...) bool`.
- `ctx.GzipResponseWriter()` is **removed**.
- `Party.HandleDir/iris.FileServer` now accepts a `http.FileSystem` instead of a string and returns a list of `[]*Route` (GET and HEAD) instead of GET only. Write: `app.HandleDir("/", iris.Dir("./assets"))` instead of `app.HandleDir("/", "./assets")` and `DirOptions.Asset, AssetNames, AssetInfo` removed, use `go-bindata -fs [..]` and `app.HandleDir("/", AssetFile())` instead.
- `Context.OnClose` and `Context.OnCloseConnection` now both accept an `iris.Handler` instead of a simple `func()` as their callback.
- `Context.StreamWriter(writer func(w io.Writer) bool)` changed to `StreamWriter(writer func(w io.Writer) error) error` and it's now the `Context.Request().Context().Done()` channel that is used to receive any close connection/manual cancel signals, instead of the deprecated `ResponseWriter().CloseNotify()` one. Same for the `Context.OnClose` and `Context.OnCloseConnection` methods.
- Fixed handler's error response not be respected when response recorder was used instead of the common writer. Fixes [#1531](https://github.com/kataras/iris/issues/1531). It contains a **BREAKING CHANGE** of: the new `Configuration.ResetOnFireErrorCode` field should be set **to true** in order to behave as it used before this update (to reset the contents on recorder).
- `Context.String()` (rarely used by end-developers) it does not return a unique string anymore, to achieve the old representation you must call the new `Context.SetID` method first.
- `iris.CookieEncode` and `CookieDecode` are replaced with the `iris.CookieEncoding`.
- `sessions#Config.Encode` and `Decode` are removed in favor of (the existing) `Encoding` field.
- `versioning.GetVersion` now returns an empty string if version wasn't found.
- Change the MIME type of `Javascript .js` and `JSONP` as the HTML specification now recommends to `"text/javascript"` instead of the obselete `"application/javascript"`. This change was pushed to the `Go` language itself as well. See <https://go-review.googlesource.com/c/go/+/186927/>.
- Remove the last input argument of `enableGzipCompression` in `Context.ServeContent`, `ServeFile` methods. This was deprecated a few versions ago. A middleware (`app.Use(iris.CompressWriter)`) or a prior call to `Context.CompressWriter(true)` will enable compression. Also these two methods and `Context.SendFile` one now support `Content-Range` and `Accept-Ranges` correctly out of the box (`net/http` had a bug, which is now fixed).
- `Context.ServeContent` no longer returns an error, see `ServeContentWithRate`, `ServeFileWithRate` and `SendFileWithRate` new methods too.
- `route.Trace() string` changed to `route.Trace(w io.Writer)`, to achieve the same result just pass a `bytes.Buffer`
- `var mvc.AutoBinding` removed as the default behavior now resolves such dependencies automatically (see [[FEATURE REQUEST] MVC serving gRPC-compatible controller](https://github.com/kataras/iris/issues/1449)).
- `mvc#Application.SortByNumMethods()` removed as the default behavior now binds the "thinnest" empty `interface{}` automatically (see [MVC: service injecting fails](https://github.com/kataras/iris/issues/1343)).
- `mvc#BeforeActivation.Dependencies().Add` should be replaced with `mvc#BeforeActivation.Dependencies().Register` instead
- **REMOVE** the `kataras/iris/v12/typescript` package in favor of the new [iris-cli](https://github.com/kataras/iris-cli). Also, the alm typescript online editor was removed as it is deprecated by its author, please consider using the [designtsx](https://designtsx.com/) instead.
There is a breaking change on the type alias of `iris.Context` which now points to the `*context.Context` instead of the `context.Context` interface. The **interface has been removed** and the ability to **override** the Context **is not** available any more. When we added the ability from end-developers to override the Context years ago, we have never imagine that we will ever had such a featured Context with more than 4000 lines of code. As of Iris 2020, it is difficult and un-productive from an end-developer to override the Iris Context, and as far as we know, nobody uses this feature anymore because of that exact reason. Beside the overriding feature support end, if you still use the `context.Context` instead of `iris.Context`, it's the time to do it: please find-and-replace to `iris.Context` as wikis, book and all examples shows for the past 3 years. For the 99.9% of the users there is no a single breaking change, you already using `iris.Context` so you are in the "safe zone".
# Su, 16 February 2020 | v12.1.8
New Features:
- [[FEATURE REQUEST] MVC serving gRPC-compatible controller](https://github.com/kataras/iris/issues/1449)
Fixes:
- [App can't find embedded pug template files by go-bindata](https://github.com/kataras/iris/issues/1450)
New Examples:
- [_examples/mvc/grpc-compatible](_examples/mvc/grpc-compatible)
# Mo, 10 February 2020 | v12.1.7
Implement **new** `SetRegisterRule(iris.RouteOverride, RouteSkip, RouteError)` to resolve: https://github.com/kataras/iris/issues/1448
New Examples:
- [_examples/routing/route-register-rule](_examples/routing/route-register-rule)
# We, 05 February 2020 | v12.1.6
Fixes:
- [jet.View - urlpath error](https://github.com/kataras/iris/issues/1438)
- [Context.ServeFile send 'application/wasm' with a wrong extra field](https://github.com/kataras/iris/issues/1440)
# Su, 02 February 2020 | v12.1.5
Various improvements and linting.
# Su, 29 December 2019 | v12.1.4
Minor fix on serving [embedded files](https://github.com/kataras/iris/wiki/File-server).
# We, 25 December 2019 | v12.1.3
Fix [[BUG] [iris.Default] RegisterView](https://github.com/kataras/iris/issues/1410)
# Th, 19 December 2019 | v12.1.2
Fix [[BUG]Session works incorrectly when meets the multi-level TLDs](https://github.com/kataras/iris/issues/1407).
# Mo, 16 December 2019 | v12.1.1
Add [Context.FindClosest(n int) []string](https://github.com/kataras/iris/blob/master/_examples/routing/intelligence/manual/main.go#L22)
```go
app := iris.New()
app.OnErrorCode(iris.StatusNotFound, notFound)
```
```go
func notFound(ctx iris.Context) {
suggestPaths := ctx.FindClosest(3)
if len(suggestPaths) == 0 {
ctx.WriteString("404 not found")
return
}
ctx.HTML("Did you mean?<ul>")
for _, s := range suggestPaths {
ctx.HTML(`<li><a href="%s">%s</a></li>`, s, s)
}
ctx.HTML("</ul>")
}
```
![](https://iris-go.com/images/iris-not-found-suggests.png)
# Fr, 13 December 2019 | v12.1.0
## Breaking Changes
Minor as many of you don't even use them but, indeed, they need to be covered here.
- Old i18n middleware(iris/middleware/i18n) was replaced by the [i18n](i18n) sub-package which lives as field at your application: `app.I18n.Load(globPathPattern string, languages ...string)` (see below)
- Community-driven i18n middleware(iris-contrib/middleware/go-i18n) has a `NewLoader` function which returns a loader which can be passed at `app.I18n.Reset(loader i18n.Loader, languages ...string)` to change the locales parser
- The Configuration's `TranslateFunctionContextKey` was replaced by `LocaleContextKey` which Context store's value (if i18n is used) returns the current Locale which contains the translate function, the language code, the language tag and the index position of it
- The `context.Translate` method was replaced by `context.Tr` as a shortcut for the new `context.GetLocale().GetMessage(format, args...)` method and it matches the view's function `{{tr format args}}` too
- If you used [Iris Django](https://github.com/kataras/iris/tree/master/_examples/view/template_django_0) view engine with `import _ github.com/flosch/pongo2-addons` you **must change** the import path to `_ github.com/iris-contrib/pongo2-addons` or add a [go mod replace](https://github.com/golang/go/wiki/Modules#when-should-i-use-the-replace-directive) to your `go.mod` file, e.g. `replace github.com/flosch/pongo2-addons => github.com/iris-contrib/pongo2-addons v0.0.1`.
## Fixes
All known issues.
1. [#1395](https://github.com/kataras/iris/issues/1395)
2. [#1369](https://github.com/kataras/iris/issues/1369)
3. [#1399](https://github.com/kataras/iris/issues/1399) with PR [#1400](https://github.com/kataras/iris/pull/1400)
4. [#1401](https://github.com/kataras/iris/issues/1401)
5. [#1406](https://github.com/kataras/iris/issues/1406)
6. [neffos/#20](https://github.com/kataras/neffos/issues/20)
7. [pio/#5](https://github.com/kataras/pio/issues/5)
## New Features
### Internationalization and localization
Support for i18n is now a **builtin feature** and is being respected across your entire application, per say [sitemap](https://github.com/kataras/iris/wiki/Sitemap) and [views](https://github.com/kataras/iris/blob/master/_examples/i18n/main.go#L50).
Refer to the wiki section: https://github.com/kataras/iris/wiki/Sitemap for details.
### Sitemaps
Iris generates and serves one or more [sitemap.xml](https://www.sitemaps.org/protocol.html) for your static routes.
Navigate through: https://github.com/kataras/iris/wiki/Sitemap for more.
## New Examples
2. [_examples/i18n](_examples/i18n)
1. [_examples/sitemap](_examples/routing/sitemap)
3. [_examples/desktop/blink](_examples/desktop/blink)
4. [_examples/desktop/lorca](_examples/desktop/lorca)
5. [_examples/desktop/webview](_examples/desktop/webview)
# Sa, 26 October 2019 | v12.0.0
- Add version suffix of the **import path**, learn why and see what people voted at [issue #1370](https://github.com/kataras/iris/issues/1370)
![](https://iris-go.com/images/vote-v12-version-suffix_26_oct_2019.png)
- All errors are now compatible with go1.13 `errors.Is`, `errors.As` and `fmt.Errorf` and a new `core/errgroup` package created
- Fix [#1383](https://github.com/kataras/iris/issues/1383)
- Report whether system couldn't find the directory of view templates
- Remove the `Party#GetReport` method, keep `Party#GetReporter` which is an `error` and an `errgroup.Group`.
- Remove the router's deprecated methods such as StaticWeb and StaticEmbedded_XXX
- The `Context#CheckIfModifiedSince` now returns an `context.ErrPreconditionFailed` type of error when client conditions are not met. Usage: `if errors.Is(err, context.ErrPreconditionFailed) { ... }`
- Add `SourceFileName` and `SourceLineNumber` to the `Route`, reports the exact position of its registration inside your project's source code.
- Fix a bug about the MVC package route binding, see [PR #1364](https://github.com/kataras/iris/pull/1364)
- Add `mvc/Application#SortByNumMethods` as requested at [#1343](https://github.com/kataras/iris/issues/1343#issuecomment-524868164)
- Add status code `103 Early Hints`
- Fix performance of session.UpdateExpiration on 200 thousands+ keys with new radix as reported at [issue #1328](https://github.com/kataras/iris/issues/1328)
- New redis session database configuration field: `Driver: redis.Redigo()` or `redis.Radix()`, see [updated examples](_examples/sessions/database/redis/)
- Add Clusters support for redis:radix session database (`Driver: redis:Radix()`) as requested at [issue #1339](https://github.com/kataras/iris/issues/1339)
- Create Iranian [README_FA](README_FA.md) translation with [PR #1360](https://github.com/kataras/iris/pull/1360)
- Create Korean [README_KO](README_KO.md) translation with [PR #1356](https://github.com/kataras/iris/pull/1356)
- Create Spanish [README_ES](README_ES.md) and [HISTORY_ES](HISTORY_ES.md) translations with [PR #1344](https://github.com/kataras/iris/pull/1344).
The iris-contrib/middleare and examples are updated to use the new `github.com/kataras/iris/v12` import path.
# Fr, 16 August 2019 | v11.2.8
- Set `Cookie.SameSite` to `Lax` when subdomains sessions share is enabled[*](https://github.com/kataras/iris/commit/6bbdd3db9139f9038641ce6f00f7b4bab6e62550)
- Add and update all [experimental handlers](https://github.com/iris-contrib/middleware)
- New `XMLMap` function which wraps a `map[string]interface{}` and converts it to a valid xml content to render through `Context.XML` method
- Add new `ProblemOptions.XML` and `RenderXML` fields to render the `Problem` as XML(application/problem+xml) instead of JSON("application/problem+json) and enrich the `Negotiate` to easily accept the `application/problem+xml` mime.
Commit log: https://github.com/kataras/iris/compare/v11.2.7...v11.2.8
# Th, 15 August 2019 | v11.2.7
This minor version contains improvements on the Problem Details for HTTP APIs implemented on [v11.2.5](#mo-12-august-2019--v1125).
- Fix https://github.com/kataras/iris/issues/1335#issuecomment-521319721
- Add `ProblemOptions` with `RetryAfter` as requested at: https://github.com/kataras/iris/issues/1335#issuecomment-521330994.
- Add `iris.JSON` alias for `context#JSON` options type.
[Example](https://github.com/kataras/iris/blob/45d7c6fedb5adaef22b9730592255f7bb375e809/_examples/routing/http-errors/main.go#L85) and [wikis](https://github.com/kataras/iris/wiki/Routing-error-handlers#the-problem-type) updated.
References:
- https://tools.ietf.org/html/rfc7231#section-7.1.3
- https://tools.ietf.org/html/rfc7807
Commit log: https://github.com/kataras/iris/compare/v11.2.6...v11.2.7
# We, 14 August 2019 | v11.2.6
Allow [handle more than one route with the same paths and parameter types but different macro validation functions](https://github.com/kataras/iris/issues/1058#issuecomment-521110639).
```go
app.Get("/{alias:string regexp(^[a-z0-9]{1,10}\\.xml$)}", PanoXML)
app.Get("/{alias:string regexp(^[a-z0-9]{1,10}$)}", Tour)
```
Commit log: https://github.com/kataras/iris/compare/v11.2.5...v11.2.6
# Mo, 12 August 2019 | v11.2.5
- [New Feature: Problem Details for HTTP APIs](https://github.com/kataras/iris/pull/1336)
- [Add Context.AbsoluteURI](https://github.com/kataras/iris/pull/1336/files#diff-15cce7299aae8810bcab9b0bf9a2fdb1R2368)
Commit log: https://github.com/kataras/iris/compare/v11.2.4...v11.2.5
# Fr, 09 August 2019 | v11.2.4
- Fixes [iris.Jet: no view engine found for '.jet' or '.html'](https://github.com/kataras/iris/issues/1327)
- Fixes [ctx.ViewData not work with JetEngine](https://github.com/kataras/iris/issues/1330)
- **New Feature**: [HTTP Method Override](https://github.com/kataras/iris/issues/1325)
- Fixes [Poor performance of session.UpdateExpiration on 200 thousands+ keys with new radix lib](https://github.com/kataras/iris/issues/1328) by introducing the `sessions.Config.Driver` configuration field which defaults to `Redigo()` but can be set to `Radix()` too, future additions are welcomed.
Commit log: https://github.com/kataras/iris/compare/v11.2.3...v11.2.4
# Tu, 30 July 2019 | v11.2.3
- [New Feature: Handle different parameter types in the same path](https://github.com/kataras/iris/issues/1315)
- [New Feature: Content Negotiation](https://github.com/kataras/iris/issues/1319)
- [Context.ReadYAML](https://github.com/kataras/iris/tree/master/_examples/request-body/read-yaml)
- Fixes https://github.com/kataras/neffos/issues/1#issuecomment-515698536
# We, 24 July 2019 | v11.2.2
Sessions as middleware:
```go
import "github.com/kataras/iris/v12/sessions"
// [...]
app := iris.New()
sess := sessions.New(sessions.Config{...})
app.Get("/path", func(ctx iris.Context){
session := sessions.Get(ctx)
// [work with session...]
})
```
- Add `Session.Len() int` to return the total number of stored values/entries.
- Make `Context.HTML` and `Context.Text` to accept an optional, variadic, `args ...interface{}` input arg(s) too.
## v11.1.1
- https://github.com/kataras/iris/issues/1298
- https://github.com/kataras/iris/issues/1207
# Tu, 23 July 2019 | v11.2.0
Read about the new release at: https://www.facebook.com/iris.framework/posts/3276606095684693

View File

@ -1,141 +0,0 @@
<!-- # History/Changelog <a href="HISTORY_ZH.md"> <img width="20px" src="https://iris-go.com/images/flag-china.svg?v=10" /></a><a href="HISTORY_ID.md"> <img width="20px" src="https://iris-go.com/images/flag-indonesia.svg?v=10" /></a><a href="HISTORY_GR.md"> <img width="20px" src="https://iris-go.com/images/flag-greece.svg?v=10" /></a> -->
# Registro de cambios
### ¿Buscando soporte gratuito y en tiempo real?
https://github.com/kataras/iris/issues
https://chat.iris-go.com
### ¿Buscando versiones anteriores?
https://github.com/kataras/iris/releases
### ¿Quieres ser contratado?
https://facebook.com/iris.framework
### ¿Debo actualizar mi versión de Iris?
Los desarrolladores no están obligados a actualizar si realmente no lo necesitan. Actualice siempre que se sienta listo.
**Cómo actualizar**: Abra su línea de comandos y ejecute este comando: `go get github.com/kataras/iris/v12@latest`.
# Su, 16 February 2020 | v12.1.8
Not translated yet, please navigate to the [english version](HISTORY.md#su-16-february-2020--v1218) instead.
# Sábado, 26 de octubre 2019 | v12.0.0
- Add version suffix of the **import path**, learn why and see what people voted at [issue #1370](https://github.com/kataras/iris/issues/1370)
![](https://iris-go.com/images/vote-v12-version-suffix_26_oct_2019.png)
- Todos los errores ahora son compatibles con `errors.Is`, `errors.As` y `fmt.Errorf` de go1.13 y ha sido creado un nuevo paquete `core/errgroup`
- Corrección [#1383](https://github.com/kataras/iris/issues/1383)
- Informar en cualquier sistema si no se logró encontrar directorio de plantillas para las vistas.
- Se removió el método `Party#GetReport`, se mantuvo `Party#GetReporter` que es un `error` y `errgroup.Group`.
- Se removieron métodos obsoletos del enrutador como StaticWeb y StaticEmbedded_XXX
- `Context#CheckIfModifiedSince` ahora returna tipo error `context.ErrPreconditionFailed` cuando no se cumplen condiciones del cliente. Uso: `if errors.Is(err, context.ErrPreconditionFailed) { ... }`
- Se agregó `SourceFileName` y `SourceLineNumber` a `Route`, informan la posición exacta de su registro dentro del código fuente de su proyecto.
- Se corrige bug sobre enlace de ruta del paquete MVC, ver [PR #1364](https://github.com/kataras/iris/pull/1364)
- Se agregó `mvc/Application#SortByNumMethods` solicitado en [#1343](https://github.com/kataras/iris/issues/1343#issuecomment-524868164)
- Código de estado `103 Early Hints` agregado.
- Se corrigió rendimiento de `session.UpdateExpiration` en nas de 200 mil registros con nuevo radix reportado en [problema #1328](https://github.com/kataras/iris/issues/1328)
- Nuevo campo de configuración de la base de datos de sesión de redis: `Driver: redis.Redigo()` o `redis.Radix()`, ver [ejemplos actualizados](_examples/sessions/database/redis/)
- Se agregó soporte de Clusters para la base de datos de sesión redis: radix (`Driver: redis: Radix ()`) como se solicitó en [problema #1339](https://github.com/kataras/iris/issues/1339)
- Se creó traducción en iraní [README_FA](README_FA.md) en [PR #1360](https://github.com/kataras/iris/pull/1360)
- Se creó traducción en koreano [README_KO](README_KO.md) en [PR #1356](https://github.com/kataras/iris/pull/1356)
- Se creó traducción en español [README_ES](README_ES.md) y [HISTORY_ES](HISTORY_ES.md) en [PR #1344](https://github.com/kataras/iris/pull/1344).
iris-contrib/middleare y ejemplos se actualizaron para utilizar la nueva ruta de importación `github.com/kataras/iris/v12`.
# Viernes, 16 de agosto 2019 | v11.2.8
- Establecer `Cookie.SameSite` como `Lax` cuando el uso compartido de sesiones de subdominios esté habilitado[*](https://github.com/kataras/iris/commit/6bbdd3db9139f9038641ce6f00f7b4bab6e62550)
- Agregados y actualizados todos los [Handlers experimentales](https://github.com/kataras/iris/tree/master/_examples/experimental-handlers)
- Nueva función `XMLMap` que envuelve un `map[string]interface{}` y la convierte en un contenido xml válido para representarlo a través del método `Context.XML`
- Se agregaron nuevos campos `ProblemOptions.XML` y ` RenderXML` para renderizar `Problem` como XML(application/problem+xml) en lugar de JSON("application/problem+json) y enriquezca el `Negotiate` para aceptar fácilmente el mime type `application/problem+xml`.
Registro de commits: https://github.com/kataras/iris/compare/v11.2.7...v11.2.8
# Jueves, 15 de agosto 2019 | v11.2.7
Esta versión menor contiene mejoras en los Detalles del problema para las API HTTP implementadas en [v11.2.5](#lunes-12-de-agosto-2019--v1125).
- Ajuste https://github.com/kataras/iris/issues/1335#issuecomment-521319721
- Agregado `ProblemOptions` con `RetryAfter` como se solicitó en: https://github.com/kataras/iris/issues/1335#issuecomment-521330994.
- Agregado alias `iris.JSON` para el tipo de opciones `context#JSON`.
[Ejemplos](https://github.com/kataras/iris/blob/45d7c6fedb5adaef22b9730592255f7bb375e809/_examples/routing/http-errors/main.go#L85) y [wikis](https://github.com/kataras/iris/wiki/Routing-error-handlers#the-problem-type) actualizados.
Referencias:
- https://tools.ietf.org/html/rfc7231#section-7.1.3
- https://tools.ietf.org/html/rfc7807
Registro de commits: https://github.com/kataras/iris/compare/v11.2.6...v11.2.7
# Miércoles, 14 de agosto 2019 | v11.2.6
Permitir [manejar más de una ruta con las mismas rutas y tipos de parámetros pero diferentes funciones de validación de macros](https://github.com/kataras/iris/issues/1058#issuecomment-521110639).
```go
app.Get("/{alias:string regexp(^[a-z0-9]{1,10}\\.xml$)}", PanoXML)
app.Get("/{alias:string regexp(^[a-z0-9]{1,10}$)}", Tour)
```
Registro de commits: https://github.com/kataras/iris/compare/v11.2.5...v11.2.6
# Lunes, 12 de agosto 2019 | v11.2.5
- [Nueva característica: Detalle del problemas para las APIs HTTP](https://github.com/kataras/iris/pull/1336)
- [Agregado Context.AbsoluteURI](https://github.com/kataras/iris/pull/1336/files#diff-15cce7299aae8810bcab9b0bf9a2fdb1R2368)
Registro de commits: https://github.com/kataras/iris/compare/v11.2.4...v11.2.5
# Viernes, 09 de agosto 2019 | v11.2.4
- Ajustes [iris.Jet: no view engine found for '.jet' or '.html'](https://github.com/kataras/iris/issues/1327)
- Ajustes [ctx.ViewData no funciona con JetEngine](https://github.com/kataras/iris/issues/1330)
- **Nueva característica**: [Override de métodos HTTP](https://github.com/kataras/iris/issues/1325)
- Ajustes [Bajo rendimiento en session.UpdateExpiration en más de 200 mil keys con nueva librería radix](https://github.com/kataras/iris/issues/1328) al introducir el campo de configuración `sessions.Config.Driver` que se establece de forma predeterminada en `Redigo()` pero también se puede establecer en `Radix()`, futuras adiciones son bienvenidas.
Registro de commits: https://github.com/kataras/iris/compare/v11.2.3...v11.2.4
# Martes, 30 de julio 2019 | v11.2.3
- [Nueva característica: Manejar diferentes tipos de parámetros en la misma ruta](https://github.com/kataras/iris/issues/1315)
- [Nueva característica: Negociación de contenido](https://github.com/kataras/iris/issues/1319)
- [Context.ReadYAML](https://github.com/kataras/iris/tree/master/_examples/request-body/read-yaml)
- Ajustes https://github.com/kataras/neffos/issues/1#issuecomment-515698536
# Miércoles, 24 de julio 2019 | v11.2.2
Sesiones como middleware:
```go
import "github.com/kataras/iris/v12/sessions"
// [...]
app := iris.New()
sess := sessions.New(sessions.Config{...})
app.Get("/path", func(ctx iris.Context){
session := sessions.Get(ctx)
// [work with session...]
})
```
- Agregado `Session.Len() int` para devolver el número total de valores/entradas almacenados.
- Permitir que `Context.HTML` y `Context.Text` acepten tambien un argumento `args ...interface{}` opcional y variable.
## v11.1.1
- https://github.com/kataras/iris/issues/1298
- https://github.com/kataras/iris/issues/1207
# Martes, 23 de julio 2019 | v11.2.0
Lea sobre la nueva versión liberada en: https://www.facebook.com/iris.framework/posts/3276606095684693

237
README.md
View File

@ -1,236 +1,3 @@
[![Black Lives Matter](https://iris-go.com/images/blacklivesmatter_banner.png)](https://support.eji.org/give/153413/#!/donation/checkout)
# Iris Web Framework
<!-- # News -->
> This is the under-**development branch**. Stay tuned for the upcoming release [v12.2.0](HISTORY.md#Next). Looking for a stable release? Head over to the [v12.1.8 branch](https://github.com/kataras/iris/tree/v12.1.8) instead.
>
> ![](https://iris-go.com/images/cli.png) Try the official [Iris Command Line Interface](https://github.com/kataras/iris-cli) today!
<!-- ![](https://iris-go.com/images/release.png) Iris version **12.1.8** has been [released](HISTORY.md#su-16-february-2020--v1218)! -->
# Iris Web Framework <a href="README_GR.md"><img width="20px" src="https://iris-go.com/images/flag-greece.svg" /></a> <a href="README_FR.md"><img width="20px" src="https://iris-go.com/images/flag-france.svg" /></a> <a href="README_ZH.md"><img width="20px" src="https://iris-go.com/images/flag-china.svg" /></a> <a href="README_ES.md"><img width="20px" src="https://iris-go.com/images/flag-spain.png" /></a> <a href="README_FA.md"><img width="20px" src="https://iris-go.com/images/flag-iran.svg" /></a> <a href="README_RU.md"><img width="20px" src="https://iris-go.com/images/flag-russia.svg" /></a> <a href="README_KO.md"><img width="20px" src="https://iris-go.com/images/flag-south-korea.svg?v=12" /></a>
[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=for-the-badge&logo=travis)](https://travis-ci.org/kataras/iris) [![view examples](https://img.shields.io/badge/examples%20-173-a83adf.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/master/_examples) [![chat](https://img.shields.io/gitter/room/iris_go/community.svg?color=cc2b5e&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community) <!--[![FOSSA Status](https://img.shields.io/badge/LICENSE%20SCAN-PASSING❤-CD2956?style=for-the-badge&logo=fossa)](https://app.fossa.io/projects/git%2Bgithub.com%2Fkataras%2Firis?ref=badge_shield)--> [![donate](https://img.shields.io/badge/support-Iris-blue.svg?style=for-the-badge&logo=paypal)](https://iris-go.com/donate) <!--[![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=for-the-badge)](https://goreportcard.com/report/github.com/kataras/iris)--><!--[![godocs](https://img.shields.io/badge/go-%20docs-488AC7.svg?style=for-the-badge)](https://pkg.go.dev/github.com/kataras/iris/v12@v12.2.0)--> <!-- [![release](https://img.shields.io/badge/release%20-v12.0-0077b3.svg?style=for-the-badge)](https://github.com/kataras/iris/releases) -->
<!-- <a href="https://iris-go.com"> <img align="right" src="https://iris-go.com/images/logo-w169.png"></a> -->
Iris is a fast, simple yet fully featured and very efficient web framework for Go.
It provides a beautifully expressive and easy to use foundation for your next website or API.
Learn what [others saying about Iris](https://iris-go.com/testimonials/) 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/)
[![Benchmarks: Jul 18, 2020 at 10:46am (UTC)](https://iris-go.com/images/benchmarks.svg)](https://github.com/kataras/server-benchmarks)
## 👑 <a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=7DPAYRDR28MMJ&item_name=Iris+Web+Framework&currency_code=EUR&source=url">Supporters</a>
<p>
<a href="https://github.com/LYF123123"><img src="https://avatars1.githubusercontent.com/u/33317812?v=4" alt ="陆 轶丰" title="LYF123123" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
<a href="https://github.com/xiaozhuai"><img src="https://avatars1.githubusercontent.com/u/4773701?v=4" alt ="Weihang Ding" title="xiaozhuai" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
<a href="https://github.com/fangli"><img src="https://avatars1.githubusercontent.com/u/3032639?v=4" alt ="Li Fang" title="fangli" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
<a href="https://github.com/TechMaster"><img src="https://avatars1.githubusercontent.com/u/1491686?v=4" alt ="TechMaster" title="TechMaster" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
<a href="https://github.com/lensesio"><img src="https://avatars1.githubusercontent.com/u/11728472?v=4" alt ="lenses.io" title="lensesio" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
<a href="https://github.com/celsosz"><img src="https://avatars1.githubusercontent.com/u/3466493?v=4" alt ="Celso Souza" title="celsosz" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
<a href="https://github.com/altafino"><img src="https://avatars1.githubusercontent.com/u/24539467?v=4" alt ="Altafino" title="altafino" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
<a href="https://github.com/thomasfr"><img src="https://avatars1.githubusercontent.com/u/287432?v=4" alt ="Thomas Fritz" title="thomasfr" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
<a href="https://github.com/hengestone"><img src="https://avatars1.githubusercontent.com/u/362587?v=4" alt ="Conrad Steenberg" title="hengestone" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
<a href="https://github.com/se77en"><img src="https://avatars1.githubusercontent.com/u/1468284?v=4" alt ="Damon Zhao" title="se77en" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
<a href="https://github.com/International"><img src="https://avatars1.githubusercontent.com/u/1022918?v=4" alt ="George Opritescu" title="International" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
<a href="https://github.com/Juanses"><img src="https://avatars1.githubusercontent.com/u/6137970?v=4" alt ="Juanses" title="Juanses" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
<a href="https://github.com/ansrivas"><img src="https://avatars1.githubusercontent.com/u/1695056?v=4" alt ="Ankur Srivastava" title="ansrivas" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
<a href="https://github.com/lexrus"><img src="https://avatars1.githubusercontent.com/u/219689?v=4" alt ="Lex Tang" title="lexrus" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
<a href="https://github.com/li3p"><img src="https://avatars1.githubusercontent.com/u/55519?v=4" alt ="li3p" title="li3p" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
</p>
## 📖 Learning Iris
```sh
# https://github.com/kataras/iris/wiki/Installation
$ go get github.com/kataras/iris/v12@master
# assume the following code in main.go file
$ cat main.go
```
```go
package main
import "github.com/kataras/iris/v12"
func main() {
app := iris.New()
booksAPI := app.Party("/books")
{
booksAPI.Use(iris.Compression)
// GET: http://localhost:8080/books
booksAPI.Get("/", list)
// POST: http://localhost:8080/books
booksAPI.Post("/", create)
}
app.Listen(":8080")
}
// Book example.
type Book struct {
Title string `json:"title"`
}
func list(ctx iris.Context) {
books := []Book{
{"Mastering Concurrency in Go"},
{"Go Design Patterns"},
{"Black Hat Go"},
}
ctx.JSON(books)
// TIP: negotiate the response between server's prioritizes
// and client's requirements, instead of ctx.JSON:
// ctx.Negotiation().JSON().MsgPack().Protobuf()
// ctx.Negotiate(books)
}
func create(ctx iris.Context) {
var b Book
err := ctx.ReadJSON(&b)
// TIP: use ctx.ReadBody(&b) to bind
// any type of incoming data instead.
if err != nil {
ctx.StopWithProblem(iris.StatusBadRequest, iris.NewProblem().
Title("Book creation failure").DetailErr(err))
// TIP: use ctx.StopWithError(code, err) when only
// plain text responses are expected on errors.
return
}
println("Received Book: " + b.Title)
ctx.StatusCode(iris.StatusCreated)
}
```
**MVC** equivalent:
```go
import "github.com/kataras/iris/v12/mvc"
```
```go
m := mvc.New(booksAPI)
m.Handle(new(BookController))
```
```go
type BookController struct {
/* dependencies */
}
// GET: http://localhost:8080/books
func (c *BookController) Get() []Book {
return []Book{
{"Mastering Concurrency in Go"},
{"Go Design Patterns"},
{"Black Hat Go"},
}
}
// POST: http://localhost:8080/books
func (c *BookController) Post(b Book) int {
println("Received Book: " + b.Title)
return iris.StatusCreated
}
```
**Run** your Iris web server:
```sh
$ go run main.go
> Now listening on: http://localhost:8080
> Application started. Press CTRL+C to shut down.
```
**List** Books:
```sh
$ curl --header 'Accept-Encoding:gzip' http://localhost:8080/books
[
{
"title": "Mastering Concurrency in Go"
},
{
"title": "Go Design Patterns"
},
{
"title": "Black Hat Go"
}
]
```
**Create** a new Book:
```sh
$ curl -i -X POST \
--header 'Content-Encoding:gzip' \
--header 'Content-Type:application/json' \
--data "{\"title\":\"Writing An Interpreter In Go\"}" \
http://localhost:8080/books
> HTTP/1.1 201 Created
```
That's how an **error** response looks like:
```sh
$ curl -X POST --data "{\"title\" \"not valid one\"}" \
http://localhost:8080/books
> HTTP/1.1 400 Bad Request
{
"status": 400,
"title": "Book creation failure"
"detail": "invalid character '\"' after object key",
}
```
</details>
[![run in the browser](https://img.shields.io/badge/Run-in%20the%20Browser-348798.svg?style=for-the-badge&logo=repl.it)](https://bit.ly/2YJeSZe)
Iris contains extensive and thorough **[wiki](https://github.com/kataras/iris/wiki)** making it easy to get started with the framework.
<!-- ![](https://media.giphy.com/media/Ur8iqy9FQfmPuyQpgy/giphy.gif) -->
For a more detailed technical documentation you can head over to our [godocs](https://godoc.org/github.com/kataras/iris). And for executable code you can always visit the [./_examples](_examples) repository's subdirectory.
### Do you like to read while traveling?
<a href="https://iris-go.com/#book"> <img alt="Book cover" src="https://iris-go.com/images/iris-book-cover-sm.jpg?v=12" /> </a>
[![follow Iris web framework on twitter](https://img.shields.io/twitter/follow/iris_framework?color=ee7506&logoColor=ee7506&style=for-the-badge)](https://twitter.com/intent/follow?screen_name=iris_framework)
You can [request](https://iris-go.com/#book) a PDF version and online access of the **E-Book** today and be participated in the development of Iris.
## 🙌 Contributing
We'd love to see your contribution to the Iris Web Framework! For more information about contributing to the Iris project please check the [CONTRIBUTING.md](CONTRIBUTING.md) file.
[List of all Contributors](https://github.com/kataras/iris/graphs/contributors)
## 🛡 Security Vulnerabilities
If you discover a security vulnerability within Iris, please send an e-mail to [iris-go@outlook.com](mailto:iris-go@outlook.com). All security vulnerabilities will be promptly addressed.
## 📝 License
This project is licensed under the [BSD 3-clause license](LICENSE), just like the Go project itself.
The project name "Iris" was inspired by the Greek mythology.
<!-- ## Stargazers over time
[![Stargazers over time](https://starchart.cc/kataras/iris.svg)](https://starchart.cc/kataras/iris) -->
Stale Release for pre-go modules. Please follow <github.com/kataras/iris/tree/master> instead.

View File

@ -1,76 +0,0 @@
# Iris Web Framework
[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=for-the-badge&logo=travis)](https://travis-ci.org/kataras/iris) [![FOSSA Status](https://img.shields.io/badge/LICENSE%20SCAN-PASSING❤-CD2956?style=for-the-badge&logo=fossa)](https://app.fossa.io/projects/git%2Bgithub.com%2Fkataras%2Firis?ref=badge_shield)<!--[![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=for-the-badge)](https://goreportcard.com/report/github.com/kataras/iris)--><!--[![godocs](https://img.shields.io/badge/go-%20docs-488AC7.svg?style=for-the-badge)](https://pkg.go.dev/github.com/kataras/iris/v12@v12.2.0)--> [![view examples](https://img.shields.io/badge/learn%20by-examples-0C8EC5.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/master/_examples) [![chat](https://img.shields.io/gitter/room/iris_go/community.svg?color=7E18DD&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community)<!--[![donate on PayPal](https://img.shields.io/badge/support-PayPal-blue.svg?style=for-the-badge)](https://iris-go.com/donate)--><!-- [![release](https://img.shields.io/badge/release%20-v12.0-0077b3.svg?style=for-the-badge)](https://github.com/kataras/iris/releases) -->
Iris es un framework web rápido, simple pero con muchas funcionalidades y muy eficiente para Go. Proporciona una base bellamente expresiva y fácil de usar para su próximo sitio web o API.
Descubra lo que [otros dicen sobre Iris](https://iris-go.com/testimonials/) y **siga** :star: este repositorio github.
[![](https://media.giphy.com/media/j5WLmtvwn98VPrm7li/giphy.gif)](https://iris-go.com/testimonials/)
[![Benchmarks: Apr 2, 2020 at 12:13pm (UTC)](https://iris-go.com/images/benchmarks.svg)](https://github.com/kataras/server-benchmarks)
## Aprende Iris
<details>
<summary>Inicio rapido</summary>
```sh
# agrega el siguiente código en el archivo ejemplo.go
$ cat ejemplo.go
```
```go
package main
import "github.com/kataras/iris/v12"
func main() {
app := iris.Default()
app.Get("/ping", func(ctx iris.Context) {
ctx.JSON(iris.Map{
"message": "pong",
})
})
app.Listen(":8080")
}
```
```sh
# ejecuta ejemplo.go y
# visita http://localhost:8080/ping en el navegador
$ go run ejemplo.go
```
> El enrutamiento es impulsado por [muxie](https://github.com/kataras/muxie), el software basado en trie más potente y rápido escrito en Go.
</details>
Iris contiene un extenso y completo **[wiki](https://github.com/kataras/iris/wiki)** que facilita comenzar con el framework.
Para obtener una documentación técnica más detallada, puede dirigirse a nuestros [godocs](https://pkg.go.dev/github.com/kataras/iris/v12@v12.2.0). Y para código ejecutable siempre puede visitar el subdirectorio del repositorio [\_examples](_examples/).
### ¿Te gusta leer mientras viajas?
<a href="https://bit.ly/iris-req-book"> <img alt="Book cover" src="https://iris-go.com/images/iris-book-cover-sm.jpg?v=12" /> </a>
[![follow author](https://img.shields.io/twitter/follow/makismaropoulos.svg?style=for-the-badge)](https://twitter.com/intent/follow?screen_name=makismaropoulos)
Puedes [solicitar](https://bit.ly/iris-req-book) una versión en PDF y acceso en línea del **E-Book** hoy y participar en el desarrollo de Iris.
## Contribuir
¡Nos encantaría ver su contribución al Framework Web Iris! Para obtener más información sobre cómo contribuir al proyecto Iris, consulte el archivo [CONTRIBUTING.md](CONTRIBUTING).
[Lista de todos los contribuyentes](https://github.com/kataras/iris/graphs/contributors)
## Vulnerabilidades de seguridad
Si descubres una vulnerabilidad de seguridad dentro de Iris, envíe un correo electrónico a [iris-go@outlook.com](mailto:iris-go@outlook.com). Todas las vulnerabilidades de seguridad serán tratadas de inmediato.
## Licencia
El nombre del proyecto "Iris" se inspiró en la mitología griega.
El Web Framework Iris es un software gratuito y de código abierto con licencia bajo la [Licencia BSD 3 cláusulas](LICENSE).

View File

@ -1,102 +0,0 @@
# Iris Web Framework
<div dir="ltr" align='left' >
[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=for-the-badge&logo=travis)](https://travis-ci.org/kataras/iris) [![FOSSA Status](https://img.shields.io/badge/LICENSE%20SCAN-PASSING❤-CD2956?style=for-the-badge&logo=fossa)](https://app.fossa.io/projects/git%2Bgithub.com%2Fkataras%2Firis?ref=badge_shield)<!--[![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=for-the-badge)](https://goreportcard.com/report/github.com/kataras/iris)--><!--[![godocs](https://img.shields.io/badge/go-%20docs-488AC7.svg?style=for-the-badge)](https://pkg.go.dev/github.com/kataras/iris/v12@v12.2.0)--> [![view examples](https://img.shields.io/badge/learn%20by-examples-0C8EC5.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/master/_examples) [![chat](https://img.shields.io/gitter/room/iris_go/community.svg?color=7E18DD&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community)<!--[![donate on PayPal](https://img.shields.io/badge/support-PayPal-blue.svg?style=for-the-badge)](https://iris-go.com/donate)--><!-- [![release](https://img.shields.io/badge/release%20-v12.0-0077b3.svg?style=for-the-badge)](https://github.com/kataras/iris/releases) -->
</div>
آیریس سریع ترین و ساده ترین و موثرترین فریمورک وب در زبان GO میباشد. آیریس ساختاری بسیار زیبا و کارآمد را فراهم کرده است تا شما از آن برای پروژه های بعدی تان استفاده کنید. .
برای این که بدانید دیگران در مورد آیریس چه می گویند لطفا در این لینک کلیک کنید [دیگران در مورد آیریس چه می گویند](https://iris-go.com/testimonials/) لطفا این پروژه را در گیتاب **استار** کنید.
[![](https://media.giphy.com/media/j5WLmtvwn98VPrm7li/giphy.gif)](https://iris-go.com/testimonials/)
[![Benchmarks: Apr 2, 2020 at 12:13pm (UTC)](https://iris-go.com/images/benchmarks.svg)](https://github.com/kataras/server-benchmarks)
## آموزش آیریس
<details>
<summary>شروع سریع</summary>
<div dir="ltr" align="left">
<div dir="rtl" align="right">
```sh
# فرض کنید همچین کدی را در فایل example.go نوشته اید
```
</div>
```sh
$ cat example.go
```
```go
package main
import "github.com/kataras/iris/v12"
func main() {
app := iris.Default()
app.Get("/ping", func(ctx iris.Context) {
ctx.JSON(iris.Map{
"message": "pong",
})
})
app.Listen(":8080")
}
```
```sh
# run example.go and
# visit http://localhost:8080/ping on browser
$ go run example.go
```
<div>
<div dir="rtl" align="right" >
> ایریس از پروژه ی [muxie](https://github.com/kataras/muxie) که موثرترین و سریع ترین پروژه مسیریابی در GO می باشد استفاده می کند.
<div>
</details>
آیریس داری **[wiki](https://github.com/kataras/iris/wiki)** بسیار کامل و گسترده ای میباشد که یادگیری ان را ساده می کند.
شما برای مشاهده و خواندن داکیومنت های فنی میتوانید به [godocs](https://pkg.go.dev/github.com/kataras/iris/v12@v12.2.0) مراجعه کنید و همچنین برای مشاهده مثال ها و کد های قابل اجرا همیشه میتوانید از [مثال ها](_examples/) استفاده کنید .
### آیا شما مطالعه کردن در طول سفر را دوست دارید ؟
<div dir="ltr" align="left">
<a href="https://bit.ly/iris-req-book"> <img alt="Book cover" src="https://iris-go.com/images/iris-book-cover-sm.jpg?v=12" /> </a>
</div>
[![follow author](https://img.shields.io/twitter/follow/makismaropoulos.svg?style=for-the-badge)](https://twitter.com/intent/follow?screen_name=makismaropoulos)
شما میتوانید در خواست یک نسخه PDF داکیومنت ر ا به صورت رایگان از اینجا بدهید [درخواست](https://bit.ly/iris-req-book)
## مشارکت کردن
ما دوست داریم که شما در فریمورک آیریس مشارکت کنید و کد ها را توسعه و بهبود ببخشید ! برای اطلاع بیشتر در مورد نحوه ی مشارکت کردن در این پروژه لطفا اینجا را بررسی کنید [CONTRIBUTING.md](CONTRIBUTING.md)
[مشاهده ی همه ی مشارکت کننده ها](https://github.com/kataras/iris/graphs/contributors)
## باگ های امنیتی
اگر شما باگ های امنیتی در آیریس پیدا کردید لطفا یک ایمیل به [iris-go@outlook.com](mailto:iris-go@outlook.com) ارسال کنید. همه ی باگ های امنیتی بلافاصله برطرف میشود.
## مجوز
نام پروژه آیریس ریشه ای یونانی دارد.
فریمورک آیریس رایگان و سورس باز و تحت مجوز [3-Clause BSD License](LICENSE) می باشد.
<div>

View File

@ -1,87 +0,0 @@
# Iris Web Framework
[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=for-the-badge&logo=travis)](https://travis-ci.org/kataras/iris) [![FOSSA Status](https://img.shields.io/badge/LICENSE%20SCAN-PASSING❤-CD2956?style=for-the-badge&logo=fossa)](https://app.fossa.io/projects/git%2Bgithub.com%2Fkataras%2Firis?ref=badge_shield)<!--[![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=for-the-badge)](https://goreportcard.com/report/github.com/kataras/iris)--><!--[![godocs](https://img.shields.io/badge/go-%20docs-488AC7.svg?style=for-the-badge)](https://pkg.go.dev/github.com/kataras/iris/v12@v12.2.0)--> [![view examples](https://img.shields.io/badge/learn%20by-examples-0C8EC5.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/master/_examples) [![chat](https://img.shields.io/gitter/room/iris_go/community.svg?color=7E18DD&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community)<!--[![donate on PayPal](https://img.shields.io/badge/support-PayPal-blue.svg?style=for-the-badge)](https://iris-go.com/donate)--><!-- [![release](https://img.shields.io/badge/release%20-v12.0-0077b3.svg?style=for-the-badge)](https://github.com/kataras/iris/releases) -->
<a href="https://iris-go.com"> <img align="right" src="https://iris-go.com/images/logo-w169.png"></a>
Iris est un framework open-source pour Go à la fois simple, rapide et pourvu de nombreuses fonctionnalités.
Il fournit des moyens simples et élégants de construire les bases et fonctionnalités de votre site, application backend ou API Rest.
Lisez [ce que les développeurs pensent d'Iris](https://iris-go.com/testimonials/) et si l'envie vous prend **[étoilez](https://github.com/kataras/iris/stargazers)** le projet pour faire monter son potentiel.
[![](https://media.giphy.com/media/j5WLmtvwn98VPrm7li/giphy.gif)](https://iris-go.com/testimonials/)
[![Benchmarks: Apr 2, 2020 at 12:13pm (UTC)](https://iris-go.com/images/benchmarks.svg)](https://github.com/kataras/server-benchmarks)
## 📖 Démarrer avec Iris
<details>
<summary>Un simple Hello World</summary>
```sh
# https://github.com/kataras/iris/wiki/Installation
$ go get github.com/kataras/iris/v12@latest
# assume the following code in example.go file
$ cat example.go
```
```go
package main
import "github.com/kataras/iris/v12"
func main() {
app := iris.New()
app.Get("/ping", func(ctx iris.Context) {
ctx.JSON(iris.Map{
"message": "pong",
})
})
app.Listen(":8080") // port d'écoute
}
```
```sh
# compile et execute example.go
$ go run example.go
# maintenant visitez http://localhost:8080/ping
```
> Le routing est géré par [muxie](https://github.com/kataras/muxie), la librairie Go la plus rapide et complète.
</details>
Iris possède un **[wiki](https://github.com/kataras/iris/wiki)** complet et précis qui vous permettra d'implémenter ses fonctionnalités rapidement et facilement.
<!-- ![](https://media.giphy.com/media/Ur8iqy9FQfmPuyQpgy/giphy.gif) -->
Pour une documentation encore plus complète vous pouvez visiter notre [godocs](https://pkg.go.dev/github.com/kataras/iris/v12@v12.2.0) (en Anglais). Et vous trouverez du code executable dans le dossier [\_examples](_examples/).
### Vous préférez une version PDF?
<a href="https://bit.ly/iris-req-book"> <img alt="Book cover" src="https://iris-go.com/images/iris-book-cover-sm.jpg?v=12"/> </a>
[![follow author](https://img.shields.io/twitter/follow/makismaropoulos.svg?style=for-the-badge)](https://twitter.com/intent/follow?screen_name=makismaropoulos)
Vous pouvez [demander](https://bit.ly/iris-req-book) une version **E-Book** (en Anglais) de la documentation et contribuer au développement d'Iris.
## 🙌 Contribuer
Toute contribution à Iris est la bienvenue ! Pour plus d'informations sur la contribution au projet référez-vous au fichier [CONTRIBUTING.md](CONTRIBUTING.md).
[Liste des contributeurs](https://github.com/kataras/iris/graphs/contributors)
## 🛡 Sécurité et vulnérabilités
Si vous trouvez une vulnérabilité dans Iris, envoyez un e-mail à [iris-go@outlook.com](mailto:iris-go@outlook.com). Toute vulnérabilité sera corrigée aussi rapidement que possible.
## 📝 Licence
Le projet est sous licence [licence BSD 3](LICENSE), tout comme le langage Go lui même.
Le nom "Iris" est inspiré de la mythologie Grecque.
<!-- ## Stargazers over time
[![Stargazers over time](https://starchart.cc/kataras/iris.svg)](https://starchart.cc/kataras/iris) -->

View File

@ -1,80 +0,0 @@
# Iris Web Framework
[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=for-the-badge&logo=travis)](https://travis-ci.org/kataras/iris) [![FOSSA Status](https://img.shields.io/badge/LICENSE%20SCAN-PASSING❤-CD2956?style=for-the-badge&logo=fossa)](https://app.fossa.io/projects/git%2Bgithub.com%2Fkataras%2Firis?ref=badge_shield)<!--[![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=for-the-badge)](https://goreportcard.com/report/github.com/kataras/iris)--><!--[![godocs](https://img.shields.io/badge/go-%20docs-488AC7.svg?style=for-the-badge)](https://pkg.go.dev/github.com/kataras/iris/v12@v12.2.0)--> [![view examples](https://img.shields.io/badge/learn%20by-examples-0C8EC5.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/master/_examples) [![chat](https://img.shields.io/gitter/room/iris_go/community.svg?color=7E18DD&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community)<!--[![donate on PayPal](https://img.shields.io/badge/support-PayPal-blue.svg?style=for-the-badge)](https://iris-go.com/donate)--><!-- [![release](https://img.shields.io/badge/release%20-v12.0-0077b3.svg?style=for-the-badge)](https://github.com/kataras/iris/releases) -->
Το Iris είναι ένα γρήγορο, απλό αλλά και πλήρως λειτουργικό και πολύ αποδοτικό web framework για τη Go γλώσσα προγραμματισμού. Παρέχει ένα εκφραστικό και εύχρηστο υπόβαθρο για την επόμενη ιστοσελίδα σας.
Μάθετε τι [λένε οι άλλοι για το Iris](https://iris-go.com/testimonials/) και δώστε ένα **αστεράκι** στο GitHub.
[![](https://media.giphy.com/media/j5WLmtvwn98VPrm7li/giphy.gif)](https://iris-go.com/testimonials/)
[![Benchmarks: Apr 2, 2020 at 12:13pm (UTC)](https://iris-go.com/images/benchmarks.svg)](https://github.com/kataras/server-benchmarks)
## Μαθαίνοντας το Iris
<details>
<summary>Γρήγορο ξεκίνημα</summary>
```sh
# υποθέτοντας ότι ο παρακάτω κώδικας
# βρίσκεται στο example.go αρχείο
#
$ cat example.go
```
```go
package main
import "github.com/kataras/iris/v12"
func main() {
app := iris.Default()
app.Get("/ping", func(ctx iris.Context) {
ctx.JSON(iris.Map{
"message": "pong",
})
})
app.Listen(":8080")
}
```
```sh
# τρέξτε το example.go και
# επισκεφτείτε την σελίδα http://localhost:8080/ping
# στο πρόγραμμα περιήγησης σας
#
$ go run example.go
```
> Η δρομολόγηση τροφοδοτείται από το [muxie](https://github.com/kataras/muxie), το πιο ισχυρό και ταχύτερο λογισμικό βασισμένο σε trie αλγόριθμο που γράφτηκε σε Go.
</details>
Το Iris περιέχει εκτενείς και λεπτομερείς **[wiki](https://github.com/kataras/iris/wiki)** καθιστώντας το εύκολο στην εκμάθηση.
Για λεπτομερέστερη τεχνική τεκμηρίωση μπορείτε να κατευθυνθείτε προς τα [godocs](https://pkg.go.dev/github.com/kataras/iris/v12@v12.2.0) μας. Και για εκτελέσιμο κώδικα μπορείτε πάντα να επισκέπτεστε τα [παραδείγματα](_examples/).
### Σας αρέσει να διαβάζετε ενώ ταξιδεύετε;
Μπορείτε να [ζητήσετε](https://bit.ly/iris-req-book) σήμερα την PDF έκδοση και την online πρόσβαση στο Ηλεκτρονικό μας **Βιβλίο(E-Book)** και να συμμετάσχετε στην ανάπτυξη του Iris.
[![https://iris-go.com/images/iris-book-cover-sm.jpg](https://iris-go.com/images/iris-book-cover-sm.jpg)](https://bit.ly/iris-req-book)
[![follow author](https://img.shields.io/twitter/follow/makismaropoulos.svg?style=for-the-badge)](https://twitter.com/intent/follow?screen_name=makismaropoulos)
## Συνεισφορά
Θα θέλαμε να δούμε τη συμβολή σας στο Iris Web Framework! Για περισσότερες πληροφορίες σχετικά με το πως μπορείτε να συμβάλετε, δείτε το [CONTRIBUTING.md](CONTRIBUTING.md) αρχείο.
[Κατάλογος όλων των συνεισφορών](https://github.com/kataras/iris/graphs/contributors).
## Αδυναμίες Ασφάλειας
Εάν εντοπίσετε κάποια αδυναμία ασφαλείας του Iris, στείλτε ένα μήνυμα ηλεκτρονικού ταχυδρομείου στο [iris-go@outlook.com](mailto:iris-go@outlook.com). Όλες οι τυχών αδυναμίες ασφαλείας θα αντιμετωπιστούν άμεσα.
## Άδεια Χρήσης
Το όνομα "Iris" εμπνεύστηκε από την ελληνική μυθολογία, συγκεκριμένα από την θεά Ίριδα.
Το Iris Web Framework είναι δωρεάν λογισμικό ανοιχτού λογισμικού με άδεια χρήσης [3-Clause BSD](LICENSE).

View File

@ -1,76 +0,0 @@
# Iris Web Framework
[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=for-the-badge&logo=travis)](https://travis-ci.org/kataras/iris) [![FOSSA Status](https://img.shields.io/badge/LICENSE%20SCAN-PASSING❤-CD2956?style=for-the-badge&logo=fossa)](https://app.fossa.io/projects/git%2Bgithub.com%2Fkataras%2Firis?ref=badge_shield)<!--[![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=for-the-badge)](https://goreportcard.com/report/github.com/kataras/iris)--><!--[![godocs](https://img.shields.io/badge/go-%20docs-488AC7.svg?style=for-the-badge)](https://pkg.go.dev/github.com/kataras/iris/v12@v12.2.0)--> [![view examples](https://img.shields.io/badge/learn%20by-examples-0C8EC5.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/master/_examples) [![chat](https://img.shields.io/gitter/room/iris_go/community.svg?color=7E18DD&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community)<!--[![donate on PayPal](https://img.shields.io/badge/support-PayPal-blue.svg?style=for-the-badge)](https://iris-go.com/donate)--><!-- [![release](https://img.shields.io/badge/release%20-v12.0-0077b3.svg?style=for-the-badge)](https://github.com/kataras/iris/releases) -->
Iris는 단순하고 빠르며 좋은 성능과 모든 기능을 갖춘 Go언어용 웹 프레임워크입니다. 당신의 웹사이트나 API를 위해서 아름답고 사용하기 쉬운 기반을 제공합니다.
[여러 사람들의 의견](https://iris-go.com/testimonials/)을 둘러보세요. 그리고 이 github repository을 **star**하세요.
[![](https://media.giphy.com/media/j5WLmtvwn98VPrm7li/giphy.gif)](https://iris-go.com/testimonials/)
[![Benchmarks: Apr 2, 2020 at 12:13pm (UTC)](https://iris-go.com/images/benchmarks.svg)](https://github.com/kataras/server-benchmarks)
## Iris 배우기
<details>
<summary>일단 해보기</summary>
```sh
# 다음 코드를 example.go 화일에 입력하세요.
$ cat example.go
```
```go
package main
import "github.com/kataras/iris/v12"
func main() {
app := iris.Default()
app.Get("/ping", func(ctx iris.Context) {
ctx.JSON(iris.Map{
"message": "pong",
})
})
app.Listen(":8080")
}
```
```sh
# example.go 를 실행하고,
# 웹브라우저에서 http://localhost:8080/ping 를 열어보세요.
$ go run example.go
```
> 라우팅은 Go로 작성한 가장 강력하고 빠른 trie기반의 소프트웨어인 [muxie](https://github.com/kataras/muxie)로 처리합니다.
</details>
Iris는 광범위하고 꼼꼼한 **[wiki](https://github.com/kataras/iris/wiki)** 를 가지고 있기 때문에 쉽게 프레임워크를 시작할 수 있습니다.
더 자세한 기술문서를 보시려면 [godocs](https://pkg.go.dev/github.com/kataras/iris/v12@v12.2.0)를 방문하세요. 그리고 실행가능한 예제코드는 [\_examples](_examples/) 하위 디렉토리에 있습니다.
### 여행하면서 독서를 즐기세요?
<a href="https://bit.ly/iris-req-book"> <img alt="Book cover" src="https://iris-go.com/images/iris-book-cover-sm.jpg?v=12"/> </a>
[![follow author](https://img.shields.io/twitter/follow/makismaropoulos.svg?style=for-the-badge)](https://twitter.com/intent/follow?screen_name=makismaropoulos)
PDF 버전과 **E-Book** 에 대한 온라인 접근을 [요청](https://bit.ly/iris-req-book)하시고 Iris의 개발에 참가하실 수 있습니다.
## 기여하기
Iris 웹 프레임워크에 대한 여러분의 기여를 환영합니다! Iris 프로젝트에 기여하는 방법에 대한 자세한 내용은 [CONTRIBUTING.md](CONTRIBUTING.md) 파일을 참조하십시오.
[기여자 리스트](https://github.com/kataras/iris/graphs/contributors)
## 보안 취약점
만약 Iris에서 보안 취약점을 발견하시면 [iris-go@outlook.com](mailto:iris-go@outlook.com) 로 메일을 보내주세요. 모든 보안 취약점은 즉 해결할 것입니다.
## 라이센스
이 프로젝트의 이름 "Iris"는 그리스 신화에서 영감을 받았습니다.
Iris 웹 프레임워크는 [3-Clause BSD License](LICENSE)를 가지는 무료 오픈소스 소프트웨어입니다.

View File

@ -1,81 +0,0 @@
# Iris Web Framework
[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=for-the-badge&logo=travis)](https://travis-ci.org/kataras/iris) [![FOSSA Status](https://img.shields.io/badge/LICENSE%20SCAN-PASSING❤-CD2956?style=for-the-badge&logo=fossa)](https://app.fossa.io/projects/git%2Bgithub.com%2Fkataras%2Firis?ref=badge_shield)<!--[![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=for-the-badge)](https://goreportcard.com/report/github.com/kataras/iris)--><!--[![godocs](https://img.shields.io/badge/go-%20docs-488AC7.svg?style=for-the-badge)](https://pkg.go.dev/github.com/kataras/iris/v12@v12.2.0)--> [![view examples](https://img.shields.io/badge/learn%20by-examples-0C8EC5.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/master/_examples) [![chat](https://img.shields.io/gitter/room/iris_go/community.svg?color=7E18DD&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community)<!--[![donate on PayPal](https://img.shields.io/badge/support-PayPal-blue.svg?style=for-the-badge)](https://iris-go.com/donate)--><!-- [![release](https://img.shields.io/badge/release%20-v12.0-0077b3.svg?style=for-the-badge)](https://github.com/kataras/iris/releases) -->
Iris — это быстрый, простой, но полнофункциональный и эффективный веб-фреймворк для Go. Он обеспечивает красивую, выразительную и простую в использовании основу для вашего следующего веб-сайта или API.
Узнайте, что [говорят другие люди об Iris](https://iris-go.com/testimonials/) и поставьте **[звёздочку](https://github.com/kataras/iris/stargazers)** этому проекту с открытым исходным кодом, чтобы поддержать его потенциал.
[![](https://media.giphy.com/media/j5WLmtvwn98VPrm7li/giphy.gif)](https://iris-go.com/testimonials/)
[![Benchmarks: Apr 2, 2020 at 12:13pm (UTC)](https://iris-go.com/images/benchmarks.svg)](https://github.com/kataras/server-benchmarks)
## Изучение Iris
<details>
<summary>Быстрый старт</summary>
```sh
# например, код в файле example.go будет таким:
$ cat example.go
```
```go
package main
import "github.com/kataras/iris/v12"
func main() {
app := iris.Default()
app.Get("/ping", func(ctx iris.Context) {
ctx.JSON(iris.Map{
"message": "pong",
})
})
app.Listen(":8080")
}
```
```sh
# запустите example.go и перейдите в браузер
# по адресу http://localhost:8080/ping
$ go run example.go
```
> Система роутинга запросов работает на [muxie](https://github.com/kataras/muxie), мощное и быстрое trie-based ПО, написанное на Go.
</details>
У Iris есть исчерпывающий и тщательный **[wiki](https://github.com/kataras/iris/wiki)**, который позволит вам быстрее начать работу с фреймворком.
<!-- ![](https://media.giphy.com/media/Ur8iqy9FQfmPuyQpgy/giphy.gif) -->
Для получения более подробной технической документации вы можете обратиться к нашему [godoc](https://pkg.go.dev/github.com/kataras/iris/v12@v12.2.0). А для живых примеров кода — вы всегда можете посетить [\_examples](_examples/) в поддиректории этого репозитория.
### Вы любите читать во время путешествий?
<a href="https://bit.ly/iris-req-book"> <img alt="Book cover" src="https://iris-go.com/images/iris-book-cover-sm.jpg?v=12" /> </a>
<!-- [![follow author](https://img.shields.io/twitter/follow/makismaropoulos.svg?style=for-the-badge)](https://twitter.com/intent/follow?screen_name=makismaropoulos) -->
Вы можете [запросить](https://bit.ly/iris-req-book) PDF версию и онлайн-доступ к **E-Book** сегодня и принять участие в разработке Iris.
## Содействие
Мы будем рады видеть ваш вклад в веб-фреймворк Iris! Для получения дополнительной информации о содействии проекту Iris, пожалуйста, проверьте файл [CONTRIBUTING.md](CONTRIBUTING.md).
[Список всех участников](https://github.com/kataras/iris/graphs/contributors)
## Уязвимость безопасности
Если вы обнаружите уязвимость безопасности в Iris, отправьте электронное письмо по адресу [iris-go@outlook.com](mailto:iris-go@outlook.com). Все уязвимости безопасности будут оперативно устранены.
## Лицензия
Название проекта «Iris» было вдохновлено греческой мифологией.
Веб-фреймворк Iris — это ПО с открытым исходным кодом под лицензией [3-Clause BSD License](LICENSE).
## Накопление звёзд со временем
[![Stargazers over time](https://starchart.cc/kataras/iris.svg)](https://starchart.cc/kataras/iris)

View File

@ -1,216 +0,0 @@
[![黑人的命也是命](https://iris-go.com/images/blacklivesmatter_banner.png)](https://support.eji.org/give/153413/#!/donation/checkout)
<!-- # News -->
> 这是一个**开发中的版本**。敬请关注即将发布的版本 [v12.2.0](HISTORY.md#Next)。如果想使用稳定版本,请查看 [v12.1.8 分支](https://github.com/kataras/iris/tree/v12.1.8) 。
>
> ![](https://iris-go.com/images/cli.png) 立即尝试官方的[Iris命令行工具](https://github.com/kataras/iris-cli)
<!-- ![](https://iris-go.com/images/release.png) Iris version **12.1.8** has been [released](HISTORY.md#su-16-february-2020--v1218)! -->
# Iris Web Framework <a href="README_GR.md"><img width="20px" src="https://iris-go.com/images/flag-greece.svg" /></a> <a href="README_FR.md"><img width="20px" src="https://iris-go.com/images/flag-france.svg" /></a> <a href="README_ZH.md"><img width="20px" src="https://iris-go.com/images/flag-china.svg" /></a> <a href="README_ES.md"><img width="20px" src="https://iris-go.com/images/flag-spain.png" /></a> <a href="README_FA.md"><img width="20px" src="https://iris-go.com/images/flag-iran.svg" /></a> <a href="README_RU.md"><img width="20px" src="https://iris-go.com/images/flag-russia.svg" /></a> <a href="README_KO.md"><img width="20px" src="https://iris-go.com/images/flag-south-korea.svg?v=12" /></a>
[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=for-the-badge&logo=travis)](https://travis-ci.org/kataras/iris) [![view examples](https://img.shields.io/badge/examples%20-173-a83adf.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/master/_examples) [![chat](https://img.shields.io/gitter/room/iris_go/community.svg?color=cc2b5e&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community) <!--[![FOSSA Status](https://img.shields.io/badge/LICENSE%20SCAN-PASSING❤-CD2956?style=for-the-badge&logo=fossa)](https://app.fossa.io/projects/git%2Bgithub.com%2Fkataras%2Firis?ref=badge_shield)--> [![donate](https://img.shields.io/badge/support-Iris-blue.svg?style=for-the-badge&logo=paypal)](https://iris-go.com/donate) <!--[![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=for-the-badge)](https://goreportcard.com/report/github.com/kataras/iris)--><!--[![godocs](https://img.shields.io/badge/go-%20docs-488AC7.svg?style=for-the-badge)](https://pkg.go.dev/github.com/kataras/iris/v12@v12.2.0)--> <!-- [![release](https://img.shields.io/badge/release%20-v12.0-0077b3.svg?style=for-the-badge)](https://github.com/kataras/iris/releases) -->
<!-- <a href="https://iris-go.com"> <img align="right" src="https://iris-go.com/images/logo-w169.png"></a> -->
Iris 是基于 Go 编写的一个快速,简单但功能齐全且非常高效的 Web 框架。
它为您的下一个网站或 API 提供了一个非常富有表现力且易于使用的基础。
看看 [其他人如何评价 Iris](https://iris-go.com/testimonials/),同时欢迎各位为此开源项目点亮 **[star](https://github.com/kataras/iris/stargazers)**。
[![](https://iris-go.com/images/reviews.gif)](https://iris-go.com/testimonials/)
[![Benchmarks: Jul 18, 2020 at 10:46am (UTC)](https://iris-go.com/images/benchmarks.svg)](https://github.com/kataras/server-benchmarks)
## 📖 开始学习 Iris
```sh
# 安装Irishttps://github.com/kataras/iris/wiki/Installation
$ go get github.com/kataras/iris/v12@master
# 假设main.go文件中已存在以下代码
$ cat main.go
```
```go
package main
import "github.com/kataras/iris/v12"
func main() {
app := iris.New()
booksAPI := app.Party("/books")
{
booksAPI.Use(iris.Compression)
// GET: http://localhost:8080/books
booksAPI.Get("/", list)
// POST: http://localhost:8080/books
booksAPI.Post("/", create)
}
app.Listen(":8080")
}
// Book example.
type Book struct {
Title string `json:"title"`
}
func list(ctx iris.Context) {
books := []Book{
{"Mastering Concurrency in Go"},
{"Go Design Patterns"},
{"Black Hat Go"},
}
ctx.JSON(books)
// 提示: 在服务器优先级和客户端请求中进行响应协商,
// 以此来代替 ctx.JSON:
// ctx.Negotiation().JSON().MsgPack().Protobuf()
// ctx.Negotiate(books)
}
func create(ctx iris.Context) {
var b Book
err := ctx.ReadJSON(&b)
// 提示: 使用 ctx.ReadBody(&b) 代替,来绑定所有类型的入参
if err != nil {
ctx.StopWithProblem(iris.StatusBadRequest, iris.NewProblem().
Title("Book creation failure").DetailErr(err))
// 提示: 如果仅有纯文本plain text错误响应
// 可使用 ctx.StopWithError(code, err)
return
}
println("Received Book: " + b.Title)
ctx.StatusCode(iris.StatusCreated)
}
```
同样地,在**MVC**中 :
```go
import "github.com/kataras/iris/v12/mvc"
```
```go
m := mvc.New(booksAPI)
m.Handle(new(BookController))
```
```go
type BookController struct {
/* dependencies */
}
// GET: http://localhost:8080/books
func (c *BookController) Get() []Book {
return []Book{
{"Mastering Concurrency in Go"},
{"Go Design Patterns"},
{"Black Hat Go"},
}
}
// POST: http://localhost:8080/books
func (c *BookController) Post(b Book) int {
println("Received Book: " + b.Title)
return iris.StatusCreated
}
```
**启动** 您的 Iris web 服务:
```sh
$ go run main.go
> Now listening on: http://localhost:8080
> Application started. Press CTRL+C to shut down.
```
Books **列表查询** :
```sh
$ curl --header 'Accept-Encoding:gzip' http://localhost:8080/books
[
{
"title": "Mastering Concurrency in Go"
},
{
"title": "Go Design Patterns"
},
{
"title": "Black Hat Go"
}
]
```
**创建** 新的Book:
```sh
$ curl -i -X POST \
--header 'Content-Encoding:gzip' \
--header 'Content-Type:application/json' \
--data "{\"title\":\"Writing An Interpreter In Go\"}" \
http://localhost:8080/books
> HTTP/1.1 201 Created
```
这是**错误**响应所展示的样子:
```sh
$ curl -X POST --data "{\"title\" \"not valid one\"}" \
http://localhost:8080/books
> HTTP/1.1 400 Bad Request
{
"status": 400,
"title": "Book creation failure"
"detail": "invalid character '\"' after object key",
}
```
</details>
[![run in the browser](https://img.shields.io/badge/Run-in%20the%20Browser-348798.svg?style=for-the-badge&logo=repl.it)](https://bit.ly/2YJeSZe)
Iris 有完整且详尽的 **[使用文档](https://github.com/kataras/iris/wiki)** ,让您可以轻松地使用此框架。
<!-- ![](https://media.giphy.com/media/Ur8iqy9FQfmPuyQpgy/giphy.gif) -->
要了解更详细的技术文档,请访问我们的 [godocs](https://godoc.org/github.com/kataras/iris)。如果想要寻找代码示例,您可以到仓库的 [./_examples](_examples) 子目录下获取。
### 你喜欢在旅行时阅读吗?
<a href="https://bit.ly/iris-req-book"> <img alt="Book cover" src="https://iris-go.com/images/iris-book-cover-sm.jpg?v=12" /> </a>
[![follow Iris web framework on twitter](https://img.shields.io/twitter/follow/iris_framework?color=ee7506&logoColor=ee7506&style=for-the-badge)](https://twitter.com/intent/follow?screen_name=iris_framework)
您可以[获取](https://bit.ly/iris-req-book)PDF版本或在线访问**电子图书**并参与到Iris的开发中。
## 🙌 贡献
我们欢迎您为Iris框架做出贡献想要知道如何为Iris项目做贡献请查看[CONTRIBUTING.md]CONTRIBUTING.md
[贡献者名单](https://github.com/kataras/iris/graphs/contributors)
## 🛡 安全漏洞
如果您发现在 Iris 存在安全漏洞,请发送电子邮件至 [iris-go@outlook.com](mailto:iris-go@outlook.com)。所有安全漏洞将会得到及时解决。
## 📝 开源协议License
就像Go语言的协议一样此项目也采用 [BSD 3-clause license](LICENSE)。
项目名称 "Iris" 的灵感来自于希腊神话。
<!-- ## Stargazers over time
[![Stargazers over time](https://starchart.cc/kataras/iris.svg)](https://starchart.cc/kataras/iris) -->

View File

@ -1 +0,0 @@
12.1.8:https://github.com/kataras/iris/releases/tag/v12.1.8

View File

@ -1,219 +0,0 @@
# Table of Contents
* [REST API for Apache Kafka](kafka-api)
* [URL Shortener](url-shortener)
* [Dropzone.js](dropzonejs)
* [Caddy](caddy)
* Database
* [MySQL, Groupcache & Docker](database/mysql)
* [MongoDB](database/mongodb)
* [Xorm](database/orm/xorm/main.go)
* [Gorm](database/orm/gorm/main.go)
* [Reform](database/orm/reform/main.go)
* HTTP Server
* [HOST:PORT](http-server/listen-addr/main.go)
* [Public Test Domain](http-server/listen-addr-public/main.go)
* [UNIX socket file](http-server/listen-unix/main.go)
* [TLS](http-server/listen-tls/main.go)
* [Letsencrypt (Automatic Certifications)](http-server/listen-letsencrypt/main.go)
* [Socket Sharding (SO_REUSEPORT)](http-server/socket-sharding/main.go)
* [Graceful Shutdown](http-server/graceful-shutdown/default-notifier/main.go)
* [Notify on shutdown](http-server/notify-on-shutdown/main.go)
* Custom TCP Listener
* [Common net.Listener](http-server/custom-listener/main.go)
* Custom HTTP Server
* [Pass a custom Server](http-server/custom-httpserver/easy-way/main.go)
* [Use Iris as a single http.Handler](http-server/custom-httpserver/std-way/main.go)
* [Multi Instances](http-server/custom-httpserver/multi/main.go)
* [HTTP/3 Quic](http-server/http3-quic)
* Configuration
* [Functional](configuration/functional/main.go)
* [Configuration Struct](configuration/from-configuration-structure/main.go)
* [Import from YAML](configuration/from-yaml-file/main.go)
* [Share Configuration across instances](configuration/from-yaml-file/shared-configuration/main.go)
* [Import from TOML](configuration/from-toml-file/main.go)
* Routing
* [Overview](routing/overview/main.go)
* [Basic](routing/basic/main.go)
* [Custom HTTP Errors](routing/http-errors/main.go)
* [Not Found - Intelligence](routing/intelligence/main.go)
* [Not Found - Suggest Closest Paths](routing/intelligence/manual/main.go)
* [Dynamic Path](routing/dynamic-path/main.go)
* [Root Wildcard](routing/dynamic-path/root-wildcard/main.go)
* [Implement a Parameter Type](routing/macros/main.go)
* [Same Path Pattern but Func](routing/dynamic-path/same-pattern-different-func/main.go)
* Middleware
* [Per Route](routing/writing-a-middleware/per-route/main.go)
* [Globally](routing/writing-a-middleware/globally/main.go)
* [Handlers Execution Rule](routing/route-handlers-execution-rules/main.go)
* [Route Register Rule](routing/route-register-rule/main.go)
* Convert net/http Handlers
* [From func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc)](convert-handlers/negroni-like/main.go)
* [From http.Handler or http.HandlerFunc](convert-handlers/nethttp/main.go)
* [From func(http.HandlerFunc) http.HandlerFunc](convert-handlers/real-usecase-raven/writing-middleware/main.go)
* [Route State](routing/route-state/main.go)
* [Reverse Routing](routing/reverse/main.go)
* [Router Wrapper](routing/custom-wrapper/main.go)
* [Custom Router](routing/custom-router/main.go)
* Subdomains
* [Single](routing/subdomains/single/main.go)
* [Multi](routing/subdomains/multi/main.go)
* [Wildcard](routing/subdomains/wildcard/main.go)
* [WWW](routing/subdomains/www/main.go)
* [Redirection](routing/subdomains/redirect/main.go)
* [HTTP Method Override](https://github.com/kataras/iris/blob/master/middleware/methodoverride/methodoverride_test.go)
* [API Versioning](routing/versioning/main.go)
* [Sitemap](routing/sitemap/main.go)
* Logging
* [Request Logger](logging/request-logger/main.go)
* [Log Requests to a File](logging/request-logger/request-logger-file/main.go)
* [Log Requests to a JSON File](logging/request-logger/request-logger-file-json/main.go)
* [Application File Logger](logging/file-logger/main.go)
* [Application JSON Logger](logging/json-logger/main.go)
* [Rollbar](logging/rollbar/main.go)
* API Documentation
* [Yaag](apidoc/yaag/main.go)
* [Swagger](https://github.com/iris-contrib/swagger/tree/master/example)
* [Testing](testing/httptest/main_test.go)
* [Recovery](recover/main.go)
* [Profiling](pprof/main.go)
* File Server
* [File Server](file-server/file-server/main.go)
* [HTTP/2 Push Targets](file-server/http2push/main.go)
* [HTTP/2 Push Targets (Embedded)](file-server/http2push-embedded/main.go)
* [HTTP/2 Push Targets (Gzipped Embedded)](file-server/http2push-embedded-gzipped/main.go)
* [Favicon](file-server/favicon/main.go)
* [Basic](file-server/basic/main.go)
* [Embedding Files Into App Executable File](file-server/embedding-files-into-app/main.go)
* [Embedding Gzipped Files Into App Executable File](file-server/embedding-gzipped-files-into-app/main.go)
* [Send Files (rate limiter included)](file-server/send-files/main.go)
* Single Page Applications
* [Basic SPA](file-server/single-page-application/basic/main.go)
* [Embedded Single Page Application](file-server/single-page-application/embedded-single-page-application/main.go)
* [Embedded Single Page Application with other routes](file-server/single-page-application/embedded-single-page-application-with-other-routes/main.go)
* [Upload File](file-server/upload-file/main.go)
* [Upload Multiple Files](file-server/upload-files/main.go)
* View
* [Overview](view/overview/main.go)
* [Basic](view/template_html_0/main.go)
* [A simple Layout](view/template_html_1/main.go)
* [Layouts: `yield` and `render` tmpl funcs](view/template_html_2/main.go)
* [The `urlpath` tmpl func](view/template_html_3/main.go)
* [The `url` tmpl func](view/template_html_4/main.go)
* [Inject Data Between Handlers](view/context-view-data/main.go)
* [Inject Engine Between Handlers](view/context-view-engine/main.go)
* [Embedding Templates Into App Executable File](view/embedding-templates-into-app/main.go)
* [Write to a custom `io.Writer`](view/write-to)
* [Blocks](view/template_blocks_0)
* [Blocks Embedded](view/template_blocks_1_embedded)
* [Pug: Greeting](view/template_pug_0)
* [Pug: `Actions`](view/template_pug_1)
* [Pug: `Includes`](view/template_pug_2)
* [Pug: `Extends`](view/template_pug_3)
* [Jet Template](view/template_jet_0)
* [Jet Embedded](view/template_jet_1_embedded)
* [Jet 'urlpath' tmpl func](view/template_jet_2)
* [Jet Template Funcs from Struct](view/template_jet_3)
- [Ace](view/template_ace_0)
* Third-Parties
* [Render `valyala/quicktemplate` templates](view/quicktemplate)
* [Render `shiyanhui/hero` templates](view/herotemplate)
* [Request ID](https://github.com/kataras/iris/blob/master/middleware/requestid/requestid_test.go)
* [Request Rate Limit](request-ratelimit/main.go)
* [Request Referrer](request-referrer/main.go)
* [Webassembly](webassembly/main.go)
* Request Body
* [Bind JSON](request-body/read-json/main.go)
* * [Struct Validation](request-body/read-json-struct-validation/main.go)
* [Bind XML](request-body/read-xml/main.go)
* [Bind MsgPack](request-body/read-msgpack/main.go)
* [Bind YAML](request-body/read-yaml/main.go)
* [Bind Form](request-body/read-form/main.go)
* [Bind Query](request-body/read-query/main.go)
* [Bind Body](request-body/read-body/main.go)
* [Bind Custom per type](request-body/read-custom-per-type/main.go)
* [Bind Custom via Unmarshaler](request-body/read-custom-via-unmarshaler/main.go)
* [Bind Many times](request-body/read-many/main.go)
* Response Writer
* [Content Negotiation](response-writer/content-negotiation)
* [Text, Markdown, YAML, HTML, JSON, JSONP, Msgpack, XML and Binary](response-writer/write-rest/main.go)
* [Protocol Buffers](response-writer/protobuf/main.go)
* [HTTP/2 Server Push](response-writer/http2push/main.go)
* [Stream Writer](response-writer/stream-writer/main.go)
* [Transactions](response-writer/transactions/main.go)
* [SSE](response-writer/sse/main.go)
* [SSE (third-party package usage for server sent events)](response-writer/sse-third-party/main.go)
* Cache
* [Simple](response-writer/cache/simple/main.go)
* [Client-Side (304)](response-writer/cache/client-side/main.go)
* Compression
* [Server-Side](compression/main.go)
* [Client-Side](compression/client/main.go)
* [Client-Side (using Iris)](compress/client-using-iris/main.go)
* Localization and Internationalization
* [i18n](i18n/main.go)
* Authentication, Authorization & Bot Detection
* [Basic Authentication](auth/basicauth/main.go)
* [CORS](auth/cors)
* [JWT](auth/jwt/main.go)
* [JWT (community edition)](https://github.com/iris-contrib/middleware/tree/v12/jwt/_example/main.go)
* [OAUth2](auth/goth/main.go)
* [Manage Permissions](auth/permissions/main.go)
* [Google reCAPTCHA](auth/recaptcha/main.go)
* [hCaptcha](auth/hcaptcha/main.go)
* Cookies
* [Basic](cookies/basic/main.go)
* [Options](cookies/options/main.go)
* [Encode/Decode (with `securecookie`)](cookies/securecookie/main.go)
* Sessions
* [Overview: Config](sessions/overview/main.go)
* [Overview: Routes](sessions/overview/example/example.go)
* [Basic](sessions/basic/main.go)
* [Secure Cookie](sessions/securecookie/main.go)
* [Flash Messages](sessions/flash-messages/main.go)
* [Databases](sessions/database)
* [Badger](sessions/database/badger/main.go)
* [BoltDB](sessions/database/boltdb/main.go)
* [Redis](sessions/database/redis/main.go)
* Websocket
* [Gorilla FileWatch (3rd-party)](websocket/gorilla-filewatch/main.go)
* [Basic](websocket/basic)
* [Server](websocket/basic/server.go)
* [Go Client](websocket/basic/go-client/client.go)
* [Browser Client](websocket/basic/browser/index.html)
* [Browser NPM Client (browserify)](websocket/basic/browserify/app.js)
* [Native Messages](websocket/native-messages/main.go)
* [TLS](websocket/secure/README.md)
* [Online Visitors](websocket/online-visitors/main.go)
* Dependency Injection
* [Overview (Movies Service)](ependency-injection/overview/main.go)
* [Basic](dependency-injection/basic/main.go)
* [Middleware](dependency-injection/basic/middleware/main.go)
* [Sessions](dependency-injection/sessions/main.go)
* [Smart Contract](dependency-injection/smart-contract/main.go)
* [JWT](dependency-injection/jwt/main.go)
* [JWT (iris-contrib)](dependency-injection/jwt/contrib/main.go)
* [Register Dependency from Context](dependency-injection/context-register-dependency/main.go)
* MVC
* [Overview](mvc/overview)
* [Repository and Service layers](mvc/repository)
* [Hello world](mvc/hello-world/main.go)
* [Basic](mvc/basic/main.go)
* [Wildcard](mvc/basic/wildcard/main.go)
* [Singleton](mvc/singleton)
* [Regexp](mvc/regexp/main.go)
* [Session Controller](mvc/session-controller/main.go)
* [Authenticated Controller](mvc/authenticated-controller/main.go)
* [Versioned Controller](mvc/versioned-controller/main.go)
* [Websocket Controller](mvc/websocket)
* [Register Middleware](mvc/middleware)
* [gRPC](mvc/grpc-compatible)
* [Login (Repository and Service layers)](mvc/login)
* [Login (Single Responsibility)](mvc/login-mvc-single-responsibility)
* [Vue.js Todo App](mvc/vuejs-todo-mvc)
* [Bootstrapper](bootstrapper)
* Desktop Applications
* [The blink package](desktop/blink)
* [The lorca package](desktop/lorca)
* [The webview package](desktop/webview)
* Middlewares [(Community)](https://github.com/iris-contrib/middleware)

View File

@ -1,3 +0,0 @@
# Swagger 2.0
Visit https://github.com/iris-contrib/swagger instead.

View File

@ -1,8 +0,0 @@
module github.com/kataras/iris/_examples/apidoc/yaag
go 1.15
require (
github.com/betacraft/yaag v1.0.1-0.20200719063524-47d781406108
github.com/kataras/iris/v12 v12.1.9-0.20200812051831-0edf0affb0bd
)

View File

@ -1,55 +0,0 @@
package main
import (
"github.com/kataras/iris/v12"
"github.com/betacraft/yaag/irisyaag"
"github.com/betacraft/yaag/yaag"
)
type myXML struct {
Result string `xml:"result"`
}
func main() {
app := iris.New()
yaag.Init(&yaag.Config{ // <- IMPORTANT, init the middleware.
On: true,
DocTitle: "Iris",
DocPath: "apidoc.html",
BaseUrls: map[string]string{"Production": "", "Staging": ""},
})
app.Use(irisyaag.New()) // <- IMPORTANT, register the middleware.
app.Get("/json", func(ctx iris.Context) {
ctx.JSON(iris.Map{"result": "Hello World!"})
})
app.Get("/plain", func(ctx iris.Context) {
ctx.Text("Hello World!")
})
app.Get("/xml", func(ctx iris.Context) {
ctx.XML(myXML{Result: "Hello World!"})
})
app.Get("/complex", func(ctx iris.Context) {
value := ctx.URLParam("key")
ctx.JSON(iris.Map{"value": value})
})
// Run our HTTP Server.
//
// Documentation of "yaag" doesn't note the follow, but in Iris we are careful on what
// we provide to you.
//
// Each incoming request results on re-generation and update of the "apidoc.html" file.
// Recommentation:
// Write tests that calls those handlers, save the generated "apidoc.html".
// Turn off the yaag middleware when in production.
//
// Example usage:
// Visit all paths and open the generated "apidoc.html" file to see the API's automated docs.
app.Listen(":8080")
}

View File

@ -1,57 +0,0 @@
package main
import (
"time"
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/middleware/basicauth"
)
func newApp() *iris.Application {
app := iris.New()
authConfig := basicauth.Config{
Users: map[string]string{"myusername": "mypassword", "mySecondusername": "mySecondpassword"},
Realm: "Authorization Required", // defaults to "Authorization Required"
Expires: time.Duration(30) * time.Minute,
}
authentication := basicauth.New(authConfig)
// to global app.Use(authentication) (or app.UseGlobal before the .Run)
// to routes
/*
app.Get("/mysecret", authentication, h)
*/
app.Get("/", func(ctx iris.Context) { ctx.Redirect("/admin") })
// to party
needAuth := app.Party("/admin", authentication)
{
//http://localhost:8080/admin
needAuth.Get("/", h)
// http://localhost:8080/admin/profile
needAuth.Get("/profile", h)
// http://localhost:8080/admin/settings
needAuth.Get("/settings", h)
}
return app
}
func main() {
app := newApp()
// open http://localhost:8080/admin
app.Listen(":8080")
}
func h(ctx iris.Context) {
username, password, _ := ctx.Request().BasicAuth()
// third parameter it will be always true because the middleware
// makes sure for that, otherwise this handler will not be executed.
ctx.Writef("%s %s:%s", ctx.Path(), username, password)
}

View File

@ -1,29 +0,0 @@
package main
import (
"testing"
"github.com/kataras/iris/v12/httptest"
)
func TestBasicAuth(t *testing.T) {
app := newApp()
e := httptest.New(t, app)
// redirects to /admin without basic auth
e.GET("/").Expect().Status(httptest.StatusUnauthorized)
// without basic auth
e.GET("/admin").Expect().Status(httptest.StatusUnauthorized)
// with valid basic auth
e.GET("/admin").WithBasicAuth("myusername", "mypassword").Expect().
Status(httptest.StatusOK).Body().Equal("/admin myusername:mypassword")
e.GET("/admin/profile").WithBasicAuth("myusername", "mypassword").Expect().
Status(httptest.StatusOK).Body().Equal("/admin/profile myusername:mypassword")
e.GET("/admin/settings").WithBasicAuth("myusername", "mypassword").Expect().
Status(httptest.StatusOK).Body().Equal("/admin/settings myusername:mypassword")
// with invalid basic auth
e.GET("/admin/settings").WithBasicAuth("invalidusername", "invalidpassword").
Expect().Status(httptest.StatusUnauthorized)
}

View File

@ -1,63 +0,0 @@
// Package main integrates the "rs/cors" net/http middleware into Iris.
// That cors third-party middleware cannot be registered through `iris.FromStd`
// as a common middleware because it should be injected before the Iris Router itself,
// it allows/dissallows HTTP Methods too.
//
// This is just an example you can use to run something, based on custom logic,
// before the Iris Router itself.
//
// In the "routing/custom-wrapper" example
// we learn how we can acquire and release an Iris context to fire an Iris Handler
// based on custom logic, before the Iris Router itself. In that example
// we will fire a net/http handler (the "rs/cors" handler one) instead.
//
// https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
package main
import (
"github.com/kataras/iris/v12"
"github.com/rs/cors"
)
func main() {
app := iris.New()
c := cors.New(cors.Options{
AllowedOrigins: []string{"*"},
AllowCredentials: true,
// Enable Debugging for testing, consider disabling in production
Debug: true,
})
// app.WrapRouter(func(w http.ResponseWriter, r *http.Request, router http.HandlerFunc) {
// [custom logic...]
// if shouldFireNetHTTPHandler {
// ...ServeHTTP(w,r)
// return
// }
// router(w,r)
// })
// In our case, the cors package has a ServeHTTP
// of the same form of app.WrapRouter's accept input argument,
// so we can just do:
app.WrapRouter(c.ServeHTTP)
// Serve ./public/index.html, main.js.
app.HandleDir("/", iris.Dir("./public"))
// Register routes here...
app.Get("/data", listData)
// http://localhost:8080 and click the "fetch data" button.
app.Listen(":8080")
}
type item struct {
Title string `json:"title"`
}
func listData(ctx iris.Context) {
ctx.JSON([]item{
{"Item 1"},
{"Item 2"},
{"Item 3"},
})
}

View File

@ -1,18 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Iris Cors Example</title>
</head>
<body>
<ul id="list">
</ul>
<input type="button" value="Fetch Data" id="fetchBtn" />
<script src="main.js"></script>
</body>
</html>

View File

@ -1,43 +0,0 @@
// https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
async function doRequest(method = 'GET', url = '', data = {}) {
// Default options are marked with *
const request = {
method: method, // *GET, POST, PUT, DELETE, etc.
mode: 'cors', // no-cors, *cors, same-origin
cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
credentials: 'same-origin', // include, *same-origin, omit
redirect: 'follow', // manual, *follow, error
referrerPolicy: 'no-referrer', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
};
if (data !== undefined && method !== 'GET' && method !== 'HEAD') {
request.headers = {
'Content-Type': 'application/json'
// 'Content-Type': 'application/x-www-form-urlencoded',
};
// body data type must match "Content-Type" header.
request.body = JSON.stringify(data);
}
const response = await fetch(url, request);
return response.json(); // parses JSON response into native JavaScript objects.
}
const ul = document.getElementById("list");
function fetchData() {
console.log("sending request...")
doRequest('GET', '/data').then(data => {
data.forEach(item => {
var li = document.createElement("li");
li.appendChild(document.createTextNode(item.title));
ul.appendChild(li);
});
console.log(data); // JSON data parsed by `response.json()` call.
});
}
document.getElementById("fetchBtn").onclick = fetchData;

View File

@ -1,407 +0,0 @@
package main
// Any OAuth2 (even the pure golang/x/net/oauth2) package
// can be used with iris but at this example we will see the markbates' goth:
//
// $ go get github.com/markbates/goth/...
//
// This OAuth2 example works with sessions, so we will need
// to attach a session manager.
// Optionally: for even more secure session values,
// developers can use any third-party package to add a custom cookie encoder/decoder.
// At this example we will use the gorilla's securecookie:
//
// $ go get github.com/gorilla/securecookie
// Example of securecookie can be found at "sessions/securecookie" example folder.
// Notes:
// The whole example is converted by markbates/goth/example/main.go.
// It's tested with my own TWITTER application and it worked, even for localhost.
// I guess that everything else works as expected, all bugs reported by goth library's community
// are fixed in the time I wrote that example, have fun!
import (
"errors"
"os"
"sort"
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/sessions"
"github.com/gorilla/securecookie" // optionally, used for session's encoder/decoder
"github.com/markbates/goth"
"github.com/markbates/goth/providers/amazon"
"github.com/markbates/goth/providers/auth0"
"github.com/markbates/goth/providers/bitbucket"
"github.com/markbates/goth/providers/box"
"github.com/markbates/goth/providers/dailymotion"
"github.com/markbates/goth/providers/deezer"
"github.com/markbates/goth/providers/digitalocean"
"github.com/markbates/goth/providers/discord"
"github.com/markbates/goth/providers/dropbox"
"github.com/markbates/goth/providers/facebook"
"github.com/markbates/goth/providers/fitbit"
"github.com/markbates/goth/providers/github"
"github.com/markbates/goth/providers/gitlab"
"github.com/markbates/goth/providers/gplus"
"github.com/markbates/goth/providers/heroku"
"github.com/markbates/goth/providers/instagram"
"github.com/markbates/goth/providers/intercom"
"github.com/markbates/goth/providers/lastfm"
"github.com/markbates/goth/providers/linkedin"
"github.com/markbates/goth/providers/meetup"
"github.com/markbates/goth/providers/onedrive"
"github.com/markbates/goth/providers/openidConnect"
"github.com/markbates/goth/providers/paypal"
"github.com/markbates/goth/providers/salesforce"
"github.com/markbates/goth/providers/slack"
"github.com/markbates/goth/providers/soundcloud"
"github.com/markbates/goth/providers/spotify"
"github.com/markbates/goth/providers/steam"
"github.com/markbates/goth/providers/stripe"
"github.com/markbates/goth/providers/twitch"
"github.com/markbates/goth/providers/twitter"
"github.com/markbates/goth/providers/uber"
"github.com/markbates/goth/providers/wepay"
"github.com/markbates/goth/providers/xero"
"github.com/markbates/goth/providers/yahoo"
"github.com/markbates/goth/providers/yammer"
)
var sessionsManager *sessions.Sessions
func init() {
// attach a session manager
cookieName := "mycustomsessionid"
hashKey := securecookie.GenerateRandomKey(64)
blockKey := securecookie.GenerateRandomKey(32)
secureCookie := securecookie.New(hashKey, blockKey)
sessionsManager = sessions.New(sessions.Config{
Cookie: cookieName,
Encoding: secureCookie,
AllowReclaim: true,
})
}
// These are some function helpers that you may use if you want
// GetProviderName is a function used to get the name of a provider
// for a given request. By default, this provider is fetched from
// the URL query string. If you provide it in a different way,
// assign your own function to this variable that returns the provider
// name for your request.
var GetProviderName = func(ctx iris.Context) (string, error) {
// try to get it from the url param "provider"
if p := ctx.URLParam("provider"); p != "" {
return p, nil
}
// try to get it from the url PATH parameter "{provider} or :provider or {provider:string} or {provider:alphabetical}"
if p := ctx.Params().Get("provider"); p != "" {
return p, nil
}
// try to get it from context's per-request storage
if p := ctx.Values().GetString("provider"); p != "" {
return p, nil
}
// if not found then return an empty string with the corresponding error
return "", errors.New("you must select a provider")
}
/*
BeginAuthHandler is a convenience handler for starting the authentication process.
It expects to be able to get the name of the provider from the query parameters
as either "provider" or ":provider".
BeginAuthHandler will redirect the user to the appropriate authentication end-point
for the requested provider.
See https://github.com/markbates/goth/examples/main.go to see this in action.
*/
func BeginAuthHandler(ctx iris.Context) {
url, err := GetAuthURL(ctx)
if err != nil {
ctx.StatusCode(iris.StatusBadRequest)
ctx.Writef("%v", err)
return
}
ctx.Redirect(url, iris.StatusTemporaryRedirect)
}
/*
GetAuthURL starts the authentication process with the requested provided.
It will return a URL that should be used to send users to.
It expects to be able to get the name of the provider from the query parameters
as either "provider" or ":provider" or from the context's value of "provider" key.
I would recommend using the BeginAuthHandler instead of doing all of these steps
yourself, but that's entirely up to you.
*/
func GetAuthURL(ctx iris.Context) (string, error) {
providerName, err := GetProviderName(ctx)
if err != nil {
return "", err
}
provider, err := goth.GetProvider(providerName)
if err != nil {
return "", err
}
sess, err := provider.BeginAuth(SetState(ctx))
if err != nil {
return "", err
}
url, err := sess.GetAuthURL()
if err != nil {
return "", err
}
session := sessionsManager.Start(ctx)
session.Set(providerName, sess.Marshal())
return url, nil
}
// SetState sets the state string associated with the given request.
// If no state string is associated with the request, one will be generated.
// This state is sent to the provider and can be retrieved during the
// callback.
var SetState = func(ctx iris.Context) string {
state := ctx.URLParam("state")
if len(state) > 0 {
return state
}
return "state"
}
// GetState gets the state returned by the provider during the callback.
// This is used to prevent CSRF attacks, see
// http://tools.ietf.org/html/rfc6749#section-10.12
var GetState = func(ctx iris.Context) string {
return ctx.URLParam("state")
}
/*
CompleteUserAuth does what it says on the tin. It completes the authentication
process and fetches all of the basic information about the user from the provider.
It expects to be able to get the name of the provider from the query parameters
as either "provider" or ":provider".
See https://github.com/markbates/goth/examples/main.go to see this in action.
*/
var CompleteUserAuth = func(ctx iris.Context) (goth.User, error) {
providerName, err := GetProviderName(ctx)
if err != nil {
return goth.User{}, err
}
provider, err := goth.GetProvider(providerName)
if err != nil {
return goth.User{}, err
}
session := sessionsManager.Start(ctx)
value := session.GetString(providerName)
if value == "" {
return goth.User{}, errors.New("session value for " + providerName + " not found")
}
sess, err := provider.UnmarshalSession(value)
if err != nil {
return goth.User{}, err
}
user, err := provider.FetchUser(sess)
if err == nil {
// user can be found with existing session data
return user, err
}
// get new token and retry fetch
_, err = sess.Authorize(provider, ctx.Request().URL.Query())
if err != nil {
return goth.User{}, err
}
session.Set(providerName, sess.Marshal())
return provider.FetchUser(sess)
}
// Logout invalidates a user session.
func Logout(ctx iris.Context) error {
providerName, err := GetProviderName(ctx)
if err != nil {
return err
}
session := sessionsManager.Start(ctx)
session.Delete(providerName)
return nil
}
// End of the "some function helpers".
func main() {
goth.UseProviders(
twitter.New(os.Getenv("TWITTER_KEY"), os.Getenv("TWITTER_SECRET"), "http://localhost:3000/auth/twitter/callback"),
// If you'd like to use authenticate instead of authorize in Twitter provider, use this instead.
// twitter.NewAuthenticate(os.Getenv("TWITTER_KEY"), os.Getenv("TWITTER_SECRET"), "http://localhost:3000/auth/twitter/callback"),
facebook.New(os.Getenv("FACEBOOK_KEY"), os.Getenv("FACEBOOK_SECRET"), "http://localhost:3000/auth/facebook/callback"),
fitbit.New(os.Getenv("FITBIT_KEY"), os.Getenv("FITBIT_SECRET"), "http://localhost:3000/auth/fitbit/callback"),
gplus.New(os.Getenv("GPLUS_KEY"), os.Getenv("GPLUS_SECRET"), "http://localhost:3000/auth/gplus/callback"),
github.New(os.Getenv("GITHUB_KEY"), os.Getenv("GITHUB_SECRET"), "http://localhost:3000/auth/github/callback"),
spotify.New(os.Getenv("SPOTIFY_KEY"), os.Getenv("SPOTIFY_SECRET"), "http://localhost:3000/auth/spotify/callback"),
linkedin.New(os.Getenv("LINKEDIN_KEY"), os.Getenv("LINKEDIN_SECRET"), "http://localhost:3000/auth/linkedin/callback"),
lastfm.New(os.Getenv("LASTFM_KEY"), os.Getenv("LASTFM_SECRET"), "http://localhost:3000/auth/lastfm/callback"),
twitch.New(os.Getenv("TWITCH_KEY"), os.Getenv("TWITCH_SECRET"), "http://localhost:3000/auth/twitch/callback"),
dropbox.New(os.Getenv("DROPBOX_KEY"), os.Getenv("DROPBOX_SECRET"), "http://localhost:3000/auth/dropbox/callback"),
digitalocean.New(os.Getenv("DIGITALOCEAN_KEY"), os.Getenv("DIGITALOCEAN_SECRET"), "http://localhost:3000/auth/digitalocean/callback", "read"),
bitbucket.New(os.Getenv("BITBUCKET_KEY"), os.Getenv("BITBUCKET_SECRET"), "http://localhost:3000/auth/bitbucket/callback"),
instagram.New(os.Getenv("INSTAGRAM_KEY"), os.Getenv("INSTAGRAM_SECRET"), "http://localhost:3000/auth/instagram/callback"),
intercom.New(os.Getenv("INTERCOM_KEY"), os.Getenv("INTERCOM_SECRET"), "http://localhost:3000/auth/intercom/callback"),
box.New(os.Getenv("BOX_KEY"), os.Getenv("BOX_SECRET"), "http://localhost:3000/auth/box/callback"),
salesforce.New(os.Getenv("SALESFORCE_KEY"), os.Getenv("SALESFORCE_SECRET"), "http://localhost:3000/auth/salesforce/callback"),
amazon.New(os.Getenv("AMAZON_KEY"), os.Getenv("AMAZON_SECRET"), "http://localhost:3000/auth/amazon/callback"),
yammer.New(os.Getenv("YAMMER_KEY"), os.Getenv("YAMMER_SECRET"), "http://localhost:3000/auth/yammer/callback"),
onedrive.New(os.Getenv("ONEDRIVE_KEY"), os.Getenv("ONEDRIVE_SECRET"), "http://localhost:3000/auth/onedrive/callback"),
// Pointed localhost.com to http://localhost:3000/auth/yahoo/callback through proxy as yahoo
// does not allow to put custom ports in redirection uri
yahoo.New(os.Getenv("YAHOO_KEY"), os.Getenv("YAHOO_SECRET"), "http://localhost.com"),
slack.New(os.Getenv("SLACK_KEY"), os.Getenv("SLACK_SECRET"), "http://localhost:3000/auth/slack/callback"),
stripe.New(os.Getenv("STRIPE_KEY"), os.Getenv("STRIPE_SECRET"), "http://localhost:3000/auth/stripe/callback"),
wepay.New(os.Getenv("WEPAY_KEY"), os.Getenv("WEPAY_SECRET"), "http://localhost:3000/auth/wepay/callback", "view_user"),
// By default paypal production auth urls will be used, please set PAYPAL_ENV=sandbox as environment variable for testing
// in sandbox environment
paypal.New(os.Getenv("PAYPAL_KEY"), os.Getenv("PAYPAL_SECRET"), "http://localhost:3000/auth/paypal/callback"),
steam.New(os.Getenv("STEAM_KEY"), "http://localhost:3000/auth/steam/callback"),
heroku.New(os.Getenv("HEROKU_KEY"), os.Getenv("HEROKU_SECRET"), "http://localhost:3000/auth/heroku/callback"),
uber.New(os.Getenv("UBER_KEY"), os.Getenv("UBER_SECRET"), "http://localhost:3000/auth/uber/callback"),
soundcloud.New(os.Getenv("SOUNDCLOUD_KEY"), os.Getenv("SOUNDCLOUD_SECRET"), "http://localhost:3000/auth/soundcloud/callback"),
gitlab.New(os.Getenv("GITLAB_KEY"), os.Getenv("GITLAB_SECRET"), "http://localhost:3000/auth/gitlab/callback"),
dailymotion.New(os.Getenv("DAILYMOTION_KEY"), os.Getenv("DAILYMOTION_SECRET"), "http://localhost:3000/auth/dailymotion/callback", "email"),
deezer.New(os.Getenv("DEEZER_KEY"), os.Getenv("DEEZER_SECRET"), "http://localhost:3000/auth/deezer/callback", "email"),
discord.New(os.Getenv("DISCORD_KEY"), os.Getenv("DISCORD_SECRET"), "http://localhost:3000/auth/discord/callback", discord.ScopeIdentify, discord.ScopeEmail),
meetup.New(os.Getenv("MEETUP_KEY"), os.Getenv("MEETUP_SECRET"), "http://localhost:3000/auth/meetup/callback"),
// Auth0 allocates domain per customer, a domain must be provided for auth0 to work
auth0.New(os.Getenv("AUTH0_KEY"), os.Getenv("AUTH0_SECRET"), "http://localhost:3000/auth/auth0/callback", os.Getenv("AUTH0_DOMAIN")),
xero.New(os.Getenv("XERO_KEY"), os.Getenv("XERO_SECRET"), "http://localhost:3000/auth/xero/callback"),
)
// OpenID Connect is based on OpenID Connect Auto Discovery URL (https://openid.net/specs/openid-connect-discovery-1_0-17.html)
// because the OpenID Connect provider initialize it self in the New(), it can return an error which should be handled or ignored
// ignore the error for now
openidConnect, _ := openidConnect.New(os.Getenv("OPENID_CONNECT_KEY"), os.Getenv("OPENID_CONNECT_SECRET"), "http://localhost:3000/auth/openid-connect/callback", os.Getenv("OPENID_CONNECT_DISCOVERY_URL"))
if openidConnect != nil {
goth.UseProviders(openidConnect)
}
m := make(map[string]string)
m["amazon"] = "Amazon"
m["bitbucket"] = "Bitbucket"
m["box"] = "Box"
m["dailymotion"] = "Dailymotion"
m["deezer"] = "Deezer"
m["digitalocean"] = "Digital Ocean"
m["discord"] = "Discord"
m["dropbox"] = "Dropbox"
m["facebook"] = "Facebook"
m["fitbit"] = "Fitbit"
m["github"] = "Github"
m["gitlab"] = "Gitlab"
m["soundcloud"] = "SoundCloud"
m["spotify"] = "Spotify"
m["steam"] = "Steam"
m["stripe"] = "Stripe"
m["twitch"] = "Twitch"
m["uber"] = "Uber"
m["wepay"] = "Wepay"
m["yahoo"] = "Yahoo"
m["yammer"] = "Yammer"
m["gplus"] = "Google Plus"
m["heroku"] = "Heroku"
m["instagram"] = "Instagram"
m["intercom"] = "Intercom"
m["lastfm"] = "Last FM"
m["linkedin"] = "Linkedin"
m["onedrive"] = "Onedrive"
m["paypal"] = "Paypal"
m["twitter"] = "Twitter"
m["salesforce"] = "Salesforce"
m["slack"] = "Slack"
m["meetup"] = "Meetup.com"
m["auth0"] = "Auth0"
m["openid-connect"] = "OpenID Connect"
m["xero"] = "Xero"
var keys []string
for k := range m {
keys = append(keys, k)
}
sort.Strings(keys)
providerIndex := &ProviderIndex{Providers: keys, ProvidersMap: m}
// create our app,
// set a view
// set sessions
// and setup the router for the showcase
app := iris.New()
// attach and build our templates
app.RegisterView(iris.HTML("./templates", ".html"))
// start of the router
app.Get("/auth/{provider}/callback", func(ctx iris.Context) {
user, err := CompleteUserAuth(ctx)
if err != nil {
ctx.StatusCode(iris.StatusInternalServerError)
ctx.Writef("%v", err)
return
}
ctx.ViewData("", user)
if err := ctx.View("user.html"); err != nil {
ctx.Writef("%v", err)
}
})
app.Get("/logout/{provider}", func(ctx iris.Context) {
Logout(ctx)
ctx.Redirect("/", iris.StatusTemporaryRedirect)
})
app.Get("/auth/{provider}", func(ctx iris.Context) {
// try to get the user without re-authenticating
if gothUser, err := CompleteUserAuth(ctx); err == nil {
ctx.ViewData("", gothUser)
if err := ctx.View("user.html"); err != nil {
ctx.Writef("%v", err)
}
} else {
BeginAuthHandler(ctx)
}
})
app.Get("/", func(ctx iris.Context) {
ctx.ViewData("", providerIndex)
if err := ctx.View("index.html"); err != nil {
ctx.Writef("%v", err)
}
})
// http://localhost:3000
app.Listen("localhost:3000")
}
type ProviderIndex struct {
Providers []string
ProvidersMap map[string]string
}

View File

@ -1,3 +0,0 @@
{{range $key,$value:=.Providers}}
<p><a href="/auth/{{$value}}">Log in with {{index $.ProvidersMap $value}}</a></p>
{{end}}

View File

@ -1,11 +0,0 @@
<p><a href="/logout/{{.Provider}}">logout</a></p>
<p>Name: {{.Name}} [{{.LastName}}, {{.FirstName}}]</p>
<p>Email: {{.Email}}</p>
<p>NickName: {{.NickName}}</p>
<p>Location: {{.Location}}</p>
<p>AvatarURL: {{.AvatarURL}} <img src="{{.AvatarURL}}"></p>
<p>Description: {{.Description}}</p>
<p>UserID: {{.UserID}}</p>
<p>AccessToken: {{.AccessToken}}</p>
<p>ExpiresAt: {{.ExpiresAt}}</p>
<p>RefreshToken: {{.RefreshToken}}</p>

View File

@ -1,3 +0,0 @@
# https://docs.hcaptcha.com/#localdev
# Add to the end of your hosts file, e.g. on windows: C:/windows/system32/drivers/etc/hosts
127.0.0.1 yourdomain.com

View File

@ -1,46 +0,0 @@
package main
import (
"os"
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/middleware/hcaptcha"
)
// Get the following values from: https://dashboard.hcaptcha.com
// Also, check: https://docs.hcaptcha.com/#localdev to test on local environment.
var (
siteKey = os.Getenv("HCAPTCHA-SITE-KEY")
secretKey = os.Getenv("HCAPTCHA-SECRET-KEY")
)
func main() {
app := iris.New()
app.RegisterView(iris.HTML("./templates", ".html"))
hCaptcha := hcaptcha.New(secretKey)
app.Get("/register", registerForm)
app.Post("/register", hCaptcha, register) // See `hcaptcha.SiteVerify` for manual validation too.
app.Logger().Infof("SiteKey = %s\tSecretKey = %s",
siteKey, secretKey)
// GET: http://yourdomain.com/register
app.Listen(":80")
}
func register(ctx iris.Context) {
hcaptchaResp, ok := hcaptcha.Get(ctx)
if !ok {
ctx.StatusCode(iris.StatusUnauthorized)
ctx.WriteString("Are you a bot?")
return
}
ctx.Writef("Register action here...action was asked by a Human.\nResponse value is: %#+v", hcaptchaResp)
}
func registerForm(ctx iris.Context) {
ctx.ViewData("SiteKey", siteKey)
ctx.View("register_form.html")
}

View File

@ -1,18 +0,0 @@
<html>
<head>
<title>hCaptcha Demo</title>
<script src="https://hcaptcha.com/1/api.js" async defer></script>
</head>
<body>
<form action="/register" method="POST">
<input type="text" name="email" placeholder="Email" />
<input type="password" name="password" placeholder="Password" />
<div class="h-captcha" data-sitekey="{{ .SiteKey }}"></div>
<br />
<input type="submit" value="Submit" />
</form>
</body>
</html>

View File

@ -1,29 +0,0 @@
# Generate RSA
```sh
$ openssl genrsa -des3 -out private_rsa.pem 2048
```
```go
b, err := ioutil.ReadFile("./private_rsa.pem")
if err != nil {
panic(err)
}
key := jwt.MustParseRSAPrivateKey(b, []byte("pass"))
```
OR
```go
import "crypto/rand"
import "crypto/rsa"
key, err := rsa.GenerateKey(rand.Reader, 2048)
```
# Generate Ed25519
```sh
$ openssl genpkey -algorithm Ed25519 -out private_ed25519.pem
$ openssl req -x509 -key private_ed25519.pem -out cert_ed25519.pem -days 365
```

View File

@ -1,155 +0,0 @@
package main
import (
"time"
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/middleware/jwt"
)
// UserClaims a custom claims structure. You can just use jwt.Claims too.
type UserClaims struct {
jwt.Claims
Username string
}
func main() {
// Get keys from system's environment variables
// JWT_SECRET (for signing and verification) and JWT_SECRET_ENC(for encryption and decryption),
// or defaults to "secret" and "itsa16bytesecret" respectfully.
//
// Use the `jwt.New` instead for more flexibility, if necessary.
j := jwt.HMAC(15*time.Minute, "secret", "itsa16bytesecret")
app := iris.New()
app.Logger().SetLevel("debug")
app.Get("/authenticate", func(ctx iris.Context) {
standardClaims := jwt.Claims{Issuer: "an-issuer", Audience: jwt.Audience{"an-audience"}}
// NOTE: if custom claims then the `j.Expiry(claims)` (or jwt.Expiry(duration, claims))
// MUST be called in order to set the expiration time.
customClaims := UserClaims{
Claims: j.Expiry(standardClaims),
Username: "kataras",
}
j.WriteToken(ctx, customClaims)
})
userRouter := app.Party("/user")
{
// userRouter.Use(j.Verify)
// userRouter.Get("/", func(ctx iris.Context) {
// var claims UserClaims
// if err := jwt.ReadClaims(ctx, &claims); err != nil {
// // Validation-only errors, the rest are already
// // checked on `j.Verify` middleware.
// ctx.StopWithStatus(iris.StatusUnauthorized)
// return
// }
//
// ctx.Writef("Claims: %#+v\n", claims)
// })
//
// OR:
userRouter.Get("/", func(ctx iris.Context) {
var claims UserClaims
if err := j.VerifyToken(ctx, &claims); err != nil {
ctx.StopWithStatus(iris.StatusUnauthorized)
return
}
ctx.Writef("Claims: %#+v\n", claims)
})
}
app.Listen(":8080")
}
/*
func default_RSA_Example() {
j := jwt.RSA(15*time.Minute)
}
Same as:
func load_File_Or_Generate_RSA_Example() {
signKey, err := jwt.LoadRSA("jwt_sign.key", 2048)
if err != nil {
panic(err)
}
j, err := jwt.New(15*time.Minute, jwt.RS256, signKey)
if err != nil {
panic(err)
}
encKey, err := jwt.LoadRSA("jwt_enc.key", 2048)
if err != nil {
panic(err)
}
err = j.WithEncryption(jwt.A128CBCHS256, jwt.RSA15, encKey)
if err != nil {
panic(err)
}
}
*/
/*
func hmac_Example() {
// hmac
key := []byte("secret")
j, err := jwt.New(15*time.Minute, jwt.HS256, key)
if err != nil {
panic(err)
}
// OPTIONAL encryption:
encryptionKey := []byte("itsa16bytesecret")
err = j.WithEncryption(jwt.A128GCM, jwt.DIRECT, encryptionKey)
if err != nil {
panic(err)
}
}
*/
/*
func load_From_File_With_Password_Example() {
b, err := ioutil.ReadFile("./rsa_password_protected.key")
if err != nil {
panic(err)
}
signKey,err := jwt.ParseRSAPrivateKey(b, []byte("pass"))
if err != nil {
panic(err)
}
j, err := jwt.New(15*time.Minute, jwt.RS256, signKey)
if err != nil {
panic(err)
}
}
*/
/*
func generate_RSA_Example() {
signKey, err := rsa.GenerateKey(rand.Reader, 4096)
if err != nil {
panic(err)
}
encryptionKey, err := rsa.GenerateKey(rand.Reader, 4096)
if err != nil {
panic(err)
}
j, err := jwt.New(15*time.Minute, jwt.RS512, signKey)
if err != nil {
panic(err)
}
err = j.WithEncryption(jwt.A128CBCHS256, jwt.RSA15, encryptionKey)
if err != nil {
panic(err)
}
}
*/

View File

@ -1,30 +0,0 @@
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,6B0BC214C94124FE
lAM48DEM/GdCDimr9Vhi+fSHLgduDb0l2BA4uhILgNby51jxY/4X3IqM6f3ImKX7
cEd9OBug+pwIugB0UW0L0f5Pd59Ovpiaz3xLci1/19ehYnMqsuP3YAnJm40hT5VP
p0gWRiR415PJ0fPeeJPFx5IsqvkTJ30LWZHUZX4EkdcL5L8PrVbmthGDbLh+OcMc
LzoP8eTglzlZF03nyvAol6+p2eZtvOJLu8nWG25q17kyBx6kEiCsWFcUBTX9G7sH
CM3naByDijqZXE/XXtmTMLSRRnlk7Q5WLxClroHlUP9y8BQFMo2TW4Z+vNjHUkc1
77ghabX1704bAlIE8LLZJKrm/C5+VKyV6117SVG/2bc4036Y5rStXpANbk1j4K0x
ADvpRhuTpifaogdvJP+8eXBdl841MQMRzWuZHp6UNYYQegoV9C+KHyJx4UPjZyzd
gblZmKgU+BsX3mV6MLhJtd6dheLZtpBsAlSstJxzmgwqz9faONYEGeItXO+NnxbA
mxAp/mI+Fz2jfgYlWjwkyPPzD4k/ZMMzB4XLkKKs9XaxUtTomiDkuYZfnABhxt73
xBy40V1rb/NyeW80pk1zEHM6Iy/48ETSp9U3k9sSOXjMhYbPXgxDtimV8w0qGFAo
2Tif7ZuaiuC38rOkoHK9C6vy2Dp8lQZ+QBnUKLeFsyhq9CaqSdnyUTMj3oEZXXf+
TqqeO+PTtl7JaNfGRq6/aMZqxACHkyVUvYvjZzx07CJ2fr+OtNqxallM6Oc/o9NZ
5u7lpgrYaKM/b67q0d2X/AoxR5zrZuM8eam3acD1PwHFQKbJWuFNmjWtnlZNuR3X
fZEmxIKwDlup8TxFcqbbZtPHuQA2mTMTqfRkf8oPSO+N6NNaUpb0ignYyA7Eu5GT
b02d/oNLETMikxUxntMSH7GhuOpfJyELz8krYTttbJ+a93h4wBeYW2+LyAr/cRLB
mbtKLtaN7f3FaOSnu8e0+zlJ7xglHPXqblRL9q6ZDM5UJtJD4rA7LPZHk/0Y1Kb6
hBh1qMDu0r3IV4X7MDacvxw7aa7D8TyXJiFSvxykVhds+ndjIe51Ics5908+lev3
nwE69PLMwyqe2vvE2oDwao4XJuBLCHjcv/VagRSz/XQGMbZqb3L6unyd3UPl8JjP
ovipNwM4rFnE54uiUUeki7TZGDYO72vQcSaLrmbeAWc2m202+rqLz0WMm6HpPmCv
IgexpX2MnIeHJ3+BlEjA2u+S6xNSD7qHGk2pb7DD8nRvUdSHAHeaQbrkEfEhhR2Q
Dw5gdw1JyQ0UKBl5ndn/1Ub2Asl016lZjpqHyMIVS4tFixACDsihEYMmq/zQmTj4
8oBZTU+fycN/KiGKZBsqxIwgYIeMz/GfvoyN5m57l6fwEZALVpveI1pP4fiZB/Z8
xLKa5JK6L10lAD1YHWc1dPhamf9Sb3JwN2CFtGvjOJ/YjAZu3jJoxi40DtRkE3Rh
HI8Cbx1OORzoo0kO0vy42rz5qunYyVmEzPKtOj+YjVEhVJ85yJZ9bTZtuyqMv8mH
cnwEeIFK8cmm9asbVzQGDwN/UGB4cO3LrMX1RYk4GRttTGlp0729BbmZmu00RnD/
-----END RSA PRIVATE KEY-----

View File

@ -1,118 +0,0 @@
package main
import (
"fmt"
"log"
"strings"
"github.com/kataras/iris/v12"
permissions "github.com/xyproto/permissionbolt"
// * PostgreSQL support:
// permissions "github.com/xyproto/pstore" and
// perm, err := permissions.New(...)
//
// * MariaDB/MySQL support:
// permissions "github.com/xyproto/permissionsql" and
// perm, err := permissions.New/NewWithDSN(...)
// * Redis support:
// permissions "github.com/xyproto/permissions2"
// perm, err := permissions.New2()
// * Bolt support (this one):
// permissions "github.com/xyproto/permissionbolt" and
// perm, err := permissions.New(...)
)
func main() {
app := iris.New()
app.Logger().SetLevel("debug")
// New permissions middleware.
perm, err := permissions.New()
if err != nil {
log.Fatalln(err)
}
// Blank slate, no default permissions
// perm.Clear()
// Set up a middleware handler for Iris, with a custom "permission denied" message.
permissionHandler := func(ctx iris.Context) {
// Check if the user has the right admin/user rights
if perm.Rejected(ctx.ResponseWriter(), ctx.Request()) {
// Deny the request, don't call other middleware handlers
ctx.StopWithText(iris.StatusForbidden, "Permission denied!")
return
}
// Call the next middleware handler
ctx.Next()
}
// Register the permissions middleware
app.Use(permissionHandler)
// Get the userstate, used in the handlers below
userstate := perm.UserState()
app.Get("/", func(ctx iris.Context) {
msg := ""
msg += fmt.Sprintf("Has user bob: %v\n", userstate.HasUser("bob"))
msg += fmt.Sprintf("Logged in on server: %v\n", userstate.IsLoggedIn("bob"))
msg += fmt.Sprintf("Is confirmed: %v\n", userstate.IsConfirmed("bob"))
msg += fmt.Sprintf("Username stored in cookies (or blank): %v\n", userstate.Username(ctx.Request()))
msg += fmt.Sprintf("Current user is logged in, has a valid cookie and *user rights*: %v\n", userstate.UserRights(ctx.Request()))
msg += fmt.Sprintf("Current user is logged in, has a valid cookie and *admin rights*: %v\n", userstate.AdminRights(ctx.Request()))
msg += fmt.Sprintln("\nTry: /register, /confirm, /remove, /login, /logout, /makeadmin, /clear, /data and /admin")
ctx.WriteString(msg)
})
app.Get("/register", func(ctx iris.Context) {
userstate.AddUser("bob", "hunter1", "bob@zombo.com")
ctx.Writef("User bob was created: %v\n", userstate.HasUser("bob"))
})
app.Get("/confirm", func(ctx iris.Context) {
userstate.MarkConfirmed("bob")
ctx.Writef("User bob was confirmed: %v\n", userstate.IsConfirmed("bob"))
})
app.Get("/remove", func(ctx iris.Context) {
userstate.RemoveUser("bob")
ctx.Writef("User bob was removed: %v\n", !userstate.HasUser("bob"))
})
app.Get("/login", func(ctx iris.Context) {
// Headers will be written, for storing a cookie
userstate.Login(ctx.ResponseWriter(), "bob")
ctx.Writef("bob is now logged in: %v\n", userstate.IsLoggedIn("bob"))
})
app.Get("/logout", func(ctx iris.Context) {
userstate.Logout("bob")
ctx.Writef("bob is now logged out: %v\n", !userstate.IsLoggedIn("bob"))
})
app.Get("/makeadmin", func(ctx iris.Context) {
userstate.SetAdminStatus("bob")
ctx.Writef("bob is now administrator: %v\n", userstate.IsAdmin("bob"))
})
app.Get("/clear", func(ctx iris.Context) {
userstate.ClearCookie(ctx.ResponseWriter())
ctx.WriteString("Clearing cookie")
})
app.Get("/data", func(ctx iris.Context) {
ctx.WriteString("user page that only logged in users must see!")
})
app.Get("/admin", func(ctx iris.Context) {
ctx.WriteString("super secret information that only logged in administrators must see!\n\n")
if usernames, err := userstate.AllUsernames(); err == nil {
ctx.Writef("list of all users: %s" + strings.Join(usernames, ", "))
}
})
// Serve
app.Listen(":8080")
}

View File

@ -1,44 +0,0 @@
package main
import (
"fmt"
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/middleware/recaptcha"
)
// keys should be obtained by https://www.google.com/recaptcha
const (
recaptchaPublic = "6Lf3WywUAAAAAKNfAm5DP2J5ahqedtZdHTYaKkJ6"
recaptchaSecret = "6Lf3WywUAAAAAJpArb8nW_LCL_PuPuokmEABFfgw"
)
func main() {
app := iris.New()
r := recaptcha.New(recaptchaSecret)
app.Get("/comment", showRecaptchaForm)
// pass the middleware before the main handler or use the `recaptcha.SiteVerify`.
app.Post("/comment", r, postComment)
app.Listen(":8080")
}
var htmlForm = `<form action="/comment" method="POST">
<script src="https://www.google.com/recaptcha/api.js"></script>
<div class="g-recaptcha" data-sitekey="%s"></div>
<input type="submit" name="button" value="Verify">
</form>`
func showRecaptchaForm(ctx iris.Context) {
contents := fmt.Sprintf(htmlForm, recaptchaPublic)
ctx.HTML(contents)
}
func postComment(ctx iris.Context) {
// [...]
ctx.JSON(iris.Map{"success": true})
}

View File

@ -1,40 +0,0 @@
package main
import (
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/middleware/recaptcha"
)
// keys should be obtained by https://www.google.com/recaptcha
const (
recaptchaPublic = ""
recaptchaSecret = ""
)
func showRecaptchaForm(ctx iris.Context, path string) {
ctx.HTML(recaptcha.GetFormHTML(recaptchaPublic, path))
}
func main() {
app := iris.New()
// On both Get and Post on this example, so you can easly
// use a single route to show a form and the main subject if recaptcha's validation result succeed.
app.HandleMany("GET POST", "/", func(ctx iris.Context) {
if ctx.Method() == iris.MethodGet {
showRecaptchaForm(ctx, "/")
return
}
result := recaptcha.SiteVerify(ctx, recaptchaSecret)
if !result.Success {
/* redirect here if u want or do nothing */
ctx.HTML("<b> failed please try again </b>")
return
}
ctx.Writef("succeed.")
})
app.Listen(":8080")
}

View File

@ -1,123 +0,0 @@
package bootstrap
import (
"time"
"github.com/gorilla/securecookie"
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/middleware/logger"
"github.com/kataras/iris/v12/middleware/recover"
"github.com/kataras/iris/v12/sessions"
"github.com/kataras/iris/v12/websocket"
)
type Configurator func(*Bootstrapper)
type Bootstrapper struct {
*iris.Application
AppName string
AppOwner string
AppSpawnDate time.Time
Sessions *sessions.Sessions
}
// New returns a new Bootstrapper.
func New(appName, appOwner string, cfgs ...Configurator) *Bootstrapper {
b := &Bootstrapper{
AppName: appName,
AppOwner: appOwner,
AppSpawnDate: time.Now(),
Application: iris.New(),
}
for _, cfg := range cfgs {
cfg(b)
}
return b
}
// SetupViews loads the templates.
func (b *Bootstrapper) SetupViews(viewsDir string) {
b.RegisterView(iris.HTML(viewsDir, ".html").Layout("shared/layout.html"))
}
// SetupSessions initializes the sessions, optionally.
func (b *Bootstrapper) SetupSessions(expires time.Duration, cookieHashKey, cookieBlockKey []byte) {
b.Sessions = sessions.New(sessions.Config{
Cookie: "SECRET_SESS_COOKIE_" + b.AppName,
Expires: expires,
Encoding: securecookie.New(cookieHashKey, cookieBlockKey),
})
}
// SetupWebsockets prepares the websocket server.
func (b *Bootstrapper) SetupWebsockets(endpoint string, handler websocket.ConnHandler) {
ws := websocket.New(websocket.DefaultGorillaUpgrader, handler)
b.Get(endpoint, websocket.Handler(ws))
}
// SetupErrorHandlers prepares the http error handlers
// `(context.StatusCodeNotSuccessful`, which defaults to >=400 (but you can change it).
func (b *Bootstrapper) SetupErrorHandlers() {
b.OnAnyErrorCode(func(ctx iris.Context) {
err := iris.Map{
"app": b.AppName,
"status": ctx.GetStatusCode(),
"message": ctx.Values().GetString("message"),
}
if jsonOutput := ctx.URLParamExists("json"); jsonOutput {
ctx.JSON(err)
return
}
ctx.ViewData("Err", err)
ctx.ViewData("Title", "Error")
ctx.View("shared/error.html")
})
}
const (
// StaticAssets is the root directory for public assets like images, css, js.
StaticAssets = "./public/"
// Favicon is the relative 9to the "StaticAssets") favicon path for our app.
Favicon = "favicon.ico"
)
// Configure accepts configurations and runs them inside the Bootstraper's context.
func (b *Bootstrapper) Configure(cs ...Configurator) {
for _, c := range cs {
c(b)
}
}
// Bootstrap prepares our application.
//
// Returns itself.
func (b *Bootstrapper) Bootstrap() *Bootstrapper {
b.SetupViews("./views")
b.SetupSessions(24*time.Hour,
[]byte("the-big-and-secret-fash-key-here"),
[]byte("lot-secret-of-characters-big-too"),
)
b.SetupErrorHandlers()
// static files
b.Favicon(StaticAssets + Favicon)
b.HandleDir("/public", iris.Dir(StaticAssets))
// middleware, after static files
b.Use(recover.New())
b.Use(logger.New())
return b
}
// Listen starts the http server with the specified "addr".
func (b *Bootstrapper) Listen(addr string, cfgs ...iris.Configurator) {
b.Run(iris.Addr(addr), cfgs...)
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

View File

@ -1,19 +0,0 @@
package main
import (
"github.com/kataras/iris/v12/_examples/bootstrapper/bootstrap"
"github.com/kataras/iris/v12/_examples/bootstrapper/middleware/identity"
"github.com/kataras/iris/v12/_examples/bootstrapper/routes"
)
func newApp() *bootstrap.Bootstrapper {
app := bootstrap.New("Awesome App", "kataras2006@hotmail.com")
app.Bootstrap()
app.Configure(identity.Configure, routes.Configure)
return app
}
func main() {
app := newApp()
app.Listen(":8080")
}

View File

@ -1,32 +0,0 @@
package main
import (
"testing"
"github.com/kataras/iris/v12/httptest"
)
// go test -v
func TestApp(t *testing.T) {
app := newApp()
e := httptest.New(t, app.Application)
// test our routes
e.GET("/").Expect().Status(httptest.StatusOK)
e.GET("/follower/42").Expect().Status(httptest.StatusOK).
Body().Equal("from /follower/{id:int64} with ID: 42")
e.GET("/following/52").Expect().Status(httptest.StatusOK).
Body().Equal("from /following/{id:int64} with ID: 52")
e.GET("/like/64").Expect().Status(httptest.StatusOK).
Body().Equal("from /like/{id:int64} with ID: 64")
// test not found
e.GET("/notfound").Expect().Status(httptest.StatusNotFound)
expectedErr := map[string]interface{}{
"app": app.AppName,
"status": httptest.StatusNotFound,
"message": "",
}
e.GET("/anotfoundwithjson").WithQuery("json", nil).
Expect().Status(httptest.StatusNotFound).JSON().Equal(expectedErr)
}

View File

@ -1,33 +0,0 @@
package identity
import (
"time"
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/_examples/bootstrapper/bootstrap"
)
// New returns a new handler which adds some headers and view data
// describing the application, i.e the owner, the startup time.
func New(b *bootstrap.Bootstrapper) iris.Handler {
return func(ctx iris.Context) {
// response headers
ctx.Header("App-Name", b.AppName)
ctx.Header("App-Owner", b.AppOwner)
ctx.Header("App-Since", time.Since(b.AppSpawnDate).String())
ctx.Header("Server", "Iris: https://iris-go.com")
// view data if ctx.View or c.Tmpl = "$page.html" will be called next.
ctx.ViewData("AppName", b.AppName)
ctx.ViewData("AppOwner", b.AppOwner)
ctx.Next()
}
}
// Configure creates a new identity middleware and registers that to the app.
func Configure(b *bootstrap.Bootstrapper) {
h := New(b)
b.UseGlobal(h)
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View File

@ -1,11 +0,0 @@
package routes
import (
"github.com/kataras/iris/v12"
)
// GetFollowerHandler handles the GET: /follower/{id}
func GetFollowerHandler(ctx iris.Context) {
id, _ := ctx.Params().GetInt64("id")
ctx.Writef("from "+ctx.GetCurrentRoute().Path()+" with ID: %d", id)
}

View File

@ -1,11 +0,0 @@
package routes
import (
"github.com/kataras/iris/v12"
)
// GetFollowingHandler handles the GET: /following/{id}
func GetFollowingHandler(ctx iris.Context) {
id, _ := ctx.Params().GetInt64("id")
ctx.Writef("from "+ctx.GetCurrentRoute().Path()+" with ID: %d", id)
}

View File

@ -1,11 +0,0 @@
package routes
import (
"github.com/kataras/iris/v12"
)
// GetIndexHandler handles the GET: /
func GetIndexHandler(ctx iris.Context) {
ctx.ViewData("Title", "Index Page")
ctx.View("index.html")
}

View File

@ -1,11 +0,0 @@
package routes
import (
"github.com/kataras/iris/v12"
)
// GetLikeHandler handles the GET: /like/{id}
func GetLikeHandler(ctx iris.Context) {
id, _ := ctx.Params().GetInt64("id")
ctx.Writef("from "+ctx.GetCurrentRoute().Path()+" with ID: %d", id)
}

View File

@ -1,13 +0,0 @@
package routes
import (
"github.com/kataras/iris/v12/_examples/bootstrapper/bootstrap"
)
// Configure registers the necessary routes to the app.
func Configure(b *bootstrap.Bootstrapper) {
b.Get("/", GetIndexHandler)
b.Get("/follower/{id:int64}", GetFollowerHandler)
b.Get("/following/{id:int64}", GetFollowingHandler)
b.Get("/like/{id:int64}", GetLikeHandler)
}

View File

@ -1 +0,0 @@
<h1>Welcome!!</h1>

View File

@ -1,5 +0,0 @@
<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>
<h3>{{.Err.status}}</h3>
<h4>{{.Err.message}}</h4>

View File

@ -1,23 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />
<title>{{.Title}} - {{.AppName}}</title>
</head>
<body>
<div>
<!-- Render the current template here -->
{{ yield }}
<hr />
<footer>
<p>&copy; 2017 - {{.AppOwner}}</p>
</footer>
</div>
</body>
</html>

View File

@ -1,9 +0,0 @@
example.com {
header / Server "Iris"
proxy / example.com:9091 # localhost:9091
}
api.example.com {
header / Server "Iris"
proxy / api.example.com:9092 # localhost:9092
}

View File

@ -1,24 +0,0 @@
# Caddy loves Iris
The `Caddyfile` shows how you can use caddy to listen on ports 80 & 443 and sit in front of iris webserver(s) that serving on a different port (9091 and 9092 in this case; see Caddyfile).
## Running our two web servers
1. Go to `$GOPATH/src/github.com/kataras/iris/_examples/caddy/server1`
2. Open a terminal window and execute `go run main.go`
3. Go to `$GOPATH/src/github.com/kataras/iris/_examples/caddy/server2`
4. Open a new terminal window and execute `go run main.go`
## Caddy installation
1. Download caddy: https://caddyserver.com/download
2. Extract its contents where the `Caddyfile` is located, the `$GOPATH/src/github.com/kataras/iris/_examples/caddy` in this case
3. Open, read and modify the `Caddyfile` to see by yourself how easy it is to configure the servers
4. Run `caddy` directly or open a terminal window and execute `caddy`
5. Go to `https://example.com` and `https://api.example.com/user/42`
## Notes
Iris has the `app.Run(iris.AutoTLS(":443", "example.com", "mail@example.com"))` which does
the exactly same thing but caddy is a great tool that helps you when you run multiple web servers from one host machine, i.e iris, apache, tomcat.

View File

@ -1,49 +0,0 @@
package main
import (
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/mvc"
)
func main() {
app := iris.New()
templates := iris.HTML("./views", ".html").Layout("shared/layout.html")
app.RegisterView(templates)
mvc.New(app).Handle(new(Controller))
// http://localhost:9091
app.Listen(":9091")
}
// Layout contains all the binding properties for the shared/layout.html
type Layout struct {
Title string
}
// Controller is our example controller, request-scoped, each request has its own instance.
type Controller struct {
Layout Layout
}
// BeginRequest is the first method fired when client requests from this Controller's root path.
func (c *Controller) BeginRequest(ctx iris.Context) {
c.Layout.Title = "Home Page"
}
// EndRequest is the last method fired.
// It's here just to complete the BaseController
// in order to be tell iris to call the `BeginRequest` before the main method.
func (c *Controller) EndRequest(ctx iris.Context) {}
// Get handles GET http://localhost:9091
func (c *Controller) Get() mvc.View {
return mvc.View{
Name: "index.html",
Data: iris.Map{
"Layout": c.Layout,
"Message": "Welcome to my website!",
},
}
}

View File

@ -1,3 +0,0 @@
<div>
{{.Message}}
</div>

View File

@ -1,11 +0,0 @@
<html>
<head>
<title>{{.Layout.Title}}</title>
</head>
<body>
{{ yield }}
</body>
</html>

View File

@ -1,68 +0,0 @@
package main
import (
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/mvc"
)
type postValue func(string) string
func main() {
app := iris.New()
mvc.New(app.Party("/user")).Register(
func(ctx iris.Context) postValue {
return ctx.PostValue
}).Handle(new(UserController))
// GET http://localhost:9092/user
// GET http://localhost:9092/user/42
// POST http://localhost:9092/user
// PUT http://localhost:9092/user/42
// DELETE http://localhost:9092/user/42
// GET http://localhost:9092/user/followers/42
app.Listen(":9092")
}
// UserController is our user example controller.
type UserController struct{}
// Get handles GET /user
func (c *UserController) Get() string {
return "Select all users"
}
// User is our test User model, nothing tremendous here.
type User struct{ ID int64 }
// GetBy handles GET /user/42, equal to .Get("/user/{id:int64}")
func (c *UserController) GetBy(id int64) User {
// Select User by ID == $id.
return User{id}
}
// Post handles POST /user
func (c *UserController) Post(post postValue) string {
username := post("username")
return "Create by user with username: " + username
}
// PutBy handles PUT /user/42
func (c *UserController) PutBy(id int) string {
// Update user by ID == $id
return "User updated"
}
// DeleteBy handles DELETE /user/42
func (c *UserController) DeleteBy(id int) bool {
// Delete user by ID == %id
//
// when boolean then true = iris.StatusOK, false = iris.StatusNotFound
return true
}
// GetFollowersBy handles GET /user/followers/42
func (c *UserController) GetFollowersBy(id int) []User {
// Select all followers by user ID == $id
return []User{ /* ... */ }
}

View File

@ -1,112 +0,0 @@
package main
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"github.com/kataras/iris/v12/context"
)
const baseURL = "http://localhost:8080"
// Available options:
// - "gzip",
// - "deflate",
// - "br" (for brotli),
// - "snappy" and
// - "s2"
const encoding = context.BROTLI
var client = http.DefaultClient
func main() {
fmt.Printf("Running client example on: %s\n", baseURL)
getExample()
postExample()
}
func getExample() {
endpoint := baseURL + "/"
req, err := http.NewRequest(http.MethodGet, endpoint, nil)
if err != nil {
panic(err)
}
// Required to receive server's compressed data.
req.Header.Set("Accept-Encoding", encoding)
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
// decompress server's compressed reply.
cr, err := context.NewCompressReader(resp.Body, encoding)
if err != nil {
panic(err)
}
defer cr.Close()
body, err := ioutil.ReadAll(cr)
if err != nil {
panic(err)
}
fmt.Printf("Received from server: %s", string(body))
}
type payload struct {
Username string `json:"username"`
}
func postExample() {
buf := new(bytes.Buffer)
// Compress client's data.
cw, err := context.NewCompressWriter(buf, encoding, -1)
if err != nil {
panic(err)
}
json.NewEncoder(cw).Encode(payload{Username: "Edward"})
// `Close` or `Flush` required before `NewRequest` call.
cw.Close()
endpoint := baseURL + "/"
req, err := http.NewRequest(http.MethodPost, endpoint, buf)
if err != nil {
panic(err)
}
req.Header.Set("Content-Type", "application/json")
// Required to send gzip compressed data to the server.
req.Header.Set("Content-Encoding", encoding)
// Required to receive server's compressed data.
req.Header.Set("Accept-Encoding", encoding)
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
// Decompress server's compressed reply.
cr, err := context.NewCompressReader(resp.Body, encoding)
if err != nil {
panic(err)
}
defer cr.Close()
body, err := ioutil.ReadAll(cr)
if err != nil {
panic(err)
}
fmt.Printf("Server replied with: %s", string(body))
}

View File

@ -1,102 +0,0 @@
package main
import (
"bytes"
"compress/gzip"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
)
var client = http.DefaultClient
const baseURL = "http://localhost:8080"
func main() {
fmt.Printf("Running client example on: %s\n", baseURL)
getExample()
postExample()
}
func getExample() {
endpoint := baseURL + "/"
req, err := http.NewRequest(http.MethodGet, endpoint, nil)
if err != nil {
panic(err)
}
// Required to receive server's compressed data.
req.Header.Set("Accept-Encoding", "gzip")
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
// decompress server's compressed reply.
r, err := gzip.NewReader(resp.Body)
if err != nil {
panic(err)
}
defer r.Close()
body, err := ioutil.ReadAll(r)
if err != nil {
panic(err)
}
fmt.Printf("Received from server: %s", string(body))
}
type payload struct {
Username string `json:"username"`
}
func postExample() {
buf := new(bytes.Buffer)
// Compress client's data.
w := gzip.NewWriter(buf)
b, err := json.Marshal(payload{Username: "Edward"})
if err != nil {
panic(err)
}
w.Write(b)
w.Close()
endpoint := baseURL + "/"
req, err := http.NewRequest(http.MethodPost, endpoint, buf)
if err != nil {
panic(err)
}
req.Header.Set("Content-Type", "application/json")
// Required to send gzip compressed data to the server.
req.Header.Set("Content-Encoding", "gzip")
// Required to receive server's compressed data.
req.Header.Set("Accept-Encoding", "gzip")
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
// Decompress server's compressed reply.
r, err := gzip.NewReader(resp.Body)
if err != nil {
panic(err)
}
defer r.Close()
body, err := ioutil.ReadAll(r)
if err != nil {
panic(err)
}
fmt.Printf("Server replied with: %s", string(body))
}

View File

@ -1,64 +0,0 @@
package main
import "github.com/kataras/iris/v12"
func main() {
app := newApp()
app.Logger().SetLevel("debug")
app.Listen(":8080")
}
func newApp() *iris.Application {
app := iris.New()
// HERE and you are ready to GO:
app.Use(iris.Compression)
app.Get("/", send)
app.Post("/", receive)
return app
}
type payload struct {
Username string `json:"username"`
}
func send(ctx iris.Context) {
ctx.JSON(payload{
Username: "Makis",
})
}
func receive(ctx iris.Context) {
var p payload
if err := ctx.ReadJSON(&p); err != nil {
ctx.Application().Logger().Debugf("ReadJSON: %v", err)
}
ctx.WriteString(p.Username)
}
/* Manually:
func enableCompression(ctx iris.Context) {
// Enable writing using compression (deflate, gzip, brotli, snappy, s2):
err := ctx.CompressWriter(true)
if err != nil {
ctx.Application().Logger().Debugf("writer: %v", err)
// if you REQUIRE server to SEND compressed data then `return` here.
// return
}
// Enable reading and binding request's compressed data:
err = ctx.CompressReader(true)
if err != nil &&
// on GET we don't expect writing with gzip from client
ctx.Method() != iris.MethodGet {
ctx.Application().Logger().Debugf("reader: %v", err)
// if you REQUIRE server to RECEIVE only
// compressed data then `return` here.
// return
}
ctx.Next()
}
*/

View File

@ -1,84 +0,0 @@
package main
import (
"encoding/json"
"reflect"
"strings"
"testing"
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/context"
"github.com/kataras/iris/v12/httptest"
)
func TestCompression(t *testing.T) {
app := newApp()
e := httptest.New(t, app)
var expectedReply = payload{Username: "Makis"}
testBody(t, e.GET("/"), expectedReply)
}
func TestCompressionAfterRecorder(t *testing.T) {
var expectedReply = payload{Username: "Makis"}
app := iris.New()
app.Use(func(ctx iris.Context) {
ctx.Record()
ctx.Next()
})
app.Use(iris.Compression)
app.Get("/", func(ctx iris.Context) {
ctx.JSON(expectedReply)
})
e := httptest.New(t, app)
testBody(t, e.GET("/"), expectedReply)
}
func TestCompressionBeforeRecorder(t *testing.T) {
var expectedReply = payload{Username: "Makis"}
app := iris.New()
app.Use(iris.Compression)
app.Use(func(ctx iris.Context) {
ctx.Record()
ctx.Next()
})
app.Get("/", func(ctx iris.Context) {
ctx.JSON(expectedReply)
})
e := httptest.New(t, app)
testBody(t, e.GET("/"), expectedReply)
}
func testBody(t *testing.T, req *httptest.Request, expectedReply interface{}) {
t.Helper()
body := req.WithHeader(context.AcceptEncodingHeaderKey, context.GZIP).Expect().
Status(httptest.StatusOK).
ContentEncoding(context.GZIP).
ContentType(context.ContentJSONHeaderValue).Body().Raw()
// Note that .Expect() consumes the response body
// and stores it to unexported "contents" field
// therefore, we retrieve it as string and put it to a new buffer.
r := strings.NewReader(body)
cr, err := context.NewCompressReader(r, context.GZIP)
if err != nil {
t.Fatal(err)
}
defer cr.Close()
var got payload
if err = json.NewDecoder(cr).Decode(&got); err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(expectedReply, got) {
t.Fatalf("expected %#+v but got %#+v", expectedReply, got)
}
}

View File

@ -1,29 +0,0 @@
package main
import (
"github.com/kataras/iris/v12"
)
func main() {
app := iris.New()
app.Get("/", func(ctx iris.Context) {
ctx.HTML("<b>Hello!</b>")
})
// [...]
// Good when you want to modify the whole configuration.
app.Listen(":8080", iris.WithConfiguration(iris.Configuration{ // default configuration:
DisableStartupLog: false,
DisableInterruptHandler: false,
DisablePathCorrection: false,
EnablePathEscape: false,
FireMethodNotAllowed: false,
DisableBodyConsumptionOnUnmarshal: false,
DisableAutoFireStatusCode: false,
TimeFormat: "Mon, 02 Jan 2006 15:04:05 GMT",
Charset: "utf-8",
}))
// or before Run:
// app.Configure(iris.WithConfiguration(iris.Configuration{...}))
}

View File

@ -1,9 +0,0 @@
DisablePathCorrection = false
EnablePathEscape = false
FireMethodNotAllowed = true
DisableBodyConsumptionOnUnmarshal = false
TimeFormat = "Mon, 01 Jan 2006 15:04:05 GMT"
Charset = "utf-8"
RemoteAddrHeaders = ["X-Real-Ip", "X-Forwarded-For", "CF-Connecting-IP"]
[Other]
MyServerName = "iris"

View File

@ -1,21 +0,0 @@
package main
import (
"github.com/kataras/iris/v12"
)
func main() {
app := iris.New()
app.Get("/", func(ctx iris.Context) {
ctx.HTML("<b>Hello!</b>")
})
// [...]
// Good when you have two configurations, one for development and a different one for production use.
app.Listen(":8080", iris.WithConfiguration(iris.TOML("./configs/iris.tml")))
// or before run:
// app.Configure(iris.WithConfiguration(iris.TOML("./configs/iris.tml")))
// app.Listen(":8080")
}

View File

@ -1,16 +0,0 @@
DisablePathCorrection: false
EnablePathEscape: false
FireMethodNotAllowed: true
DisableBodyConsumptionOnUnmarshal: true
TimeFormat: Mon, 01 Jan 2006 15:04:05 GMT
Charset: UTF-8
SSLProxyHeaders:
X-Forwarded-Proto: https
HostProxyHeaders:
X-Host: true
RemoteAddrHeaders:
- X-Real-Ip
- X-Forwarded-For
- CF-Connecting-IP
Other:
Addr: :8080

View File

@ -1,24 +0,0 @@
package main
import (
"github.com/kataras/iris/v12"
)
func main() {
app := iris.New()
app.Get("/", func(ctx iris.Context) {
ctx.HTML("<b>Hello!</b>")
})
// [...]
// Good when you have two configurations, one for development and a different one for production use.
// If iris.YAML's input string argument is "~" then it loads the configuration from the home directory
// and can be shared between many iris instances.
cfg := iris.YAML("./configs/iris.yml")
addr := cfg.Other["Addr"].(string)
app.Listen(addr, iris.WithConfiguration(cfg))
// or before run:
// app.Configure(iris.WithConfiguration(iris.YAML("./configs/iris.yml")))
// app.Listen(":8080")
}

View File

@ -1,21 +0,0 @@
package main
import (
"github.com/kataras/iris/v12"
)
func main() {
app := iris.New()
app.Get("/", func(ctx iris.Context) {
ctx.HTML("<b>Hello!</b>")
})
// [...]
// Good when you share configuration between multiple iris instances.
// This configuration file lives in your $HOME/iris.yml for unix hosts
// or %HOMEDRIVE%+%HOMEPATH%/iris.yml for windows hosts, and you can modify it.
app.Listen(":8080", iris.WithGlobalConfiguration)
// or before run:
// app.Configure(iris.WithGlobalConfiguration)
// app.Listen(":8080")
}

View File

@ -1,23 +0,0 @@
package main
import (
"github.com/kataras/iris/v12"
)
func main() {
app := iris.New()
app.Get("/", func(ctx iris.Context) {
ctx.HTML("<b>Hello!</b>")
})
// [...]
// Good when you want to change some of the configuration's field.
// Prefix: "With", code editors will help you navigate through all
// configuration options without even a glitch to the documentation.
app.Listen(":8080", iris.WithoutStartupLog, iris.WithCharset("utf-8"))
// or before run:
// app.Configure(iris.WithoutStartupLog, iris.WithCharset("utf-8"))
// app.Listen(":8080")
}

View File

@ -1,41 +0,0 @@
package main
import (
"net/http"
"github.com/kataras/iris/v12"
)
func main() {
app := iris.New()
irisMiddleware := iris.FromStd(negronilikeTestMiddleware)
app.Use(irisMiddleware)
// Method GET: http://localhost:8080/
app.Get("/", func(ctx iris.Context) {
ctx.HTML("<h1> Home </h1>")
// this will print an error,
// this route's handler will never be executed because the middleware's criteria not passed.
})
// Method GET: http://localhost:8080/ok
app.Get("/ok", func(ctx iris.Context) {
ctx.Writef("Hello world!")
// this will print "OK. Hello world!".
})
// http://localhost:8080
// http://localhost:8080/ok
app.Listen(":8080")
}
func negronilikeTestMiddleware(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
if r.URL.Path == "/ok" && r.Method == "GET" {
w.Write([]byte("OK. "))
next(w, r) // go to the next route's handler
return
}
// else print an error and do not forward to the route's handler.
w.WriteHeader(iris.StatusBadRequest)
w.Write([]byte("Bad request"))
}

View File

@ -1,31 +0,0 @@
package main
import (
"net/http"
"github.com/kataras/iris/v12"
)
func main() {
app := iris.New()
irisMiddleware := iris.FromStd(nativeTestMiddleware)
app.Use(irisMiddleware)
// Method GET: http://localhost:8080/
app.Get("/", func(ctx iris.Context) {
ctx.HTML("Home")
})
// Method GET: http://localhost:8080/ok
app.Get("/ok", func(ctx iris.Context) {
ctx.HTML("<b>Hello world!</b>")
})
// http://localhost:8080
// http://localhost:8080/ok
app.Listen(":8080")
}
func nativeTestMiddleware(w http.ResponseWriter, r *http.Request) {
println("Request path: " + r.URL.Path)
}

View File

@ -1,45 +0,0 @@
package main
import (
"errors"
"fmt"
"net/http"
"runtime/debug"
"github.com/kataras/iris/v12"
"github.com/getsentry/raven-go"
)
// https://docs.sentry.io/clients/go/integrations/http/
func init() {
raven.SetDSN("https://<key>:<secret>@sentry.io/<project>")
}
func main() {
app := iris.New()
app.Get("/", func(ctx iris.Context) {
ctx.Writef("Hi")
})
// Example for WrapRouter is already here:
// https://github.com/kataras/iris/blob/master/_examples/routing/custom-wrapper/main.go#L53
app.WrapRouter(func(w http.ResponseWriter, r *http.Request, irisRouter http.HandlerFunc) {
// Exactly the same source code:
// https://github.com/getsentry/raven-go/blob/379f8d0a68ca237cf8893a1cdfd4f574125e2c51/http.go#L70
defer func() {
if rval := recover(); rval != nil {
debug.PrintStack()
rvalStr := fmt.Sprint(rval)
packet := raven.NewPacket(rvalStr, raven.NewException(errors.New(rvalStr), raven.NewStacktrace(2, 3, nil)), raven.NewHttp(r))
raven.Capture(packet, nil)
w.WriteHeader(http.StatusInternalServerError)
}
}()
irisRouter(w, r)
})
app.Listen(":8080")
}

View File

@ -1,57 +0,0 @@
package main
import (
"errors"
"fmt"
"runtime/debug"
"github.com/kataras/iris/v12"
"github.com/getsentry/raven-go"
)
// At this example you will see how to convert any net/http middleware
// that has the form of `(HandlerFunc) HandlerFunc`.
// If the `raven.RecoveryHandler` had the form of
// `(http.HandlerFunc)` or `(http.HandlerFunc, next http.HandlerFunc)`
// you could just use the `irisMiddleware := iris.FromStd(nativeHandler)`
// but it doesn't, however as you already know Iris can work with net/http directly
// because of the `ctx.ResponseWriter()` and `ctx.Request()` are the original
// http.ResponseWriter and *http.Request.
// (this one is a big advantage, as a result you can use Iris for ever :)).
//
// The source code of the native middleware does not change at all.
// https://github.com/getsentry/raven-go/blob/379f8d0a68ca237cf8893a1cdfd4f574125e2c51/http.go#L70
// The only addition is the Line 18 and Line 39 (instead of handler(w,r))
// and you have a new iris middleware ready to use!
func irisRavenMiddleware(ctx iris.Context) {
w, r := ctx.ResponseWriter(), ctx.Request()
defer func() {
if rval := recover(); rval != nil {
debug.PrintStack()
rvalStr := fmt.Sprint(rval)
packet := raven.NewPacket(rvalStr, raven.NewException(errors.New(rvalStr), raven.NewStacktrace(2, 3, nil)), raven.NewHttp(r))
raven.Capture(packet, nil)
w.WriteHeader(iris.StatusInternalServerError)
}
}()
ctx.Next()
}
// https://docs.sentry.io/clients/go/integrations/http/
func init() {
raven.SetDSN("https://<key>:<secret>@sentry.io/<project>")
}
func main() {
app := iris.New()
app.Use(irisRavenMiddleware)
app.Get("/", func(ctx iris.Context) {
ctx.Writef("Hi")
})
app.Listen(":8080")
}

View File

@ -1,64 +0,0 @@
package main
import "github.com/kataras/iris/v12"
func newApp() *iris.Application {
app := iris.New()
// Set A Cookie.
app.Get("/cookies/{name}/{value}", func(ctx iris.Context) {
name := ctx.Params().Get("name")
value := ctx.Params().Get("value")
ctx.SetCookieKV(name, value) // <--
// Alternatively: ctx.SetCookie(&http.Cookie{...})
//
// If you want to set custom the path:
// ctx.SetCookieKV(name, value, iris.CookiePath("/custom/path/cookie/will/be/stored"))
//
// If you want to be visible only to current request path:
// (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 /* or iris.CookiePath("") */)
// More:
// iris.CookieExpires(time.Duration)
// iris.CookieHTTPOnly(false)
ctx.Writef("cookie added: %s = %s", name, value)
})
// Retrieve A Cookie.
app.Get("/cookies/{name}", func(ctx iris.Context) {
name := ctx.Params().Get("name")
value := ctx.GetCookie(name) // <--
// If you want more than the value then:
// cookie, err := ctx.Request().Cookie(name)
// if err != nil {
// handle error.
// }
ctx.WriteString(value)
})
// Delete A Cookie.
app.Delete("/cookies/{name}", func(ctx iris.Context) {
name := ctx.Params().Get("name")
ctx.RemoveCookie(name) // <--
// If you want to set custom the path:
// ctx.SetCookieKV(name, value, iris.CookiePath("/custom/path/cookie/will/be/stored"))
ctx.Writef("cookie %s removed", name)
})
return app
}
func main() {
app := newApp()
// GET: http://localhost:8080/cookies/my_name/my_value
// GET: http://localhost:8080/cookies/my_name
// DELETE: http://localhost:8080/cookies/my_name
app.Listen(":8080")
}

View File

@ -1,32 +0,0 @@
package main
import (
"fmt"
"testing"
"github.com/kataras/iris/v12/httptest"
)
func TestCookiesBasic(t *testing.T) {
app := newApp()
e := httptest.New(t, app, httptest.URL("http://example.com"))
cookieName, cookieValue := "my_cookie_name", "my_cookie_value"
// Test set a Cookie.
t1 := e.GET(fmt.Sprintf("/cookies/%s/%s", cookieName, cookieValue)).Expect().Status(httptest.StatusOK)
t1.Cookie(cookieName).Value().Equal(cookieValue) // validate cookie's existence, it should be there now.
t1.Body().Contains(cookieValue)
// Test retrieve a Cookie.
t2 := e.GET(fmt.Sprintf("/cookies/%s", cookieName)).Expect().Status(httptest.StatusOK)
t2.Body().Equal(cookieValue)
// Test remove a Cookie.
t3 := e.DELETE(fmt.Sprintf("/cookies/%s", cookieName)).Expect().Status(httptest.StatusOK)
t3.Body().Contains(cookieName)
t4 := e.GET(fmt.Sprintf("/cookies/%s", cookieName)).Expect().Status(httptest.StatusOK)
t4.Cookies().Empty()
t4.Body().Empty()
}

View File

@ -1,76 +0,0 @@
package main
import (
"github.com/kataras/iris/v12"
)
func main() {
app := newApp()
// http://localhost:8080/set/name1/value1
// http://localhost:8080/get/name1
// http://localhost:8080/remove/name1
app.Listen(":8080", iris.WithLogLevel("debug"))
}
func newApp() *iris.Application {
app := iris.New()
app.Use(withCookieOptions)
app.Get("/set/{name}/{value}", setCookie)
app.Get("/get/{name}", getCookie)
app.Get("/remove/{name}", removeCookie)
return app
}
func withCookieOptions(ctx iris.Context) {
// Register cookie options for request-lifecycle.
// To register per cookie, just add the CookieOption
// on the last variadic input argument of
// SetCookie, SetCookieKV, UpsertCookie, RemoveCookie
// and GetCookie Context methods.
//
// * CookieAllowReclaim
// * CookieAllowSubdomains
// * CookieSecure
// * CookieHTTPOnly
// * CookieSameSite
// * CookiePath
// * CookieCleanPath
// * CookieExpires
// * CookieEncoding
ctx.AddCookieOptions(iris.CookieAllowReclaim())
ctx.Next()
}
func setCookie(ctx iris.Context) {
name := ctx.Params().Get("name")
value := ctx.Params().Get("value")
ctx.SetCookieKV(name, value)
// By-default net/http does not remove or set the Cookie on the Request object.
//
// With the `CookieAllowReclaim` option, whenever you set or remove a cookie
// it will be also reflected in the Request object immediately (of the same request lifecycle)
// therefore, any of the next handlers in the chain are not holding the old value.
valueIsAvailableInRequestObject := ctx.GetCookie(name)
ctx.Writef("cookie %s=%s", name, valueIsAvailableInRequestObject)
}
func getCookie(ctx iris.Context) {
name := ctx.Params().Get("name")
value := ctx.GetCookie(name)
ctx.WriteString(value)
}
func removeCookie(ctx iris.Context) {
name := ctx.Params().Get("name")
ctx.RemoveCookie(name)
removedFromRequestObject := ctx.GetCookie(name) // CookieAllowReclaim feature.
ctx.Writef("cookie %s removed, value should be empty=%s", name, removedFromRequestObject)
}

View File

@ -1,32 +0,0 @@
package main
import (
"fmt"
"testing"
"github.com/kataras/iris/v12/httptest"
)
func TestCookieOptions(t *testing.T) {
app := newApp()
e := httptest.New(t, app, httptest.URL("http://example.com"))
cookieName, cookieValue := "my_cookie_name", "my_cookie_value"
// Test set a Cookie.
t1 := e.GET(fmt.Sprintf("/set/%s/%s", cookieName, cookieValue)).Expect().Status(httptest.StatusOK)
t1.Cookie(cookieName).Value().Equal(cookieValue)
t1.Body().Contains(fmt.Sprintf("%s=%s", cookieName, cookieValue))
// Test retrieve a Cookie.
t2 := e.GET(fmt.Sprintf("/get/%s", cookieName)).Expect().Status(httptest.StatusOK)
t2.Body().Equal(cookieValue)
// Test remove a Cookie.
t3 := e.GET(fmt.Sprintf("/remove/%s", cookieName)).Expect().Status(httptest.StatusOK)
t3.Body().Contains(fmt.Sprintf("cookie %s removed, value should be empty=%s", cookieName, ""))
t4 := e.GET(fmt.Sprintf("/get/%s", cookieName)).Expect().Status(httptest.StatusOK)
t4.Cookies().Empty()
t4.Body().Empty()
}

View File

@ -1,72 +0,0 @@
package main
// developers can use any library to add a custom cookie encoder/decoder.
// At this example we use the gorilla's securecookie package:
// $ go get github.com/gorilla/securecookie
// $ go run main.go
import (
"github.com/kataras/iris/v12"
"github.com/gorilla/securecookie"
)
func main() {
app := newApp()
// http://localhost:8080/cookies/name/value
// http://localhost:8080/cookies/name
// http://localhost:8080/cookies/remove/name
app.Listen(":8080")
}
func newApp() *iris.Application {
app := iris.New()
r := app.Party("/cookies")
{
r.Use(useSecureCookies())
// Set A Cookie.
r.Get("/{name}/{value}", func(ctx iris.Context) {
name := ctx.Params().Get("name")
value := ctx.Params().Get("value")
ctx.SetCookieKV(name, value)
ctx.Writef("cookie added: %s = %s", name, value)
})
// Retrieve A Cookie.
r.Get("/{name}", func(ctx iris.Context) {
name := ctx.Params().Get("name")
value := ctx.GetCookie(name)
ctx.WriteString(value)
})
r.Get("/remove/{name}", func(ctx iris.Context) {
name := ctx.Params().Get("name")
ctx.RemoveCookie(name)
ctx.Writef("cookie %s removed", name)
})
}
return app
}
func useSecureCookies() iris.Handler {
var (
hashKey = securecookie.GenerateRandomKey(64)
blockKey = securecookie.GenerateRandomKey(32)
s = securecookie.New(hashKey, blockKey)
)
return func(ctx iris.Context) {
ctx.AddCookieOptions(iris.CookieEncoding(s))
ctx.Next()
}
}

View File

@ -1,34 +0,0 @@
package main
import (
"fmt"
"testing"
"github.com/kataras/iris/v12/httptest"
)
func TestSecureCookie(t *testing.T) {
app := newApp()
e := httptest.New(t, app, httptest.URL("http://example.com"))
cookieName, cookieValue := "my_cookie_name", "my_cookie_value"
// Test set a Cookie.
t1 := e.GET(fmt.Sprintf("/cookies/%s/%s", cookieName, cookieValue)).Expect().Status(httptest.StatusOK)
// note that this will not work because it doesn't always returns the same value:
// cookieValueEncoded, _ := sc.Encode(cookieName, cookieValue)
t1.Cookie(cookieName).Value().NotEqual(cookieValue) // validate cookie's existence and value is not on its raw form.
t1.Body().Contains(cookieValue)
// Test retrieve a Cookie.
t2 := e.GET(fmt.Sprintf("/cookies/%s", cookieName)).Expect().Status(httptest.StatusOK)
t2.Body().Equal(cookieValue)
// Test remove a Cookie.
t3 := e.GET(fmt.Sprintf("/cookies/remove/%s", cookieName)).Expect().Status(httptest.StatusOK)
t3.Body().Contains(cookieName)
t4 := e.GET(fmt.Sprintf("/cookies/%s", cookieName)).Expect().Status(httptest.StatusOK)
t4.Cookies().Empty()
t4.Body().Empty()
}

View File

@ -1,2 +0,0 @@
PORT=8080
DSN=mongodb://localhost:27017

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

View File

@ -1,17 +0,0 @@
# docker build -t myapp .
# docker run --rm -it -p 8080:8080 myapp:latest
FROM golang:latest AS builder
RUN apt-get update
ENV GO111MODULE=on \
CGO_ENABLED=0 \
GOOS=linux \
GOARCH=amd64
WORKDIR /go/src/app
COPY go.mod .
RUN go mod download
COPY . .
RUN go install
FROM scratch
COPY --from=builder /go/bin/myapp .
ENTRYPOINT ["./myapp"]

View File

@ -1,64 +0,0 @@
# Build RESTful API with the official MongoDB Go Driver and Iris
Article is coming soon, follow and stay tuned
- <https://medium.com/@kataras>
- <https://dev.to/kataras>
Read [the fully functional example](main.go).
## Run
### Docker
Install [Docker](https://www.docker.com/) and execute the command below
```sh
$ docker-compose up
```
### Manually
```sh
# .env file contents
PORT=8080
DSN=mongodb://localhost:27017
```
```sh
$ go run main.go
> 2019/01/28 05:17:59 Loading environment variables from file: .env
> 2019/01/28 05:17:59 ◽ Port=8080
> 2019/01/28 05:17:59 ◽ DSN=mongodb://localhost:27017
> Now listening on: http://localhost:8080
```
```sh
GET : http://localhost:8080/api/store/movies
POST : http://localhost:8080/api/store/movies
GET : http://localhost:8080/api/store/movies/{id}
PUT : http://localhost:8080/api/store/movies/{id}
DELETE : http://localhost:8080/api/store/movies/{id}
```
## Screens
### Add a Movie
![](0_create_movie.png)
### Update a Movie
![](1_update_movie.png)
### Get all Movies
![](2_get_all_movies.png)
### Get a Movie by its ID
![](3_get_movie.png)
### Delete a Movie by its ID
![](4_delete_movie.png)

View File

@ -1,101 +0,0 @@
package storeapi
import (
"myapp/httputil"
"myapp/store"
"github.com/kataras/iris/v12"
)
type MovieHandler struct {
service store.MovieService
}
func NewMovieHandler(service store.MovieService) *MovieHandler {
return &MovieHandler{service: service}
}
func (h *MovieHandler) GetAll(ctx iris.Context) {
movies, err := h.service.GetAll(nil)
if err != nil {
httputil.InternalServerErrorJSON(ctx, err, "Server was unable to retrieve all movies")
return
}
if movies == nil {
// will return "null" if empty, with this "trick" we return "[]" json.
movies = make([]store.Movie, 0)
}
ctx.JSON(movies)
}
func (h *MovieHandler) Get(ctx iris.Context) {
id := ctx.Params().Get("id")
m, err := h.service.GetByID(nil, id)
if err != nil {
if err == store.ErrNotFound {
ctx.NotFound()
} else {
httputil.InternalServerErrorJSON(ctx, err, "Server was unable to retrieve movie [%s]", id)
}
return
}
ctx.JSON(m)
}
func (h *MovieHandler) Add(ctx iris.Context) {
m := new(store.Movie)
err := ctx.ReadJSON(m)
if err != nil {
httputil.FailJSON(ctx, iris.StatusBadRequest, err, "Malformed request payload")
return
}
err = h.service.Create(nil, m)
if err != nil {
httputil.InternalServerErrorJSON(ctx, err, "Server was unable to create a movie")
return
}
ctx.StatusCode(iris.StatusCreated)
ctx.JSON(m)
}
func (h *MovieHandler) Update(ctx iris.Context) {
id := ctx.Params().Get("id")
var m store.Movie
err := ctx.ReadJSON(&m)
if err != nil {
httputil.FailJSON(ctx, iris.StatusBadRequest, err, "Malformed request payload")
return
}
err = h.service.Update(nil, id, m)
if err != nil {
if err == store.ErrNotFound {
ctx.NotFound()
return
}
httputil.InternalServerErrorJSON(ctx, err, "Server was unable to update movie [%s]", id)
return
}
}
func (h *MovieHandler) Delete(ctx iris.Context) {
id := ctx.Params().Get("id")
err := h.service.Delete(nil, id)
if err != nil {
if err == store.ErrNotFound {
ctx.NotFound()
return
}
httputil.InternalServerErrorJSON(ctx, err, "Server was unable to delete movie [%s]", id)
return
}
}

View File

@ -1,18 +0,0 @@
version: "3.1"
services:
app:
build: .
environment:
Port: 8080
DSN: db:27017
ports:
- 8080:8080
depends_on:
- db
db:
image: mongo
environment:
MONGO_INITDB_DATABASE: store
ports:
- 27017:27017

View File

@ -1,85 +0,0 @@
package env
import (
"fmt"
"log"
"os"
"path/filepath"
"strings"
"github.com/joho/godotenv"
)
var (
// Port is the PORT environment variable or 8080 if missing.
// Used to open the tcp listener for our web server.
Port string
// DSN is the DSN environment variable or mongodb://localhost:27017 if missing.
// Used to connect to the mongodb.
DSN string
)
func parse() {
Port = getDefault("PORT", "8080")
DSN = getDefault("DSN", "mongodb://localhost:27017")
log.Printf("• Port=%s\n", Port)
log.Printf("• DSN=%s\n", DSN)
}
// Load loads environment variables that are being used across the whole app.
// Loading from file(s), i.e .env or dev.env
//
// Example of a 'dev.env':
// PORT=8080
// DSN=mongodb://localhost:27017
//
// After `Load` the callers can get an environment variable via `os.Getenv`.
func Load(envFileName string) {
if args := os.Args; len(args) > 1 && args[1] == "help" {
fmt.Fprintln(os.Stderr, "https://github.com/kataras/iris/blob/master/_examples/database/mongodb/README.md")
os.Exit(-1)
}
// If more than one filename passed with comma separated then load from all
// of these, a env file can be a partial too.
envFiles := strings.Split(envFileName, ",")
for _, envFile := range envFiles {
if filepath.Ext(envFile) == "" {
envFile += ".env"
}
if fileExists(envFile) {
log.Printf("Loading environment variables from file: %s\n", envFile)
if err := godotenv.Load(envFile); err != nil {
panic(fmt.Sprintf("error loading environment variables from [%s]: %v", envFile, err))
}
}
}
// envMap, _ := godotenv.Read(envFiles...)
// for k, v := range envMap {
// log.Printf("◽ %s=%s\n", k, v)
// }
parse()
}
func getDefault(key string, def string) string {
value := os.Getenv(key)
if value == "" {
os.Setenv(key, def)
value = def
}
return value
}
func fileExists(filename string) bool {
info, err := os.Stat(filename)
if os.IsNotExist(err) {
return false
}
return !info.IsDir()
}

View File

@ -1,9 +0,0 @@
module myapp
go 1.15
require (
github.com/joho/godotenv v1.3.0
github.com/kataras/iris/v12 v12.1.9-0.20200812051831-0edf0affb0bd
go.mongodb.org/mongo-driver v1.3.4
)

View File

@ -1,130 +0,0 @@
package httputil
import (
"errors"
"fmt"
"io"
"net/http"
"os"
"runtime"
"runtime/debug"
"strings"
"time"
"github.com/kataras/iris/v12"
)
var validStackFuncs = []func(string) bool{
func(file string) bool {
return strings.Contains(file, "/mongodb/api/")
},
}
// RuntimeCallerStack returns the app's `file:line` stacktrace
// to give more information about an error cause.
func RuntimeCallerStack() (s string) {
var pcs [10]uintptr
n := runtime.Callers(1, pcs[:])
frames := runtime.CallersFrames(pcs[:n])
for {
frame, more := frames.Next()
for _, fn := range validStackFuncs {
if fn(frame.File) {
s += fmt.Sprintf("\n\t\t\t%s:%d", frame.File, frame.Line)
}
}
if !more {
break
}
}
return s
}
// HTTPError describes an HTTP error.
type HTTPError struct {
error
Stack string `json:"-"` // the whole stacktrace.
CallerStack string `json:"-"` // the caller, file:lineNumber
When time.Time `json:"-"` // the time that the error occurred.
// ErrorCode int: maybe a collection of known error codes.
StatusCode int `json:"statusCode"`
// could be named as "reason" as well
// it's the message of the error.
Description string `json:"description"`
}
func newError(statusCode int, err error, format string, args ...interface{}) HTTPError {
if format == "" {
format = http.StatusText(statusCode)
}
desc := fmt.Sprintf(format, args...)
if err == nil {
err = errors.New(desc)
}
return HTTPError{
err,
string(debug.Stack()),
RuntimeCallerStack(),
time.Now(),
statusCode,
desc,
}
}
func (err HTTPError) writeHeaders(ctx iris.Context) {
ctx.StatusCode(err.StatusCode)
ctx.Header("X-Content-Type-Options", "nosniff")
}
// LogFailure will print out the failure to the "logger".
func LogFailure(logger io.Writer, ctx iris.Context, err HTTPError) {
timeFmt := err.When.Format("2006/01/02 15:04:05")
firstLine := fmt.Sprintf("%s %s: %s", timeFmt, http.StatusText(err.StatusCode), err.Error())
whitespace := strings.Repeat(" ", len(timeFmt)+1)
fmt.Fprintf(logger, "%s\n%sIP: %s\n%sURL: %s\n%sSource: %s\n",
firstLine, whitespace, ctx.RemoteAddr(), whitespace, ctx.FullRequestURI(), whitespace, err.CallerStack)
}
// Fail will send the status code, write the error's reason
// and return the HTTPError for further use, i.e logging, see `InternalServerError`.
func Fail(ctx iris.Context, statusCode int, err error, format string, args ...interface{}) HTTPError {
httpErr := newError(statusCode, err, format, args...)
httpErr.writeHeaders(ctx)
ctx.WriteString(httpErr.Description)
return httpErr
}
// FailJSON will send to the client the error data as JSON.
// Useful for APIs.
func FailJSON(ctx iris.Context, statusCode int, err error, format string, args ...interface{}) HTTPError {
httpErr := newError(statusCode, err, format, args...)
httpErr.writeHeaders(ctx)
ctx.JSON(httpErr)
return httpErr
}
// InternalServerError logs to the server's terminal
// and dispatches to the client the 500 Internal Server Error.
// Internal Server errors are critical, so we log them to the `os.Stderr`.
func InternalServerError(ctx iris.Context, err error, format string, args ...interface{}) {
LogFailure(os.Stderr, ctx, Fail(ctx, iris.StatusInternalServerError, err, format, args...))
}
// InternalServerErrorJSON acts exactly like `InternalServerError` but instead it sends the data as JSON.
// Useful for APIs.
func InternalServerErrorJSON(ctx iris.Context, err error, format string, args ...interface{}) {
LogFailure(os.Stderr, ctx, FailJSON(ctx, iris.StatusInternalServerError, err, format, args...))
}
// UnauthorizedJSON sends JSON format of StatusUnauthorized(401) HTTPError value.
func UnauthorizedJSON(ctx iris.Context, err error, format string, args ...interface{}) HTTPError {
return FailJSON(ctx, iris.StatusUnauthorized, err, format, args...)
}

View File

@ -1,83 +0,0 @@
package main
// go get -u go.mongodb.org/mongo-driver
// go get -u github.com/joho/godotenv
import (
"context"
"flag"
"fmt"
"log"
"os"
// APIs
storeapi "myapp/api/store"
//
"myapp/env"
"myapp/store"
"github.com/kataras/iris/v12"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
const version = "0.0.1"
func init() {
envFileName := ".env"
flagset := flag.CommandLine
flagset.StringVar(&envFileName, "env", envFileName, "the env file which web app will use to extract its environment variables")
flagset.Parse(os.Args[1:])
env.Load(envFileName)
}
func main() {
clientOptions := options.Client().SetHosts([]string{env.DSN})
client, err := mongo.Connect(context.Background(), clientOptions)
if err != nil {
log.Fatal(err)
}
err = client.Ping(context.Background(), nil)
if err != nil {
log.Fatal(err)
}
defer client.Disconnect(context.TODO())
db := client.Database("store")
var (
// Collections.
moviesCollection = db.Collection("movies")
// Services.
movieService = store.NewMovieService(moviesCollection)
)
app := iris.New()
app.Use(func(ctx iris.Context) {
ctx.Header("Server", "Iris MongoDB/"+version)
ctx.Next()
})
storeAPI := app.Party("/api/store")
{
movieHandler := storeapi.NewMovieHandler(movieService)
storeAPI.Get("/movies", movieHandler.GetAll)
storeAPI.Post("/movies", movieHandler.Add)
storeAPI.Get("/movies/{id}", movieHandler.Get)
storeAPI.Put("/movies/{id}", movieHandler.Update)
storeAPI.Delete("/movies/{id}", movieHandler.Delete)
}
// GET: http://localhost:8080/api/store/movies
// POST: http://localhost:8080/api/store/movies
// GET: http://localhost:8080/api/store/movies/{id}
// PUT: http://localhost:8080/api/store/movies/{id}
// DELETE: http://localhost:8080/api/store/movies/{id}
app.Listen(fmt.Sprintf(":%s", env.Port), iris.WithOptimizations)
}

View File

@ -1,180 +0,0 @@
package store
import (
"context"
"errors"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
// up to you:
// "go.mongodb.org/mongo-driver/mongo/options"
)
type Movie struct {
ID primitive.ObjectID `json:"_id" bson:"_id"` /* you need the bson:"_id" to be able to retrieve with ID filled */
Name string `json:"name"`
Cover string `json:"cover"`
Description string `json:"description"`
}
type MovieService interface {
GetAll(ctx context.Context) ([]Movie, error)
GetByID(ctx context.Context, id string) (Movie, error)
Create(ctx context.Context, m *Movie) error
Update(ctx context.Context, id string, m Movie) error
Delete(ctx context.Context, id string) error
}
type movieService struct {
C *mongo.Collection
}
var _ MovieService = (*movieService)(nil)
func NewMovieService(collection *mongo.Collection) MovieService {
// up to you:
// indexOpts := new(options.IndexOptions)
// indexOpts.SetName("movieIndex").
// SetUnique(true).
// SetBackground(true).
// SetSparse(true)
// collection.Indexes().CreateOne(context.Background(), mongo.IndexModel{
// Keys: []string{"_id", "name"},
// Options: indexOpts,
// })
return &movieService{C: collection}
}
func (s *movieService) GetAll(ctx context.Context) ([]Movie, error) {
// Note:
// The mongodb's go-driver's docs says that you can pass `nil` to "find all" but this gives NilDocument error,
// probably it's a bug or a documentation's mistake, you have to pass `bson.D{}` instead.
cur, err := s.C.Find(ctx, bson.D{})
if err != nil {
return nil, err
}
defer cur.Close(ctx)
var results []Movie
for cur.Next(ctx) {
if err = cur.Err(); err != nil {
return nil, err
}
// elem := bson.D{}
var elem Movie
err = cur.Decode(&elem)
if err != nil {
return nil, err
}
// results = append(results, Movie{ID: elem[0].Value.(primitive.ObjectID)})
results = append(results, elem)
}
return results, nil
}
func matchID(id string) (bson.D, error) {
objectID, err := primitive.ObjectIDFromHex(id)
if err != nil {
return nil, err
}
filter := bson.D{{Key: "_id", Value: objectID}}
return filter, nil
}
var ErrNotFound = errors.New("not found")
func (s *movieService) GetByID(ctx context.Context, id string) (Movie, error) {
var movie Movie
filter, err := matchID(id)
if err != nil {
return movie, err
}
err = s.C.FindOne(ctx, filter).Decode(&movie)
if err == mongo.ErrNoDocuments {
return movie, ErrNotFound
}
return movie, err
}
func (s *movieService) Create(ctx context.Context, m *Movie) error {
if m.ID.IsZero() {
m.ID = primitive.NewObjectID()
}
_, err := s.C.InsertOne(ctx, m)
if err != nil {
return err
}
// The following doesn't work if you have the `bson:"_id` on Movie.ID field,
// therefore we manually generate a new ID (look above).
// res, err := ...InsertOne
// objectID := res.InsertedID.(primitive.ObjectID)
// m.ID = objectID
return nil
}
func (s *movieService) Update(ctx context.Context, id string, m Movie) error {
filter, err := matchID(id)
if err != nil {
return err
}
// update := bson.D{
// {Key: "$set", Value: m},
// }
// ^ this will override all fields, you can do that, depending on your design. but let's check each field:
elem := bson.D{}
if m.Name != "" {
elem = append(elem, bson.E{Key: "name", Value: m.Name})
}
if m.Description != "" {
elem = append(elem, bson.E{Key: "description", Value: m.Description})
}
if m.Cover != "" {
elem = append(elem, bson.E{Key: "cover", Value: m.Cover})
}
update := bson.D{
{Key: "$set", Value: elem},
}
_, err = s.C.UpdateOne(ctx, filter, update)
if err != nil {
if err == mongo.ErrNoDocuments {
return ErrNotFound
}
return err
}
return nil
}
func (s *movieService) Delete(ctx context.Context, id string) error {
filter, err := matchID(id)
if err != nil {
return err
}
_, err = s.C.DeleteOne(ctx, filter)
if err != nil {
if err == mongo.ErrNoDocuments {
return ErrNotFound
}
return err
}
return nil
}

View File

@ -1,17 +0,0 @@
# docker build -t myapp .
# docker run --rm -it -p 8080:8080 myapp:latest
FROM golang:latest AS builder
RUN apt-get update
ENV GO111MODULE=on \
CGO_ENABLED=0 \
GOOS=linux \
GOARCH=amd64
WORKDIR /go/src/app
COPY go.mod .
RUN go mod download
COPY . .
RUN go install
FROM scratch
COPY --from=builder /go/bin/myapp .
ENTRYPOINT ["./myapp"]

View File

@ -1,146 +0,0 @@
# Iris, MySQL, Groupcache & Docker Example
## 📘 Endpoints
| Method | Path | Description | URL Parameters | Body | Auth Required |
|--------|---------------------|------------------------|--------------- |----------------------------|---------------|
| ANY | /token | Prints a new JWT Token | - | - | - |
| GET | /category | Lists a set of Categories | offset, limit, order | - | - |
| POST | /category | Creates a Category | - | JSON [Full Category](migration/api_category/create_category.json) | Token |
| PUT | /category | Fully-Updates a Category | - | JSON [Full Category](migration/api_category/update_category.json) | Token |
| PATCH | /category/{id} | Partially-Updates a Category | - | JSON [Partial Category](migration/api_category/update_partial_category.json) | Token |
| GET | /category/{id} | Prints a Category | - | - | - |
| DELETE | /category/{id} | Deletes a Category | - | - | Token |
| GET | /category/{id}/products | Lists all Products from a Category | offset, limit, order | - | - |
| POST | /category/{id}/products | (Batch) Assigns one or more Products to a Category | - | JSON [Products](migration/api_category/insert_products_category.json) | Token |
| GET | /product | Lists a set of Products (cache) | offset, limit, order | - | - |
| POST | /product | Creates a Product | - | JSON [Full Product](migration/api_product/create_product.json) | Token |
| PUT | /product | Fully-Updates a Product | - | JSON [Full Product](migration/api_product/update_product.json) | Token |
| PATCH | /product/{id} | Partially-Updates a Product | - | JSON [Partial Product](migration/api_product/update_partial_product.json) | Token |
| GET | /product/{id} | Prints a Product (cache) | - | - | - |
| DELETE | /product/{id} | Deletes a Product | - | - | Token |
## 📑 Responses
* **Content-Type** of `"application/json;charset=utf-8"`, snake_case naming (identical to the database columns)
* **Status Codes**
* 500 for server(db) errors,
* 422 for validation errors, e.g.
```json
{
"code": 422,
"message": "required fields are missing",
"timestamp": 1589306271
}
```
* 400 for malformed syntax, e.g.
```json
{
"code": 400,
"message": "json: cannot unmarshal number -2 into Go struct field Category.position of type uint64",
"timestamp": 1589306325
}
```
```json
{
"code": 400,
"message": "json: unknown field \"field_not_exists\"",
"timestamp": 1589306367
}
```
* 404 for entity not found, e.g.
```json
{
"code": 404,
"message": "entity does not exist",
"timestamp": 1589306199
}
```
* 304 for unaffected UPDATE or DELETE,
* 201 for CREATE with the last inserted ID,
* 200 for GET, UPDATE and DELETE
## ⚡ Get Started
Download the folder.
### Install (Docker)
Install [Docker](https://www.docker.com/) and execute the command below
```sh
$ docker-compose up
```
### Install (Manually)
Run `go build` or `go run main.go` and read below.
#### MySQL
Environment variables:
```sh
MYSQL_USER=user_myapp
MYSQL_PASSWORD=dbpassword
MYSQL_HOST=localhost
MYSQL_DATABASE=myapp
```
Download the schema from [migration/myapp.sql](migration/myapp.sql) and execute it against your MySQL server instance.
```sql
CREATE DATABASE IF NOT EXISTS myapp DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE myapp;
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
DROP TABLE IF EXISTS categories;
CREATE TABLE categories (
id int(11) NOT NULL AUTO_INCREMENT,
title varchar(255) NOT NULL,
position int(11) NOT NULL,
image_url varchar(255) NOT NULL,
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id)
);
DROP TABLE IF EXISTS products;
CREATE TABLE products (
id int(11) NOT NULL AUTO_INCREMENT,
category_id int,
title varchar(255) NOT NULL,
image_url varchar(255) NOT NULL,
price decimal(10,2) NOT NULL,
description text NOT NULL,
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id),
FOREIGN KEY (category_id) REFERENCES categories(id)
);
SET FOREIGN_KEY_CHECKS = 1;
```
### Requests
Some request bodies can be found at: [migration/api_category](migration/api_category) and [migration/api_product](migration/api_product). **However** I've provided a [postman.json](migration/myapp_postman.json) Collection that you can import to your [POSTMAN](https://learning.postman.com/docs/postman/collections/importing-and-exporting-data/#collections) and start playing with the API.
All write-access endpoints are "protected" via JWT, a client should "verify" itself. You'll need to manually take the **token** from the `http://localhost:8080/token` and put it on url parameter `?token=$token` or to the `Authentication: Bearer $token` request header.
### Unit or End-To-End Testing?
Testing is important. The code is written in a way that testing should be trivial (Pseudo/memory Database or SQLite local file could be integrated as well, for end-to-end tests a Docker image with MySQL and fire tests against that server). However, there is [nothing(?)](service/category_service_test.go) to see here.
## Packages
- https://github.com/dgrijalva/jwt-go (JWT parsing)
- https://github.com/go-sql-driver/mysql (Go Driver for MySQL)
- https://github.com/DATA-DOG/go-sqlmock (Testing DB see [service/category_service_test.go](service/category_service_test.go))
- https://github.com/kataras/iris (HTTP)
- https://github.com/mailgun/groupcache (Caching)

View File

@ -1,97 +0,0 @@
// Package api contains the handlers for our HTTP Endpoints.
package api
import (
"time"
"myapp/service"
"myapp/sql"
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/middleware/jwt"
"github.com/kataras/iris/v12/middleware/requestid"
)
// Router accepts any required dependencies and returns the main server's handler.
func Router(db sql.Database, secret string) func(iris.Party) {
return func(r iris.Party) {
j := jwt.HMAC(15*time.Minute, secret)
r.Use(requestid.New())
r.Use(verifyToken(j))
// Generate a token for testing by navigating to
// http://localhost:8080/token endpoint.
// Copy-paste it to a ?token=$token url parameter or
// open postman and put an Authentication: Bearer $token to get
// access on create, update and delete endpoinds.
r.Get("/token", writeToken(j))
var (
categoryService = service.NewCategoryService(db)
productService = service.NewProductService(db)
)
cat := r.Party("/category")
{
// TODO: new Use to add middlewares to specific
// routes per METHOD ( we already have the per path through parties.)
handler := NewCategoryHandler(categoryService)
cat.Get("/", handler.List)
cat.Post("/", handler.Create)
cat.Put("/", handler.Update)
cat.Get("/{id:int64}", handler.GetByID)
cat.Patch("/{id:int64}", handler.PartialUpdate)
cat.Delete("/{id:int64}", handler.Delete)
/* You can also do something like that:
cat.PartyFunc("/{id:int64}", func(c iris.Party) {
c.Get("/", handler.GetByID)
c.Post("/", handler.PartialUpdate)
c.Delete("/", handler.Delete)
})
*/
cat.Get("/{id:int64}/products", handler.ListProducts)
cat.Post("/{id:int64}/products", handler.InsertProducts(productService))
}
prod := r.Party("/product")
{
handler := NewProductHandler(productService)
prod.Get("/", handler.List)
prod.Post("/", handler.Create)
prod.Put("/", handler.Update)
prod.Get("/{id:int64}", handler.GetByID)
prod.Patch("/{id:int64}", handler.PartialUpdate)
prod.Delete("/{id:int64}", handler.Delete)
}
}
}
func writeToken(j *jwt.JWT) iris.Handler {
return func(ctx iris.Context) {
claims := jwt.Claims{
Issuer: "https://iris-go.com",
Audience: jwt.Audience{requestid.Get(ctx)},
}
j.WriteToken(ctx, claims)
}
}
func verifyToken(j *jwt.JWT) iris.Handler {
return func(ctx iris.Context) {
// Allow all GET.
if ctx.Method() == iris.MethodGet {
ctx.Next()
return
}
j.Verify(ctx)
}
}

View File

@ -1,251 +0,0 @@
package api
import (
"myapp/entity"
"myapp/service"
"myapp/sql"
"github.com/kataras/iris/v12"
)
// CategoryHandler is the http mux for categories.
type CategoryHandler struct {
// [...options]
service *service.CategoryService
}
// NewCategoryHandler returns the main controller for the categories API.
func NewCategoryHandler(service *service.CategoryService) *CategoryHandler {
return &CategoryHandler{service}
}
// GetByID fetches a single record from the database and sends it to the client.
// Method: GET.
func (h *CategoryHandler) GetByID(ctx iris.Context) {
id := ctx.Params().GetInt64Default("id", 0)
var cat entity.Category
err := h.service.GetByID(ctx.Request().Context(), &cat, id)
if err != nil {
if err == sql.ErrNoRows {
writeEntityNotFound(ctx)
return
}
debugf("CategoryHandler.GetByID(id=%d): %v", id, err)
writeInternalServerError(ctx)
return
}
ctx.JSON(cat)
}
/*
type (
List struct {
Data interface{} `json:"data"`
Order string `json:"order"`
Next Range `json:"next,omitempty"`
Prev Range `json:"prev,omitempty"`
}
Range struct {
Offset int64 `json:"offset"`
Limit int64 `json:"limit`
}
)
*/
// List lists a set of records from the database.
// Method: GET.
func (h *CategoryHandler) List(ctx iris.Context) {
q := ctx.Request().URL.Query()
opts := sql.ParseListOptions(q)
// initialize here in order to return an empty json array `[]` instead of `null`.
categories := entity.Categories{}
err := h.service.List(ctx.Request().Context(), &categories, opts)
if err != nil && err != sql.ErrNoRows {
debugf("CategoryHandler.List(DB) (limit=%d offset=%d where=%s=%v): %v",
opts.Limit, opts.Offset, opts.WhereColumn, opts.WhereValue, err)
writeInternalServerError(ctx)
return
}
ctx.JSON(categories)
}
// Create adds a record to the database.
// Method: POST.
func (h *CategoryHandler) Create(ctx iris.Context) {
var cat entity.Category
if err := ctx.ReadJSON(&cat); err != nil {
return
}
id, err := h.service.Insert(ctx.Request().Context(), cat)
if err != nil {
if err == sql.ErrUnprocessable {
ctx.StopWithJSON(iris.StatusUnprocessableEntity, newError(iris.StatusUnprocessableEntity, ctx.Request().Method, ctx.Path(), "required fields are missing"))
return
}
debugf("CategoryHandler.Create(DB): %v", err)
writeInternalServerError(ctx)
return
}
// Send 201 with body of {"id":$last_inserted_id"}.
ctx.StatusCode(iris.StatusCreated)
ctx.JSON(iris.Map{cat.PrimaryKey(): id})
}
// Update performs a full-update of a record in the database.
// Method: PUT.
func (h *CategoryHandler) Update(ctx iris.Context) {
var cat entity.Category
if err := ctx.ReadJSON(&cat); err != nil {
return
}
affected, err := h.service.Update(ctx.Request().Context(), cat)
if err != nil {
if err == sql.ErrUnprocessable {
ctx.StopWithJSON(iris.StatusUnprocessableEntity, newError(iris.StatusUnprocessableEntity, ctx.Request().Method, ctx.Path(), "required fields are missing"))
return
}
debugf("CategoryHandler.Update(DB): %v", err)
writeInternalServerError(ctx)
return
}
status := iris.StatusOK
if affected == 0 {
status = iris.StatusNotModified
}
ctx.StatusCode(status)
}
// PartialUpdate is the handler for partially update one or more fields of the record.
// Method: PATCH.
func (h *CategoryHandler) PartialUpdate(ctx iris.Context) {
id := ctx.Params().GetInt64Default("id", 0)
var attrs map[string]interface{}
if err := ctx.ReadJSON(&attrs); err != nil {
return
}
affected, err := h.service.PartialUpdate(ctx.Request().Context(), id, attrs)
if err != nil {
if err == sql.ErrUnprocessable {
ctx.StopWithJSON(iris.StatusUnprocessableEntity, newError(iris.StatusUnprocessableEntity, ctx.Request().Method, ctx.Path(), "unsupported value(s)"))
return
}
debugf("CategoryHandler.PartialUpdate(DB): %v", err)
writeInternalServerError(ctx)
return
}
status := iris.StatusOK
if affected == 0 {
status = iris.StatusNotModified
}
ctx.StatusCode(status)
}
// Delete removes a record from the database.
// Method: DELETE.
func (h *CategoryHandler) Delete(ctx iris.Context) {
id := ctx.Params().GetInt64Default("id", 0)
affected, err := h.service.DeleteByID(ctx.Request().Context(), id)
if err != nil {
debugf("CategoryHandler.Delete(DB): %v", err)
writeInternalServerError(ctx)
return
}
status := iris.StatusOK // StatusNoContent
if affected == 0 {
status = iris.StatusNotModified
}
ctx.StatusCode(status)
}
// Products.
// ListProducts lists products of a Category.
// Example: from cheap to expensive:
// http://localhost:8080/category/3/products?offset=0&limit=30&by=price&order=asc
// Method: GET.
func (h *CategoryHandler) ListProducts(ctx iris.Context) {
id := ctx.Params().GetInt64Default("id", 0)
// NOTE: could add cache here too.
q := ctx.Request().URL.Query()
opts := sql.ParseListOptions(q).Where("category_id", id)
opts.Table = "products"
if opts.OrderByColumn == "" {
opts.OrderByColumn = "updated_at"
}
var products entity.Products
err := h.service.List(ctx.Request().Context(), &products, opts)
if err != nil {
debugf("CategoryHandler.ListProducts(DB) (table=%s where=%s=%v limit=%d offset=%d): %v",
opts.Table, opts.WhereColumn, opts.WhereValue, opts.Limit, opts.Offset, err)
writeInternalServerError(ctx)
return
}
ctx.JSON(products)
}
// InsertProducts assigns new products to a Category (accepts a list of products).
// Method: POST.
func (h *CategoryHandler) InsertProducts(productService *service.ProductService) iris.Handler {
return func(ctx iris.Context) {
categoryID := ctx.Params().GetInt64Default("id", 0)
var products []entity.Product
if err := ctx.ReadJSON(&products); err != nil {
return
}
for i := range products {
products[i].CategoryID = categoryID
}
inserted, err := productService.BatchInsert(ctx.Request().Context(), products)
if err != nil {
if err == sql.ErrUnprocessable {
ctx.StopWithJSON(iris.StatusUnprocessableEntity, newError(iris.StatusUnprocessableEntity, ctx.Request().Method, ctx.Path(), "required fields are missing"))
return
}
debugf("CategoryHandler.InsertProducts(DB): %v", err)
writeInternalServerError(ctx)
return
}
if inserted == 0 {
ctx.StatusCode(iris.StatusNotModified)
return
}
// Send 201 with body of {"inserted":$inserted"}.
ctx.StatusCode(iris.StatusCreated)
ctx.JSON(iris.Map{"inserted": inserted})
}
}

View File

@ -1,25 +0,0 @@
package api
import (
"log"
"github.com/kataras/iris/v12"
)
const debug = true
func debugf(format string, args ...interface{}) {
if !debug {
return
}
log.Printf(format, args...)
}
func writeInternalServerError(ctx iris.Context) {
ctx.StopWithJSON(iris.StatusInternalServerError, newError(iris.StatusInternalServerError, ctx.Request().Method, ctx.Path(), ""))
}
func writeEntityNotFound(ctx iris.Context) {
ctx.StopWithJSON(iris.StatusNotFound, newError(iris.StatusNotFound, ctx.Request().Method, ctx.Path(), "entity does not exist"))
}

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