1
0
mirror of https://github.com/kataras/iris.git synced 2026-01-09 13:05:56 +00:00

Remove big README examples, will be replaced with small quick examples so new users can found easier to get started

Former-commit-id: d5e804babf0edc0765a4195247071e410b27ea27
This commit is contained in:
Gerasimos (Makis) Maropoulos
2017-12-22 10:25:34 +02:00
parent ef3a09c126
commit b8b9643ca7
3 changed files with 8 additions and 2546 deletions

View File

@@ -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`方法可以识别路径namenumTimes参数因为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
<!-- file: web/views/hello/index.html -->
<html>
<head>
<title>{{.Title}} - My App</title>
</head>
<body>
<p>{{.MyMessage}}</p>
</body>
</html>
```
```html
<!-- file: web/views/hello/name.html -->
<html>
<head>
<title>{{.}}' Portfolio - My App</title>
</head>
<body>
<h1>Hello {{.}}</h1>
</body>
</html>
```
> 戳[_examples/view](_examples/#view) 可以找到更多关于layoutstmplrouting的例子
#### 程序入口
程序入口可以将任何组件包含进来
```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迈进一步了