mirror of
https://github.com/kataras/iris.git
synced 2025-02-10 11:06:19 +01:00
Update README_CN.md
Former-commit-id: fa2180d8fe9c50283eef754c1a2f009443c7fd60
This commit is contained in:
parent
d9708e1dc3
commit
c963e91cea
177
README_CN.md
177
README_CN.md
|
@ -66,22 +66,22 @@ If you're coming from [nodejs](https://nodejs.org) world, Iris is the [expressjs
|
||||||
* [快速入门](#getting-started)
|
* [快速入门](#getting-started)
|
||||||
* [进阶](_examples/)
|
* [进阶](_examples/)
|
||||||
* [MVC (Model View Controller)](_examples/#mvc) **NEW**
|
* [MVC (Model View Controller)](_examples/#mvc) **NEW**
|
||||||
* [Structuring](_examples/#structuring) **NEW**
|
* [结构](_examples/#structuring) **NEW**
|
||||||
* [HTTP Listening](_examples/#http-listening)
|
* [HTTP 监听](_examples/#http-listening)
|
||||||
* [Configuration](_examples/#configuration)
|
* [配置](_examples/#configuration)
|
||||||
* [Routing, Grouping, Dynamic Path Parameters, "Macros" and Custom Context](_examples/#routing-grouping-dynamic-path-parameters-macros-and-custom-context)
|
* [路由,分组,动态参数,“宏定义”已经自定义Context](_examples/#routing-grouping-dynamic-path-parameters-macros-and-custom-context)
|
||||||
* [Subdomains](_examples/#subdomains)
|
* [子域名处理](_examples/#subdomains)
|
||||||
* [Wrap `http.Handler/HandlerFunc`](_examples/#convert-httphandlerhandlerfunc)
|
* [`http.Handler/HandlerFunc` 使用](_examples/#convert-httphandlerhandlerfunc)
|
||||||
* [View](_examples/#view)
|
* [视图处理](_examples/#view)
|
||||||
* [Authentication](_examples/#authentication)
|
* [认证](_examples/#authentication)
|
||||||
* [File Server](_examples/#file-server)
|
* [文件服务器](_examples/#file-server)
|
||||||
* [How to Read from `context.Request() *http.Request`](_examples/#how-to-read-from-contextrequest-httprequest)
|
* [如何从`context.Request() *http.Request` 读数据](_examples/#how-to-read-from-contextrequest-httprequest)
|
||||||
* [How to Write to `context.ResponseWriter() http.ResponseWriter`](_examples/#how-to-write-to-contextresponsewriter-httpresponsewriter)
|
* [如何给`context.ResponseWriter() http.ResponseWriter`写数据](_examples/#how-to-write-to-contextresponsewriter-httpresponsewriter)
|
||||||
* [Test](_examples/#testing)
|
* [测试](_examples/#testing)
|
||||||
* [Cache](_examples/#caching)
|
* [缓存](_examples/#caching)
|
||||||
* [Sessions](_examples/#sessions)
|
* [会话](_examples/#sessions)
|
||||||
* [Websockets](_examples/#websockets)
|
* [Websockets](_examples/#websockets)
|
||||||
* [Miscellaneous](_examples/#miscellaneous)
|
* [其它杂项](_examples/#miscellaneous)
|
||||||
* [POC: Convert the medium-sized project "Parrot" from native to Iris](https://github.com/iris-contrib/parrot)
|
* [POC: Convert the medium-sized project "Parrot" from native to Iris](https://github.com/iris-contrib/parrot)
|
||||||
* [POC: Isomorphic react/hot reloadable/redux/css-modules starter kit](https://github.com/kataras/iris-starter-kit)
|
* [POC: Isomorphic react/hot reloadable/redux/css-modules starter kit](https://github.com/kataras/iris-starter-kit)
|
||||||
* [Typescript Automation Tools](typescript/#table-of-contents)
|
* [Typescript Automation Tools](typescript/#table-of-contents)
|
||||||
|
@ -100,15 +100,12 @@ If you're coming from [nodejs](https://nodejs.org) world, Iris is the [expressjs
|
||||||
## 安装
|
## 安装
|
||||||
|
|
||||||
仅仅依赖[Go语言](https://golang.org/dl/)
|
仅仅依赖[Go语言](https://golang.org/dl/)
|
||||||
The only requirement is the [Go Programming Language](https://golang.org/dl/)
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
$ go get -u github.com/kataras/iris
|
$ go get -u github.com/kataras/iris
|
||||||
```
|
```
|
||||||
Iris使用[vendor](https://docs.google.com/document/d/1Bz5-UB7g2uPBdOx-rw5t9MxJwkfpx90cqG9AFL0JAYo) 包依赖管理方式。vendor包管理的方式可以有效处理包依赖更新问题
|
Iris使用[vendor](https://docs.google.com/document/d/1Bz5-UB7g2uPBdOx-rw5t9MxJwkfpx90cqG9AFL0JAYo) 包依赖管理方式。vendor包管理的方式可以有效处理包依赖更新问题
|
||||||
|
|
||||||
Iris takes advantage of the [vendor directory](https://docs.google.com/document/d/1Bz5-UB7g2uPBdOx-rw5t9MxJwkfpx90cqG9AFL0JAYo) feature. You get truly reproducible builds, as this method guards against upstream renames and deletes.
|
|
||||||
|
|
||||||
## 入门
|
## 入门
|
||||||
|
|
||||||
```go
|
```go
|
||||||
|
@ -168,11 +165,10 @@ $ go run main.go
|
||||||
> 应用已经启动按键 CTRL+C 停止服务
|
> 应用已经启动按键 CTRL+C 停止服务
|
||||||
```
|
```
|
||||||
|
|
||||||
> 想要实现当代码改变后自动重启应用吗?那就装个[rizla](https://github.com/kataras/rizla)工具,启动go文件用 `rizla main.go` 来代替 `go run main.go`.
|
> 想要实现当代码改变后自动重启应用吗?那就装个[rizla](https://github.com/kataras/rizla)工具,启动go文件用 `rizla main.go` 来代替 `go run main.go`。
|
||||||
|
|
||||||
Iris的一些开发约定可以看看这里[_examples/structuring](_examples/#structuring)。
|
Iris的一些开发约定可以看看这里[_examples/structuring](_examples/#structuring)。
|
||||||
|
|
||||||
|
|
||||||
### MVC指南
|
### MVC指南
|
||||||
|
|
||||||
```go
|
```go
|
||||||
|
@ -234,37 +230,25 @@ func (c *HelloWorldController) GetWelcomeBy(name string, numTimes int) {
|
||||||
```
|
```
|
||||||
> [_examples/mvc](_examples/mvc) 和 [mvc/controller_test.go](https://github.com/kataras/iris/blob/master/mvc/controller_test.go) 两个简单的例子可以让你更好的了解 Iris MVC 的使用方式
|
> [_examples/mvc](_examples/mvc) 和 [mvc/controller_test.go](https://github.com/kataras/iris/blob/master/mvc/controller_test.go) 两个简单的例子可以让你更好的了解 Iris MVC 的使用方式
|
||||||
|
|
||||||
> The [_examples/mvc](_examples/mvc) and [mvc/controller_test.go](https://github.com/kataras/iris/blob/master/mvc/controller_test.go) files explain each feature with simple paradigms, they show how you can take advandage of the Iris MVC Binder, Iris MVC Models and many more...
|
|
||||||
|
|
||||||
每一个在controller中导出的Go方法名都和HTTP方法(`Get`, `Post`, `Put`, `Delete`...) 一一对应
|
每一个在controller中导出的Go方法名都和HTTP方法(`Get`, `Post`, `Put`, `Delete`...) 一一对应
|
||||||
|
|
||||||
Every `exported` func prefixed with an HTTP Method(`Get`, `Post`, `Put`, `Delete`...) in a controller is callable as an HTTP endpoint. In the sample above, all funcs writes a string to the response. Note the comments preceding each method.
|
在Web应用中一个HTTP访问的资源就是一个URL(统一资源定位符),比如`http://localhost:8080/helloworld`是由HTTP协议、Web服务网络位置(包括TCP端口):`localhost:8080`以及资源名称URI(统一资源标志符) `/helloworld`组成的。
|
||||||
|
|
||||||
在Web应用中一个HTTP访问的资源就是一个URL,比如`http://localhost:8080/helloworld`是由,HTTP协议、Web服务网络位置(包括TCP端口):`localhost:8080`以及资源名称URI `/helloworld`组成的。
|
|
||||||
|
|
||||||
An HTTP endpoint is a targetable URL in the web application, such as `http://localhost:8080/helloworld`, and combines the protocol used: HTTP, the network location of the web server (including the TCP port): `localhost:8080` and the target 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"
|
上面例子第一个方法映射到[HTTP GET](https://www.w3schools.com/tags/ref_httpmethods.asp)方法,访问资源是"/helloworld",第三个方法映射到[HTTP GET](https://www.w3schools.com/tags/ref_httpmethods.asp)方法,访问资源是"/helloworld/welcome"
|
||||||
|
|
||||||
The first comment states this is an [HTTP GET](https://www.w3schools.com/tags/ref_httpmethods.asp) method that is invoked by appending "/helloworld" to the base URL. The third comment specifies an [HTTP GET](https://www.w3schools.com/tags/ref_httpmethods.asp) method that is invoked by appending "/helloworld/welcome" to the URL.
|
|
||||||
|
|
||||||
|
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)的提示
|
||||||
|
|
||||||
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)的提示
|
|
||||||
|
|
||||||
Controller knows how to handle the "name" on `GetBy` or the "name" and "numTimes" at `GetWelcomeBy`, because of the `By` keyword, and builds the dynamic route without boilerplate; the third comment specifies an [HTTP GET](https://www.w3schools.com/tags/ref_httpmethods.asp) dynamic method that is invoked by any URL that starts with "/helloworld/welcome" and followed by two more path parts, the first one can accept any value and the second can accept only numbers, i,e: "http://localhost:8080/helloworld/welcome/golang/32719", otherwise a [404 Not Found HTTP Error](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) will be sent to the client instead.
|
|
||||||
|
|
||||||
### MVC 快速指南 2
|
### MVC 快速指南 2
|
||||||
|
|
||||||
Iris对MVC的支持非常 **棒[高性能](_benchmarks)** , 通过方法的返回值,Iris可以给客户端返回任意类型的数据。
|
Iris对MVC的支持非常 **棒[高性能](_benchmarks)** , Iris通过方法的返回值,可以给客户端返回任意类型的数据:
|
||||||
|
|
||||||
Iris has a very powerful and **blazing [fast](_benchmarks)** MVC support, you can return any value of any type from a method function
|
* 如果返回的是 `string` 类型,就直接给客户端返回字符串
|
||||||
and it will be sent to the client as expected.
|
* 如果第二个返回值是 `string` 类型,那么这个值就是ContentType(HTTP header)的值
|
||||||
|
* 如果返回的是 `int` 类型,这个值就是HTTP状态码
|
||||||
* 如果是 `string` 类型,就直接返回字符串
|
* 如果返回 `error` 值不是空,Iris 将会把这个值作为HTTP 400页面的返回值内容
|
||||||
* 如果第二个返回值是 `string` 类型,那么这个值就是ContentType的值
|
* 如果返回 `(int, error)` 类型,并且error不为空,那么Iris返回error的内容,同时把 `int` 值作为HTTP状态码
|
||||||
* 如果是 `int` 类型,这个值就是HTTP状态码
|
|
||||||
* 如果 `error` 值不是空,Iris 将会把这个值作为HTTP400页面的返回值内容
|
|
||||||
* 如果是 `(int, error)` 类型,并且error不为空,那么Iris返回error的内容,同时把 `int` 值作为HTTP状态码
|
|
||||||
* 如果返回 `bool` 类型,并且值是 false ,Iris直接返回404页面
|
* 如果返回 `bool` 类型,并且值是 false ,Iris直接返回404页面
|
||||||
* 如果返回自定义` struct` 、 `interface{}` 、 `slice` 及 `map` ,Iris 将按照JSON的方式返回,注意如果第二个返回值是 `string`,那么Iris就按照这个 `string` 值的ContentType处理了(不一定是'application/json')
|
* 如果返回自定义` struct` 、 `interface{}` 、 `slice` 及 `map` ,Iris 将按照JSON的方式返回,注意如果第二个返回值是 `string`,那么Iris就按照这个 `string` 值的ContentType处理了(不一定是'application/json')
|
||||||
* 如果 `mvc.Result` 调用了 `Dispatch` 函数, 就会按照自己的逻辑重新处理
|
* 如果 `mvc.Result` 调用了 `Dispatch` 函数, 就会按照自己的逻辑重新处理
|
||||||
|
@ -394,19 +378,12 @@ func (c *MoviesController) DeleteBy(id int) iris.Map {
|
||||||
|
|
||||||
Iris是一个底层的Web开发框架,如果你喜欢按 **目录结构** 的约定方式开发,那么Iris框架对此毫无影响。
|
Iris是一个底层的Web开发框架,如果你喜欢按 **目录结构** 的约定方式开发,那么Iris框架对此毫无影响。
|
||||||
|
|
||||||
Nothing stops you from using your favorite **folder structure**. Iris is a low level web framework, it has got MVC first-class support but it doesn't limit your folder structure, this is your choice.
|
|
||||||
|
|
||||||
你可以根据自己的需求来创建目录结构,但是我建议你还是最好看看如下的目录结构例子:
|
你可以根据自己的需求来创建目录结构,但是我建议你还是最好看看如下的目录结构例子:
|
||||||
|
|
||||||
[![目录结构例子](_examples/mvc/overview/folder_structure.png)](_examples/mvc/overview)
|
[![目录结构例子](_examples/mvc/overview/folder_structure.png)](_examples/mvc/overview)
|
||||||
|
|
||||||
好了,直接上代码。
|
好了,直接上代码。
|
||||||
|
|
||||||
Structuring depends on your own needs. We can't tell you how to design your own application for sure but you're free to take a closer look to one typical example below;
|
|
||||||
|
|
||||||
[![folder structure example](_examples/mvc/overview/folder_structure.png)](_examples/mvc/overview)
|
|
||||||
|
|
||||||
Shhh, let's spread the code itself.
|
|
||||||
|
|
||||||
#### 数据模型层
|
#### 数据模型层
|
||||||
|
|
||||||
|
@ -416,7 +393,7 @@ Shhh, let's spread the code itself.
|
||||||
package datamodels
|
package datamodels
|
||||||
|
|
||||||
// Movie是我们例子数据结构
|
// Movie是我们例子数据结构
|
||||||
// 此Movie对象可能会在"web/viewmodels/movie.go"的文件里持有
|
// 此Movie可能会定义在类似"web/viewmodels/movie.go"的文件
|
||||||
// Movie的数据模型在应用中只有一个,这样使用就很简单了
|
// Movie的数据模型在应用中只有一个,这样使用就很简单了
|
||||||
type Movie struct {
|
type Movie struct {
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
|
@ -520,7 +497,7 @@ type movieMemoryRepository struct {
|
||||||
const (
|
const (
|
||||||
// 只读模式
|
// 只读模式
|
||||||
ReadOnlyMode = iota
|
ReadOnlyMode = iota
|
||||||
// 写模式
|
// 读写模式
|
||||||
ReadWriteMode
|
ReadWriteMode
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -556,18 +533,6 @@ func (r *movieMemoryRepository) Exec(query Query, action Query, actionLimit int,
|
||||||
// Select 将会返回查询到的最新找到的movie数据,这样可以减少代码量
|
// Select 将会返回查询到的最新找到的movie数据,这样可以减少代码量
|
||||||
//
|
//
|
||||||
// 自从我第一次想到用这种简单的原型函数后,我就经常用它了,希望这也对你有用
|
// 自从我第一次想到用这种简单的原型函数后,我就经常用它了,希望这也对你有用
|
||||||
// Select receives a query function
|
|
||||||
// which is fired for every single movie model inside
|
|
||||||
// our imaginary data source.
|
|
||||||
// When that function returns true then it stops the iteration.
|
|
||||||
//
|
|
||||||
// It returns the query's return last known "found" value
|
|
||||||
// and the last known movie model
|
|
||||||
// to help callers to reduce the LOC.
|
|
||||||
//
|
|
||||||
// It's actually a simple but very clever prototype function
|
|
||||||
// I'm using everywhere since I firstly think of it,
|
|
||||||
// hope you'll find it very useful as well.
|
|
||||||
func (r *movieMemoryRepository) Select(query Query) (movie datamodels.Movie, found bool) {
|
func (r *movieMemoryRepository) Select(query Query) (movie datamodels.Movie, found bool) {
|
||||||
found = r.Exec(query, func(m datamodels.Movie) bool {
|
found = r.Exec(query, func(m datamodels.Movie) bool {
|
||||||
movie = m
|
movie = m
|
||||||
|
@ -585,8 +550,6 @@ func (r *movieMemoryRepository) Select(query Query) (movie datamodels.Movie, fou
|
||||||
|
|
||||||
// 如果要查找很多值,用法基本一致,不过会返回datamodels.Movie slice。
|
// 如果要查找很多值,用法基本一致,不过会返回datamodels.Movie slice。
|
||||||
// 如果limit<=0,将返回全部数据
|
// 如果limit<=0,将返回全部数据
|
||||||
// SelectMany same as Select but returns one or more datamodels.Movie as a slice.
|
|
||||||
// If limit <=0 then it returns everything.
|
|
||||||
func (r *movieMemoryRepository) SelectMany(query Query, limit int) (results []datamodels.Movie) {
|
func (r *movieMemoryRepository) SelectMany(query Query, limit int) (results []datamodels.Movie) {
|
||||||
r.Exec(query, func(m datamodels.Movie) bool {
|
r.Exec(query, func(m datamodels.Movie) bool {
|
||||||
results = append(results, m)
|
results = append(results, m)
|
||||||
|
@ -596,22 +559,16 @@ func (r *movieMemoryRepository) SelectMany(query Query, limit int) (results []da
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//插入或跟新数据
|
// 插入或更新数据
|
||||||
// InsertOrUpdate adds or updates a movie to the (memory) storage.
|
|
||||||
//
|
//
|
||||||
// 返回一个新的movie对象和error对象
|
// 返回一个新的movie对象和error对象
|
||||||
// Returns the new movie and an error if any.
|
|
||||||
func (r *movieMemoryRepository) InsertOrUpdate(movie datamodels.Movie) (datamodels.Movie, error) {
|
func (r *movieMemoryRepository) InsertOrUpdate(movie datamodels.Movie) (datamodels.Movie, error) {
|
||||||
id := movie.ID
|
id := movie.ID
|
||||||
|
|
||||||
if id == 0 { // Create new action
|
if id == 0 { // Create new action
|
||||||
var lastID int64
|
var lastID int64
|
||||||
// 为了数据不重复,找到最大的ID
|
// 为了数据不重复,找到最大的ID。
|
||||||
// 生成环境你可以用第三方库生成一个UUID字串
|
// 生产环境你可以用第三方库生成一个UUID字串
|
||||||
|
|
||||||
// find the biggest ID in order to not have duplications
|
|
||||||
// in productions apps you can use a third-party
|
|
||||||
// library to generate a UUID as string.
|
|
||||||
r.mu.RLock()
|
r.mu.RLock()
|
||||||
for _, item := range r.source {
|
for _, item := range r.source {
|
||||||
if item.ID > lastID {
|
if item.ID > lastID {
|
||||||
|
@ -631,19 +588,14 @@ func (r *movieMemoryRepository) InsertOrUpdate(movie datamodels.Movie) (datamode
|
||||||
return movie, nil
|
return movie, nil
|
||||||
}
|
}
|
||||||
//通过movie.ID更新数据
|
//通过movie.ID更新数据
|
||||||
//这里举个例子看如果更新poster和genre非空值
|
//这里举个例子看如果更新非空的poster和genre
|
||||||
//其实我们可以直接更新对象r.source[id] = movie
|
//其实我们可以直接更新对象r.source[id] = movie
|
||||||
//用Select的话如下所示
|
//用Select的话如下所示
|
||||||
// Update action based on the movie.ID,
|
|
||||||
// here we will allow updating the poster and genre if not empty.
|
|
||||||
// Alternatively we could do pure replace instead:
|
|
||||||
// r.source[id] = movie
|
|
||||||
// and comment the code below;
|
|
||||||
current, exists := r.Select(func(m datamodels.Movie) bool {
|
current, exists := r.Select(func(m datamodels.Movie) bool {
|
||||||
return m.ID == id
|
return m.ID == id
|
||||||
})
|
})
|
||||||
|
|
||||||
if !exists { // ID不存在,返回error ID is not a real one, return an error.
|
if !exists { // ID不存在,返回error ID
|
||||||
return datamodels.Movie{}, errors.New("failed to update a nonexistent movie")
|
return datamodels.Movie{}, errors.New("failed to update a nonexistent movie")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -657,8 +609,8 @@ func (r *movieMemoryRepository) InsertOrUpdate(movie datamodels.Movie) (datamode
|
||||||
current.Genre = movie.Genre
|
current.Genre = movie.Genre
|
||||||
}
|
}
|
||||||
|
|
||||||
// map-specific thing
|
// 类map结构的处理
|
||||||
r.mu.Lock()
|
r.mu.Lock()
|
||||||
r.source[id] = current
|
r.source[id] = current
|
||||||
r.mu.Unlock()
|
r.mu.Unlock()
|
||||||
|
|
||||||
|
@ -677,7 +629,6 @@ func (r *movieMemoryRepository) Delete(query Query, limit int) bool {
|
||||||
|
|
||||||
服务层主要调用“数据仓库”和“数据模型”的方法(即使是数据模型很简单的应用)。这一层将包含主要的数据处理逻辑。
|
服务层主要调用“数据仓库”和“数据模型”的方法(即使是数据模型很简单的应用)。这一层将包含主要的数据处理逻辑。
|
||||||
|
|
||||||
The layer which has access to call functions from the "repositories" and "models" (or even "datamodels" if simple application). It should contain the most of the domain logic.
|
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// file: services/movie_service.go
|
// file: services/movie_service.go
|
||||||
|
@ -691,16 +642,9 @@ import (
|
||||||
|
|
||||||
// MovieService主要包括对movie的CRUID(增删改查)操作。
|
// MovieService主要包括对movie的CRUID(增删改查)操作。
|
||||||
// MovieService主要调用movie 数据仓库的方法。
|
// MovieService主要调用movie 数据仓库的方法。
|
||||||
// 下面例子的数据源是从更高级别的组件
|
// 下面例子的数据源是更高级别的组件
|
||||||
// 这样可以用同样的逻辑可以返回不同的数据仓库
|
// 这样可以用同样的逻辑可以返回不同的数据仓库
|
||||||
// MovieService是一个接口,任何实现的地方等能用,这样替换不同的业务逻辑可以用来测试
|
// MovieService是一个接口,任何实现的地方都能用,这样可以替换不同的业务逻辑用来测试
|
||||||
|
|
||||||
// MovieService handles some of the CRUID operations of the movie datamodel.
|
|
||||||
// It depends on a movie repository for its actions.
|
|
||||||
// It's here to decouple the data source from the higher level compoments.
|
|
||||||
// As a result a different repository type can be used with the same logic without any aditional changes.
|
|
||||||
// It's an interface and it's used as interface everywhere
|
|
||||||
// because we may need to change or try an experimental different domain logic at the future.
|
|
||||||
type MovieService interface {
|
type MovieService interface {
|
||||||
GetAll() []datamodels.Movie
|
GetAll() []datamodels.Movie
|
||||||
GetByID(id int64) (datamodels.Movie, bool)
|
GetByID(id int64) (datamodels.Movie, bool)
|
||||||
|
@ -757,7 +701,6 @@ func (s *movieService) DeleteByID(id int64) bool {
|
||||||
#### 视图模型
|
#### 视图模型
|
||||||
|
|
||||||
视图模型将处理结果返回给客户端
|
视图模型将处理结果返回给客户端
|
||||||
There should be the view models, the structure that the client will be able to see.
|
|
||||||
|
|
||||||
例子:
|
例子:
|
||||||
Example:
|
Example:
|
||||||
|
@ -779,36 +722,21 @@ func (m Movie) IsValid() bool {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Iris允许在HTTP Response Dispatcher中使用任何自定义数据结构,所以理论上下面的代码不建议使用
|
Iris允许在HTTP Response Dispatcher中使用任何自定义数据结构,
|
||||||
|
所以理论上来说,除非万不得已,下面的代码不建议使用
|
||||||
Iris is able to convert any custom data Structure into an HTTP Response Dispatcher,
|
|
||||||
so theoretically, something like the following is permitted if it's really necessary;
|
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// Dispatch实现了`kataras/iris/mvc#Result`接口。在函数最后发送了一个`Movie`对象作为http response对象。
|
// Dispatch实现了`kataras/iris/mvc#Result`接口。在函数最后发送了一个`Movie`对象作为http response对象。
|
||||||
// 如果ID小于等于0就回返回404,或者就返回json数据。
|
// 如果ID小于等于0就回返回404,或者就返回json数据。
|
||||||
//(这样就像控制器的方法默认返回自定义类型一样)
|
//(这样就像控制器的方法默认返回自定义类型一样)
|
||||||
// Dispatch completes the `kataras/iris/mvc#Result` interface.
|
|
||||||
// Sends a `Movie` as a controlled http response.
|
|
||||||
// If its ID is zero or less then it returns a 404 not found error
|
|
||||||
// else it returns its json representation,
|
|
||||||
// (just like the controller's functions do for custom types by default).
|
|
||||||
//
|
//
|
||||||
// 不要在这里写过多的代码,应用的主要逻辑不在这里
|
// 不要在这里写过多的代码,应用的主要逻辑不在这里
|
||||||
// 在方法返回之前可以做个简单验证处理等等;
|
// 在方法返回之前可以做个简单验证处理等等;
|
||||||
// Don't overdo it, the application's logic should not be here.
|
|
||||||
// It's just one more step of validation before the response,
|
|
||||||
// simple checks can be added here.
|
|
||||||
//
|
//
|
||||||
// 这里只是一个小例子,想想这个优势在设计大型应用是很有作用的
|
// 这里只是一个小例子,想想这个优势在设计大型应用是很有作用的
|
||||||
// It's just a showcase,
|
|
||||||
// imagine the potentials this feature gives when designing a bigger application.
|
|
||||||
//
|
//
|
||||||
// 这个方法是在`Movie`类型的控制器调用的。
|
// 这个方法是在`Movie`类型的控制器调用的。
|
||||||
// 例子在这里:`controllers/movie_controller.go#GetBy`。
|
// 例子在这里:`controllers/movie_controller.go#GetBy`。
|
||||||
// This is called where the return value from a controller's method functions
|
|
||||||
// is type of `Movie`.
|
|
||||||
// For example the `controllers/movie_controller.go#GetBy`.
|
|
||||||
func (m Movie) Dispatch(ctx context.Context) {
|
func (m Movie) Dispatch(ctx context.Context) {
|
||||||
if !m.IsValid() {
|
if !m.IsValid() {
|
||||||
ctx.NotFound()
|
ctx.NotFound()
|
||||||
|
@ -817,15 +745,12 @@ func (m Movie) Dispatch(ctx context.Context) {
|
||||||
ctx.JSON(m, context.JSON{Indent: " "})
|
ctx.JSON(m, context.JSON{Indent: " "})
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
然而,我们仅仅用"datamodels"作为一个数据模型包是因为Movie数据结构没有包含敏感数据,客户端可以访问到其所有字段,我们不需要再有额外的功能去做验证处理了
|
然而,我们仅仅用"datamodels"作为一个数据模型包,是因为Movie数据结构没有包含敏感数据,客户端可以访问到其所有字段,我们不需要再有额外的功能去做验证处理了
|
||||||
However, we will use the "datamodels" as the only one models package because
|
|
||||||
Movie structure doesn't contain any sensitive data, clients are able to see all of its fields
|
|
||||||
and we don't need any extra functionality or validation inside it.
|
|
||||||
|
|
||||||
#### 控制器
|
#### 控制器
|
||||||
|
|
||||||
控制器处理Web请求,它是服务层和客户端之间的桥梁
|
控制器处理Web请求,它是服务层和客户端之间的桥梁
|
||||||
Handles web requests, bridge between the services and the client.
|
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// file: web/controllers/movie_controller.go
|
// file: web/controllers/movie_controller.go
|
||||||
|
@ -847,8 +772,6 @@ type MovieController struct {
|
||||||
mvc.C
|
mvc.C
|
||||||
|
|
||||||
// MovieService是一个接口,主app对象会持有它
|
// MovieService是一个接口,主app对象会持有它
|
||||||
// Our MovieService, it's an interface which
|
|
||||||
// is binded from the main application.
|
|
||||||
Service services.MovieService
|
Service services.MovieService
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -907,8 +830,6 @@ func (c *MovieController) DeleteBy(id int64) interface{} {
|
||||||
}
|
}
|
||||||
//现在我们可以看到这里可以返回一个有2个返回值(map或int)的函数
|
//现在我们可以看到这里可以返回一个有2个返回值(map或int)的函数
|
||||||
//我们并没有指定一个返回的类型
|
//我们并没有指定一个返回的类型
|
||||||
// right here we can see that a method function can return any of those two types(map or int),
|
|
||||||
// we don't have to specify the return type to a specific type.
|
|
||||||
return iris.StatusBadRequest
|
return iris.StatusBadRequest
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -951,8 +872,6 @@ func (c *HelloController) Get() mvc.Result {
|
||||||
var errBadName = errors.New("bad name")
|
var errBadName = errors.New("bad name")
|
||||||
|
|
||||||
//你也可以将error包裹在mvc.Response中,这样就和mvc.Result类型兼容了
|
//你也可以将error包裹在mvc.Response中,这样就和mvc.Result类型兼容了
|
||||||
// you can just return it as error or even better
|
|
||||||
// wrap this error with an mvc.Response to make it an mvc.Result compatible type.
|
|
||||||
var badName = mvc.Response{Err: errBadName, Code: 400}
|
var badName = mvc.Response{Err: errBadName, Code: 400}
|
||||||
|
|
||||||
// GetBy 返回 "Hello {name}" response
|
// GetBy 返回 "Hello {name}" response
|
||||||
|
@ -1027,7 +946,6 @@ var BasicAuth = basicauth.New(basicauth.Config{
|
||||||
#### 程序入口
|
#### 程序入口
|
||||||
|
|
||||||
程序入口可以将任何组件包含进来
|
程序入口可以将任何组件包含进来
|
||||||
This file creates any necessary component and links them together.
|
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// file: main.go
|
// file: main.go
|
||||||
|
@ -1059,12 +977,9 @@ func main() {
|
||||||
movieService := services.NewMovieService(repo)
|
movieService := services.NewMovieService(repo)
|
||||||
|
|
||||||
app.Controller("/movies", new(controllers.MovieController),
|
app.Controller("/movies", new(controllers.MovieController),
|
||||||
// Bind the "movieService" to the MovieController's Service (interface) field.
|
|
||||||
// 将"movieService"绑定在 MovieController的Service接口
|
// 将"movieService"绑定在 MovieController的Service接口
|
||||||
movieService,
|
movieService,
|
||||||
// 为/movies请求添加basic authentication(admin:password)中间件
|
// 为/movies请求添加basic authentication(admin:password)中间件
|
||||||
// Add the basic authentication(admin:password) middleware
|
|
||||||
// for the /movies based requests.
|
|
||||||
middleware.BasicAuth)
|
middleware.BasicAuth)
|
||||||
|
|
||||||
// 启动应用localhost:8080
|
// 启动应用localhost:8080
|
||||||
|
@ -1083,16 +998,11 @@ func main() {
|
||||||
|
|
||||||
更多指南戳 [_examples/#structuring](_examples/#structuring)
|
更多指南戳 [_examples/#structuring](_examples/#structuring)
|
||||||
|
|
||||||
More folder structure guidelines can be found at the [_examples/#structuring](_examples/#structuring) section.
|
## 现在你已经准备好进入下一阶段,又向专家级gopher迈进一步了
|
||||||
|
|
||||||
## 现在你已经准备好进入下一个阶段了,又向专家级gopher更近一步了
|
|
||||||
## Now you are ready to move to the next step and get closer to becoming a pro gopher
|
|
||||||
|
|
||||||
恭喜你看到这里了,我们为你准备了更高水平的内容,向真正的专家级gopher进军吧😃
|
恭喜你看到这里了,我们为你准备了更高水平的内容,向真正的专家级gopher进军吧😃
|
||||||
Congratulations, since you've made it so far, we've crafted just for you some next level content to turn you into a real pro gopher 😃
|
|
||||||
|
|
||||||
> 准备好咖啡,尽情享受吧
|
> 准备好咖啡,尽情享受吧!
|
||||||
> Don't forget to prepare yourself a cup of coffee, or tea, whatever enjoys you the most!
|
|
||||||
|
|
||||||
* [Iris Go 矿建+ MongoDB](https://medium.com/go-language/iris-go-framework-mongodb-552e349eab9c)
|
* [Iris Go 矿建+ 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-build-a-file-upload-form-using-dropzonejs-and-go-8fb9f258a991)
|
||||||
|
@ -1120,14 +1030,11 @@ Congratulations, since you've made it so far, we've crafted just for you some ne
|
||||||
|
|
||||||
你可以通过[PayPal](https://www.paypal.me/kataras) 或 [BTC](https://iris-go.com/v8/donate)来捐赠这个项目,这样可以促进开发者们创造更棒、更优秀的Iris。
|
你可以通过[PayPal](https://www.paypal.me/kataras) 或 [BTC](https://iris-go.com/v8/donate)来捐赠这个项目,这样可以促进开发者们创造更棒、更优秀的Iris。
|
||||||
|
|
||||||
Help this project to continue deliver awesome and unique features with the higher code quality as possible by donating any amount via [PayPal](https://www.paypal.me/kataras) or [BTC](https://iris-go.com/v8/donate).
|
|
||||||
|
|
||||||
[如何贡献代码](CONTRIBUTING.md)
|
[如何贡献代码](CONTRIBUTING.md)
|
||||||
For more information about contributing to the Iris project please check the [CONTRIBUTING.md file](CONTRIBUTING.md).
|
|
||||||
|
|
||||||
### 我们期待你能帮助我们翻译Iris文档
|
### 我们期待你能帮助我们翻译Iris文档
|
||||||
|
|
||||||
Iris需要你的帮助,你可以帮助我们翻译[README](README.md)和https://iris-go.com ,同时你也会得到奖励的。
|
Iris需要你的帮助,帮助我们翻译[README](README.md)和https://iris-go.com ,同时你也会得到奖励的。
|
||||||
|
|
||||||
你可以在这里https://github.com/kataras/iris/issues/796 看到详细的有关翻译的信息
|
你可以在这里https://github.com/kataras/iris/issues/796 看到详细的有关翻译的信息
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user