IRIS MVC overview示例

目录结构

主目录overview

  1. —— datamodels
  2. —— movie.go
  3. —— datasource
  4. —— movie.go
  5. —— main.go
  6. —— repositories
  7. —— movie_repository.go
  8. —— services
  9. —— movie_service.go
  10. —— web
  11. —— controllers
  12. —— hello_controller.go
  13. —— movie_controller.go
  14. —— middleware
  15. —— basicauth.go
  16. —— viewmodels
  17. —— README.md
  18. —— views
  19. —— hello
  20. —— index.html
  21. —— name.html

代码示例

main.go

  1. //文件:main.go
  2. package main
  3. import (
  4. "./datasource"
  5. "./repositories"
  6. "./services"
  7. "./web/controllers"
  8. "./web/middleware"
  9. "github.com/kataras/iris"
  10. "github.com/kataras/iris/mvc"
  11. )
  12. func main() {
  13. app := iris.New()
  14. app.Logger().SetLevel("debug")
  15. //加载模板文件
  16. app.RegisterView(iris.HTML("./web/views", ".html"))
  17. //服务控制器
  18. mvc.New(app.Party("/hello")).Handle(new(controllers.HelloController))
  19. //您还可以拆分您编写的代码以配置mvc.Application
  20. //使用`mvc.Configure`方法,如下所示。
  21. mvc.Configure(app.Party("/movies"), movies)
  22. // http://localhost:8080/hello
  23. // http://localhost:8080/hello/iris
  24. // http://localhost:8080/movies
  25. // http://localhost:8080/movies/1
  26. app.Run(
  27. //在localhost:8080启动Web服务器
  28. iris.Addr("localhost:8080"),
  29. //按下CTRL/CMD+C时跳过错误的服务器:
  30. iris.WithoutServerError(iris.ErrServerClosed),
  31. //启用更快的json序列化和优化:
  32. iris.WithOptimizations,
  33. )
  34. }
  35. //注意mvc.Application,它不是iris.Application。
  36. func movies(app *mvc.Application) {
  37. //添加基本身份验证(admin:password)中间件
  38. //用于基于/movies的请求。
  39. app.Router.Use(middleware.BasicAuth)
  40. //使用数据源中的一些(内存)数据创建我们的电影资源库。
  41. repo := repositories.NewMovieRepository(datasource.Movies)
  42. //创建我们的电影服务,我们将它绑定到电影应用程序的依赖项中
  43. movieService := services.NewMovieService(repo)
  44. app.Register(movieService)
  45. //为我们的电影控制器服务
  46. //请注意,您可以为多个控制器提供服务
  47. //你也可以使用`movies.Party(relativePath)`或`movies.Clone(app.Party(...))创建子mvc应用程序
  48. // 如果你想。
  49. app.Handle(new(controllers.MovieController))
  50. }

/datamodels/movie.go

  1. //文件: datamodels/movie.go
  2. package datamodels
  3. //Movie是我们的示例数据结构。
  4. //注意结构体字段可导出
  5. //应该保存在其他文件中,例如web/viewmodels/movie.go
  6. //可以通过嵌入datamodels.Movie或 声明新字段但我们将使用此数据模型
  7. //作为我们应用程序中唯一的一个Movie模型,为了不冲突。
  8. type Movie struct {
  9. ID int64 `json:"id"`
  10. Name string `json:"name"`
  11. Year int `json:"year"`
  12. Genre string `json:"genre"`
  13. Poster string `json:"poster"`
  14. }

/datasource/movie.go

  1. //文件: datasource/movies.go
  2. package datasource
  3. import "../datamodels"
  4. //Movies是我们想象中的数据源。
  5. var Movies = map[int64]datamodels.Movie{
  6. 1: {
  7. ID: 1,
  8. Name: "Casablanca",
  9. Year: 1942,
  10. Genre: "Romance",
  11. Poster: "https://iris-go.com/images/examples/mvc-movies/1.jpg",
  12. },
  13. 2: {
  14. ID: 2,
  15. Name: "Gone with the Wind",
  16. Year: 1939,
  17. Genre: "Romance",
  18. Poster: "https://iris-go.com/images/examples/mvc-movies/2.jpg",
  19. },
  20. 3: {
  21. ID: 3,
  22. Name: "Citizen Kane",
  23. Year: 1941,
  24. Genre: "Mystery",
  25. Poster: "https://iris-go.com/images/examples/mvc-movies/3.jpg",
  26. },
  27. 4: {
  28. ID: 4,
  29. Name: "The Wizard of Oz",
  30. Year: 1939,
  31. Genre: "Fantasy",
  32. Poster: "https://iris-go.com/images/examples/mvc-movies/4.jpg",
  33. },
  34. 5: {
  35. ID: 5,
  36. Name: "North by Northwest",
  37. Year: 1959,
  38. Genre: "Thriller",
  39. Poster: "https://iris-go.com/images/examples/mvc-movies/5.jpg",
  40. },
  41. }

