iris Context 上下文

上下文 iris.Context 是服务器用于所有客户端的中间人 "对象"。 对于每一个新的连接,会从 sync.Pool 中获取一个新上下文对象。

上下文 iris.Context 是 iris 的 http 流中最重要的部分。开发者从客户端的请求上下文中获取请求信息,然后通过一个上下文开发送响应到客户端。

// 
// 
// context 是 context.Context 子包的一个实现。
// context.Context 是很好扩展,所以开发者可以按照实际所需重写它的方法。
type Context interface {
    // ResponseWriter 如期返回一个兼容 http.ResponseWriter 的 响应writer。
    ResponseWriter() ResponseWriter
    // ResetResponseWriter 应该改变或者升级上下文的 ResponseWriter。
    ResetResponseWriter(ResponseWriter)

    // Request 方法如期返回原始的 *http.Request。
    Request() *http.Request

    // SetCurrentRouteName 方法设置内部路由名称,为了当开发者调用
    // `GetCurrentRoute()` 方法的时候能够正确返回当前 「只读」 路由。
    // 它使用 Router 初始化,如果你手动更改了名称,除了当你是使用`GetCurrentRoute()` 
    // 的时候将获取到其他路由,其它没啥变化。
    // 为了从上下文中执行一个不同的路径,你应该使用 `Exec` 函数,
    // 或者通过 `SetHandlers/AddHandler` 函数改变处理方法。

    SetCurrentRouteName(currentRouteName string)
    // GetCurrentRoute 返回当前注册到当前请求路径的 「只读」路由。
    GetCurrentRoute() RouteReadOnly

    // AddHandler 可以在服务时添加处理方法到当前请求,但是这些处理方法不会持久化到路由。
    // 
    // Router 将会调用这些添加到某个路由的处理方法。如果 AddHandler 被调用,
    // 那么处理方法将被添加到已经定义的路由的处理方法的后面。
    AddHandler(...Handler)
    // SetHandlers 替换所有原有的处理方法。
    SetHandlers(Handlers)
    // Handlers 记录当前的处理方法。
    Handlers() Handlers

    // HandlerIndex 设置当前上下文处理方法链中当前索引。
    // 如果传入 -1 ,不会当前索引。
    //
    // 也可以查看 Handlers(), Next() and StopExecution()。
    HandlerIndex(n int) (currentIndex int)
    // HandlerName 返回当前路由名称,方便调试。
    HandlerName() string
    // Next 调用从处理方法链中选择剩下的进行调用,他应该被用于一个中间件中。
    // 
    // 提醒:自定义的上下文应该重写这个方法,以便能够传入他自己的 context.Context 实现。
    Next()
    // NextHandler 从处理链中返回下一个处理方法(但不执行)。
    // 
    // 为了执行下一个 ,可以使用 .Skip() 跳过某个处理方法。
    NextHandler() Handler
    // Skip 从处理链中 忽略/跳过 下一个处理方法。
    // 它应该在中间件内使用。
    Skip()
    // 如果调用了 StopExecution ,接下来的 .Next 调用将被局略。
    StopExecution()
    // IsStopped 检查当前位置的Context是否是255, 如果是, 则返回true, 意味着 StopExecution() 被调用了。
    IsStopped() bool

    //  +------------------------------------------------------------+
    //  | 当前的 "user/request" 存储                                    |
    //  | 处理方法之间共享信息 - Values().                                  |
    //  | 保存并获取路径参数 - Params()                                    |
    //  +------------------------------------------------------------+

    // Params 返回当前URL中的命名参数。命名的路径参数是被保存在这里的。
    // 这个存储对象随着整个上下文,存活于每个请求声明周期。
    Params() *RequestParams

    // Values 返回当前 「用户」的存储信息。
    // 命名路径参数和任何可选数据可以保存在这里。
    // 这个存储对象,也是存在于整个上下文,每个请求的声明周期中。
    // 
    // 你可以用这个函数设置和获取用于在处理方法和中间件之间共享信息的局部值。
    Values() *memstore.Store
    // Translate 是 i18n 函数,用于本地化。它调用 Get("translate") 返回翻译值。
    //
    // 示例:https://github.com/kataras/iris/tree/master/_examples/miscellaneous/i18n
    Translate(format string, args ...interface{}) string

    //  +------------------------------------------------------------+
    //  | 路径, 主机, 子域名, IP, HTTP 头 等                            |
    //  +------------------------------------------------------------+

    // Method 返回 request.Method, 客户端的请求方法。
    Method() string
    // Path 返回完整请求路径,如果 EnablePathEscape 为 True,将会转义。
    Path() string
    // RequestPath 返回转义过的请求完整路径。
    RequestPath(escape bool) string

    // Host 返回当前URL的主机部分。
    Host() string
    // Subdomain 返回当前请求的子域名,如果有。
    // 提醒,这个方法可能在某些情况下不能正常使用。
    Subdomain() (subdomain string)
    // RemoteAddr 尝试解析并返回客户端正式IP。
    //
    // 基于允许的头名称,可以通过 Configuration.RemoteAddrHeaders  修改。
    //
    // 如果基于这些请求头的解析失败,将会 Request 的 `RemoteAddr` 字段,它在 Http 处理方法之前有 server 填充。
    //
    // 查看 `Configuration.RemoteAddrHeaders`,
    //      `Configuration.WithRemoteAddrHeader(...)`,
    //      `Configuration.WithoutRemoteAddrHeader(...)` 获取更多信息。
    RemoteAddr() string
    // GetHeader 返回指定的请求头值。
    GetHeader(name string) string
    // IsAjax 返回这个请求是否是一个 'ajax request'( XMLHttpRequest)。
    //
    // 不能百分之百确定一个请求是否是Ajax模式。
    // 永远不要信任来自客户端的数据,他们很容易被篡改。
    //
    // 提醒,"X-Requested-With" 头可以被任何客户端修改,对于十分严重的情况,不要依赖于 IsAjax。
    // 试试另外的鉴别方式,例如,内容类型(content-type)。
    // 有很多描述这些问题的博客并且提供了很多不同的解决方案,这就是为什么说 `IsAjax` 
    // 太简单,只能用于一般目的。
    //
    // 更多请看: https://developer.mozilla.org/en-US/docs/AJAX
    // 以及: https://xhr.spec.whatwg.org/
    IsAjax() bool

    //  +------------------------------------------------------------+
    //  | 响应头助手                                                   |
    //  +------------------------------------------------------------+

    // Header 添加响应头到响应 writer。
    Header(name string, value string)

    // ContentType 设置响应头 "Content-Type" 为 'cType'。
    ContentType(cType string)
    // GetContentType 返回响应头 "Content-Type" 的值。
    GetContentType() string

    // StatusCode 设置响应状态码。
    // 也可查看 .GetStatusCode。
    StatusCode(statusCode int)
    // GetStatusCode 返回当前响应的状态码。
    // 也可查阅 StatusCode。
    GetStatusCode() int

    // Redirect 发送一个重定向响应到客户端,接受两个参数,字符串和可选的证书。
    // 第一个参数是重定向的URL,第二个是重定向状态码,默认是302。
    // 如果必要,你可以设置为301,代表永久转义。
    Redirect(urlToRedirect string, statusHeader ...int)

    //  +------------------------------------------------------------+
    //  | 各种请求和 POST 数据                                         |
    //  +------------------------------------------------------------+

    // URLParam 返回请求中的参数,如果有。
    URLParam(name string) string
    // URLParamInt 从请求返回 int 类型的URL参数,如果解析失败,返回错误。
    URLParamInt(name string) (int, error)
    // URLParamInt64 从请求返回 int64 类型的参数,如果解析失败,返回错误。
    URLParamInt64(name string) (int64, error)
    // URLParams 返回请求查询参数映射,如果没有,返回为空。
    URLParams() map[string]string

    // FormValue 返回一个表单值。
    FormValue(name string) string
    // FormValues 从 data,get,post 和 查询参数中返回所有的数据值以及他们的键。
    //
    // 提醒: 检查是否是 nil 是很有必要的。
    FormValues() map[string][]string
    // PostValue 仅仅根据名称返回表单的post值,类似于 Request.PostFormValue。
    PostValue(name string) string
    // FormFile 返回键指定的第一个文件。
    // 如果有必要,FormFile 调用 ctx.Request.ParseMultipartForm 和 ParseForm。
    //
    // 类似于 Request.FormFile.
    FormFile(key string) (multipart.File, *multipart.FileHeader, error)

    //  +------------------------------------------------------------+
    //  | 自定义 HTTP 错误                                             |
    //  +------------------------------------------------------------+

    // NotFound 发送一个 404 错误到客户端,使用自定义的错误处理方法。
    // 如果你不想剩下的处理方法被执行,你可能需要去调用 ctx.StopExecution()。
    // 你可以将错误码改成更具体的,例如:
    // users := app.Party("/users")
    // users.Done(func(ctx context.Context){ if ctx.StatusCode() == 400 { /*  custom error code for /users */ }})
    NotFound()

    //  +------------------------------------------------------------+
    //  | Body Readers                                               |
    //  +------------------------------------------------------------+

    // SetMaxRequestBodySize 设置请求体大小的上限,应该在读取请求体之前调用。
    SetMaxRequestBodySize(limitOverBytes int64)

    // UnmarshalBody 读取请求体,并把它绑定到一个任何类型或者指针的值。
    // 使用实例: context.ReadJSON, context.ReadXML。
    UnmarshalBody(v interface{}, unmarshaler Unmarshaler) error
    // ReadJSON 从请求体读取 JSON,并把它绑定到任何json有效类型的值。
    ReadJSON(jsonObject interface{}) error
    // ReadXML 从请求体读取 XML,并把它绑定到任何xml有效类型的值。
    ReadXML(xmlObject interface{}) error
    // ReadForm 是用表单数据绑定 formObject,支持任何类型的结构体。
    ReadForm(formObject interface{}) error

    //  +------------------------------------------------------------+
    //  | Body (raw) Writers                                         |
    //  +------------------------------------------------------------+

    // Write 将数据作为一个HTTP响应的一部分写入连接。
    // 
    // 如果 WriteHeader 还没有调用,Write 将在 写入数据之前调用 WriteHeader(http.StatusOK)。
    // 如果 Header 没有 Content-Type,Write 添加一个 Content-Type,设置为写入数据的前
    // 512 字节的类型。
    //
    // 取决于 HTTP 版本和客户端,调用 Write 或者 WriteHeader 可能组织以后读取 Request.Body。
    // 对于 HTTP/1.x 请求,处理方法应该在写入响应之前读取所有必要的请求体数据。一旦 HTTP 头被清掉
    // (显示调用 Flusher.Flush 或者写入了足够的数据触发了清空操作),请求体可能变得不可用。
    // 对于 HTTP/2 请求,Go HTTP 服务器允许在写入响应的同时读取请求体。然而,这种行为可能不被所有
    // HTTP/2 客户端支持。处理方法应该尽可能读取最大量的数据在写入之前。
    Write(body []byte) (int, error)
    // Writef 根据格式声明器格式化,然后写入响应。
    //
    // 返回写入的字节数量以及任何写入错误。
    Writef(format string, args ...interface{}) (int, error)
    // WriteString 将一个简单的字符串写入响应。
    //
    // 返回写入的字节数量以及任何写入错误。
    WriteString(body string) (int, error)
    // WriteWithExpiration 很像 Write,但是它发送了一个失效时间,它会被每个包级别的
    // `StaticCacheDuration` 字段刷新。
    WriteWithExpiration(body []byte, modtime time.Time) (int, error)
    // StreamWriter 注册给定的流用于发布响应体。
    //
    // 这个函数可能被用于一下这些情况:
    // 
    //     * 如果响应体太大(超过了iris.LimitRequestBodySize)
    //     * 如果响应体是慢慢从外部资源流入
    //     * 如果响应体必须分片流向客户端(例如 `http server push`)
    StreamWriter(writer func(w io.Writer) bool)

    //  +------------------------------------------------------------+
    //  | 带压缩的 Body Writers                            |
    //  +------------------------------------------------------------+
    // 如果客户端支持 gzip 压缩,ClientSupportsGzip 返回 true。
    ClientSupportsGzip() bool
    // WriteGzip accepts bytes, which are compressed to gzip format and sent to the client.
    // WriteGzip 接受压缩成 gzip 格式的字节然后发送给客户端,并返回写入的字节数量和错误(如果错误不支持 gzip 格式)
    //
    // 这个函数写入临时的 gzip 内容,ResponseWriter 不会改变。
    WriteGzip(b []byte) (int, error)
    // TryWriteGzip 接受 gzip 格式压缩的字节,然后发送给客户端。
    // 如果客户端不支持 gzip,就按照他们原来未压缩的样子写入。
    //
    // 这个函数写入临时的 gzip 内容,ResponseWriter 不会改变。
    TryWriteGzip(b []byte) (int, error)
    // GzipResponseWriter converts the current response writer into a response writer
    // GzipResponseWriter 将当前的响应 writer 转化为一个 gzip 响应 writer。
    // 当它的 .Write 方法被调用的时候,数据被压缩成 gzip 格式然后把他们写入客户端。
    //
    // 也可以使用 .Disable 禁用以及使用 .ResetBody 回滚到常规的响应写入器。
    GzipResponseWriter() *GzipResponseWriter
    // Gzip 开启或者禁用 gzip 响应写入器,如果客户端支持 gzip 压缩,所以接下来的响应数据将被作为
    // 压缩的 gzip 数据发送给客户端。
    Gzip(enable bool)

    //  +------------------------------------------------------------+
    //  | 富文本内容渲染器                                              |
    //  +------------------------------------------------------------+

    // ViewLayout 设置 「布局」选项,如果随后在相同的请求中 .View 被调用。
    // 当需要去改变或者设置处理链中前一个方法的布局时很有用。
    // 
    // 注意 'layoutTmplFile' 参数可以被设置为 iris.NoLayout 或者 view.NoLayout 去禁用某个试图渲染动作的布局。
    // 它禁用了配置项的布局属性。
    //
    // 也可查看 .ViewData 和 .View。
    // 
    // 示例:https://github.com/kataras/iris/tree/master/_examples/view/context-view-data/
    ViewLayout(layoutTmplFile string)

    // ViewData 保存一个或者多个键值对为了在后续的 .View 被调用的时候使用。
    // 当需要处理链中前一个处理器的模板数据的时候是很有用的。
    //
    // 如果 .View 的绑定参数不是 nil 也不是 map 类型,这些数据就会被忽略,绑定是有优先级的,所以住路由的处理方法仍然有效。
    // 如果绑定是一个map或者context.Map,这些数据然后就被添加到视图数据然后传递给模板。
    //
    // .View 调用之后,这些数据不会被丢掉,为了在必要的时候重用(再次声明,同一个请求中),为了清除这些数据,开发者可以调用
    // ctx.Set(ctx.Application().ConfigurationReadOnly().GetViewDataContextKey(), nil)。
    // 
    // 如果 'key' 是空的,然后 值被作为它的(struct 或者 map)添加,并且开发者不能添加其他值。
    //
    // 推荐查看 .ViewLayout 和 .View。
    //
    // 示例:https://github.com/kataras/iris/tree/master/_examples/view/context-view-data/
    ViewData(key string, value interface{})

    // GetViewData 返回 `context#ViewData` 注册的值。
    // 返回值是 `map[string]interface{}` 类型,这意味着如果一个自定义的结构体被添加到 ViewData, 这个函数将把它解析成
    // map,如果失败返回 nil。
    // 如果不同类型的值或者没有数据通过 `ViewData` 注册,检查是否为nil总是好的编程规范。
    // 
    // 类似于 `viewData := ctx.Values().Get("iris.viewData")` 或者 
    // `viewData := ctx.Values().Get(ctx.Application().ConfigurationReadOnly().GetViewDataContextKey())`。
    GetViewData() map[string]interface{}

    // View 基于适配的视图引擎渲染模板。第一个参数是接受相对于视图引擎目录的文件名,
    // 例如如果目录是 "./templates",想要渲染: "./templates/users/index.html"
    // 你应该传递 "users/index.html" 作为文件名参数。
    // 也可以查看 .ViewData 和 .ViewLayout。
    // 示例:https://github.com/kataras/iris/tree/master/_examples/view/
    View(filename string) error

    // Binary 将原生字节作为二进制数据返回。
    Binary(data []byte) (int, error)
    // Text 将字符串作为空白文本返回。
    Text(text string) (int, error)
    // HTML 将字符串作为 text/html 返回.
    HTML(htmlContents string) (int, error)
    // JSON 格式化给定的数据并且返回 json 数据。
    JSON(v interface{}, options ...JSON) (int, error)
    // JSONP 格式化给定的数据并且返回 json 数据。
    JSONP(v interface{}, options ...JSONP) (int, error)
    // XML 格式话给定数据,并返回 XML 数据。
    XML(v interface{}, options ...XML) (int, error)
    // Markdown 解析 markdown 数据为 HTML 返回给客户端。
    Markdown(markdownB []byte, options ...Markdown) (int, error)

    //  +------------------------------------------------------------+
    //  | 文件响应                                                     |
    //  +------------------------------------------------------------+

    // ServeContent 返回的内容头是自动设置的,接受三个参数,它是一个低级的函数,你可以调用 .ServeFile(string,bool)/SendFile(string,string)。
    // 这个函数调用之后,你可以定义自己的 "Content-Type" 头,不要实现 resuming,而应该使用 ctx.SendFile。
    ServeContent(content io.ReadSeeker, filename string, modtime time.Time, gzipCompression bool) error
    // ServeFile 渲染一个视图文件,如果要发送一个文件(例如zip文件)到客户端,你应该使用 SendFile(serverfilename,clientfilename)。
    // 接受两个参数:
    // filename/path (string)
    // gzipCompression (bool)
    // 这个函数调用之后,你可以定义自己的 "Content-Type" 头,这个函数没有实现 resuming,你应该使用 ctx.SendFile。
    ServeFile(filename string, gzipCompression bool) error
    // SendFile 发送强制下载的文件到客户端
    // 
    // 使用这个而不是 ServeFile 用于大文件下载到客户端。
    SendFile(filename string, destinationName string) error

    //  +------------------------------------------------------------+
    //  | Cookies                                                    |
    //  +------------------------------------------------------------+

    // SetCookie 添加cookie
    SetCookie(cookie *http.Cookie)
    // SetCookieKV 添加一个 cookie,仅仅接受一个名字(字符串)和一个值(字符串)
    //
    // 如果你用这个方法设置cookie,它将在两小时之后失效。
    // 如果你想去设置或者改变更多字段,使用 ctx.SetCookie 或者 http.SetCookie。
    SetCookieKV(name, value string)
    // GetCookie 通过名称返回值,如果没找到返回空字符串。
    GetCookie(name string) string
    // RemoveCookie 通过名字删除 cookie。
    RemoveCookie(name string)
    // VisitAllCookies 接受一个 visitor 循环每个cookie,visitor 接受两个参数:名称和值。
    VisitAllCookies(visitor func(name string, value string))

    // MaxAge 返回 "cache-control" 请求头的值,单位为:秒,类型为 int64
    // 如果头没有发现或者解析失败返回 -1。
    MaxAge() int64

    //  +------------------------------------------------------------+
    //  | 高级部分: 响应记录器和事务                                     |
    //  +------------------------------------------------------------+

    // Record 转化上下文基本的 responseWriter 为 ResponseRecorder,它可以被用于在任何时候重置内容体,
    // 重置响应头,获取内容体,获取和设置状态码。
    Record()
    // Recorder 返回上下文的 ResponseRecorder,如果没有 recording 然后它将开始记录并返回新的上下文的 ResponseRecorder。
    Recorder() *ResponseRecorder
    // IsRecording 返回响应记录器以及一个bool值
    // true 表示响应记录器正在记录状态码,内容体,HTTP 头以及更多,否则就是 false
    IsRecording() (*ResponseRecorder, bool)

    // BeginTransaction 开启一个有界事务。
    //
    // 你可以搜索第三方文章或者查看事务 Transaction 如何工作(这里相当简单特别)。
    //
    // 记着这个是唯一的,也是新的,目前为止,我没有在这个项目中看到任何例子和代码,就大多数 iris 功能而言。
    // 它没有覆盖所有路径,例如数据库,这个应该由你使用的创建数据库连接的库管理,这个事务域仅仅用于上下文响应,
    //
    // 阅读 https://github.com/kataras/iris/tree/master/_examples/ 查看更多。
    BeginTransaction(pipe func(t *Transaction))
    // 如果调用 SkipTransactions 将跳过剩余的事务,或者如果在第一个事务之前调用,将跳过所有
    SkipTransactions()
    // TransactionsSkipped 返回事务到底被跳过还是被取消了。
    TransactionsSkipped() bool

    // Exec根据这个上下文调用framewrok的ServeCtx,但是改变了方法和路径,就像用户请求的那样,但事实并非如此。
    //
    // 离线意味着路线已注册到 iris 并具有正常路由所具有的所有功能。
    // 但是它不能通过浏览获得,它的处理程序仅在其他处理程序的上下文调用它们时执行,它可以验证路径,拥有会话,路径参数等。
    //
    // 你可以通过 app.GetRoute("theRouteName") 找到路由,你可以设置一个路由名称如:
    // myRoute := app.Get("/mypath", handler)("theRouteName")
    // 这个将给路由设置一个名称并且返回它的 RouteInfo 实例为了进一步使用。
    //
    // 它不会更改全局状态,如果路由处于“脱机”状态,它将保持脱机状态。
    //
    // app.None(...) and app.GetRoutes().Offline(route)/.Online(route, method)
    //
    // 实例:https://github.com/kataras/iris/tree/master/_examples/routing/route-state
    //
    // 用户可以通过简单调用得到响应:rec := ctx.Recorder(); rec.Body()/rec.StatusCode()/rec.Header()
    //
    // Context 的 Values 和 Session 被记住为了能够通过结果路由通信,
    //
    // 它仅仅由于特别案例,99% 的用户不会用到的。
    Exec(method string, path string)

    // Application 返回 属于这个上下文的 iris 实例。
    // 值得留意的是这个函数返回 Application 的一个接口,它包含的方法能够安全地在运行是执行。
    // 为了开发者的安全性,整个 app 的 字段和方法这里是不可用的。
    Application() Application

下一章:iris 动态路由参数

iris 拥有最简单、最强大的路由处理能力。Iris 有自己的路由解释器,可以解析路径声明语法、动态路径参数以及进行计算。iris 路由语法解释器称为「宏」(Macros)。它不仅支持最基本的路由声明,而且支持正则表 ...