[TOC]
  • hero overview小项目示例目录结构代码示例

hero overview小项目示例

目录结构

主目录overview

    —— datamodels        —— movie.go    —— datasource        —— movies.go    —— main.go    —— repositories        —— movie_repository.go    —— services        —— movie_service.go    —— web        —— middleware            —— basicauth.go        —— routes            —— hello.go            —— movies.go        —— views            —— hello                —— index.html                —— name.html

代码示例

main.go

//文件: main.gopackage mainimport (    "./datasource"    "./repositories"    "./services"    "./web/middleware"    "./web/routes"    "github.com/kataras/iris"    "github.com/kataras/iris/hero")func main() {    app := iris.New()    app.Logger().SetLevel("debug")    //加载模板文件    app.RegisterView(iris.HTML("./web/views", ".html"))    //获取仓库所电影资源    repo := repositories.NewMovieRepository(datasource.Movies)    //创建我们的电影服务,我们将它绑定到电影应用程序的依赖项。    movieService := services.NewMovieService(repo)    hero.Register(movieService)    //使用hero处理程序对应我们的路由    app.PartyFunc("/hello", func(r iris.Party) {        r.Get("/", hero.Handler(routes.Hello))        r.Get("/{name}", hero.Handler(routes.HelloName))    })    app.PartyFunc("/movies", func(r iris.Party) {        //添加基本basic authentication(admin:password)中间件用户/movies 请求        r.Use(middleware.BasicAuth)        r.Get("/", hero.Handler(routes.Movies))        r.Get("/{id:long}", hero.Handler(routes.MovieByID))        r.Put("/{id:long}", hero.Handler(routes.UpdateMovieByID))        r.Delete("/{id:long}", hero.Handler(routes.DeleteMovieByID))    })    // http://localhost:8080/hello    // http://localhost:8080/hello/iris    // http://localhost:8080/movies    // http://localhost:8080/movies/1    app.Run(        //在localhost:8080启动Web服务器        iris.Addr("localhost:8080"),        //按下CTRL/CMD+C时跳过错误的服务器:        iris.WithoutServerError(iris.ErrServerClosed),        //启用更快的json序列化和优化:        iris.WithOptimizations,    )}

/datamodels/movie.go

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

/datamodels/movie.go

//文件: datasource/movies.gopackage datasourceimport "../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",    },}

/repositories/movie_repository.go

//文件: repositories/movie_repository.gopackage repositoriesimport (    "errors"    "sync"    "../datamodels")// Query表示访问者和操作查询type Query func(datamodels.Movie) bool// MovieRepository处理电影实体/模型的基本操作。//它是一个可测试的接口,即一个内存电影库或连接到sql数据库。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返回一个新的基于电影内存的repository,//我们示例中唯一的repository类型。func NewMovieRepository(source map[int64]datamodels.Movie) MovieRepository {    return &movieMemoryRepository{source: source}}// movieMemoryRepository是一个MovieRepository//使用内存数据源(map)管理电影。type movieMemoryRepository struct {    source map[int64]datamodels.Movie    mu     sync.RWMutex}const (    // ReadOnlyMode将RLock(读取)数据。    ReadOnlyMode = iota    // ReadWriteMode将锁定(读/写)数据。    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接收查询函数//为内部的每个电影模型触发//我们想象中的数据源//当该函数返回true时,它会停止迭代//它实际上是一个简单但非常聪明的原型函数//自从我第一次想到它以来,我一直在使用它,//希望你会发现它也很有用。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,如果根本找不到的话。    if !found {        movie = datamodels.Movie{}    }    return}// SelectMany与Select相同但返回一个或多个datamodels.Movie作为切片。//如果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}// InsertOrUpdate将movie添加或更新到map中存储。//返回新电影,如果有则返回错误。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更新动作,    //这里我们将允许更新海报和流派,如果不是空的话。    //或者我们可以做替换:    // r.source [id] =movie    //向下面的代码一样;    current, exists := r.Select(func(m datamodels.Movie) bool {        return m.ID == id    })    if !exists { // ID不是真实的,返回错误。        return datamodels.Movie{}, errors.New("failed to update a nonexistent movie")    }    //或注释这些和r.source[id] = m进行纯替换    if movie.Poster != "" {        current.Poster = movie.Poster    }    if movie.Genre != "" {        current.Genre = movie.Genre    }    //锁定数据    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/movie_service.go