/repositories/movie_repository.go

  1. //文件: repositories/movie_repository.go
  2. package repositories
  3. import (
  4. "errors"
  5. "sync"
  6. "../datamodels"
  7. )
  8. // Query表示访问者和操作查询
  9. type Query func(datamodels.Movie) bool
  10. // MovieRepository处理电影实体/模型的基本操作。
  11. //它是一个可测试的接口,即一个内存电影库或连接到sql数据库。
  12. type MovieRepository interface {
  13. Exec(query Query, action Query, limit int, mode int) (ok bool)
  14. Select(query Query) (movie datamodels.Movie, found bool)
  15. SelectMany(query Query, limit int) (results []datamodels.Movie)
  16. InsertOrUpdate(movie datamodels.Movie) (updatedMovie datamodels.Movie, err error)
  17. Delete(query Query, limit int) (deleted bool)
  18. }
  19. //NewMovieRepository返回一个新的基于电影内存的repository,
  20. //我们示例中唯一的repository类型。
  21. func NewMovieRepository(source map[int64]datamodels.Movie) MovieRepository {
  22. return &movieMemoryRepository{source: source}
  23. }
  24. // movieMemoryRepository是一个MovieRepository
  25. //使用内存数据源(map)管理电影。
  26. type movieMemoryRepository struct {
  27. source map[int64]datamodels.Movie
  28. mu sync.RWMutex
  29. }
  30. const (
  31. // ReadOnlyMode将RLock(读取)数据。
  32. ReadOnlyMode = iota
  33. // ReadWriteMode将锁定(读/写)数据。
  34. ReadWriteMode
  35. )
  36. func (r *movieMemoryRepository) Exec(query Query, action Query, actionLimit int, mode int) (ok bool) {
  37. loops := 0
  38. if mode == ReadOnlyMode {
  39. r.mu.RLock()
  40. defer r.mu.RUnlock()
  41. } else {
  42. r.mu.Lock()
  43. defer r.mu.Unlock()
  44. }
  45. for _, movie := range r.source {
  46. ok = query(movie)
  47. if ok {
  48. if action(movie) {
  49. loops++
  50. if actionLimit >= loops {
  51. break // break
  52. }
  53. }
  54. }
  55. }
  56. return
  57. }
  58. //Select接收查询函数
  59. //为内部的每个电影模型触发
  60. //我们想象中的数据源
  61. //当该函数返回true时,它会停止迭代
  62. //它实际上是一个简单但非常聪明的原型函数
  63. //自从我第一次想到它以来,我一直在使用它,
  64. //希望你会发现它也很有用。
  65. func (r *movieMemoryRepository) Select(query Query) (movie datamodels.Movie, found bool) {
  66. found = r.Exec(query, func(m datamodels.Movie) bool {
  67. movie = m
  68. return true
  69. }, 1, ReadOnlyMode)
  70. //设置一个空的datamodels.Movie,如果根本找不到的话。
  71. if !found {
  72. movie = datamodels.Movie{}
  73. }
  74. return
  75. }
  76. // SelectMany与Select相同但返回一个或多个datamodels.Movie作为切片。
  77. //如果limit <= 0则返回所有内容。
  78. func (r *movieMemoryRepository) SelectMany(query Query, limit int) (results []datamodels.Movie) {
  79. r.Exec(query, func(m datamodels.Movie) bool {
  80. results = append(results, m)
  81. return true
  82. }, limit, ReadOnlyMode)
  83. return
  84. }
  85. // InsertOrUpdate将movie添加或更新到map中存储。
  86. //返回新电影,如果有则返回错误。
  87. func (r *movieMemoryRepository) InsertOrUpdate(movie datamodels.Movie) (datamodels.Movie, error) {
  88. id := movie.ID
  89. if id == 0 { // Create new action //创建新记录
  90. var lastID int64
  91. //找到最大的ID,以便不重复
  92. //在制作应用中,您可以使用第三方
  93. //库以生成UUID作为字符串。
  94. r.mu.RLock()
  95. for _, item := range r.source {
  96. if item.ID > lastID {
  97. lastID = item.ID
  98. }
  99. }
  100. r.mu.RUnlock()
  101. id = lastID + 1
  102. movie.ID = id
  103. // map-specific thing
  104. r.mu.Lock()
  105. r.source[id] = movie
  106. r.mu.Unlock()
  107. return movie, nil
  108. }
  109. //基于movie.ID更新动作,
  110. //这里我们将允许更新海报和流派,如果不是空的话。
  111. //或者我们可以做替换:
  112. // r.source [id] =movie
  113. //向下面的代码一样;
  114. current, exists := r.Select(func(m datamodels.Movie) bool {
  115. return m.ID == id
  116. })
  117. if !exists { // ID不是真实的,返回错误。
  118. return datamodels.Movie{}, errors.New("failed to update a nonexistent movie")
  119. }
  120. //或注释这些和r.source[id] = m进行纯替换
  121. if movie.Poster != "" {
  122. current.Poster = movie.Poster
  123. }
  124. if movie.Genre != "" {
  125. current.Genre = movie.Genre
  126. }
  127. //锁定数据
  128. r.mu.Lock()
  129. r.source[id] = current
  130. r.mu.Unlock()
  131. return movie, nil
  132. }
  133. func (r *movieMemoryRepository) Delete(query Query, limit int) bool {
  134. return r.Exec(query, func(m datamodels.Movie) bool {
  135. delete(r.source, m.ID)
  136. return true
  137. }, limit, ReadWriteMode)
  138. }

