hero overview小项目示例

目录结构

主目录overview

  1. —— datamodels
  2. —— movie.go
  3. —— datasource
  4. —— movies.go
  5. —— main.go
  6. —— repositories
  7. —— movie_repository.go
  8. —— services
  9. —— movie_service.go
  10. —— web
  11. —— middleware
  12. —— basicauth.go
  13. —— routes
  14. —— hello.go
  15. —— movies.go
  16. —— views
  17. —— hello
  18. —— index.html
  19. —— name.html

代码示例

main.go

  1. //文件: main.go
  2. package main
  3. import (
  4. "./datasource"
  5. "./repositories"
  6. "./services"
  7. "./web/middleware"
  8. "./web/routes"
  9. "github.com/kataras/iris"
  10. "github.com/kataras/iris/hero"
  11. )
  12. func main() {
  13. app := iris.New()
  14. app.Logger().SetLevel("debug")
  15. //加载模板文件
  16. app.RegisterView(iris.HTML("./web/views", ".html"))
  17. //获取仓库所电影资源
  18. repo := repositories.NewMovieRepository(datasource.Movies)
  19. //创建我们的电影服务,我们将它绑定到电影应用程序的依赖项。
  20. movieService := services.NewMovieService(repo)
  21. hero.Register(movieService)
  22. //使用hero处理程序对应我们的路由
  23. app.PartyFunc("/hello", func(r iris.Party) {
  24. r.Get("/", hero.Handler(routes.Hello))
  25. r.Get("/{name}", hero.Handler(routes.HelloName))
  26. })
  27. app.PartyFunc("/movies", func(r iris.Party) {
  28. //添加基本basic authentication(admin:password)中间件用户/movies 请求
  29. r.Use(middleware.BasicAuth)
  30. r.Get("/", hero.Handler(routes.Movies))
  31. r.Get("/{id:long}", hero.Handler(routes.MovieByID))
  32. r.Put("/{id:long}", hero.Handler(routes.UpdateMovieByID))
  33. r.Delete("/{id:long}", hero.Handler(routes.DeleteMovieByID))
  34. })
  35. // http://localhost:8080/hello
  36. // http://localhost:8080/hello/iris
  37. // http://localhost:8080/movies
  38. // http://localhost:8080/movies/1
  39. app.Run(
  40. //在localhost:8080启动Web服务器
  41. iris.Addr("localhost:8080"),
  42. //按下CTRL/CMD+C时跳过错误的服务器:
  43. iris.WithoutServerError(iris.ErrServerClosed),
  44. //启用更快的json序列化和优化:
  45. iris.WithOptimizations,
  46. )
  47. }

/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. }

/datamodels/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/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/routes/hello.go

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

/web/routes/movies.go

  1. // file: web/routes/movie.go
  2. package routes
  3. import (
  4. "errors"
  5. "../../datamodels"
  6. "../../services"
  7. "github.com/kataras/iris"
  8. )
  9. //Movies返回电影列表
  10. // 例子:
  11. // curl -i http://localhost:8080/movies
  12. func Movies(service services.MovieService) (results []datamodels.Movie) {
  13. return service.GetAll()
  14. }
  15. // MovieByID返回指定id的一部电影
  16. // 例子:
  17. // curl -i http://localhost:8080/movies/1
  18. func MovieByID(service services.MovieService, id int64) (movie datamodels.Movie, found bool) {
  19. return service.GetByID(id) //如果未找到返回404.
  20. }
  21. // UpdateMovieByID更新电影。
  22. // 例子:
  23. // curl -i -X PUT -F "genre=Thriller" -F "poster=@/Users/kataras/Downloads/out.gif" http://localhost:8080/movies/1
  24. func UpdateMovieByID(ctx iris.Context, service services.MovieService, id int64) (datamodels.Movie, error) {
  25. //获取海报和流派的请求数据
  26. file, info, err := ctx.FormFile("poster")
  27. if err != nil {
  28. return datamodels.Movie{}, errors.New("failed due form file 'poster' missing")
  29. }
  30. //关闭文件。
  31. file.Close()
  32. //想象这是上传文件的网址...
  33. poster := info.Filename
  34. genre := ctx.FormValue("genre")
  35. return service.UpdatePosterAndGenreByID(id, poster, genre)
  36. }
  37. // DeleteMovieByID删除电影。
  38. // 例子:
  39. // curl -i -X DELETE -u admin:password http://localhost:8080/movies/1
  40. func DeleteMovieByID(service services.MovieService, id int64) interface{} {
  41. wasDel := service.DeleteByID(id)
  42. if wasDel {
  43. //返回已删除电影的ID
  44. return iris.Map{"deleted": id}
  45. }
  46. //在这里我们可以看到方法函数可以返回这两种类型中的任何一种(map或int),
  47. //我们不必将返回类型指定为特定类型。
  48. return iris.StatusBadRequest
  49. }

/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/hello/name.html


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