// 文件: services/movie_service.gopackage servicesimport (    "../datamodels"    "../repositories")//MovieService处理电影数据模型的一些CRUID操作。//这依赖于影片repository的方法。//这是将数据源与更高级别的组件分离。//因此,不同的repository类型可以使用相同的逻辑,而无需任何更改。//它是一个接口,它在任何地方都被用作接口//因为我们可能需要在将来更改或尝试实验性的不同域逻辑。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 servicefunc NewMovieService(repo repositories.MovieRepository) MovieService {    return &movieService{        repo: repo,    }}type movieService struct {    repo repositories.MovieRepository}// GetAll返回所有电影func (s *movieService) GetAll() []datamodels.Movie {    return s.repo.SelectMany(func(_ datamodels.Movie) bool {        return true    }, -1)}// GetByID根据其id返回一部电影func (s *movieService) GetByID(id int64) (datamodels.Movie, bool) {    return s.repo.Select(func(m datamodels.Movie) bool {        return m.ID == id    })}// UpdatePosterAndGenreByID更新电影的海报和流派。func (s *movieService) UpdatePosterAndGenreByID(id int64, poster string, genre string) (datamodels.Movie, error) {    //更新电影并将其返回。    return s.repo.InsertOrUpdate(datamodels.Movie{        ID:     id,        Poster: poster,        Genre:  genre,    })}// DeleteByID按ID删除电影。//如果删除则返回true,否则返回false。func (s *movieService) DeleteByID(id int64) bool {    return s.repo.Delete(func(m datamodels.Movie) bool {        return m.ID == id    }, 1)}

/web/middleware/basicauth.go

//文件: web/middleware/basicauth.gopackage middlewareimport "github.com/kataras/iris/middleware/basicauth"// BasicAuth中间件示例。var BasicAuth = basicauth.New(basicauth.Config{    Users: map[string]string{        "admin": "password",    },})

/web/routes/hello.go

// file: web/routes/hello.gopackage routesimport (    "errors"    "github.com/kataras/iris/hero")var helloView = hero.View{    Name: "hello/index.html",    Data: map[string]interface{}{        "Title":     "Hello Page",        "MyMessage": "Welcome to my awesome website",    },}// Hello将返回带有绑定数据的预定义视图。//`hero.Result`只是一个带有'Dispatch`功能的接口。//`hero.Response`和`hero.View`是内置的结果类型调度程序//你甚至可以创建自定义响应调度程序//实现`github.com/kataras/iris/hero#Result`接口。func Hello() hero.Result {    return helloView}//您可以定义标准错误,以便在应用中的任何位置重复使用。var errBadName = errors.New("bad name")//您可以将其作为错误返回,甚至其他更好类型的返回//用hero.Response包装此错误,使其成为hero.Result兼容类型。var badName = hero.Response{Err: errBadName, Code: 400}// HelloName 返回 "Hello {name}".// 例子:// curl -i http://localhost:8080/hello/iris// curl -i http://localhost:8080/hello/anythingfunc HelloName(name string) hero.Result {    if name != "iris" {        return badName    }    // 或者返回 hero.Response{Text: "Hello " + name}:    return hero.View{        Name: "hello/name.html",        Data: name,    }}

/web/routes/movies.go

// file: web/routes/movie.gopackage routesimport (    "errors"    "../../datamodels"    "../../services"    "github.com/kataras/iris")//Movies返回电影列表// 例子:// curl -i http://localhost:8080/moviesfunc Movies(service services.MovieService) (results []datamodels.Movie) {    return service.GetAll()}// MovieByID返回指定id的一部电影// 例子:// curl -i http://localhost:8080/movies/1func MovieByID(service services.MovieService, id int64) (movie datamodels.Movie, found bool) {    return service.GetByID(id) //如果未找到返回404.}// UpdateMovieByID更新电影。// 例子:// curl -i -X PUT -F "genre=Thriller" -F "poster=@/Users/kataras/Downloads/out.gif" http://localhost:8080/movies/1func UpdateMovieByID(ctx iris.Context, service services.MovieService, id int64) (datamodels.Movie, error) {    //获取海报和流派的请求数据    file, info, err := ctx.FormFile("poster")    if err != nil {        return datamodels.Movie{}, errors.New("failed due form file 'poster' missing")    }    //关闭文件。    file.Close()    //想象这是上传文件的网址...    poster := info.Filename    genre := ctx.FormValue("genre")    return service.UpdatePosterAndGenreByID(id, poster, genre)}// DeleteMovieByID删除电影。// 例子:// curl -i -X DELETE -u admin:password http://localhost:8080/movies/1func DeleteMovieByID(service services.MovieService, id int64) interface{} {    wasDel := service.DeleteByID(id)    if wasDel {        //返回已删除电影的ID        return iris.Map{"deleted": id}    }    //在这里我们可以看到方法函数可以返回这两种类型中的任何一种(map或int),    //我们不必将返回类型指定为特定类型。    return iris.StatusBadRequest}

/web/views/hello/index.html

<!-- 文件: web/views/hello/index.html --><html><head>    <title>{{.Title}} - My App</title></head><body>    <p>{{.MyMessage}}</p></body></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>