mirror of
https://github.com/kataras/iris.git
synced 2026-05-14 18:13:49 +00:00
last version of v12
This commit is contained in:
17
_examples/mvc/overview/Dockerfile
Normal file
17
_examples/mvc/overview/Dockerfile
Normal file
@@ -0,0 +1,17 @@
|
||||
# docker build -t app .
|
||||
# docker run --rm -it -p 8080:8080 app: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/app .
|
||||
ENTRYPOINT ["./app"]
|
||||
377
_examples/mvc/overview/README.md
Normal file
377
_examples/mvc/overview/README.md
Normal file
@@ -0,0 +1,377 @@
|
||||
# Quick start
|
||||
|
||||
The following guide is just a simple example of usage of some of the **Iris MVC** features. You are not limited to that data structure or code flow.
|
||||
|
||||
Create a folder, let's assume its path is `app`. The structure should look like that:
|
||||
|
||||
```
|
||||
│ main.go
|
||||
│ go.mod
|
||||
│ go.sum
|
||||
└───environment
|
||||
│ environment.go
|
||||
└───model
|
||||
│ request.go
|
||||
│ response.go
|
||||
└───database
|
||||
│ database.go
|
||||
│ mysql.go
|
||||
│ sqlite.go
|
||||
└───service
|
||||
│ greet_service.go
|
||||
└───controller
|
||||
greet_controller.go
|
||||
```
|
||||
|
||||
Navigate to that `app` folder and execute the following command:
|
||||
|
||||
```sh
|
||||
$ go init app
|
||||
$ go get github.com/kataras/iris/v12@latest
|
||||
```
|
||||
|
||||
## Environment
|
||||
|
||||
Let's start by defining the available environments that our web-application can behave on.
|
||||
|
||||
We'll just work on two available environments, the "development" and the "production", as they define the two most common scenarios. The `ReadEnv` will read from the `Env` type of a system's environment variable (see `main.go` in the end of the page).
|
||||
|
||||
Create a `environment/environment.go` file and put the following contents:
|
||||
|
||||
```go
|
||||
package environment
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
PROD Env = "production"
|
||||
DEV Env = "development"
|
||||
)
|
||||
|
||||
type Env string
|
||||
|
||||
func (e Env) String() string {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
func ReadEnv(key string, def Env) Env {
|
||||
v := Getenv(key, def.String())
|
||||
if v == "" {
|
||||
return def
|
||||
}
|
||||
|
||||
env := Env(strings.ToLower(v))
|
||||
switch env {
|
||||
case PROD, DEV: // allowed.
|
||||
default:
|
||||
panic("unexpected environment " + v)
|
||||
}
|
||||
|
||||
return env
|
||||
}
|
||||
|
||||
func Getenv(key string, def string) string {
|
||||
if v := os.Getenv(key); v != "" {
|
||||
return v
|
||||
}
|
||||
|
||||
return def
|
||||
}
|
||||
```
|
||||
|
||||
## Database
|
||||
|
||||
We will use two database management systems, the `MySQL` and the `SQLite`. The first one for "production" use and the other for "development".
|
||||
|
||||
Create a `database/database.go` file and copy-paste the following:
|
||||
|
||||
```go
|
||||
package database
|
||||
|
||||
import "app/environment"
|
||||
|
||||
type DB interface {
|
||||
Exec(q string) error
|
||||
}
|
||||
|
||||
func NewDB(env environment.Env) DB {
|
||||
switch env {
|
||||
case environment.PROD:
|
||||
return &mysql{}
|
||||
case environment.DEV:
|
||||
return &sqlite{}
|
||||
default:
|
||||
panic("unknown environment")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Let's simulate our MySQL and SQLite `DB` instances. Create a `database/mysql.go` file which looks like the following one:
|
||||
|
||||
```go
|
||||
package database
|
||||
|
||||
import "fmt"
|
||||
|
||||
type mysql struct{}
|
||||
|
||||
func (db *mysql) Exec(q string) error {
|
||||
return fmt.Errorf("mysql: not implemented <%s>", q)
|
||||
}
|
||||
```
|
||||
|
||||
And a `database/sqlite.go` file.
|
||||
|
||||
```go
|
||||
package database
|
||||
|
||||
type sqlite struct{}
|
||||
|
||||
func (db *sqlite) Exec(q string) error { return nil }
|
||||
```
|
||||
|
||||
The `DB` depends on the `Environment.
|
||||
|
||||
> A practical and operational database example, including Docker images, can be found at the following guide: https://github.com/kataras/iris/tree/main/_examples/database/mysql
|
||||
|
||||
## Service
|
||||
|
||||
We'll need a service that will communicate with a database instance in behalf of our Controller(s).
|
||||
|
||||
In our case we will only need a single service, the Greet Service.
|
||||
|
||||
For the sake of the example, let's use two implementations of a greet service based on the `Environment`. The `GreetService` interface contains a single method of `Say(input string) (output string, err error)`. Create a `./service/greet_service.go` file and write the following code:
|
||||
|
||||
```go
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"app/database"
|
||||
"app/environment"
|
||||
)
|
||||
|
||||
// GreetService example service.
|
||||
type GreetService interface {
|
||||
Say(input string) (string, error)
|
||||
}
|
||||
|
||||
// NewGreetService returns a service backed with a "db" based on "env".
|
||||
func NewGreetService(env environment.Env, db database.DB) GreetService {
|
||||
service := &greeter{db: db, prefix: "Hello"}
|
||||
|
||||
switch env {
|
||||
case environment.PROD:
|
||||
return service
|
||||
case environment.DEV:
|
||||
return &greeterWithLogging{service}
|
||||
default:
|
||||
panic("unknown environment")
|
||||
}
|
||||
}
|
||||
|
||||
type greeter struct {
|
||||
prefix string
|
||||
db database.DB
|
||||
}
|
||||
|
||||
func (s *greeter) Say(input string) (string, error) {
|
||||
if err := s.db.Exec("simulate a query..."); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
result := s.prefix + " " + input
|
||||
return result, nil
|
||||
}
|
||||
|
||||
type greeterWithLogging struct {
|
||||
*greeter
|
||||
}
|
||||
|
||||
func (s *greeterWithLogging) Say(input string) (string, error) {
|
||||
result, err := s.greeter.Say(input)
|
||||
fmt.Printf("result: %s\nerror: %v\n", result, err)
|
||||
return result, err
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
The `greeter` will be used on "production" and the `greeterWithLogging` on "development". The `GreetService` depends on the `Environment` and the `DB`.
|
||||
|
||||
## Models
|
||||
|
||||
Continue by creating our HTTP request and response models.
|
||||
|
||||
Create a `model/request.go` file and copy-paste the following code:
|
||||
|
||||
```go
|
||||
package model
|
||||
|
||||
type Request struct {
|
||||
Name string `url:"name"`
|
||||
}
|
||||
```
|
||||
|
||||
Same for the `model/response.go` file.
|
||||
|
||||
```go
|
||||
package model
|
||||
|
||||
type Response struct {
|
||||
Message string `json:"msg"`
|
||||
}
|
||||
```
|
||||
|
||||
The server will accept a URL Query Parameter of `name` (e.g. `/greet?name=kataras`) and will reply back with a JSON message.
|
||||
|
||||
## Controller
|
||||
|
||||
MVC Controllers are responsible for controlling the flow of the application execution. When you make a request (means request a page) to MVC Application, a controller is responsible for returning the response to that request.
|
||||
|
||||
We will only need the `GreetController` for our mini web-application. Create a file at `controller/greet_controller.go` which looks like that:
|
||||
|
||||
```go
|
||||
package controller
|
||||
|
||||
import (
|
||||
"app/model"
|
||||
"app/service"
|
||||
)
|
||||
|
||||
type GreetController struct {
|
||||
Service service.GreetService
|
||||
// Ctx iris.Context
|
||||
}
|
||||
|
||||
func (c *GreetController) Get(req model.Request) (model.Response, error) {
|
||||
message, err := c.Service.Say(req.Name)
|
||||
if err != nil {
|
||||
return model.Response{}, err
|
||||
}
|
||||
|
||||
return model.Response{Message: message}, nil
|
||||
}
|
||||
```
|
||||
|
||||
The `GreetController` depends on the `GreetService`. It serves the `GET: /greet` index path through its `Get` method. The `Get` method accepts a `model.Request` which contains a single field name of `Name` which will be extracted from the `URL Query Parameter 'name'` (because it's a `GET` requst and its `url:"name"` struct field).
|
||||
|
||||
## Wrap up
|
||||
|
||||
```sh
|
||||
+-------------------+
|
||||
| Env (DEV, PROD) |
|
||||
+---------+---------+
|
||||
| | |
|
||||
| | |
|
||||
| | |
|
||||
DEV | | | PROD
|
||||
-------------------+---------------------+ | +----------------------+-------------------
|
||||
| | |
|
||||
| | |
|
||||
+---+-----+ +----------------v------------------+ +----+----+
|
||||
| sqlite | | NewDB(Env) DB | | mysql |
|
||||
+---+-----+ +----------------+---+--------------+ +----+----+
|
||||
| | | |
|
||||
| | | |
|
||||
| | | |
|
||||
+--------------+-----+ +-------------------v---v-----------------+ +----+------+
|
||||
| greeterWithLogging | | NewGreetService(Env, DB) GreetService | | greeter |
|
||||
+--------------+-----+ +---------------------------+-------------+ +----+------+
|
||||
| | |
|
||||
| | |
|
||||
| +-----------------------------------------+ |
|
||||
| | GreetController | | |
|
||||
| | | | |
|
||||
| | - Service GreetService <-- | |
|
||||
| | | |
|
||||
| +-------------------+---------------------+ |
|
||||
| | |
|
||||
| | |
|
||||
| | |
|
||||
| +-----------+-----------+ |
|
||||
| | HTTP Request | |
|
||||
| +-----------------------+ |
|
||||
| | /greet?name=kataras | |
|
||||
| +-----------+-----------+ |
|
||||
| | |
|
||||
+------------------+--------+ +------------+------------+ +-------+------------------+
|
||||
| model.Response (JSON) | | Response (JSON, error) | | Bad Request |
|
||||
+---------------------------+ +-------------------------+ +--------------------------+
|
||||
| { | | mysql: not implemented |
|
||||
| "msg": "Hello kataras" | +--------------------------+
|
||||
| } |
|
||||
+---------------------------+
|
||||
```
|
||||
|
||||
Now it's the time to wrap all the above into our `main.go` file. Copy-paste the following code:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"app/controller"
|
||||
"app/database"
|
||||
"app/environment"
|
||||
"app/service"
|
||||
|
||||
"github.com/kataras/iris/v12"
|
||||
"github.com/kataras/iris/v12/mvc"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := iris.New()
|
||||
app.Get("/ping", pong).Describe("healthcheck")
|
||||
|
||||
mvc.Configure(app.Party("/greet"), setup)
|
||||
|
||||
// http://localhost:8080/greet?name=kataras
|
||||
app.Listen(":8080", iris.WithLogLevel("debug"))
|
||||
}
|
||||
|
||||
func pong(ctx iris.Context) {
|
||||
ctx.WriteString("pong")
|
||||
}
|
||||
|
||||
func setup(app *mvc.Application) {
|
||||
// Register Dependencies.
|
||||
app.Register(
|
||||
environment.DEV, // DEV, PROD
|
||||
database.NewDB, // sqlite, mysql
|
||||
service.NewGreetService, // greeterWithLogging, greeter
|
||||
)
|
||||
|
||||
// Register Controllers.
|
||||
app.Handle(new(controller.GreetController))
|
||||
}
|
||||
```
|
||||
|
||||
The `mvc.Application.Register` method registers one more dependencies, dependencies can depend on previously registered dependencies too. Thats the reason we pass, first, the `Environment(DEV)`, then the `NewDB` which depends on that `Environment`, following by the `NewGreetService` function which depends on both the `Environment(DEV)` and the `DB`.
|
||||
|
||||
The `mvc.Application.Handle` registers a new controller, which depends on the `GreetService`, for the targeted sub-router of `Party("/greet")`.
|
||||
|
||||
## Run
|
||||
|
||||
Install [Go](https://golang.org/dl) and run the application with:
|
||||
|
||||
```sh
|
||||
go run main.go
|
||||
```
|
||||
|
||||
<details><summary>Docker</summary>
|
||||
|
||||
Download the [Dockerfile](https://raw.githubusercontent.com/kataras/iris/9b93c0dbb491dcedf49c91e89ca13bab884d116f/_examples/mvc/overview/Dockerfile) and [docker-compose.yml](https://raw.githubusercontent.com/kataras/iris/9b93c0dbb491dcedf49c91e89ca13bab884d116f/_examples/mvc/overview/docker-compose.yml) files to the `app` folder.
|
||||
|
||||
Install [Docker](https://www.docker.com/) and execute the following command:
|
||||
|
||||
```sh
|
||||
$ docker-compose up
|
||||
```
|
||||
</details>
|
||||
|
||||
Visit http://localhost:8080?name=kataras.
|
||||
|
||||
Optionally, replace the `main.go`'s `app.Register(environment.DEV` with `environment.PROD`, restart the application and refresh. You will see that a new database (`sqlite`) and another service of (`greeterWithLogging`) will be binded to the `GreetController`. With **a single change** you achieve to completety change the result.
|
||||
23
_examples/mvc/overview/controller/greet_controller.go
Normal file
23
_examples/mvc/overview/controller/greet_controller.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"app/model"
|
||||
"app/service"
|
||||
)
|
||||
|
||||
// GreetController handles the index.
|
||||
type GreetController struct {
|
||||
Service service.GreetService
|
||||
// Ctx iris.Context
|
||||
}
|
||||
|
||||
// Get serves [GET] /.
|
||||
// Query: name
|
||||
func (c *GreetController) Get(req model.Request) (model.Response, error) {
|
||||
message, err := c.Service.Say(req.Name)
|
||||
if err != nil {
|
||||
return model.Response{}, err
|
||||
}
|
||||
|
||||
return model.Response{Message: message}, nil
|
||||
}
|
||||
20
_examples/mvc/overview/database/database.go
Normal file
20
_examples/mvc/overview/database/database.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package database
|
||||
|
||||
import "app/environment"
|
||||
|
||||
// DB example database interface.
|
||||
type DB interface {
|
||||
Exec(q string) error
|
||||
}
|
||||
|
||||
// NewDB returns a database based on "env".
|
||||
func NewDB(env environment.Env) DB {
|
||||
switch env {
|
||||
case environment.PROD:
|
||||
return &mysql{}
|
||||
case environment.DEV:
|
||||
return &sqlite{}
|
||||
default:
|
||||
panic("unknown environment")
|
||||
}
|
||||
}
|
||||
10
_examples/mvc/overview/database/mysql.go
Normal file
10
_examples/mvc/overview/database/mysql.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package database
|
||||
|
||||
import "fmt"
|
||||
|
||||
type mysql struct{}
|
||||
|
||||
func (db *mysql) Exec(q string) error {
|
||||
// simulate an error response.
|
||||
return fmt.Errorf("mysql: not implemented <%s>", q)
|
||||
}
|
||||
5
_examples/mvc/overview/database/sqlite.go
Normal file
5
_examples/mvc/overview/database/sqlite.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package database
|
||||
|
||||
type sqlite struct{}
|
||||
|
||||
func (db *sqlite) Exec(q string) error { return nil }
|
||||
@@ -1 +0,0 @@
|
||||
# Data Model Layer
|
||||
@@ -1,18 +0,0 @@
|
||||
// file: datamodels/movie.go
|
||||
|
||||
package datamodels
|
||||
|
||||
// Movie is our sample data structure.
|
||||
// Keep note that the tags for public-use (for our web app)
|
||||
// should be kept in other file like "web/viewmodels/movie.go"
|
||||
// which could wrap by embedding the datamodels.Movie or
|
||||
// declare new fields instead butwe will use this datamodel
|
||||
// as the only one Movie model in our application,
|
||||
// for the shake of simplicty.
|
||||
type Movie struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Year int `json:"year"`
|
||||
Genre string `json:"genre"`
|
||||
Poster string `json:"poster"`
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
# Data Source / Data Store Layer
|
||||
@@ -1,44 +0,0 @@
|
||||
// file: datasource/movies.go
|
||||
|
||||
package datasource
|
||||
|
||||
import "github.com/kataras/iris/v12/_examples/mvc/overview/datamodels"
|
||||
|
||||
// Movies is our imaginary data source.
|
||||
var Movies = map[int64]datamodels.Movie{
|
||||
1: {
|
||||
ID: 1,
|
||||
Name: "Casablanca",
|
||||
Year: 1942,
|
||||
Genre: "Romance",
|
||||
Poster: "https://iris-go.com/images/examples/mvc-movies/1.jpg",
|
||||
},
|
||||
2: {
|
||||
ID: 2,
|
||||
Name: "Gone with the Wind",
|
||||
Year: 1939,
|
||||
Genre: "Romance",
|
||||
Poster: "https://iris-go.com/images/examples/mvc-movies/2.jpg",
|
||||
},
|
||||
3: {
|
||||
ID: 3,
|
||||
Name: "Citizen Kane",
|
||||
Year: 1941,
|
||||
Genre: "Mystery",
|
||||
Poster: "https://iris-go.com/images/examples/mvc-movies/3.jpg",
|
||||
},
|
||||
4: {
|
||||
ID: 4,
|
||||
Name: "The Wizard of Oz",
|
||||
Year: 1939,
|
||||
Genre: "Fantasy",
|
||||
Poster: "https://iris-go.com/images/examples/mvc-movies/4.jpg",
|
||||
},
|
||||
5: {
|
||||
ID: 5,
|
||||
Name: "North by Northwest",
|
||||
Year: 1959,
|
||||
Genre: "Thriller",
|
||||
Poster: "https://iris-go.com/images/examples/mvc-movies/5.jpg",
|
||||
},
|
||||
}
|
||||
16
_examples/mvc/overview/docker-compose.yml
Normal file
16
_examples/mvc/overview/docker-compose.yml
Normal file
@@ -0,0 +1,16 @@
|
||||
version: '3.1'
|
||||
|
||||
services:
|
||||
app:
|
||||
build: .
|
||||
ports:
|
||||
- 8080:8080
|
||||
environment:
|
||||
PORT: 8080
|
||||
ENVIRONMENT: development
|
||||
restart: on-failure
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8080/ping"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
50
_examples/mvc/overview/environment/environment.go
Normal file
50
_examples/mvc/overview/environment/environment.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package environment
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Available environments example.
|
||||
const (
|
||||
PROD Env = "production"
|
||||
DEV Env = "development"
|
||||
)
|
||||
|
||||
// Env is the environment type.
|
||||
type Env string
|
||||
|
||||
// String just returns the string representation of the Env.
|
||||
func (e Env) String() string {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
// ReadEnv returns the environment of the system environment variable of "key".
|
||||
// Returns the "def" if not found.
|
||||
// Reports a panic message if the environment variable found
|
||||
// but the Env is unknown.
|
||||
func ReadEnv(key string, def Env) Env {
|
||||
v := Getenv(key, def.String())
|
||||
if v == "" {
|
||||
return def
|
||||
}
|
||||
|
||||
env := Env(strings.ToLower(v))
|
||||
switch env {
|
||||
case PROD, DEV: // allowed.
|
||||
default:
|
||||
panic("unexpected environment " + v)
|
||||
}
|
||||
|
||||
return env
|
||||
}
|
||||
|
||||
// Getenv returns the value of a system environment variable "key".
|
||||
// Defaults to "def" if not found.
|
||||
func Getenv(key string, def string) string {
|
||||
if v := os.Getenv(key); v != "" {
|
||||
return v
|
||||
}
|
||||
|
||||
return def
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 30 KiB |
62
_examples/mvc/overview/go.mod
Normal file
62
_examples/mvc/overview/go.mod
Normal file
@@ -0,0 +1,62 @@
|
||||
module app
|
||||
|
||||
go 1.22
|
||||
|
||||
require github.com/kataras/iris/v12 v12.2.11-0.20240424221303-df7737ed577a
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.3.2 // indirect
|
||||
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect
|
||||
github.com/CloudyKit/jet/v6 v6.2.0 // indirect
|
||||
github.com/Joker/jade v1.1.3 // indirect
|
||||
github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06 // indirect
|
||||
github.com/andybalholm/brotli v1.1.0 // indirect
|
||||
github.com/aymerick/douceur v0.2.0 // indirect
|
||||
github.com/blang/semver/v4 v4.0.0 // indirect
|
||||
github.com/fatih/structs v1.1.0 // indirect
|
||||
github.com/flosch/pongo2/v4 v4.0.2 // indirect
|
||||
github.com/gobwas/httphead v0.1.0 // indirect
|
||||
github.com/gobwas/pool v0.2.1 // indirect
|
||||
github.com/gobwas/ws v1.3.2 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/gomarkdown/markdown v0.0.0-20240328165702-4d01890c35c0 // indirect
|
||||
github.com/google/go-cmp v0.5.9 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gorilla/css v1.0.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.1 // indirect
|
||||
github.com/iris-contrib/schema v0.0.6 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/kataras/blocks v0.0.8 // indirect
|
||||
github.com/kataras/golog v0.1.11 // indirect
|
||||
github.com/kataras/neffos v0.0.24-0.20240408172741-99c879ba0ede // indirect
|
||||
github.com/kataras/pio v0.0.13 // indirect
|
||||
github.com/kataras/sitemap v0.0.6 // indirect
|
||||
github.com/kataras/tunnel v0.0.4 // indirect
|
||||
github.com/klauspost/compress v1.17.7 // indirect
|
||||
github.com/mailgun/raymond/v2 v2.0.48 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mediocregopher/radix/v3 v3.8.1 // indirect
|
||||
github.com/microcosm-cc/bluemonday v1.0.26 // indirect
|
||||
github.com/nats-io/nats.go v1.34.1 // indirect
|
||||
github.com/nats-io/nkeys v0.4.7 // indirect
|
||||
github.com/nats-io/nuid v1.0.1 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/schollz/closestmatch v2.1.0+incompatible // indirect
|
||||
github.com/sirupsen/logrus v1.8.1 // indirect
|
||||
github.com/tdewolff/minify/v2 v2.20.19 // indirect
|
||||
github.com/tdewolff/parse/v2 v2.7.12 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||
github.com/yosssi/ace v0.0.5 // indirect
|
||||
golang.org/x/crypto v0.22.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0 // indirect
|
||||
golang.org/x/net v0.24.0 // indirect
|
||||
golang.org/x/sys v0.19.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
google.golang.org/protobuf v1.33.0 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
200
_examples/mvc/overview/go.sum
generated
Normal file
200
_examples/mvc/overview/go.sum
generated
Normal file
@@ -0,0 +1,200 @@
|
||||
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
|
||||
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c=
|
||||
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=
|
||||
github.com/CloudyKit/jet/v6 v6.2.0 h1:EpcZ6SR9n28BUGtNJSvlBqf90IpjeFr36Tizxhn/oME=
|
||||
github.com/CloudyKit/jet/v6 v6.2.0/go.mod h1:d3ypHeIRNo2+XyqnGA8s+aphtcVpjP5hPwP/Lzo7Ro4=
|
||||
github.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc=
|
||||
github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=
|
||||
github.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk=
|
||||
github.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=
|
||||
github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06 h1:KkH3I3sJuOLP3TjA/dfr4NAY8bghDwnXiU7cTKxQqo0=
|
||||
github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06/go.mod h1:7erjKLwalezA0k99cWs5L11HWOAPNjdUZ6RxH1BXbbM=
|
||||
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
||||
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
||||
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
|
||||
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
||||
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
|
||||
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
|
||||
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
|
||||
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
|
||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||
github.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw=
|
||||
github.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8=
|
||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
|
||||
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
|
||||
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
|
||||
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||
github.com/gobwas/ws v1.3.2 h1:zlnbNHxumkRvfPWgfXu8RBwyNR1x8wh9cf5PTOCqs9Q=
|
||||
github.com/gobwas/ws v1.3.2/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/gomarkdown/markdown v0.0.0-20240328165702-4d01890c35c0 h1:4gjrh/PN2MuWCCElk8/I4OCKRKWCCo2zEct3VKCbibU=
|
||||
github.com/gomarkdown/markdown v0.0.0-20240328165702-4d01890c35c0/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
|
||||
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
|
||||
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
|
||||
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
|
||||
github.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=
|
||||
github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
|
||||
github.com/iris-contrib/httpexpect/v2 v2.15.2 h1:T9THsdP1woyAqKHwjkEsbCnMefsAFvk8iJJKokcJ3Go=
|
||||
github.com/iris-contrib/httpexpect/v2 v2.15.2/go.mod h1:JLDgIqnFy5loDSUv1OA2j0mb6p/rDhiCqigP22Uq9xE=
|
||||
github.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw=
|
||||
github.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/kataras/blocks v0.0.8 h1:MrpVhoFTCR2v1iOOfGng5VJSILKeZZI+7NGfxEh3SUM=
|
||||
github.com/kataras/blocks v0.0.8/go.mod h1:9Jm5zx6BB+06NwA+OhTbHW1xkMOYxahnqTN5DveZ2Yg=
|
||||
github.com/kataras/golog v0.1.11 h1:dGkcCVsIpqiAMWTlebn/ZULHxFvfG4K43LF1cNWSh20=
|
||||
github.com/kataras/golog v0.1.11/go.mod h1:mAkt1vbPowFUuUGvexyQ5NFW6djEgGyxQBIARJ0AH4A=
|
||||
github.com/kataras/iris/v12 v12.2.11-0.20240424221303-df7737ed577a h1:uaDFlkh2LzTPtoOk/1e6jy/ChoM5/FxTgTiEehlJuD0=
|
||||
github.com/kataras/iris/v12 v12.2.11-0.20240424221303-df7737ed577a/go.mod h1:uMAeX8OqG9vqdhyrIPv8Lajo/wXTtAF43wchP9WHt2w=
|
||||
github.com/kataras/neffos v0.0.24-0.20240408172741-99c879ba0ede h1:ZnSJQ+ri9x46Yz15wHqSb93Q03yY12XMVLUFDJJ0+/g=
|
||||
github.com/kataras/neffos v0.0.24-0.20240408172741-99c879ba0ede/go.mod h1:i0dtcTbpnw1lqIbojYtGtZlu6gDWPxJ4Xl2eJ6oQ1bE=
|
||||
github.com/kataras/pio v0.0.13 h1:x0rXVX0fviDTXOOLOmr4MUxOabu1InVSTu5itF8CXCM=
|
||||
github.com/kataras/pio v0.0.13/go.mod h1:k3HNuSw+eJ8Pm2lA4lRhg3DiCjVgHlP8hmXApSej3oM=
|
||||
github.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY=
|
||||
github.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4=
|
||||
github.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA=
|
||||
github.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw=
|
||||
github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg=
|
||||
github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw=
|
||||
github.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mediocregopher/radix/v3 v3.8.1 h1:rOkHflVuulFKlwsLY01/M2cM2tWCjDoETcMqKbAWu1M=
|
||||
github.com/mediocregopher/radix/v3 v3.8.1/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8=
|
||||
github.com/microcosm-cc/bluemonday v1.0.26 h1:xbqSvqzQMeEHCqMi64VAs4d8uy6Mequs3rQ0k/Khz58=
|
||||
github.com/microcosm-cc/bluemonday v1.0.26/go.mod h1:JyzOCs9gkyQyjs+6h10UEVSe02CGwkhd72Xdqh78TWs=
|
||||
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
|
||||
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
|
||||
github.com/nats-io/nats.go v1.34.1 h1:syWey5xaNHZgicYBemv0nohUPPmaLteiBEUT6Q5+F/4=
|
||||
github.com/nats-io/nats.go v1.34.1/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8=
|
||||
github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI=
|
||||
github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc=
|
||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=
|
||||
github.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=
|
||||
github.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk=
|
||||
github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=
|
||||
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tdewolff/minify/v2 v2.20.19 h1:tX0SR0LUrIqGoLjXnkIzRSIbKJ7PaNnSENLD4CyH6Xo=
|
||||
github.com/tdewolff/minify/v2 v2.20.19/go.mod h1:ulkFoeAVWMLEyjuDz1ZIWOA31g5aWOawCFRp9R/MudM=
|
||||
github.com/tdewolff/parse/v2 v2.7.12 h1:tgavkHc2ZDEQVKy1oWxwIyh5bP4F5fEh/JmBwPP/3LQ=
|
||||
github.com/tdewolff/parse/v2 v2.7.12/go.mod h1:3FbJWZp3XT9OWVN3Hmfp0p/a08v4h8J9W1aghka0soA=
|
||||
github.com/tdewolff/test v1.0.11-0.20231101010635-f1265d231d52/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
|
||||
github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739 h1:IkjBCtQOOjIn03u/dMQK9g+Iw9ewps4mCl1nB8Sscbo=
|
||||
github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=
|
||||
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=
|
||||
github.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA=
|
||||
github.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0=
|
||||
github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=
|
||||
github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=
|
||||
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=
|
||||
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
|
||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
|
||||
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
||||
golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0 h1:985EYyeCOxTpcgOTJpflJUwOeEz0CQOdPt73OzpE9F8=
|
||||
golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
|
||||
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U=
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
moul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs=
|
||||
moul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE=
|
||||
@@ -1,13 +1,10 @@
|
||||
// file: main.go
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris/v12/_examples/mvc/overview/datasource"
|
||||
"github.com/kataras/iris/v12/_examples/mvc/overview/repositories"
|
||||
"github.com/kataras/iris/v12/_examples/mvc/overview/services"
|
||||
"github.com/kataras/iris/v12/_examples/mvc/overview/web/controllers"
|
||||
"github.com/kataras/iris/v12/_examples/mvc/overview/web/middleware"
|
||||
"app/controller"
|
||||
"app/database"
|
||||
"app/environment"
|
||||
"app/service"
|
||||
|
||||
"github.com/kataras/iris/v12"
|
||||
"github.com/kataras/iris/v12/mvc"
|
||||
@@ -17,44 +14,77 @@ func main() {
|
||||
app := iris.New()
|
||||
app.Logger().SetLevel("debug")
|
||||
|
||||
// Load the template files.
|
||||
app.RegisterView(iris.HTML("./web/views", ".html"))
|
||||
app.Get("/ping", pong).Describe("healthcheck")
|
||||
|
||||
// Serve our controllers.
|
||||
mvc.New(app.Party("/hello")).Handle(new(controllers.HelloController))
|
||||
// You can also split the code you write to configure an mvc.Application
|
||||
// using the `mvc.Configure` method, as shown below.
|
||||
mvc.Configure(app.Party("/movies"), movies)
|
||||
mvc.Configure(app.Party("/greet"), setup)
|
||||
|
||||
// http://localhost:8080/hello
|
||||
// http://localhost:8080/hello/iris
|
||||
// http://localhost:8080/movies
|
||||
// http://localhost:8080/movies/1
|
||||
app.Run(
|
||||
// Start the web server at localhost:8080
|
||||
iris.Addr("localhost:8080"),
|
||||
// skip err server closed when CTRL/CMD+C pressed:
|
||||
iris.WithoutServerError(iris.ErrServerClosed),
|
||||
// enables faster json serialization and more:
|
||||
iris.WithOptimizations,
|
||||
// http://localhost:8080/greet?name=kataras
|
||||
addr := ":" + environment.Getenv("PORT", "8080")
|
||||
app.Listen(addr)
|
||||
}
|
||||
|
||||
func pong(ctx iris.Context) {
|
||||
ctx.WriteString("pong")
|
||||
}
|
||||
|
||||
/*
|
||||
+-------------------+
|
||||
| Env (DEV, PROD) |
|
||||
+---------+---------+
|
||||
| | |
|
||||
| | |
|
||||
| | |
|
||||
DEV | | | PROD
|
||||
|
||||
-------------------+---------------------+ | +----------------------+-------------------
|
||||
|
||||
| | |
|
||||
| | |
|
||||
+---+-----+ +----------------v------------------+ +----+----+
|
||||
| sqlite | | NewDB(Env) DB | | mysql |
|
||||
+---+-----+ +----------------+---+--------------+ +----+----+
|
||||
| | | |
|
||||
| | | |
|
||||
| | | |
|
||||
+--------------+-----+ +-------------------v---v-----------------+ +----+------+
|
||||
| greeterWithLogging | | NewGreetService(Env, DB) GreetService | | greeter |
|
||||
+--------------+-----+ +---------------------------+-------------+ +----+------+
|
||||
| | |
|
||||
| | |
|
||||
| +-----------------------------------------+ |
|
||||
| | GreetController | | |
|
||||
| | | | |
|
||||
| | - Service GreetService <-- | |
|
||||
| | | |
|
||||
| +-------------------+---------------------+ |
|
||||
| | |
|
||||
| | |
|
||||
| | |
|
||||
| +-----------+-----------+ |
|
||||
| | HTTP Request | |
|
||||
| +-----------------------+ |
|
||||
| | /greet?name=kataras | |
|
||||
| +-----------+-----------+ |
|
||||
| | |
|
||||
|
||||
+------------------+--------+ +------------+------------+ +-------+------------------+
|
||||
| model.Response (JSON) | | Response (JSON, error) | | Bad Request |
|
||||
+---------------------------+ +-------------------------+ +--------------------------+
|
||||
| { | | mysql: not implemented |
|
||||
| "msg": "Hello kataras" | +--------------------------+
|
||||
| } |
|
||||
+---------------------------+
|
||||
*/
|
||||
func setup(app *mvc.Application) {
|
||||
// Register Dependencies.
|
||||
// Tip: A dependency can depend on other dependencies too.
|
||||
env := environment.ReadEnv("ENVIRONMENT", environment.DEV)
|
||||
app.Register(
|
||||
env, // DEV, PROD
|
||||
database.NewDB, // sqlite, mysql
|
||||
service.NewGreetService, // greeterWithLogging, greeter
|
||||
)
|
||||
}
|
||||
|
||||
// note the mvc.Application, it's not iris.Application.
|
||||
func movies(app *mvc.Application) {
|
||||
// Add the basic authentication(admin:password) middleware
|
||||
// for the /movies based requests.
|
||||
app.Router.Use(middleware.BasicAuth)
|
||||
|
||||
// Create our movie repository with some (memory) data from the datasource.
|
||||
repo := repositories.NewMovieRepository(datasource.Movies)
|
||||
// Create our movie service, we will bind it to the movie app's dependencies.
|
||||
movieService := services.NewMovieService(repo)
|
||||
app.Register(movieService)
|
||||
|
||||
// serve our movies controller.
|
||||
// Note that you can serve more than one controller
|
||||
// you can also create child mvc apps using the `movies.Party(relativePath)` or `movies.Clone(app.Party(...))`
|
||||
// if you want.
|
||||
app.Handle(new(controllers.MovieController))
|
||||
|
||||
// Register Controllers.
|
||||
app.Handle(new(controller.GreetController))
|
||||
}
|
||||
|
||||
6
_examples/mvc/overview/model/request.go
Normal file
6
_examples/mvc/overview/model/request.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package model
|
||||
|
||||
// Request example incoming request.
|
||||
type Request struct {
|
||||
Name string `json:"name" url:"name"`
|
||||
}
|
||||
6
_examples/mvc/overview/model/response.go
Normal file
6
_examples/mvc/overview/model/response.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package model
|
||||
|
||||
// Response example server's response.
|
||||
type Response struct {
|
||||
Message string `json:"msg"`
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
# Domain Models
|
||||
|
||||
There should be the domain/business-level models.
|
||||
|
||||
Example:
|
||||
|
||||
```go
|
||||
import "github.com/kataras/iris/v12/_examples/mvc/overview/datamodels"
|
||||
|
||||
type Movie struct {
|
||||
datamodels.Movie
|
||||
}
|
||||
|
||||
func (m Movie) Validate() (Movie, error) {
|
||||
/* do some checks and return an error if that Movie is not valid */
|
||||
}
|
||||
```
|
||||
|
||||
However, we will use the "datamodels" as the only one models package because
|
||||
Movie structure we don't need any extra functionality or validation inside it.
|
||||
@@ -1,3 +0,0 @@
|
||||
# Repositories
|
||||
|
||||
The package which has direct access to the "datasource" and can manipulate data directly.
|
||||
@@ -1,176 +0,0 @@
|
||||
// file: repositories/movie_repository.go
|
||||
|
||||
package repositories
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
|
||||
"github.com/kataras/iris/v12/_examples/mvc/overview/datamodels"
|
||||
)
|
||||
|
||||
// Query represents the visitor and action queries.
|
||||
type Query func(datamodels.Movie) bool
|
||||
|
||||
// MovieRepository handles the basic operations of a movie entity/model.
|
||||
// It's an interface in order to be testable, i.e a memory movie repository or
|
||||
// a connected to an sql database.
|
||||
type MovieRepository interface {
|
||||
Exec(query Query, action Query, limit int, mode int) (ok bool)
|
||||
|
||||
Select(query Query) (movie datamodels.Movie, found bool)
|
||||
SelectMany(query Query, limit int) (results []datamodels.Movie)
|
||||
|
||||
InsertOrUpdate(movie datamodels.Movie) (updatedMovie datamodels.Movie, err error)
|
||||
Delete(query Query, limit int) (deleted bool)
|
||||
}
|
||||
|
||||
// NewMovieRepository returns a new movie memory-based repository,
|
||||
// the one and only repository type in our example.
|
||||
func NewMovieRepository(source map[int64]datamodels.Movie) MovieRepository {
|
||||
return &movieMemoryRepository{source: source}
|
||||
}
|
||||
|
||||
// movieMemoryRepository is a "MovieRepository"
|
||||
// which manages the movies using the memory data source (map).
|
||||
type movieMemoryRepository struct {
|
||||
source map[int64]datamodels.Movie
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
const (
|
||||
// ReadOnlyMode will RLock(read) the data .
|
||||
ReadOnlyMode = iota
|
||||
// ReadWriteMode will Lock(read/write) the data.
|
||||
ReadWriteMode
|
||||
)
|
||||
|
||||
func (r *movieMemoryRepository) Exec(query Query, action Query, actionLimit int, mode int) (ok bool) {
|
||||
loops := 0
|
||||
|
||||
if mode == ReadOnlyMode {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
} else {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
}
|
||||
|
||||
for _, movie := range r.source {
|
||||
ok = query(movie)
|
||||
if ok {
|
||||
if action(movie) {
|
||||
loops++
|
||||
if actionLimit >= loops {
|
||||
break // break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Select receives a query function
|
||||
// which is fired for every single movie model inside
|
||||
// our imaginary data source.
|
||||
// When that function returns true then it stops the iteration.
|
||||
//
|
||||
// It returns the query's return last known "found" value
|
||||
// and the last known movie model
|
||||
// to help callers to reduce the LOC.
|
||||
//
|
||||
// It's actually a simple but very clever prototype function
|
||||
// I'm using everywhere since I firstly think of it,
|
||||
// hope you'll find it very useful as well.
|
||||
func (r *movieMemoryRepository) Select(query Query) (movie datamodels.Movie, found bool) {
|
||||
found = r.Exec(query, func(m datamodels.Movie) bool {
|
||||
movie = m
|
||||
return true
|
||||
}, 1, ReadOnlyMode)
|
||||
|
||||
// set an empty datamodels.Movie if not found at all.
|
||||
if !found {
|
||||
movie = datamodels.Movie{}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// SelectMany same as Select but returns one or more datamodels.Movie as a slice.
|
||||
// If limit <=0 then it returns everything.
|
||||
func (r *movieMemoryRepository) SelectMany(query Query, limit int) (results []datamodels.Movie) {
|
||||
r.Exec(query, func(m datamodels.Movie) bool {
|
||||
results = append(results, m)
|
||||
return true
|
||||
}, limit, ReadOnlyMode)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// InsertOrUpdate adds or updates a movie to the (memory) storage.
|
||||
//
|
||||
// Returns the new movie and an error if any.
|
||||
func (r *movieMemoryRepository) InsertOrUpdate(movie datamodels.Movie) (datamodels.Movie, error) {
|
||||
id := movie.ID
|
||||
|
||||
if id == 0 { // Create new action
|
||||
var lastID int64
|
||||
// find the biggest ID in order to not have duplications
|
||||
// in productions apps you can use a third-party
|
||||
// library to generate a UUID as string.
|
||||
r.mu.RLock()
|
||||
for _, item := range r.source {
|
||||
if item.ID > lastID {
|
||||
lastID = item.ID
|
||||
}
|
||||
}
|
||||
r.mu.RUnlock()
|
||||
|
||||
id = lastID + 1
|
||||
movie.ID = id
|
||||
|
||||
// map-specific thing
|
||||
r.mu.Lock()
|
||||
r.source[id] = movie
|
||||
r.mu.Unlock()
|
||||
|
||||
return movie, nil
|
||||
}
|
||||
|
||||
// Update action based on the movie.ID,
|
||||
// here we will allow updating the poster and genre if not empty.
|
||||
// Alternatively we could do pure replace instead:
|
||||
// r.source[id] = movie
|
||||
// and comment the code below;
|
||||
current, exists := r.Select(func(m datamodels.Movie) bool {
|
||||
return m.ID == id
|
||||
})
|
||||
|
||||
if !exists { // ID is not a real one, return an error.
|
||||
return datamodels.Movie{}, errors.New("failed to update a nonexistent movie")
|
||||
}
|
||||
|
||||
// or comment these and r.source[id] = m for pure replace
|
||||
if movie.Poster != "" {
|
||||
current.Poster = movie.Poster
|
||||
}
|
||||
|
||||
if movie.Genre != "" {
|
||||
current.Genre = movie.Genre
|
||||
}
|
||||
|
||||
// map-specific thing
|
||||
r.mu.Lock()
|
||||
r.source[id] = current
|
||||
r.mu.Unlock()
|
||||
|
||||
return movie, nil
|
||||
}
|
||||
|
||||
func (r *movieMemoryRepository) Delete(query Query, limit int) bool {
|
||||
return r.Exec(query, func(m datamodels.Movie) bool {
|
||||
delete(r.source, m.ID)
|
||||
return true
|
||||
}, limit, ReadWriteMode)
|
||||
}
|
||||
51
_examples/mvc/overview/service/greet_service.go
Normal file
51
_examples/mvc/overview/service/greet_service.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"app/database"
|
||||
"app/environment"
|
||||
)
|
||||
|
||||
// GreetService example service.
|
||||
type GreetService interface {
|
||||
Say(input string) (string, error)
|
||||
}
|
||||
|
||||
// NewGreetService returns a service backed with a "db" based on "env".
|
||||
func NewGreetService(env environment.Env, db database.DB) GreetService {
|
||||
service := &greeter{db: db, prefix: "Hello"}
|
||||
|
||||
switch env {
|
||||
case environment.PROD:
|
||||
return service
|
||||
case environment.DEV:
|
||||
return &greeterWithLogging{service}
|
||||
default:
|
||||
panic("unknown environment")
|
||||
}
|
||||
}
|
||||
|
||||
type greeter struct {
|
||||
prefix string
|
||||
db database.DB
|
||||
}
|
||||
|
||||
func (s *greeter) Say(input string) (string, error) {
|
||||
if err := s.db.Exec("simulate a query..."); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
result := s.prefix + " " + input
|
||||
return result, nil
|
||||
}
|
||||
|
||||
type greeterWithLogging struct {
|
||||
*greeter
|
||||
}
|
||||
|
||||
func (s *greeterWithLogging) Say(input string) (string, error) {
|
||||
result, err := s.greeter.Say(input)
|
||||
fmt.Printf("result: %s\nerror: %v\n", result, err)
|
||||
return result, err
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
# Service Layer
|
||||
|
||||
The package which has access to call functions from the "repositories" and "models" ("datamodels" only in that simple example). It should contain the domain logic.
|
||||
@@ -1,65 +0,0 @@
|
||||
// file: services/movie_service.go
|
||||
|
||||
package services
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris/v12/_examples/mvc/overview/datamodels"
|
||||
"github.com/kataras/iris/v12/_examples/mvc/overview/repositories"
|
||||
)
|
||||
|
||||
// MovieService handles some of the CRUID operations of the movie datamodel.
|
||||
// It depends on a movie repository for its actions.
|
||||
// It's here to decouple the data source from the higher level compoments.
|
||||
// As a result a different repository type can be used with the same logic without any aditional changes.
|
||||
// It's an interface and it's used as interface everywhere
|
||||
// because we may need to change or try an experimental different domain logic at the future.
|
||||
type MovieService interface {
|
||||
GetAll() []datamodels.Movie
|
||||
GetByID(id int64) (datamodels.Movie, bool)
|
||||
DeleteByID(id int64) bool
|
||||
UpdatePosterAndGenreByID(id int64, poster string, genre string) (datamodels.Movie, error)
|
||||
}
|
||||
|
||||
// NewMovieService returns the default movie service.
|
||||
func NewMovieService(repo repositories.MovieRepository) MovieService {
|
||||
return &movieService{
|
||||
repo: repo,
|
||||
}
|
||||
}
|
||||
|
||||
type movieService struct {
|
||||
repo repositories.MovieRepository
|
||||
}
|
||||
|
||||
// GetAll returns all movies.
|
||||
func (s *movieService) GetAll() []datamodels.Movie {
|
||||
return s.repo.SelectMany(func(_ datamodels.Movie) bool {
|
||||
return true
|
||||
}, -1)
|
||||
}
|
||||
|
||||
// GetByID returns a movie based on its id.
|
||||
func (s *movieService) GetByID(id int64) (datamodels.Movie, bool) {
|
||||
return s.repo.Select(func(m datamodels.Movie) bool {
|
||||
return m.ID == id
|
||||
})
|
||||
}
|
||||
|
||||
// UpdatePosterAndGenreByID updates a movie's poster and genre.
|
||||
func (s *movieService) UpdatePosterAndGenreByID(id int64, poster string, genre string) (datamodels.Movie, error) {
|
||||
// update the movie and return it.
|
||||
return s.repo.InsertOrUpdate(datamodels.Movie{
|
||||
ID: id,
|
||||
Poster: poster,
|
||||
Genre: genre,
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteByID deletes a movie by its id.
|
||||
//
|
||||
// Returns true if deleted otherwise false.
|
||||
func (s *movieService) DeleteByID(id int64) bool {
|
||||
return s.repo.Delete(func(m datamodels.Movie) bool {
|
||||
return m.ID == id
|
||||
}, 1)
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
// file: web/controllers/hello_controller.go
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/kataras/iris/v12/mvc"
|
||||
)
|
||||
|
||||
// HelloController is our sample controller
|
||||
// it handles GET: /hello and GET: /hello/{name}
|
||||
type HelloController struct{}
|
||||
|
||||
var helloView = mvc.View{
|
||||
Name: "hello/index.html",
|
||||
Data: map[string]interface{}{
|
||||
"Title": "Hello Page",
|
||||
"MyMessage": "Welcome to my awesome website",
|
||||
},
|
||||
}
|
||||
|
||||
// Get will return a predefined view with bind data.
|
||||
//
|
||||
// `mvc.Result` is just an interface with a `Dispatch` function.
|
||||
// `mvc.Response` and `mvc.View` are the builtin result type dispatchers
|
||||
// you can even create custom response dispatchers by
|
||||
// implementing the `github.com/kataras/iris/hero#Result` interface.
|
||||
func (c *HelloController) Get() mvc.Result {
|
||||
return helloView
|
||||
}
|
||||
|
||||
// you can define a standard error in order to re-use anywhere in your app.
|
||||
var errBadName = errors.New("bad name")
|
||||
|
||||
// you can just return it as error or even better
|
||||
// wrap this error with an mvc.Response to make it an mvc.Result compatible type.
|
||||
var badName = mvc.Response{Err: errBadName, Code: 400}
|
||||
|
||||
// GetBy returns a "Hello {name}" response.
|
||||
// Demos:
|
||||
// curl -i http://localhost:8080/hello/iris
|
||||
// curl -i http://localhost:8080/hello/anything
|
||||
func (c *HelloController) GetBy(name string) mvc.Result {
|
||||
if name != "iris" {
|
||||
return badName
|
||||
// or
|
||||
// GetBy(name string) (mvc.Result, error) {
|
||||
// return nil, errBadName
|
||||
// }
|
||||
}
|
||||
|
||||
// return mvc.Response{Text: "Hello " + name} OR:
|
||||
return mvc.View{
|
||||
Name: "hello/name.html",
|
||||
Data: name,
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
// file: web/controllers/movie_controller.go
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/kataras/iris/v12/_examples/mvc/overview/datamodels"
|
||||
"github.com/kataras/iris/v12/_examples/mvc/overview/services"
|
||||
|
||||
"github.com/kataras/iris/v12"
|
||||
)
|
||||
|
||||
// MovieController is our /movies controller.
|
||||
type MovieController struct {
|
||||
// Our MovieService, it's an interface which
|
||||
// is binded from the main application.
|
||||
Service services.MovieService
|
||||
}
|
||||
|
||||
// Get returns list of the movies.
|
||||
// Demo:
|
||||
// curl -i http://localhost:8080/movies
|
||||
//
|
||||
// The correct way if you have sensitive data:
|
||||
// func (c *MovieController) Get() (results []viewmodels.Movie) {
|
||||
// data := c.Service.GetAll()
|
||||
//
|
||||
// for _, movie := range data {
|
||||
// results = append(results, viewmodels.Movie{movie})
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
// otherwise just return the datamodels.
|
||||
func (c *MovieController) Get() (results []datamodels.Movie) {
|
||||
return c.Service.GetAll()
|
||||
}
|
||||
|
||||
// GetBy returns a movie.
|
||||
// Demo:
|
||||
// curl -i http://localhost:8080/movies/1
|
||||
func (c *MovieController) GetBy(id int64) (movie datamodels.Movie, found bool) {
|
||||
return c.Service.GetByID(id) // it will throw 404 if not found.
|
||||
}
|
||||
|
||||
// PutBy updates a movie.
|
||||
// Demo:
|
||||
// curl -i -X PUT -F "genre=Thriller" -F "poster=@/Users/kataras/Downloads/out.gif" http://localhost:8080/movies/1
|
||||
func (c *MovieController) PutBy(ctx iris.Context, id int64) (datamodels.Movie, error) {
|
||||
// get the request data for poster and genre
|
||||
file, info, err := ctx.FormFile("poster")
|
||||
if err != nil {
|
||||
return datamodels.Movie{}, errors.New("failed due form file 'poster' missing")
|
||||
}
|
||||
// we don't need the file so close it now.
|
||||
file.Close()
|
||||
|
||||
// imagine that is the url of the uploaded file...
|
||||
poster := info.Filename
|
||||
genre := ctx.FormValue("genre")
|
||||
|
||||
return c.Service.UpdatePosterAndGenreByID(id, poster, genre)
|
||||
}
|
||||
|
||||
// DeleteBy deletes a movie.
|
||||
// Demo:
|
||||
// curl -i -X DELETE -u admin:password http://localhost:8080/movies/1
|
||||
func (c *MovieController) DeleteBy(id int64) interface{} {
|
||||
wasDel := c.Service.DeleteByID(id)
|
||||
if wasDel {
|
||||
// return the deleted movie's ID
|
||||
return iris.Map{"deleted": id}
|
||||
}
|
||||
// right here we can see that a method function can return any of those two types(map or int),
|
||||
// we don't have to specify the return type to a specific type.
|
||||
return iris.StatusBadRequest
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
// file: web/middleware/basicauth.go
|
||||
|
||||
package middleware
|
||||
|
||||
import "github.com/kataras/iris/v12/middleware/basicauth"
|
||||
|
||||
// BasicAuth middleware sample.
|
||||
var BasicAuth = basicauth.New(basicauth.Config{
|
||||
Users: map[string]string{
|
||||
"admin": "password",
|
||||
},
|
||||
})
|
||||
@@ -1,56 +0,0 @@
|
||||
# View Models
|
||||
|
||||
There should be the view models, the structure that the client will be able to see.
|
||||
|
||||
Example:
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/kataras/iris/v12/_examples/mvc/overview/datamodels"
|
||||
|
||||
"github.com/kataras/iris/v12"
|
||||
"github.com/kataras/iris/v12/context"
|
||||
)
|
||||
|
||||
type Movie struct {
|
||||
datamodels.Movie
|
||||
}
|
||||
|
||||
func (m Movie) IsValid() bool {
|
||||
/* do some checks and return true if it's valid... */
|
||||
return m.ID > 0
|
||||
}
|
||||
```
|
||||
|
||||
Iris is able to convert any custom data Structure into an HTTP Response Dispatcher,
|
||||
so theoretically, something like the following is permitted if it's really necessary;
|
||||
|
||||
```go
|
||||
// Dispatch completes the `kataras/iris/mvc#Result` interface.
|
||||
// Sends a `Movie` as a controlled http response.
|
||||
// If its ID is zero or less then it returns a 404 not found error
|
||||
// else it returns its json representation,
|
||||
// (just like the controller's functions do for custom types by default).
|
||||
//
|
||||
// Don't overdo it, the application's logic should not be here.
|
||||
// It's just one more step of validation before the response,
|
||||
// simple checks can be added here.
|
||||
//
|
||||
// It's just a showcase,
|
||||
// imagine the potentials this feature gives when designing a bigger application.
|
||||
//
|
||||
// This is called where the return value from a controller's method functions
|
||||
// is type of `Movie`.
|
||||
// For example the `controllers/movie_controller.go#GetBy`.
|
||||
func (m Movie) Dispatch(ctx iris.Context) {
|
||||
if !m.IsValid() {
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
ctx.JSON(m, context.JSON{Indent: " "})
|
||||
}
|
||||
```
|
||||
|
||||
However, we will use the "datamodels" as the only one models package because
|
||||
Movie structure doesn't contain any sensitive data, clients are able to see all of its fields
|
||||
and we don't need any extra functionality or validation inside it.
|
||||
@@ -1,12 +0,0 @@
|
||||
<!-- file: web/views/hello/index.html -->
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>{{.Title}} - My App</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p>{{.MyMessage}}</p>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,12 +0,0 @@
|
||||
<!-- file: web/views/hello/name.html -->
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>{{.}}' Portfolio - My App</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Hello {{.}}</h1>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Reference in New Issue
Block a user