/services/movie_service.go

  1. // 文件: services/movie_service.go
  2. package services
  3. import (
  4. "../datamodels"
  5. "../repositories"
  6. )
  7. //MovieService处理电影数据模型的一些CRUID操作。
  8. //这依赖于影片repository的方法。
  9. //这是将数据源与更高级别的组件分离。
  10. //因此,不同的repository类型可以使用相同的逻辑,而无需任何更改。
  11. //它是一个接口,它在任何地方都被用作接口
  12. //因为我们可能需要在将来更改或尝试实验性的不同域逻辑。
  13. type MovieService interface {
  14. GetAll() []datamodels.Movie
  15. GetByID(id int64) (datamodels.Movie, bool)
  16. DeleteByID(id int64) bool
  17. UpdatePosterAndGenreByID(id int64, poster string, genre string) (datamodels.Movie, error)
  18. }
  19. // NewMovieService返回默认的movie service
  20. func NewMovieService(repo repositories.MovieRepository) MovieService {
  21. return &movieService{
  22. repo: repo,
  23. }
  24. }
  25. type movieService struct {
  26. repo repositories.MovieRepository
  27. }
  28. // GetAll返回所有电影
  29. func (s *movieService) GetAll() []datamodels.Movie {
  30. return s.repo.SelectMany(func(_ datamodels.Movie) bool {
  31. return true
  32. }, -1)
  33. }
  34. // GetByID根据其id返回一部电影
  35. func (s *movieService) GetByID(id int64) (datamodels.Movie, bool) {
  36. return s.repo.Select(func(m datamodels.Movie) bool {
  37. return m.ID == id
  38. })
  39. }
  40. // UpdatePosterAndGenreByID更新电影的海报和流派。
  41. func (s *movieService) UpdatePosterAndGenreByID(id int64, poster string, genre string) (datamodels.Movie, error) {
  42. //更新电影并将其返回。
  43. return s.repo.InsertOrUpdate(datamodels.Movie{
  44. ID: id,
  45. Poster: poster,
  46. Genre: genre,
  47. })
  48. }
  49. // DeleteByID按ID删除电影。
  50. //如果删除则返回true,否则返回false。
  51. func (s *movieService) DeleteByID(id int64) bool {
  52. return s.repo.Delete(func(m datamodels.Movie) bool {
  53. return m.ID == id
  54. }, 1)
  55. }

/web/controllers/hello_controller.go

  1. //文件: web/controllers/hello_controller.go
  2. package controllers
  3. import (
  4. "errors"
  5. "github.com/kataras/iris/mvc"
  6. )
  7. // HelloController是我们的示例控制器
  8. //它处理 GET:/hello和GET:/hello/{name}
  9. type HelloController struct{}
  10. var helloView = mvc.View{
  11. Name: "hello/index.html",
  12. Data: map[string]interface{}{
  13. "Title": "Hello Page",
  14. "MyMessage": "Welcome to my awesome website",
  15. },
  16. }
  17. //Get将返回带有绑定数据的预定义视图
  18. //`mvc.Result`只是一个带有'Dispatch`功能的接口。
  19. //`mvc.Response`和`mvc.View`是内置的结果类型调度程序
  20. //你甚至可以创建自定义响应调度程序
  21. //实现`github.com/kataras/iris/hero#Result`接口。
  22. func (c *HelloController) Get() mvc.Result {
  23. return helloView
  24. }
  25. //您可以定义标准错误,以便在应用中的任何位置重复使用
  26. var errBadName = errors.New("bad name")
  27. //您可以将其作为错误返回
  28. //使用mvc.Response包装此错误,使其成为mvc.Result兼容类型。
  29. var badName = mvc.Response{Err: errBadName, Code: 400}
  30. // GetBy 返回 "Hello {name}" response.
  31. // 示例:
  32. // curl -i http://localhost:8080/hello/iris
  33. // curl -i http://localhost:8080/hello/anything
  34. func (c *HelloController) GetBy(name string) mvc.Result {
  35. if name != "iris" {
  36. return badName
  37. // 或
  38. // GetBy(name string) (mvc.Result, error) {
  39. // return nil, errBadName
  40. // }
  41. }
  42. // return mvc.Response{Text: "Hello " + name} 或:
  43. return mvc.View{
  44. Name: "hello/name.html",
  45. Data: name,
  46. }
  47. }

