diff --git a/README.md b/README.md
index 272f9204..552625e5 100644
--- a/README.md
+++ b/README.md
@@ -167,857 +167,6 @@ func main() {
Guidelines for bootstrapping applications can be found at the [_examples/structuring](_examples/#structuring).
-### Quick MVC Tutorial
-
-```go
-package main
-
-import (
- "github.com/kataras/iris"
- "github.com/kataras/iris/mvc"
-)
-
-func main() {
- app := iris.New()
-
- app.Controller("/helloworld", new(HelloWorldController))
-
- app.Run(iris.Addr("localhost:8080"))
-}
-
-type HelloWorldController struct {
- mvc.C
-
- // [ Your fields here ]
- // Request lifecycle data
- // Models
- // Database
- // Global properties
-}
-
-//
-// GET: /helloworld
-
-func (c *HelloWorldController) Get() string {
- return "This is my default action..."
-}
-
-//
-// GET: /helloworld/{name:string}
-
-func (c *HelloWorldController) GetBy(name string) string {
- return "Hello " + name
-}
-
-//
-// GET: /helloworld/welcome
-
-func (c *HelloWorldController) GetWelcome() (string, int) {
- return "This is the GetWelcome action func...", iris.StatusOK
-}
-
-//
-// GET: /helloworld/welcome/{name:string}/{numTimes:int}
-
-func (c *HelloWorldController) GetWelcomeBy(name string, numTimes int) {
- // Access to the low-level Context,
- // output arguments are optional of course so we don't have to use them here.
- c.Ctx.Writef("Hello %s, NumTimes is: %d", name, numTimes)
-}
-```
-
-> The [_examples/mvc](_examples/mvc) and [mvc/controller_test.go](https://github.com/kataras/iris/blob/master/mvc/controller_test.go) files explain each feature with simple paradigms, they show how you can take advantage of the Iris MVC Binder, Iris MVC Models and many more...
-
-Every `exported` func prefixed with an HTTP Method(`Get`, `Post`, `Put`, `Delete`...) in a controller is callable as an HTTP endpoint. In the sample above, all funcs writes a string to the response. Note the comments preceding each method.
-
-An HTTP endpoint is a targetable URL in the web application, such as `http://localhost:8080/helloworld`, and combines the protocol used: HTTP, the network location of the web server (including the TCP port): `localhost:8080` and the target URI `/helloworld`.
-
-The first comment states this is an [HTTP GET](https://www.w3schools.com/tags/ref_httpmethods.asp) method that is invoked by appending "/helloworld" to the base URL. The third comment specifies an [HTTP GET](https://www.w3schools.com/tags/ref_httpmethods.asp) method that is invoked by appending "/helloworld/welcome" to the URL.
-
-Controller knows how to handle the "name" on `GetBy` or the "name" and "numTimes" at `GetWelcomeBy`, because of the `By` keyword, and builds the dynamic route without boilerplate; the third comment specifies an [HTTP GET](https://www.w3schools.com/tags/ref_httpmethods.asp) dynamic method that is invoked by any URL that starts with "/helloworld/welcome" and followed by two more path parts, the first one can accept any value and the second can accept only numbers, i,e: "http://localhost:8080/helloworld/welcome/golang/32719", otherwise a [404 Not Found HTTP Error](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) will be sent to the client instead.
-
-### Quick MVC Tutorial #2
-
-Iris has a very powerful and **blazing [fast](_benchmarks)** MVC support, you can return any value of any type from a method function
-and it will be sent to the client as expected.
-
-* if `string` then it's the body.
-* if `string` is the second output argument then it's the content type.
-* if `int` then it's the status code.
-* if `error` and not nil then (any type) response will be omitted and error's text with a 400 bad request will be rendered instead.
-* if `(int, error)` and error is not nil then the response result will be the error's text with the status code as `int`.
-* if `bool` is false then it throws 404 not found http error by skipping everything else.
-* if `custom struct` or `interface{}` or `slice` or `map` then it will be rendered as json, unless a `string` content type is following.
-* if `mvc.Result` then it executes its `Dispatch` function, so good design patters can be used to split the model's logic where needed.
-
-The example below is not intended to be used in production but it's a good showcase of some of the return types we saw before;
-
-```go
-package main
-
-import (
- "github.com/kataras/iris"
- "github.com/kataras/iris/middleware/basicauth"
- "github.com/kataras/iris/mvc"
-)
-
-// Movie is our sample data structure.
-type Movie struct {
- Name string `json:"name"`
- Year int `json:"year"`
- Genre string `json:"genre"`
- Poster string `json:"poster"`
-}
-
-// movies contains our imaginary data source.
-var movies = []Movie{
- {
- Name: "Casablanca",
- Year: 1942,
- Genre: "Romance",
- Poster: "https://iris-go.com/images/examples/mvc-movies/1.jpg",
- },
- {
- Name: "Gone with the Wind",
- Year: 1939,
- Genre: "Romance",
- Poster: "https://iris-go.com/images/examples/mvc-movies/2.jpg",
- },
- {
- Name: "Citizen Kane",
- Year: 1941,
- Genre: "Mystery",
- Poster: "https://iris-go.com/images/examples/mvc-movies/3.jpg",
- },
- {
- Name: "The Wizard of Oz",
- Year: 1939,
- Genre: "Fantasy",
- Poster: "https://iris-go.com/images/examples/mvc-movies/4.jpg",
- },
-}
-
-
-var basicAuth = basicauth.New(basicauth.Config{
- Users: map[string]string{
- "admin": "password",
- },
-})
-
-
-func main() {
- app := iris.New()
-
- app.Use(basicAuth)
-
- app.Controller("/movies", new(MoviesController))
-
- app.Run(iris.Addr(":8080"))
-}
-
-// MoviesController is our /movies controller.
-type MoviesController struct {
- mvc.C
-}
-
-// Get returns list of the movies
-// Demo:
-// curl -i http://localhost:8080/movies
-func (c *MoviesController) Get() []Movie {
- return movies
-}
-
-// GetBy returns a movie
-// Demo:
-// curl -i http://localhost:8080/movies/1
-func (c *MoviesController) GetBy(id int) Movie {
- return movies[id]
-}
-
-// 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 *MoviesController) PutBy(id int) Movie {
- // get the movie
- m := movies[id]
-
- // get the request data for poster and genre
- file, info, err := c.Ctx.FormFile("poster")
- if err != nil {
- c.Ctx.StatusCode(iris.StatusInternalServerError)
- return Movie{}
- }
- file.Close() // we don't need the file
- poster := info.Filename // imagine that as the url of the uploaded file...
- genre := c.Ctx.FormValue("genre")
-
- // update the poster
- m.Poster = poster
- m.Genre = genre
- movies[id] = m
-
- return m
-}
-
-// DeleteBy deletes a movie
-// Demo:
-// curl -i -X DELETE -u admin:password http://localhost:8080/movies/1
-func (c *MoviesController) DeleteBy(id int) iris.Map {
- // delete the entry from the movies slice
- deleted := movies[id].Name
- movies = append(movies[:id], movies[id+1:]...)
- // and return the deleted movie's name
- return iris.Map{"deleted": deleted}
-}
-```
-
-### Quick MVC Tutorial #3
-
-Nothing stops you from using your favorite **folder structure**. Iris is a low level web framework, it has got MVC first-class support but it doesn't limit your folder structure, this is your choice.
-
-Structuring depends on your own needs. We can't tell you how to design your own application for sure but you're free to take a closer look to one typical example below;
-
-[![folder structure example](_examples/mvc/overview/folder_structure.png)](_examples/mvc/overview)
-
-Shhh, let's spread the code itself.
-
-#### Data Model Layer
-
-```go
-// 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"`
-}
-```
-
-#### Data Source / Data Store Layer
-
-```go
-// file: datasource/movies.go
-
-package datasource
-
-import "github.com/kataras/iris/_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",
- },
-}
-```
-
-#### Repositories
-
-The layer which has direct access to the "datasource" and can manipulate data directly.
-
-```go
-// file: repositories/movie_repository.go
-
-package repositories
-
-import (
- "errors"
- "sync"
-
- "github.com/kataras/iris/_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)
-}
-```
-
-#### Services
-
-The layer which has access to call functions from the "repositories" and "models" (or even "datamodels" if simple application). It should contain the most of the domain logic.
-
-```go
-// file: services/movie_service.go
-
-package services
-
-import (
- "github.com/kataras/iris/_examples/mvc/overview/datamodels"
- "github.com/kataras/iris/_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)
-}
-```
-
-#### 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/_examples/mvc/overview/datamodels"
-
- "github.com/kataras/iris/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 context.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.
-
-#### Controllers
-
-Handles web requests, bridge between the services and the client.
-
-```go
-// file: web/controllers/movie_controller.go
-
-package controllers
-
-import (
- "errors"
-
- "github.com/kataras/iris/_examples/mvc/overview/datamodels"
- "github.com/kataras/iris/_examples/mvc/overview/services"
-
- "github.com/kataras/iris"
- "github.com/kataras/iris/mvc"
-)
-
-// MovieController is our /movies controller.
-type MovieController struct {
- mvc.C
-
- // 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(id int64) (datamodels.Movie, error) {
- // get the request data for poster and genre
- file, info, err := c.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 := c.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
-}
-```
-
-```go
-// file: web/controllers/hello_controller.go
-
-package controllers
-
-import (
- "errors"
-
- "github.com/kataras/iris/mvc"
-)
-
-// HelloController is our sample controller
-// it handles GET: /hello and GET: /hello/{name}
-type HelloController struct {
- mvc.C
-}
-
-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 built'n result type dispatchers
-// you can even create custom response dispatchers by
-// implementing the `github.com/kataras/iris/mvc#Result` interface.
-func (c *HelloController) Get() mvc.Result {
- return helloView
-}
-
-// you can define a standard error in order to be re-usable 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,
- }
-}
-```
-
-```go
-// file: web/middleware/basicauth.go
-
-package middleware
-
-import "github.com/kataras/iris/middleware/basicauth"
-
-// BasicAuth middleware sample.
-var BasicAuth = basicauth.New(basicauth.Config{
- Users: map[string]string{
- "admin": "password",
- },
-})
-```
-
-```html
-
-
-
-
- {{.Title}} - My App
-
-
-
- {{.MyMessage}}
-
-
-
-```
-
-```html
-
-
-
-
- {{.}}' Portfolio - My App
-
-
-
- Hello {{.}}
-
-
-
-```
-
-> Navigate to the [_examples/view](_examples/#view) for more examples
-like shared layouts, tmpl funcs, reverse routing and more!
-
-#### Main
-
-This file creates any necessary component and links them together.
-
-```go
-// file: main.go
-
-package main
-
-import (
- "github.com/kataras/iris/_examples/mvc/overview/datasource"
- "github.com/kataras/iris/_examples/mvc/overview/repositories"
- "github.com/kataras/iris/_examples/mvc/overview/services"
- "github.com/kataras/iris/_examples/mvc/overview/web/controllers"
- "github.com/kataras/iris/_examples/mvc/overview/web/middleware"
-
- "github.com/kataras/iris"
-)
-
-func main() {
- app := iris.New()
-
- // Load the template files.
- app.RegisterView(iris.HTML("./web/views", ".html"))
-
- // Register our controllers.
- app.Controller("/hello", new(controllers.HelloController))
-
- // 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 controller.
- movieService := services.NewMovieService(repo)
-
- app.Controller("/movies", new(controllers.MovieController),
- // Bind the "movieService" to the MovieController's Service (interface) field.
- movieService,
- // Add the basic authentication(admin:password) middleware
- // for the /movies based requests.
- middleware.BasicAuth)
-
- // Start the web server at localhost:8080
- // http://localhost:8080/hello
- // http://localhost:8080/hello/iris
- // http://localhost:8080/movies
- // http://localhost:8080/movies/1
- app.Run(
- iris.Addr("localhost:8080"),
- iris.WithoutVersionChecker,
- iris.WithoutServerError(iris.ErrServerClosed),
- iris.WithOptimizations, // enables faster json serialization and more
- )
-}
-```
-
-More folder structure guidelines can be found at the [_examples/#structuring](_examples/#structuring) section.
-
## Now you are ready to move to the next step and get closer to becoming a pro gopher
Congratulations, since you've made it so far, we've crafted just for you some next level content to turn you into a real pro gopher 😃
diff --git a/README_ZH.md b/README_ZH.md
index a4718c1e..da42f19e 100644
--- a/README_ZH.md
+++ b/README_ZH.md
@@ -147,834 +147,6 @@ $ go run main.go
Iris的一些开发约定可以看看这里[_examples/structuring](_examples/#structuring)。
-### MVC指南
-
-```go
-package main
-
-import (
- "github.com/kataras/iris"
- "github.com/kataras/iris/mvc"
-)
-
-func main() {
- app := iris.New()
-
- app.Controller("/helloworld", new(HelloWorldController))
-
- app.Run(iris.Addr("localhost:8080"))
-}
-
-type HelloWorldController struct {
- mvc.C
-
- // [ Your fields here ]
- // Request lifecycle data
- // Models
- // Database
- // Global properties
-}
-
-//
-// GET: /helloworld
-
-func (c *HelloWorldController) Get() string {
- return "This is my default action..."
-}
-
-//
-// GET: /helloworld/{name:string}
-
-func (c *HelloWorldController) GetBy(name string) string {
- return "Hello " + name
-}
-
-//
-// GET: /helloworld/welcome
-
-func (c *HelloWorldController) GetWelcome() (string, int) {
- return "This is the GetWelcome action func...", iris.StatusOK
-}
-
-//
-// GET: /helloworld/welcome/{name:string}/{numTimes:int}
-
-func (c *HelloWorldController) GetWelcomeBy(name string, numTimes int) {
- // Access to the low-level Context,
- // output arguments are optional of course so we don't have to use them here.
- c.Ctx.Writef("Hello %s, NumTimes is: %d", name, numTimes)
-}
-
-```
-> [_examples/mvc](_examples/mvc) 和 [mvc/controller_test.go](https://github.com/kataras/iris/blob/master/mvc/controller_test.go) 两个简单的例子可以让你更好的了解 Iris MVC 的使用方式
-
-每一个在controller中导出的Go方法名都和HTTP方法(`Get`, `Post`, `Put`, `Delete`...) 一一对应
-
-在Web应用中一个HTTP访问的资源就是一个URL(统一资源定位符),比如`http://localhost:8080/helloworld`是由HTTP协议、Web服务网络位置(包括TCP端口):`localhost:8080`以及资源名称URI(统一资源标志符) `/helloworld`组成的。
-
-上面例子第一个方法映射到[HTTP GET](https://www.w3schools.com/tags/ref_httpmethods.asp)方法,访问资源是"/helloworld",第三个方法映射到[HTTP GET](https://www.w3schools.com/tags/ref_httpmethods.asp)方法,访问资源是"/helloworld/welcome"
-
-
-Controller在处理`GetBy`方法时可以识别路径‘name’参数,`GetWelcomeBy`方法可以识别路径‘name’和‘numTimes’参数,因为Controller在识别`By`关键字后可以动态灵活的处理路由;上面第四个方法指示使用 [HTTP GET](https://www.w3schools.com/tags/ref_httpmethods.asp)方法,而且只处理以"/helloworld/welcome"开头的资源位置路径,并且此路径还得包括两部分,第一部分类型没有限制,第二部分只能是数字类型,比如"http://localhost:8080/helloworld/welcome/golang/32719" 是合法的,其它的就会给客户端返回[404 找不到](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5)的提示
-
-
-### MVC 快速指南 2
-
-Iris对MVC的支持非常**棒[看看基准测试](_benchmarks)** ,Iris通过方法的返回值,可以给客户端返回任意类型的数据:
-
-* 如果返回的是 `string` 类型,就直接给客户端返回字符串
-* 如果第二个返回值是 `string` 类型,那么这个值就是ContentType(HTTP header)的值
-* 如果返回的是 `int` 类型,这个值就是HTTP状态码
-* 如果返回 `error` 值不是空,Iris 将会把这个值作为HTTP 400页面的返回值内容
-* 如果返回 `(int, error)` 类型,并且error不为空,那么Iris返回error的内容,同时把 `int` 值作为HTTP状态码
-* 如果返回 `bool` 类型,并且值是 false ,Iris直接返回404页面
-* 如果返回自定义` struct` 、 `interface{}` 、 `slice` 及 `map` ,Iris 将按照JSON的方式返回,注意如果第二个返回值是 `string`,那么Iris就按照这个 `string` 值的ContentType处理了(不一定是'application/json')
-* 如果 `mvc.Result` 调用了 `Dispatch` 函数, 就会按照自己的逻辑重新处理
-
-下面这些例子仅供参考,生产环境谨慎使用
-
-```go
-package main
-
-import (
- "github.com/kataras/iris"
- "github.com/kataras/iris/middleware/basicauth"
- "github.com/kataras/iris/mvc"
-)
-
-// Movie 是自定义数据结构
-type Movie struct {
- Name string `json:"name"`
- Year int `json:"year"`
- Genre string `json:"genre"`
- Poster string `json:"poster"`
-}
-
-// movies 对象模拟数据源
-var movies = []Movie{
- {
- Name: "Casablanca",
- Year: 1942,
- Genre: "Romance",
- Poster: "https://iris-go.com/images/examples/mvc-movies/1.jpg",
- },
- {
- Name: "Gone with the Wind",
- Year: 1939,
- Genre: "Romance",
- Poster: "https://iris-go.com/images/examples/mvc-movies/2.jpg",
- },
- {
- Name: "Citizen Kane",
- Year: 1941,
- Genre: "Mystery",
- Poster: "https://iris-go.com/images/examples/mvc-movies/3.jpg",
- },
- {
- Name: "The Wizard of Oz",
- Year: 1939,
- Genre: "Fantasy",
- Poster: "https://iris-go.com/images/examples/mvc-movies/4.jpg",
- },
-}
-
-
-var basicAuth = basicauth.New(basicauth.Config{
- Users: map[string]string{
- "admin": "password",
- },
-})
-
-
-func main() {
- app := iris.New()
-
- app.Use(basicAuth)
-
- app.Controller("/movies", new(MoviesController))
-
- app.Run(iris.Addr(":8080"))
-}
-
-// MoviesController 是 /movies controller.
-type MoviesController struct {
- mvc.C
-}
-
-// 返回 movies列表
-// 例子:
-// curl -i http://localhost:8080/movies
-func (c *MoviesController) Get() []Movie {
- return movies
-}
-
-// GetBy 返回一个 movie
-// 例子:
-// curl -i http://localhost:8080/movies/1
-func (c *MoviesController) GetBy(id int) Movie {
- return movies[id]
-}
-
-// PutBy 更新一个 movie
-// 例子:
-// curl -i -X PUT -F "genre=Thriller" -F "poster=@/Users/kataras/Downloads/out.gif" http://localhost:8080/movies/1
-func (c *MoviesController) PutBy(id int) Movie {
- // 获取一个 movie
- m := movies[id]
-
- // 获取一个poster文件
- file, info, err := c.Ctx.FormFile("poster")
- if err != nil {
- c.Ctx.StatusCode(iris.StatusInternalServerError)
- return Movie{}
- }
- file.Close() // 我们不需要这个文件
- poster := info.Filename // 比如这就是上传的文件url
- genre := c.Ctx.FormValue("genre")
-
- // 更新poster
- m.Poster = poster
- m.Genre = genre
- movies[id] = m
-
- return m
-}
-
-// DeleteBy 删除一个 movie
-// 例子:
-// curl -i -X DELETE -u admin:password http://localhost:8080/movies/1
-func (c *MoviesController) DeleteBy(id int) iris.Map {
- //从movies slice中删除索引
- deleted := movies[id].Name
- movies = append(movies[:id], movies[id+1:]...)
- // 返回删除movie的名称
- return iris.Map{"deleted": deleted}
-}
-```
-
-### MVC 快速指南 3
-
-Iris是一个底层的Web开发框架,如果你喜欢按 **目录结构** 的约定方式开发,那么Iris框架对此毫无影响。
-
-你可以根据自己的需求来创建目录结构,但是我建议你还是最好看看如下的目录结构例子:
-
-[![目录结构例子](_examples/mvc/overview/folder_structure.png)](_examples/mvc/overview)
-
-好了,直接上代码。
-
-
-#### 数据模型层
-
-```go
-// file: datamodels/movie.go
-
-package datamodels
-
-// Movie是我们例子数据结构
-// 此Movie可能会定义在类似"web/viewmodels/movie.go"的文件
-// Movie的数据模型在应用中只有一个,这样使用就很简单了
-type Movie struct {
- ID int64 `json:"id"`
- Name string `json:"name"`
- Year int `json:"year"`
- Genre string `json:"genre"`
- Poster string `json:"poster"`
-}
-```
-
-#### 数据层 / 数据存储层
-
-```go
-// file: datasource/movies.go
-
-package datasource
-
-import "github.com/kataras/iris/_examples/mvc/overview/datamodels"
-
-// Movies是模拟的数据源
-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",
- },
-}
-```
-
-#### 数据仓库
-
-数据仓库层直接访问数据源
-
-```go
-// file: repositories/movie_repository.go
-
-package repositories
-
-import (
- "errors"
- "sync"
-
- "github.com/kataras/iris/_examples/mvc/overview/datamodels"
-)
-
-// Query 是数据访问的集合入口
-type Query func(datamodels.Movie) bool
-
-// MovieRepository 中会有对movie实体的基本操作
-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 返回movie内存数据
-func NewMovieRepository(source map[int64]datamodels.Movie) MovieRepository {
- return &movieMemoryRepository{source: source}
-}
-
-// movieMemoryRepository 就是 "MovieRepository",它管理movie的内存数据
-type movieMemoryRepository struct {
- source map[int64]datamodels.Movie
- mu sync.RWMutex
-}
-
-const (
- // 只读模式
- ReadOnlyMode = iota
- // 读写模式
- 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方法返回从模拟数据源找出的一个movie数据。
-// 当找到时就返回true,并停止迭代
-//
-// Select 将会返回查询到的最新找到的movie数据,这样可以减少代码量
-//
-// 自从我第一次想到用这种简单的原型函数后,我就经常用它了,希望这也对你有用
-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)
-
- // 如果没有找到就让datamodels.Movie为空
- // set an empty datamodels.Movie if not found at all.
- if !found {
- movie = datamodels.Movie{}
- }
-
- return
-}
-
-// 如果要查找很多值,用法基本一致,不过会返回datamodels.Movie slice。
-// 如果limit<=0,将返回全部数据
-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
-}
-
-// 插入或更新数据
-//
-// 返回一个新的movie对象和error对象
-func (r *movieMemoryRepository) InsertOrUpdate(movie datamodels.Movie) (datamodels.Movie, error) {
- id := movie.ID
-
- if id == 0 { // Create new action
- var lastID int64
- // 为了数据不重复,找到最大的ID。
- // 生产环境你可以用第三方库生成一个UUID字串
- 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
- }
- //通过movie.ID更新数据
- //这里举个例子看如果更新非空的poster和genre
- //其实我们可以直接更新对象r.source[id] = movie
- //用Select的话如下所示
- current, exists := r.Select(func(m datamodels.Movie) bool {
- return m.ID == id
- })
-
- if !exists { // ID不存在,返回error ID
- 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结构的处理
- 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)
-}
-```
-
-#### 服务层
-
-服务层主要调用“数据仓库”和“数据模型”的方法(即使是数据模型很简单的应用)。这一层将包含主要的数据处理逻辑。
-
-
-```go
-// file: services/movie_service.go
-
-package services
-
-import (
- "github.com/kataras/iris/_examples/mvc/overview/datamodels"
- "github.com/kataras/iris/_examples/mvc/overview/repositories"
-)
-
-// MovieService主要包括对movie的CRUID(增删改查)操作。
-// MovieService主要调用movie 数据仓库的方法。
-// 下面例子的数据源是更高级别的组件
-// 这样可以用同样的逻辑可以返回不同的数据仓库
-// MovieService是一个接口,任何实现的地方都能用,这样可以替换不同的业务逻辑用来测试
-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 返回一个 movie 服务.
-func NewMovieService(repo repositories.MovieRepository) MovieService {
- return &movieService{
- repo: repo,
- }
-}
-
-type movieService struct {
- repo repositories.MovieRepository
-}
-
-// GetAll 返回所有 movies.
-func (s *movieService) GetAll() []datamodels.Movie {
- return s.repo.SelectMany(func(_ datamodels.Movie) bool {
- return true
- }, -1)
-}
-
-// GetByID 是通过id找到movie.
-func (s *movieService) GetByID(id int64) (datamodels.Movie, bool) {
- return s.repo.Select(func(m datamodels.Movie) bool {
- return m.ID == id
- })
-}
-
-
-// UpdatePosterAndGenreByID 更新一个 movie的 poster 和 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 通过id删除一个movie
-//
-// 返回true表示成功,其它都是失败
-func (s *movieService) DeleteByID(id int64) bool {
- return s.repo.Delete(func(m datamodels.Movie) bool {
- return m.ID == id
- }, 1)
-}
-```
-
-#### 视图模型
-
-视图模型将处理结果返回给客户端
-
-例子:
-Example:
-
-```go
-import (
- "github.com/kataras/iris/_examples/mvc/overview/datamodels"
-
- "github.com/kataras/iris/context"
-)
-
-type Movie struct {
- datamodels.Movie
-}
-
-func (m Movie) IsValid() bool {
- /* 做一些检测,如果ID合法就返回true */
- return m.ID > 0
-}
-```
-
-Iris允许在HTTP Response Dispatcher中使用任何自定义数据结构,
-所以理论上来说,除非万不得已,下面的代码不建议使用
-
-```go
-// Dispatch实现了`kataras/iris/mvc#Result`接口。在函数最后发送了一个`Movie`对象作为http response对象。
-// 如果ID小于等于0就回返回404,或者就返回json数据。
-//(这样就像控制器的方法默认返回自定义类型一样)
-//
-// 不要在这里写过多的代码,应用的主要逻辑不在这里
-// 在方法返回之前可以做个简单验证处理等等;
-//
-// 这里只是一个小例子,想想这个优势在设计大型应用是很有作用的
-//
-// 这个方法是在`Movie`类型的控制器调用的。
-// 例子在这里:`controllers/movie_controller.go#GetBy`。
-func (m Movie) Dispatch(ctx context.Context) {
- if !m.IsValid() {
- ctx.NotFound()
- return
- }
- ctx.JSON(m, context.JSON{Indent: " "})
-}
-```
-然而,我们仅仅用"datamodels"作为一个数据模型包,是因为Movie数据结构没有包含敏感数据,客户端可以访问到其所有字段,我们不需要再有额外的功能去做验证处理了
-
-
-#### 控制器
-
-控制器处理Web请求,它是服务层和客户端之间的桥梁
-
-```go
-// file: web/controllers/movie_controller.go
-
-package controllers
-
-import (
- "errors"
-
- "github.com/kataras/iris/_examples/mvc/overview/datamodels"
- "github.com/kataras/iris/_examples/mvc/overview/services"
-
- "github.com/kataras/iris"
- "github.com/kataras/iris/mvc"
-)
-
-// MovieController是/movies的控制器
-type MovieController struct {
- mvc.C
-
- // MovieService是一个接口,主app对象会持有它
- Service services.MovieService
-}
-
-// 获取movies列表
-// 例子:
-// curl -i http://localhost:8080/movies
-//
-// 如果你有一些敏感的数据要处理的话,可以按照如下所示的方式:
-// func (c *MovieController) Get() (results []viewmodels.Movie) {
-// data := c.Service.GetAll()
-//
-// for _, movie := range data {
-// results = append(results, viewmodels.Movie{movie})
-// }
-// return
-// }
-//否则直接返回数据模型
-func (c *MovieController) Get() (results []datamodels.Movie) {
- return c.Service.GetAll()
-}
-
-// GetBy返回一个movie对象
-// 例子:
-// curl -i http://localhost:8080/movies/1
-func (c *MovieController) GetBy(id int64) (movie datamodels.Movie, found bool) {
- return c.Service.GetByID(id) // 404 没有找到
-}
-
-// PutBy更新一个movie.
-// 例子:
-// curl -i -X PUT -F "genre=Thriller" -F "poster=@/Users/kataras/Downloads/out.gif" http://localhost:8080/movies/1
-func (c *MovieController) PutBy(id int64) (datamodels.Movie, error) {
- // 从请求中获取poster和genre
- file, info, err := c.Ctx.FormFile("poster")
- if err != nil {
- return datamodels.Movie{}, errors.New("failed due form file 'poster' missing")
- }
- // 关闭文件
- file.Close()
-
- //想象这就是一个上传文件的url
- poster := info.Filename
- genre := c.Ctx.FormValue("genre")
-
- return c.Service.UpdatePosterAndGenreByID(id, poster, genre)
-}
-
-// DeleteBy删除一个movie对象
-// 例子:
-// 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 {
- // 返回要删除的ID
- return iris.Map{"deleted": id}
- }
- //现在我们可以看到这里可以返回一个有2个返回值(map或int)的函数
- //我们并没有指定一个返回的类型
- return iris.StatusBadRequest
-}
-```
-
-```go
-// file: web/controllers/hello_controller.go
-
-package controllers
-
-import (
- "errors"
-
- "github.com/kataras/iris/mvc"
-)
-
-// HelloController是控制器的例子
-// 下面会处理GET: /hello and GET: /hello/{name}
-type HelloController struct {
- mvc.C
-}
-
-var helloView = mvc.View{
- Name: "hello/index.html",
- Data: map[string]interface{}{
- "Title": "Hello Page",
- "MyMessage": "Welcome to my awesome website",
- },
-}
-
-// Get会返回预定义绑定数据的视图
-//
-// `mvc.Result`是一个含有`Dispatch`方法的接口
-// `mvc.Response` 和 `mvc.View` dispatchers 内置类型
-// 你也可以通过实现`github.com/kataras/iris/mvc#Result`接口来自定义dispatchers
-func (c *HelloController) Get() mvc.Result {
- return helloView
-}
-
-// 你可以定义一个标准通用的error
-var errBadName = errors.New("bad name")
-
-//你也可以将error包裹在mvc.Response中,这样就和mvc.Result类型兼容了
-var badName = mvc.Response{Err: errBadName, Code: 400}
-
-// GetBy 返回 "Hello {name}" response
-// 例子:
-// 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
- // 或者
- // GetBy(name string) (mvc.Result, error) {
- // return nil, errBadName
- // }
- }
-
- // 返回 mvc.Response{Text: "Hello " + name} 或者:
- return mvc.View{
- Name: "hello/name.html",
- Data: name,
- }
-}
-```
-
-```go
-// file: web/middleware/basicauth.go
-
-package middleware
-
-import "github.com/kataras/iris/middleware/basicauth"
-
-// BasicAuth 中间件例
-var BasicAuth = basicauth.New(basicauth.Config{
- Users: map[string]string{
- "admin": "password",
- },
-})
-```
-
-```html
-
-
-
-
- {{.Title}} - My App
-
-
-
- {{.MyMessage}}
-
-
-
-```
-
-```html
-
-
-
-
- {{.}}' Portfolio - My App
-
-
-
- Hello {{.}}
-
-
-
-```
-
-> 戳[_examples/view](_examples/#view) 可以找到更多关于layouts,tmpl,routing的例子
-
-
-#### 程序入口
-
-程序入口可以将任何组件包含进来
-
-```go
-// file: main.go
-
-package main
-
-import (
- "github.com/kataras/iris/_examples/mvc/overview/datasource"
- "github.com/kataras/iris/_examples/mvc/overview/repositories"
- "github.com/kataras/iris/_examples/mvc/overview/services"
- "github.com/kataras/iris/_examples/mvc/overview/web/controllers"
- "github.com/kataras/iris/_examples/mvc/overview/web/middleware"
-
- "github.com/kataras/iris"
-)
-
-func main() {
- app := iris.New()
-
- // 加载模板文件
- app.RegisterView(iris.HTML("./web/views", ".html"))
-
- // 注册控制器
- app.Controller("/hello", new(controllers.HelloController))
-
- // 创建movie 数据仓库,次仓库包含的是内存级的数据源
- repo := repositories.NewMovieRepository(datasource.Movies)
- // 创建movie服务, 然后将其与控制器绑定
- movieService := services.NewMovieService(repo)
-
- app.Controller("/movies", new(controllers.MovieController),
- // 将"movieService"绑定在 MovieController的Service接口
- movieService,
- // 为/movies请求添加basic authentication(admin:password)中间件
- middleware.BasicAuth)
-
- // 启动应用localhost:8080
- // http://localhost:8080/hello
- // http://localhost:8080/hello/iris
- // http://localhost:8080/movies
- // http://localhost:8080/movies/1
- app.Run(
- iris.Addr("localhost:8080"),
- iris.WithoutVersionChecker,
- iris.WithoutServerError(iris.ErrServerClosed),
- iris.WithOptimizations, // 可以启用快速json序列化等优化配置
- )
-}
-```
-
-更多指南戳 [_examples/#structuring](_examples/#structuring)
## 现在你已经准备好进入下一阶段,又向专家级gopher迈进一步了
diff --git a/_examples/mvc/README.md b/_examples/mvc/README.md
index a47ef736..e898b22e 100644
--- a/_examples/mvc/README.md
+++ b/_examples/mvc/README.md
@@ -133,872 +133,13 @@ By creating components that are independent of one another, developers are able
If you're new to back-end web development read about the MVC architectural pattern first, a good start is that [wikipedia article](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller).
-## Quick MVC Tutorial Part 1 (without output result)
+## Examples
-```go
-package main
+- [Hello world](mvc/hello-world/main.go) **UPDATED**
+- [Session Controller](mvc/session-controller/main.go) **UPDATED**
+- [Overview - Plus Repository and Service layers](mvc/overview) **UPDATED**
+- [Login showcase - Plus Repository and Service layers](mvc/login) **UPDATED**
+- [Singleton](mvc/singleton) **NEW**
+- [Websocket Controller](mvc/websocket) **NEW**
-import (
- "github.com/kataras/iris"
- "github.com/kataras/iris/mvc"
-)
-
-func main() {
- app := iris.New()
-
- app.Controller("/helloworld", new(HelloWorldController))
-
- app.Run(iris.Addr("localhost:8080"))
-}
-
-type HelloWorldController struct {
- mvc.C
-
- // [ Your fields here ]
- // Request lifecycle data
- // Models
- // Database
- // Global properties
-}
-
-//
-// GET: /helloworld
-
-func (c *HelloWorldController) Get() string {
- return "This is my default action..."
-}
-
-//
-// GET: /helloworld/{name:string}
-
-func (c *HelloWorldController) GetBy(name string) string {
- return "Hello " + name
-}
-
-//
-// GET: /helloworld/welcome
-
-func (c *HelloWorldController) GetWelcome() (string, int) {
- return "This is the GetWelcome action func...", iris.StatusOK
-}
-
-//
-// GET: /helloworld/welcome/{name:string}/{numTimes:int}
-
-func (c *HelloWorldController) GetWelcomeBy(name string, numTimes int) {
- // Access to the low-level Context,
- // output arguments are optional of course so we don't have to use them here.
- c.Ctx.Writef("Hello %s, NumTimes is: %d", name, numTimes)
-}
-
-/*
-func (c *HelloWorldController) Post() {} handles HTTP POST method requests
-func (c *HelloWorldController) Put() {} handles HTTP PUT method requests
-func (c *HelloWorldController) Delete() {} handles HTTP DELETE method requests
-func (c *HelloWorldController) Connect() {} handles HTTP CONNECT method requests
-func (c *HelloWorldController) Head() {} handles HTTP HEAD method requests
-func (c *HelloWorldController) Patch() {} handles HTTP PATCH method requests
-func (c *HelloWorldController) Options() {} handles HTTP OPTIONS method requests
-func (c *HelloWorldController) Trace() {} handles HTTP TRACE method requests
-*/
-
-/*
-func (c *HelloWorldController) All() {} handles All method requests
-// OR
-func (c *HelloWorldController) Any() {} handles All method requests
-*/
-```
-
-> The [_examples/mvc](https://github.com/kataras/iris/tree/master/_examples/mvc) and [mvc/controller_test.go](https://github.com/kataras/iris/blob/master/mvc/controller_test.go) files explain each feature with simple paradigms, they show how you can take advantage of the Iris MVC Binder, Iris MVC Models and many more...
-
-Every `exported` func prefixed with an HTTP Method(`Get`, `Post`, `Put`, `Delete`...) in a controller is callable as an HTTP endpoint. In the sample above, all funcs writes a string to the response. Note the comments preceding each method.
-
-An HTTP endpoint is a targetable URL in the web application, such as `http://localhost:8080/helloworld`, and combines the protocol used: HTTP, the network location of the web server (including the TCP port): `localhost:8080` and the target URI `/helloworld`.
-
-The first comment states this is an [HTTP GET](https://www.w3schools.com/tags/ref_httpmethods.asp) method that is invoked by appending "/helloworld" to the base URL. The third comment specifies an [HTTP GET](https://www.w3schools.com/tags/ref_httpmethods.asp) method that is invoked by appending "/helloworld/welcome" to the URL.
-
-Controller knows how to handle the "name" on `GetBy` or the "name" and "numTimes" at `GetWelcomeBy`, because of the `By` keyword, and builds the dynamic route without boilerplate; the third comment specifies an [HTTP GET](https://www.w3schools.com/tags/ref_httpmethods.asp) dynamic method that is invoked by any URL that starts with "/helloworld/welcome" and followed by two more path parts, the first one can accept any value and the second can accept only numbers, i,e: "http://localhost:8080/helloworld/welcome/golang/32719", otherwise a [404 Not Found HTTP Error](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) will be sent to the client instead.
-
-----
-
-### Quick MVC Tutorial #2
-
-Iris has a very powerful and **blazing [fast](_benchmarks)** MVC support, you can return any value of any type from a method function
-and it will be sent to the client as expected.
-
-* if `string` then it's the body.
-* if `string` is the second output argument then it's the content type.
-* if `int` then it's the status code.
-* if `error` and not nil then (any type) response will be omitted and error's text with a 400 bad request will be rendered instead.
-* if `(int, error)` and error is not nil then the response result will be the error's text with the status code as `int`.
-* if `bool` is false then it throws 404 not found http error by skipping everything else.
-* if `custom struct` or `interface{}` or `slice` or `map` then it will be rendered as json, unless a `string` content type is following.
-* if `mvc.Result` then it executes its `Dispatch` function, so good design patters can be used to split the model's logic where needed.
-
-The example below is not intended to be used in production but it's a good showcase of some of the return types we saw before;
-
-```go
-package main
-
-import (
- "github.com/kataras/iris"
- "github.com/kataras/iris/middleware/basicauth"
- "github.com/kataras/iris/mvc"
-)
-
-// Movie is our sample data structure.
-type Movie struct {
- Name string `json:"name"`
- Year int `json:"year"`
- Genre string `json:"genre"`
- Poster string `json:"poster"`
-}
-
-// movies contains our imaginary data source.
-var movies = []Movie{
- {
- Name: "Casablanca",
- Year: 1942,
- Genre: "Romance",
- Poster: "https://iris-go.com/images/examples/mvc-movies/1.jpg",
- },
- {
- Name: "Gone with the Wind",
- Year: 1939,
- Genre: "Romance",
- Poster: "https://iris-go.com/images/examples/mvc-movies/2.jpg",
- },
- {
- Name: "Citizen Kane",
- Year: 1941,
- Genre: "Mystery",
- Poster: "https://iris-go.com/images/examples/mvc-movies/3.jpg",
- },
- {
- Name: "The Wizard of Oz",
- Year: 1939,
- Genre: "Fantasy",
- Poster: "https://iris-go.com/images/examples/mvc-movies/4.jpg",
- },
-}
-
-
-var basicAuth = basicauth.New(basicauth.Config{
- Users: map[string]string{
- "admin": "password",
- },
-})
-
-
-func main() {
- app := iris.New()
-
- app.Use(basicAuth)
-
- app.Controller("/movies", new(MoviesController))
-
- app.Run(iris.Addr(":8080"))
-}
-
-// MoviesController is our /movies controller.
-type MoviesController struct {
- mvc.C
-}
-
-// Get returns list of the movies
-// Demo:
-// curl -i http://localhost:8080/movies
-func (c *MoviesController) Get() []Movie {
- return movies
-}
-
-// GetBy returns a movie
-// Demo:
-// curl -i http://localhost:8080/movies/1
-func (c *MoviesController) GetBy(id int) Movie {
- return movies[id]
-}
-
-// 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 *MoviesController) PutBy(id int) Movie {
- // get the movie
- m := movies[id]
-
- // get the request data for poster and genre
- file, info, err := c.Ctx.FormFile("poster")
- if err != nil {
- c.Ctx.StatusCode(iris.StatusInternalServerError)
- return Movie{}
- }
- file.Close() // we don't need the file
- poster := info.Filename // imagine that as the url of the uploaded file...
- genre := c.Ctx.FormValue("genre")
-
- // update the poster
- m.Poster = poster
- m.Genre = genre
- movies[id] = m
-
- return m
-}
-
-// DeleteBy deletes a movie
-// Demo:
-// curl -i -X DELETE -u admin:password http://localhost:8080/movies/1
-func (c *MoviesController) DeleteBy(id int) iris.Map {
- // delete the entry from the movies slice
- deleted := movies[id].Name
- movies = append(movies[:id], movies[id+1:]...)
- // and return the deleted movie's name
- return iris.Map{"deleted": deleted}
-}
-```
-
-### Quick MVC Tutorial #3
-
-Nothing stops you from using your favorite **folder structure**. Iris is a low level web framework, it has got MVC first-class support but it doesn't limit your folder structure, this is your choice.
-
-Structuring depends on your own needs. We can't tell you how to design your own application for sure but you're free to take a closer look to one typical example below;
-
-[![folder structure example](_examples/mvc/overview/folder_structure.png)](_examples/mvc/overview)
-
-Shhh, let's spread the code itself.
-
-#### Data Model Layer
-
-```go
-// 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"`
-}
-```
-
-#### Data Source / Data Store Layer
-
-```go
-// file: datasource/movies.go
-
-package datasource
-
-import "github.com/kataras/iris/_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",
- },
-}
-```
-
-#### Repositories
-
-The layer which has direct access to the "datasource" and can manipulate data directly.
-
-```go
-// file: repositories/movie_repository.go
-
-package repositories
-
-import (
- "errors"
- "sync"
-
- "github.com/kataras/iris/_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)
-}
-```
-
-#### Services
-
-The layer which has access to call functions from the "repositories" and "models" (or even "datamodels" if simple application). It should contain the most of the domain logic.
-
-```go
-// file: services/movie_service.go
-
-package services
-
-import (
- "github.com/kataras/iris/_examples/mvc/overview/datamodels"
- "github.com/kataras/iris/_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)
-}
-```
-
-#### 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/_examples/mvc/overview/datamodels"
-
- "github.com/kataras/iris/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 context.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.
-
-#### Controllers
-
-Handles web requests, bridge between the services and the client.
-
-```go
-// file: web/controllers/movie_controller.go
-
-package controllers
-
-import (
- "errors"
-
- "github.com/kataras/iris/_examples/mvc/overview/datamodels"
- "github.com/kataras/iris/_examples/mvc/overview/services"
-
- "github.com/kataras/iris"
- "github.com/kataras/iris/mvc"
-)
-
-// MovieController is our /movies controller.
-type MovieController struct {
- mvc.C
-
- // 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(id int64) (datamodels.Movie, error) {
- // get the request data for poster and genre
- file, info, err := c.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 := c.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
-}
-```
-
-```go
-// file: web/controllers/hello_controller.go
-
-package controllers
-
-import (
- "errors"
-
- "github.com/kataras/iris/mvc"
-)
-
-// HelloController is our sample controller
-// it handles GET: /hello and GET: /hello/{name}
-type HelloController struct {
- mvc.C
-}
-
-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 built'n result type dispatchers
-// you can even create custom response dispatchers by
-// implementing the `github.com/kataras/iris/mvc#Result` interface.
-func (c *HelloController) Get() mvc.Result {
- return helloView
-}
-
-// you can define a standard error in order to be re-usable 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,
- }
-}
-```
-
-```go
-// file: web/middleware/basicauth.go
-
-package middleware
-
-import "github.com/kataras/iris/middleware/basicauth"
-
-// BasicAuth middleware sample.
-var BasicAuth = basicauth.New(basicauth.Config{
- Users: map[string]string{
- "admin": "password",
- },
-})
-```
-
-```html
-
-
-
-
- {{.Title}} - My App
-
-
-
- {{.MyMessage}}
-
-
-
-```
-
-```html
-
-
-
-
- {{.}}' Portfolio - My App
-
-
-
- Hello {{.}}
-
-
-
-```
-
-> Navigate to the [_examples/view](https://github.com/kataras/iris/tree/master/_examples/#view) for more examples
-like shared layouts, tmpl funcs, reverse routing and more!
-
-#### Main
-
-This file creates any necessary component and links them together.
-
-```go
-// file: main.go
-
-package main
-
-import (
- "github.com/kataras/iris/_examples/mvc/overview/datasource"
- "github.com/kataras/iris/_examples/mvc/overview/repositories"
- "github.com/kataras/iris/_examples/mvc/overview/services"
- "github.com/kataras/iris/_examples/mvc/overview/web/controllers"
- "github.com/kataras/iris/_examples/mvc/overview/web/middleware"
-
- "github.com/kataras/iris"
-)
-
-func main() {
- app := iris.New()
-
- // Load the template files.
- app.RegisterView(iris.HTML("./web/views", ".html"))
-
- // Register our controllers.
- app.Controller("/hello", new(controllers.HelloController))
-
- // 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 controller.
- movieService := services.NewMovieService(repo)
-
- app.Controller("/movies", new(controllers.MovieController),
- // Bind the "movieService" to the MovieController's Service (interface) field.
- movieService,
- // Add the basic authentication(admin:password) middleware
- // for the /movies based requests.
- middleware.BasicAuth)
-
- // Start the web server at localhost:8080
- // http://localhost:8080/hello
- // http://localhost:8080/hello/iris
- // http://localhost:8080/movies
- // http://localhost:8080/movies/1
- app.Run(
- iris.Addr("localhost:8080"),
- iris.WithoutVersionChecker,
- iris.WithoutServerError(iris.ErrServerClosed),
- iris.WithOptimizations, // enables faster json serialization and more
- )
-}
-```
-
-More folder structure guidelines can be found at the [_examples/#structuring](https://github.com/kataras/iris/tree/master/_examples/#structuring) section.
\ No newline at end of file
+Folder structure guidelines can be found at the [_examples/#structuring](https://github.com/kataras/iris/tree/master/_examples/#structuring) section.
\ No newline at end of file