diff --git a/README_CN.md b/README_CN.md new file mode 100644 index 00000000..a4718c1e --- /dev/null +++ b/README_CN.md @@ -0,0 +1,1057 @@ +# [![Logo created by @santoshanand](logo_white_35_24.png)](https://iris-go.com) Iris + +[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=flat-square)](https://travis-ci.org/kataras/iris)[![Backers on Open Collective](https://opencollective.com/iris/backers/badge.svg?style=flat-square)](#backers)[![Sponsors on Open Collective](https://opencollective.com/iris/sponsors/badge.svg?style=flat-square)](#sponsors)[![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=flat-square)](http://goreportcard.com/report/kataras/iris)[![github closed issues](https://img.shields.io/github/issues-closed-raw/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/issues?q=is%3Aissue+is%3Aclosed)[![release](https://img.shields.io/github/release/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/releases)[![view examples](https://img.shields.io/badge/learn%20by-examples-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/tree/master/_examples)[![chat](https://img.shields.io/badge/community-%20chat-00BCD4.svg?style=flat-square)](https://kataras.rocket.chat/channel/iris)[![CLA assistant](https://cla-assistant.io/readme/badge/kataras/iris?style=flat-square)](https://cla-assistant.io/kataras/iris) + + + Sponsor + + + +Iris是一个超快、简单并且高效的Go语言Web开发框架。 + +Iris功能很强大,使用又很简单,它将会是你下一个网站、API服务或者分布式应用基础框架的不二之选。 + +看看[别人是如何评价Iris](https://www.youtube.com/watch?v=jGx0LkuUs4A),同时欢迎各位[成为Iris星探](https://github.com/kataras/iris/stargazers),或者关注[Iris facebook主页](https://facebook.com/iris.framework)。 + +[![Iris vs .NET Core(C#) vs Node.js (Express)](https://iris-go.com/images/benchmark-new-gray.png)](_benchmarks) + +
+上图是第三发机构发布的REST Web框架的基准测试 + +![Comparison with other frameworks](https://raw.githubusercontent.com/smallnest/go-web-framework-benchmark/4db507a22c964c9bc9774c5b31afdc199a0fe8b7/benchmark.png) + +更新于: [2017年9月29,星期五](_benchmarks)_ +
+ +## 前言 ♥️ + +在发现Iris之前,我想你一定也看过其它Go Web开发框架。也许你已经摩拳擦掌并马上要使用了,但我会很遗憾的告诉你,将来你还是会使用Iris的。不仅仅因为Iris性能卓越和使用简单,更重要的是Iris独树一帜,他可以让你成为真正的极客界摇滚明星。 + +不管你是想开发微服务或者大型Web应用,Iris都能满足你的需求。Iris可能是你在网上能找到最好的Web后台开发软件之一了。 + +Iris现在已经到第8版了,但是我们从未停止开发。有很多非常棒的功能已经提上开发日程了,而且我们非常乐意加入很多有创意的想法。 + +如果你想用CDN加速,我推荐用[KeyCDN](https://www.keycdn.com/),因为KeyCDN简单、速度快而且稳定。 + +我们用[微软](https://www.microsoft.com)开发的[Visual Studio Code](https://code.visualstudio.com/)来开发Golang应用。 + +如果你之前使用[nodejs](https://nodejs.org)做开发,恭喜你,Iris使用基本和[expressjs](https://github.com/expressjs/express)一样。 + +## 内容列表 + +* [安装](#installation) +* [最近更新](https://github.com/kataras/iris/blob/master/HISTORY.md#tu-07-november-2017--v857) +* [快速入门](#getting-started) +* [进阶](_examples/) + * [MVC (模型 视图 控制器)](_examples/#mvc) **NEW** + * [结构](_examples/#structuring) **NEW** + * [HTTP 监听](_examples/#http-listening) + * [配置](_examples/#configuration) + * [路由,分组,动态参数,“宏定义”已经自定义Context](_examples/#routing-grouping-dynamic-path-parameters-macros-and-custom-context) + * [子域名处理](_examples/#subdomains) + * [`http.Handler/HandlerFunc` 使用](_examples/#convert-httphandlerhandlerfunc) + * [视图处理](_examples/#view) + * [认证](_examples/#authentication) + * [文件服务器](_examples/#file-server) + * [如何从`context.Request() *http.Request` 读数据](_examples/#how-to-read-from-contextrequest-httprequest) + * [如何给`context.ResponseWriter() http.ResponseWriter`写数据](_examples/#how-to-write-to-contextresponsewriter-httpresponsewriter) + * [测试](_examples/#testing) + * [缓存](_examples/#caching) + * [会话](_examples/#sessions) + * [Websockets](_examples/#websockets) + * [其它杂项](_examples/#miscellaneous) + * [将"Parrot"项目转换为Iris实现](https://github.com/iris-contrib/parrot) + * [Iris和react/hot reloadable/redux/css-modules配合使用](https://github.com/kataras/iris-starter-kit) + * [Typescript自动化操作工具](typescript/#table-of-contents) + * [指南: 用Iris和Bolt实现短连接服务](https://medium.com/@kataras/a-url-shortener-service-using-go-iris-and-bolt-4182f0b00ae7) + * [指南: 如何统计在线访问人数](_examples/tutorial/online-visitors) + * [指南: Caddy](_examples/tutorial/caddy) + * [指南: 如何用DropzoneJS上传文件](https://hackernoon.com/how-to-build-a-file-upload-form-using-dropzonejs-and-go-8fb9f258a991) + * [指南: Iris+MongoDB](https://medium.com/go-language/iris-go-framework-mongodb-552e349eab9c) +* [中间件](middleware/) +* [Docker例子](https://github.com/iris-contrib/cloud-native-go) +* [贡献](CONTRIBUTING.md) +* [常见问题](FAQ.md) +* [更新计划?](#now-you-are-ready-to-move-to-the-next-step-and-get-closer-to-becoming-a-pro-gopher) +* [开发者](#people) + +## 安装 + +仅仅依赖[Go语言](https://golang.org/dl/) + +```sh +$ go get -u github.com/kataras/iris +``` +Iris使用[vendor](https://docs.google.com/document/d/1Bz5-UB7g2uPBdOx-rw5t9MxJwkfpx90cqG9AFL0JAYo) 包依赖管理方式。vendor包管理的方式可以有效处理包依赖更新问题 + +## 入门 + +```go +package main + +import "github.com/kataras/iris" + +func main() { + app := iris.New() +    // 从"./views"目录加载HTML模板 +    // 模板解析html后缀文件 +    // 此方式是用`html/template`标准包(Iris的模板引擎) + app.RegisterView(iris.HTML("./views", ".html")) + +    // HTTP方法: GET +    // 路径: http://localhost:8080 + app.Get("/", func(ctx iris.Context) { +        // {{.message}} 和 "Hello world!" 字串绑定 + ctx.ViewData("message", "Hello world!") +        // 映射HTML模板文件路径 ./views/hello.html + ctx.View("hello.html") + }) + + // HTTP方法: GET + // 路径: http://localhost:8080/user/42 + // +    // 想在路径中用正则吗?So easy! +    // 如下所示 +    // app.Get("/user/{id:string regexp(^[0-9]+$)}") + app.Get("/user/{id:long}", func(ctx iris.Context) { + userID, _ := ctx.Params().GetInt64("id") + ctx.Writef("User ID: %d", userID) + }) + +    // 绑定端口并启动服务. + app.Run(iris.Addr(":8080")) +} +``` + +> 想要了解更多关于路径参数配置,戳[这里](https://github.com/kataras/iris/blob/master/_examples/routing/dynamic-path/main.go#L31). + +```html + + + + Hello Page + + +

{{.message}}

+ + +``` + +```sh +$ go run main.go +> 在这里监听服务: http://localhost:8080 +> 应用已经启动按键 CTRL+C 停止服务 +``` + +> 想要实现当代码改变后自动重启应用吗?那就装个[rizla](https://github.com/kataras/rizla)工具,启动go文件用 `rizla main.go` 来代替 `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迈进一步了 + +恭喜你看到这里了,我们为你准备了更高水平的内容,向真正的专家级gopher进军吧😃 + +> 准备好咖啡,尽情享受吧! + +* [Iris框架+MongoDB](https://medium.com/go-language/iris-go-framework-mongodb-552e349eab9c) +* [用DropzoneJS 和 Go来构建表单文件上传](https://hackernoon.com/how-to-build-a-file-upload-form-using-dropzonejs-and-go-8fb9f258a991) +* [用DropzoneJS 和 Go来呈现服务器上的问题](https://hackernoon.com/how-to-display-existing-files-on-server-using-dropzonejs-and-go-53e24b57ba19) +* [Iris模块化Web开发框架](https://medium.com/@corebreaker/iris-web-cd684b4685c7) +* [按照 HTTP 性能来比较Go 和 .NET Core](https://medium.com/@kataras/go-vs-net-core-in-terms-of-http-performance-7535a61b67b8) +* [按照 HTTP 性能来比较Go 和 .NET Core Kestrel](https://hackernoon.com/iris-go-vs-net-core-kestrel-in-terms-of-http-performance-806195dc93d5) +* [在Android设备上搭建Web服务器](https://twitter.com/ThePracticalDev/status/892022594031017988) +* [在hasura上部署Iris应用](https://medium.com/@HasuraHQ/deploy-an-iris-golang-app-with-backend-apis-in-minutes-25a559bf530b) +* [用Iris 和 Bolt实现短连接服务](https://medium.com/@kataras/a-url-shortener-service-using-go-iris-and-bolt-4182f0b00ae7) + +## 作者 + + Iris的作者是[@kataras](https://github.com/kataras), 你可以通过以下方式来了解作者: + +* [Medium](https://medium.com/@kataras) +* [Twitter](https://twitter.com/makismaropoulos) +* [Dev.to](https://dev.to/@kataras) +* [Facebook](https://facebook.com/iris.framework) +* [Mail](mailto:kataras2006@hotmail.com?subject=Iris%20I%20need%20some%20help%20please) + +[作者](AUTHORS) + +[贡献者列表](https://github.com/kataras/iris/graphs/contributors) + +你可以通过[PayPal](https://www.paypal.me/kataras) 或 [BTC](https://iris-go.com/v8/donate)来捐赠这个项目,这样可以促进开发者们创造更棒、更优秀的Iris。 + +[如何贡献代码](CONTRIBUTING.md) + +### 我们期待你能帮助我们翻译Iris文档 + +Iris需要你的帮助,帮助我们翻译[README](README.md)和https://iris-go.com ,同时你也会得到奖励的。 + +你可以在这里https://github.com/kataras/iris/issues/796 看到详细的有关翻译的信息 + + +### Iris 用户体验反馈 | 2017年10月3号 + +**请放心** Iris用户体验反馈就是一些简单的表单提交,**2分钟**就能搞定。 + +这些表单里有些问题是为了更好的了解你,了解你可以让我们更好的为你服务。 + +https://docs.google.com/forms/d/e/1FAIpQLSdCxZXPANg_xHWil4kVAdhmh7EBBHQZ_4_xSZVDL-oCC_z5pA/viewform?usp=sf_link + +## 贡献者列表 + +非常感谢所有对Iris的贡献者,没有你们就没有Iris [贡献者](CONTRIBUTING.md) + + +## 资助者 + +万分感谢所有的资助者🙏 [成为资助者](https://opencollective.com/iris#backer) + + + +## 赞助商 + +资助Iris,你将是Iris的赞助商,你的logo将会出现在下面的列表中,[成为赞助商](https://opencollective.com/iris#sponsor) + + + + + + + + + + + + +## 开源许可证 + +Iris使用3-Clause BSD [许可证](LICENSE)开源许可 。Iris绝对是100%开源的。 +对这个许可有任何疑问请[联系我们](mailto:kataras2006@hotmail.com?subject=Iris%20License)