> For the complete documentation index, see [llms.txt](https://go.sai.show/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://go.sai.show/part05.context/5.02-contextbeegocontext-she-ji-fen-xi.md).

# 5.02 Context-Beego Context设计分析

## PART1. Beego Context设计

### 1.1 `Context`

在之前的课程中说过,Beego的[Context](https://github.com/beego/beego/blob/develop/server/web/context/context.go#L72)中,既有`Input`又有`Request`;既有`Output`又有`ResponseWriter`.很明显功能上是重叠的,道理上来讲,`Input`和`Request`留一个即可;`Output`和`ResponseWriter`留一个即可;或者将`Request`放到`Input`中,将`ResponseWriter`放到`Output`中.

```go
type Context struct {
	Input          *BeegoInput
	Output         *BeegoOutput
	Request        *http.Request
	ResponseWriter *Response
	_xsrfToken     string
}
```

这样的设计,对于使用者而言,很难理解`Input`和`Request`、`Output`和`ResponseWriter`之间的区别和各自的使用场景.

其中:

* `Input`:对输入的封装
* `Output`:对输出的封装
* `ResponseWriter`:对响应的封装

### 1.2 `BeegoInput`

[BeegoInput](https://github.com/beego/beego/blob/develop/server/web/context/input.go#L46)是对输入的封装

```go
type BeegoInput struct {
	Context       *Context
	CruSession    session.Store
	pnames        []string
	pvalues       []string
	data          map[interface{}]interface{}
	dataLock      sync.RWMutex
	RequestBody   []byte
	RunMethod     string
	RunController reflect.Type
}
```

* `BeegoInput.Context`:反向引用了`Context`.而`Context`中也通过`Input`字段引用了`BeegoInput`,这种A引用B同时B引用A的场景在GO中是比较常见的设计
* `BeegoInput.CruSession`:耦合了Session
* `BeegoInput.pnames`:记录了参数路径的参数名
* `BeegoInput.pvalues`:记录了参数路径的参数值
* `BeegoInput.data`:记录了输入的数据(具体是啥我也不知道,没用过这个框架)
* `BeegoInput.RequestBody`:若将[`web.Config.CopyRequestBody`](https://github.com/beego/beego/blob/develop/server/web/config.go#L73)设置为true,则框架会将原生的`http.Request.Body`中的内容读到这个字段上

### 1.3 `BeegoOutput`

[BeegoOutput](https://github.com/beego/beego/blob/develop/server/web/context/output.go#L39)是对输出的封装

```go
// BeegoOutput does work for sending response header.
type BeegoOutput struct {
	Context    *Context
	Status     int
	EnableGzip bool
}
```

* `BeegoOutput.Context`:同样反向引用了`Context`
* `BeegoOutput.Status`:HTTP响应码
* `BeegoOutput.EnableGzip`:在写Response时是否启用Gzip压缩

### 1.4 `Response`

[`Response`](https://github.com/beego/beego/blob/develop/server/web/context/context.go#L334)

```go
type Response struct {
	http.ResponseWriter
	Started bool
	Status  int
	Elapsed time.Duration
}
```

* 组合了原生的`http.ResponseWriter`
* `Response.Started`:若该字段为true,则表示当前`Response`结构体实例已经被写入过了

## PART2. Beego 处理输入的方法

### 2.1 负责处理来自`http.Request.Body`输入的方法

#### 2.1.1 `Context`的Bind族方法

![Beego处理输入的方法-Bind族](https://1407465062-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FyoSgzgsmuneFp7VNWUbE%2Fuploads%2Fgit-blob-a1fc5cbd30f4182721f218144cbe83280f080810%2FBeego%E5%A4%84%E7%90%86%E8%BE%93%E5%85%A5%E7%9A%84%E6%96%B9%E6%B3%95-Bind%E6%97%8F.png?alt=media)

`Context`中的`Bind`族方法,用于将不同形式的`Body`转化为具体的结构体.

#### 2.1.2 `Input`的`Bind()`方法

[`Input.Bind()`](https://github.com/beego/beego/blob/develop/server/web/context/input.go#L443)方法用于将输入的一部分(通过`key`参数指定)绑定到给定的`dest`上(这个`dest`可能是变量/map/结构体等).

### 2.2 负责处理来自其他部位的方法

![BeegoInput中负责从其他部位获取输入的方法](https://1407465062-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FyoSgzgsmuneFp7VNWUbE%2Fuploads%2Fgit-blob-746165312f54978b653eeaf0e7d7d871d980eabd%2FBeegoInput%E4%B8%AD%E8%B4%9F%E8%B4%A3%E4%BB%8E%E5%85%B6%E4%BB%96%E9%83%A8%E4%BD%8D%E8%8E%B7%E5%8F%96%E8%BE%93%E5%85%A5%E7%9A%84%E6%96%B9%E6%B3%95.png?alt=media)

这些方法负责从`http.Request`的其他部位(例如Header、查询参数等)上获取输入

![BeegoInput中负责从其他部位获取输入的方法-2](https://1407465062-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FyoSgzgsmuneFp7VNWUbE%2Fuploads%2Fgit-blob-4e90a6dd84f13aea873526289d1ad3de524d75fa%2FBeegoInput%E4%B8%AD%E8%B4%9F%E8%B4%A3%E4%BB%8E%E5%85%B6%E4%BB%96%E9%83%A8%E4%BD%8D%E8%8E%B7%E5%8F%96%E8%BE%93%E5%85%A5%E7%9A%84%E6%96%B9%E6%B3%95-2.png?alt=media)

这里的`GetData()`中的data指的是其他Beego内部的其他组件或Middleware向`BeegoInput.data`中写入的数据

### 2.3 各种判断的方法

![Input中各种判断的方法](https://1407465062-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FyoSgzgsmuneFp7VNWUbE%2Fuploads%2Fgit-blob-15ff70d3b861085c4a5ea3687b788ff854f66aef%2FInput%E4%B8%AD%E5%90%84%E7%A7%8D%E5%88%A4%E6%96%AD%E7%9A%84%E6%96%B9%E6%B3%95.png?alt=media)

实际上这一类方法完全没必要提供,完全可以让用户自己判断,例如`if method == http.MethodGet`的方式去判断.

## PART3. Beego 处理输出的方法

### 3.1 将输入序列化之后输出的方法

![Context中处理序列化输出的方法](https://1407465062-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FyoSgzgsmuneFp7VNWUbE%2Fuploads%2Fgit-blob-ac715f19f3a673390d57d2f2fd59a592a91d8168%2FContext%E4%B8%AD%E5%A4%84%E7%90%86%E5%BA%8F%E5%88%97%E5%8C%96%E8%BE%93%E5%87%BA%E7%9A%84%E6%96%B9%E6%B3%95.png?alt=media)

这一类方法(Resp族的方法)将入参`data`转化成对应的格式,然后输出到响应中

### 3.2 渲染模板输出的方法

![Controller中渲染模板输出的方法](https://1407465062-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FyoSgzgsmuneFp7VNWUbE%2Fuploads%2Fgit-blob-815eb9b90944d46af39f629763dabd1634339d91%2FController%E4%B8%AD%E6%B8%B2%E6%9F%93%E6%A8%A1%E6%9D%BF%E8%BE%93%E5%87%BA%E7%9A%84%E6%96%B9%E6%B3%95.png?alt=media)

这一类方法是在`web.Controller`结构体上的,用于渲染页面并输出

### 3.3 输出各种格式数据的方法

![Output中定义输出各种格式数据的方法](https://1407465062-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FyoSgzgsmuneFp7VNWUbE%2Fuploads%2Fgit-blob-0fba5d39e2a7e5394abc6bf877ff2bd4a42eeac2%2FOutput%E4%B8%AD%E5%AE%9A%E4%B9%89%E8%BE%93%E5%87%BA%E5%90%84%E7%A7%8D%E6%A0%BC%E5%BC%8F%E6%95%B0%E6%8D%AE%E7%9A%84%E6%96%B9%E6%B3%95.png?alt=media)

这一类方法是在`context.BeegoOutput`结构体上的,实际上3.1小节中的Resp族的方法,最终调用的是`BeegoOutput`上对应数据格式的方法.

而且,`context.BeegoOutput`还有一些直接输出Body(`Body()`方法)和Header(`Header()`方法)的方法.

也就是说,写响应时,可以使用`Context`,也可以使用`Context.BeegoOutput`,看自己喜好即可.当然,这也会导致框架的使用者会有不同的风格来控制输出.