/web/controllers/movie_controller.go

  1. // 文件: web/controllers/movie_controller.go
  2. package controllers
  3. import (
  4. "errors"
  5. "../../datamodels"
  6. "../../services"
  7. "github.com/kataras/iris"
  8. )
  9. // MovieController是我们的/movies controller。
  10. type MovieController struct {
  11. //我们的MovieService,它是一个接口,从主应用程序绑定。
  12. Service services.MovieService
  13. }
  14. //获取电影的返回列表。
  15. // 示例:
  16. // curl -i http://localhost:8080/movies
  17. //
  18. // func (c *MovieController) Get() (results []viewmodels.Movie) {
  19. // data := c.Service.GetAll()
  20. // for _, movie := range data {
  21. // results = append(results, viewmodels.Movie{movie})
  22. // }
  23. // return
  24. // }
  25. //否则只返回数据模型
  26. func (c *MovieController) Get() (results []datamodels.Movie) {
  27. return c.Service.GetAll()
  28. }
  29. // MovieByID返回指定id的一部电影
  30. // 例子:
  31. // curl -i http://localhost:8080/movies/1
  32. func (c *MovieController) GetBy(id int64) (movie datamodels.Movie, found bool) {
  33. return c.Service.GetByID(id) // it will throw 404 if not found.
  34. }
  35. // PutBy 更新指点电影数据.
  36. // 例子:
  37. // curl -i -X PUT -F "genre=Thriller" -F "poster=@/Users/kataras/Downloads/out.gif" http://localhost:8080/movies/1
  38. func (c *MovieController) PutBy(ctx iris.Context, id int64) (datamodels.Movie, error) {
  39. // get the request data for poster and genre
  40. file, info, err := ctx.FormFile("poster")
  41. if err != nil {
  42. return datamodels.Movie{}, errors.New("failed due form file 'poster' missing")
  43. }
  44. //关闭文件。
  45. file.Close()
  46. //想象这是上传文件的网址...
  47. poster := info.Filename
  48. genre := ctx.FormValue("genre")
  49. return c.Service.UpdatePosterAndGenreByID(id, poster, genre)
  50. }
  51. // DeleteBy删除电影。
  52. // 例子:
  53. // curl -i -X DELETE -u admin:password http://localhost:8080/movies/1
  54. func (c *MovieController) DeleteBy(id int64) interface{} {
  55. wasDel := c.Service.DeleteByID(id)
  56. if wasDel {
  57. //返回已删除电影的ID
  58. return iris.Map{"deleted": id}
  59. }
  60. //在这里我们可以看到方法函数可以返回这两种类型中的任何一种(map或int),
  61. //我们不必将返回类型指定为特定类型。
  62. return iris.StatusBadRequest
  63. }

/web/middleware/basicauth.go

  1. //文件: web/middleware/basicauth.go
  2. package middleware
  3. import "github.com/kataras/iris/middleware/basicauth"
  4. // BasicAuth中间件示例。
  5. var BasicAuth = basicauth.New(basicauth.Config{
  6. Users: map[string]string{
  7. "admin": "password",
  8. },
  9. })

/web/views/hello/index.html

  1. <!-- 文件: web/views/hello/index.html -->
  2. <html>
  3. <head>
  4. <title>{{.Title}} - My App</title>
  5. </head>
  6. <body>
  7. <p>{{.MyMessage}}</p>
  8. </body>
  9. </html>

/web/views/name.go


<!-- 文件: web/views/hello/name.html -->
<html>
<head>
    <title>{{.}}' Portfolio - My App</title>
</head>
<body>
    <h1>Hello {{.}}</h1>
</body>
</html>

MVC目录结构