Go web 框架概览

Beego

Controller抽象

Beego 是基于MVC(Model-View-Controller)的,所以它定义了一个核心接口ControllerInterface。
Controllerlnterface 定义了一个控制器必须要解决什么问题。
同时Contollerlnterface的默认实现Controller提供了实现自定义控制器的各种辅助方法,所以在Beego里面,一般都是组合Controller 来实现自己的 Controller。

// ControllerInterface is an interface to uniform all controller handler.
type ControllerInterface interface {
	Init(ct *context.Context, controllerName, actionName string, app interface{})
	Prepare()
	Get()
	Post()
	Delete()
	Put()
	Head()
	Patch()
	Options()
	Trace()
	Finish()
	Render() error
	XSRFToken() string
	CheckXSRFCookie() bool
	HandlerFunc(fn string) bool
	URLMapping()
}
type UserController struct {
	web.Controller
}

func (c *UserController) GetUser() {
	c.Ctx.WriteString("hello,world")
}

测试代码

func TestUserController(t *testing.T) {
	web.BConfig.CopyRequestBody = true
	c := &UserController{}
	web.Router("/user", c, "get:GetUser")
	// 监听 8081 端口
	web.Run(":8081")
}

浏览器访问http://localhost:8081/user
注意到用户虽然被要求组合Controller,但是路由注册和服务器启动是通过另外一套机制来完成的。

HttpServer 和 ControllerRegister

Controllerlnterface 可以看做核心接口,因为它直接体现了Beego的设计初衷:MVC模式。同时它也是用户核心接入点。
但是如果从功能特性上来说,HttpServer和ControllerRegister 才是核心。

  • HttpServer:代表一个”服务器”,大多数时候它就是一个进程。
  • ControllerRegister:真正干活的人。注册路由,路由匹配和执行业务代码都是透过它来完成的。
// HttpServer defines beego application with a new PatternServeMux.
type HttpServer struct {
	Handlers           *ControllerRegister
	Server             *http.Server
	Cfg                *Config
	LifeCycleCallbacks []LifeCycleCallback
}

// ControllerRegister containers registered router rules, controller handlers and filters.
type ControllerRegister struct {
	routers      map[string]*Tree
	enablePolicy bool
	enableFilter bool
	policies     map[string]*Tree
	filters      [FinishRouter + 1][]*FilterRouter
	pool         sync.Pool

	// the filter created by FilterChain
	chainRoot *FilterRouter

	// keep registered chain and build it when serve http
	filterChains []filterChainConfig

	cfg *Config
}

Context抽象

用户操作请求和响应是通过Ctx来达成的。它代表的是整个请求执行过程的上下文。
进一步,Beego将Context细分了几个部分:

  • Input:定义了很多和处理请求有关的方法
  • Output:定义了很多和响应有关的方法
  • Response:对http.ResponseWriter的二次封装
// Context Http request context struct including BeegoInput, BeegoOutput, http.Request and http.ResponseWriter.
// BeegoInput and BeegoOutput provides an api to operate request and response more easily.
type Context struct {
	Input          *BeegoInput
	Output         *BeegoOutput
	Request        *http.Request
	ResponseWriter *Response
	_xsrfToken     string
}
func (c *UserController) CreateUser() {
	u := &User{}
	err := c.Ctx.BindJSON(u)
	if err != nil {
		c.Ctx.WriteString(err.Error())
		return
	}

	_ = c.Ctx.JSONResp(u)
}

type User struct {
	Name string
}
// Controller defines some basic http request handler operations, such as
// http context, template and view, session and xsrf.
type Controller struct {
	// context data
	Ctx  *context.Context
	Data map[interface{}]interface{}

	// route controller info
	controllerName string
	actionName     string
	methodMapping  map[string]func() //method:routertree
	AppController  interface{}

	// template data
	TplName        string
	ViewPath       string
	Layout         string
	LayoutSections map[string]string // the key is the section name and the value is the template name
	TplPrefix      string
	TplExt         string
	EnableRender   bool

	// xsrf data
	EnableXSRF bool
	_xsrfToken string
	XSRFExpire int

	// session
	CruSession session.Store
}

核心抽象总结

ControllerRegister 最为基础,它解决了路由注册和路由匹配这个基础问题。
Context和Controller为用户提供了丰富API,用于辅助构建系统。
HttpServer作为服务器抽象,用于管理应用生命周期和资源隔离单位。
image.png

Gin

IRoutes接口

核心接口lRoutes:提供的是注册路由的抽象。它的实现类Engine 类似于ControllerRegister。
Use方法提供了用户接入自定义逻辑的能力,这个一般情况下也被看做是插件机制。
还额外提供了静态文件的接口。

// IRoutes defines all router handle interface.
type IRoutes interface {
	Use(...HandlerFunc) IRoutes

	Handle(string, string, ...HandlerFunc) IRoutes
	Any(string, ...HandlerFunc) IRoutes
	GET(string, ...HandlerFunc) IRoutes
	POST(string, ...HandlerFunc) IRoutes
	DELETE(string, ...HandlerFunc) IRoutes
	PATCH(string, ...HandlerFunc) IRoutes
	PUT(string, ...HandlerFunc) IRoutes
	OPTIONS(string, ...HandlerFunc) IRoutes
	HEAD(string, ...HandlerFunc) IRoutes

	StaticFile(string, string) IRoutes
	StaticFileFS(string, string, http.FileSystem) IRoutes
	Static(string, string) IRoutes
	StaticFS(string, http.FileSystem) IRoutes
}

Gin没有Controller的抽象。所以在这方面我和Gin的倾向比较一致,即MVC应该是用户组织Web项目的模式,而不是我们中间件设计者要考虑的。

Engine实现

Engine 可以看做是Beego中HttpServer和ControllerRegister的合体。

  • 实现了路由树功能,提供了注册和匹配路由的功能
  • 本身可以作为一个Handler传递到http包,用于启动服务器

Engine的路由树功能本质上是依赖于methodTree的。

methodTrees 和methodTree

methodTree才是真实的路由树。
Gin 定义了methodTrees,它实际上代表的是森林,即每一个HTTP方法都对应到一棵树。

// Engine is the framework's instance, it contains the muxer, middleware and configuration settings.
// Create an instance of Engine, by using New() or Default()
type Engine struct {
	RouterGroup

	// RedirectTrailingSlash enables automatic redirection if the current route can't be matched but a
	// handler for the path with (without) the trailing slash exists.
	// For example if /foo/ is requested but a route only exists for /foo, the
	// client is redirected to /foo with http status code 301 for GET requests
	// and 307 for all other request methods.
	RedirectTrailingSlash bool

	// RedirectFixedPath if enabled, the router tries to fix the current request path, if no
	// handle is registered for it.
	// First superfluous path elements like ../ or // are removed.
	// Afterwards the router does a case-insensitive lookup of the cleaned path.
	// If a handle can be found for this route, the router makes a redirection
	// to the corrected path with status code 301 for GET requests and 307 for
	// all other request methods.
	// For example /FOO and /..//Foo could be redirected to /foo.
	// RedirectTrailingSlash is independent of this option.
	RedirectFixedPath bool

	// HandleMethodNotAllowed if enabled, the router checks if another method is allowed for the
	// current route, if the current request can not be routed.
	// If this is the case, the request is answered with 'Method Not Allowed'
	// and HTTP status code 405.
	// If no other Method is allowed, the request is delegated to the NotFound
	// handler.
	HandleMethodNotAllowed bool

	// ForwardedByClientIP if enabled, client IP will be parsed from the request's headers that
	// match those stored at `(*gin.Engine).RemoteIPHeaders`. If no IP was
	// fetched, it falls back to the IP obtained from
	// `(*gin.Context).Request.RemoteAddr`.
	ForwardedByClientIP bool

	// AppEngine was deprecated.
	// Deprecated: USE `TrustedPlatform` WITH VALUE `gin.PlatformGoogleAppEngine` INSTEAD
	// #726 #755 If enabled, it will trust some headers starting with
	// 'X-AppEngine...' for better integration with that PaaS.
	AppEngine bool

	// UseRawPath if enabled, the url.RawPath will be used to find parameters.
	UseRawPath bool

	// UnescapePathValues if true, the path value will be unescaped.
	// If UseRawPath is false (by default), the UnescapePathValues effectively is true,
	// as url.Path gonna be used, which is already unescaped.
	UnescapePathValues bool

	// RemoveExtraSlash a parameter can be parsed from the URL even with extra slashes.
	// See the PR #1817 and issue #1644
	RemoveExtraSlash bool

	// RemoteIPHeaders list of headers used to obtain the client IP when
	// `(*gin.Engine).ForwardedByClientIP` is `true` and
	// `(*gin.Context).Request.RemoteAddr` is matched by at least one of the
	// network origins of list defined by `(*gin.Engine).SetTrustedProxies()`.
	RemoteIPHeaders []string

	// TrustedPlatform if set to a constant of value gin.Platform*, trusts the headers set by
	// that platform, for example to determine the client IP
	TrustedPlatform string

	// MaxMultipartMemory value of 'maxMemory' param that is given to http.Request's ParseMultipartForm
	// method call.
	MaxMultipartMemory int64

	// UseH2C enable h2c support.
	UseH2C bool

	// ContextWithFallback enable fallback Context.Deadline(), Context.Done(), Context.Err() and Context.Value() when Context.Request.Context() is not nil.
	ContextWithFallback bool

	delims           render.Delims
	secureJSONPrefix string
	HTMLRender       render.HTMLRender
	FuncMap          template.FuncMap
	allNoRoute       HandlersChain
	allNoMethod      HandlersChain
	noRoute          HandlersChain
	noMethod         HandlersChain
	pool             sync.Pool
	trees            methodTrees
	maxParams        uint16
	maxSections      uint16
	trustedProxies   []string
	trustedCIDRs     []*net.IPNet
}
type methodTree struct {
	method string
	root   *node
}

type methodTrees []methodTree

HandlerFunc 和HandlersChain

HandlerFunc 定义了核心抽象——处理逻辑。
在默认情况下,它代表了注册路由的业务代码。
HandlersChain 则是构造了责任链模式。

// HandlerFunc defines the handler used by gin middleware as return value.
type HandlerFunc func(*Context)

// HandlersChain defines a HandlerFunc slice.
type HandlersChain []HandlerFunc

image.png
最后一个则是封装了业务逻辑的HandlerFunc

Context抽象

image.png
Context也是代表了执行的上下文,提供了丰富的APl:

  • 处理请求的API,代表的是以Get和Bind为前缀的方法
  • 处理响应的APl,例如返回JSON或者XML响应的方法
  • 渲染页面,如HTML方法
// Context is the most important part of gin. It allows us to pass variables between middleware,
// manage the flow, validate the JSON of a request and render a JSON response for example.
type Context struct {
	writermem responseWriter
	Request   *http.Request
	Writer    ResponseWriter

	Params   Params
	handlers HandlersChain
	index    int8
	fullPath string

	engine       *Engine
	params       *Params
	skippedNodes *[]skippedNode

	// This mutex protects Keys map.
	mu sync.RWMutex

	// Keys is a key/value pair exclusively for the context of each request.
	Keys map[string]any

	// Errors is a list of errors attached to all the handlers/middlewares who used this context.
	Errors errorMsgs

	// Accepted defines a list of manually accepted formats for content negotiation.
	Accepted []string

	// queryCache caches the query result from c.Request.URL.Query().
	queryCache url.Values

	// formCache caches c.Request.PostForm, which contains the parsed form data from POST, PATCH,
	// or PUT body parameters.
	formCache url.Values

	// SameSite allows a server to define a cookie attribute making it impossible for
	// the browser to send this cookie along with cross-site requests.
	sameSite http.SameSite
}

抽象总结

image.png

Iris

Application

Application是Iris的核心抽象,它代表的是”应用”。实际上这个语义更加接近Beego的HttpServer和Gin的Engine。
它提供了:

  • 生命周期控制功能,如Shutdown 等方法
  • 注册路由的API
func TestHelloWorld(t *testing.T) {

	app := iris.New()

	app.Get("/", func(ctx iris.Context) {
		_, _ = ctx.HTML("Hello <strong>%s</strong>!", "World")
	})

	_ = app.Listen(":8083")
}

个人认为Application 这个名字不是很合适,比如说有些应用会监听多个端口。不同的端口提供不同的功能,也就是常说的多Server应用。相比之下,HttpServer 或者Engine会更合适一点。

路由相关

Iris的设计非常复杂。在Beego和Gin里面能够明显看到路由树的痕迹,但是在Iris 里面就很难看出来。
和处理路由相关的三个抽象:

  • Route:直接代表了已经注册的路由。在Beego和Gin里面,对应的是路由树的节点
  • APIBuilder:创建 Route的Builder模式,Party 也是它创建的
  • repository:存储了所有的Routes,有点接近Gin的methodTrees的概念
// repository passed to all parties(subrouters), it's the object witch keeps
// all the routes.
type repository struct {
	routes []*Route
	pos    map[string]int
}
// Route contains the information about a registered Route.
// If any of the following fields are changed then the
// caller should Refresh the router.
type Route struct {
	Name       string         `json:"name"`   // "userRoute"
	Method     string         `json:"method"` // "GET"
	methodBckp string         // if Method changed to something else (which is possible at runtime as well, via RefreshRouter) then this field will be filled with the old one.
	Subdomain  string         `json:"subdomain"` // "admin."
	tmpl       macro.Template // Tmpl().Src: "/api/user/{id:uint64}"
	// temp storage, they're appended to the Handlers on build.
	// Execution happens before Handlers, can be empty.
	beginHandlers context.Handlers
	// Handlers are the main route's handlers, executed by order.
	// Cannot be empty.
	Handlers        context.Handlers `json:"-"`
	MainHandlerName string           `json:"mainHandlerName"`
	// temp storage, they're appended to the Handlers on build.
	// Execution happens after Begin and main Handler(s), can be empty.
	doneHandlers context.Handlers

	Path string `json:"path"` // the underline router's representation, i.e "/api/user/:id"
	// FormattedPath all dynamic named parameters (if any) replaced with %v,
	// used by Application to validate param values of a Route based on its name.
	FormattedPath string `json:"formattedPath"`

	// the source code's filename:filenumber that this route was created from.
	SourceFileName   string
	SourceLineNumber int

	// StaticSites if not empty, refers to the system (or virtual if embedded) directory
	// and sub directories that this "GET" route was registered to serve files and folders
	// that contain index.html (a site). The index handler may registered by other
	// route, manually or automatic by the framework,
	// get the route by `Application#GetRouteByPath(staticSite.RequestPath)`.
	StaticSites []context.StaticSite `json:"staticSites"`
	topLink     *Route

	// Sitemap properties: https://www.sitemaps.org/protocol.html
	LastMod    time.Time `json:"lastMod,omitempty"`
	ChangeFreq string    `json:"changeFreq,omitempty"`
	Priority   float32   `json:"priority,omitempty"`
}

个人观点:过于复杂,职责不清晰,不符合一般人的直觉,新人学习和维护门槛高。不要学

Context抽象

Context也是代表上下文
Context本身也是提供了各种处理请求和响应的方法
基本上和Beego和Gin的Context 没啥区别。
比较有特色的是它的Context 支持请求级别的添加Handler,即AddHandler方法。

type Context interface {
	// BeginRequest is executing once for each request
	// it should prepare the (new or acquired from pool) context's fields for the new request.
	//
	// To follow the iris' flow, developer should:
	// 1. reset handlers to nil
	// 2. reset values to empty
	// 3. reset sessions to nil
	// 4. reset response writer to the http.ResponseWriter
	// 5. reset request to the *http.Request
	// and any other optional steps, depends on dev's application type.
	BeginRequest(http.ResponseWriter, *http.Request)
	// EndRequest is executing once after a response to the request was sent and this context is useless or released.
	//
	// To follow the iris' flow, developer should:
	// 1. flush the response writer's result
	// 2. release the response writer
	// and any other optional steps, depends on dev's application type.
	EndRequest()

	// ResponseWriter returns an http.ResponseWriter compatible response writer, as expected.
	ResponseWriter() ResponseWriter
	// ResetResponseWriter should change or upgrade the Context's ResponseWriter.
	ResetResponseWriter(ResponseWriter)

	// Request returns the original *http.Request, as expected.
	Request() *http.Request
	// ResetRequest sets the Context's Request,
	// It is useful to store the new request created by a std *http.Request#WithContext() into Iris' Context.
	// Use `ResetRequest` when for some reason you want to make a full
	// override of the *http.Request.
	// Note that: when you just want to change one of each fields you can use the Request() which returns a pointer to Request,
	// so the changes will have affect without a full override.
	// Usage: you use a native http handler which uses the standard "context" package
	// to get values instead of the Iris' Context#Values():
	// r := ctx.Request()
	// stdCtx := context.WithValue(r.Context(), key, val)
	// ctx.ResetRequest(r.WithContext(stdCtx)).
	ResetRequest(r *http.Request)

	// SetCurrentRouteName sets the route's name internally,
	// in order to be able to find the correct current "read-only" Route when
	// end-developer calls the `GetCurrentRoute()` function.
	// It's being initialized by the Router, if you change that name
	// manually nothing really happens except that you'll get other
	// route via `GetCurrentRoute()`.
	// Instead, to execute a different path
	// from this context you should use the `Exec` function
	// or change the handlers via `SetHandlers/AddHandler` functions.
	SetCurrentRouteName(currentRouteName string)
	// GetCurrentRoute returns the current registered "read-only" route that
	// was being registered to this request's path.
	GetCurrentRoute() RouteReadOnly

	// Do calls the SetHandlers(handlers)
	// and executes the first handler,
	// handlers should not be empty.
	//
	// It's used by the router, developers may use that
	// to replace and execute handlers immediately.
	Do(Handlers)

	// AddHandler can add handler(s)
	// to the current request in serve-time,
	// these handlers are not persistenced to the router.
	//
	// Router is calling this function to add the route's handler.
	// If AddHandler called then the handlers will be inserted
	// to the end of the already-defined route's handler.
	//
	AddHandler(...Handler)
	// SetHandlers replaces all handlers with the new.
	SetHandlers(Handlers)
	// Handlers keeps tracking of the current handlers.
	Handlers() Handlers

	// HandlerIndex sets the current index of the
	// current context's handlers chain.
	// If -1 passed then it just returns the
	// current handler index without change the current index.
	//
	// Look Handlers(), Next() and StopExecution() too.
	HandlerIndex(n int) (currentIndex int)
	// Proceed is an alternative way to check if a particular handler
	// has been executed and called the `ctx.Next` function inside it.
	// This is useful only when you run a handler inside
	// another handler. It justs checks for before index and the after index.
	//
	// A usecase example is when you want to execute a middleware
	// inside controller's `BeginRequest` that calls the `ctx.Next` inside it.
	// The Controller looks the whole flow (BeginRequest, method handler, EndRequest)
	// as one handler, so `ctx.Next` will not be reflected to the method handler
	// if called from the `BeginRequest`.
	//
	// Although `BeginRequest` should NOT be used to call other handlers,
	// the `BeginRequest` has been introduced to be able to set
	// common data to all method handlers before their execution.
	// Controllers can accept middleware(s) from the MVC's Application's Router as normally.
	//
	// That said let's see an example of `ctx.Proceed`:
	//
	// var authMiddleware = basicauth.New(basicauth.Config{
	// 	Users: map[string]string{
	// 		"admin": "password",
	// 	},
	// })
	//
	// func (c *UsersController) BeginRequest(ctx iris.Context) {
	// 	if !ctx.Proceed(authMiddleware) {
	// 		ctx.StopExecution()
	// 	}
	// }
	// This Get() will be executed in the same handler as `BeginRequest`,
	// internally controller checks for `ctx.StopExecution`.
	// So it will not be fired if BeginRequest called the `StopExecution`.
	// func(c *UsersController) Get() []models.User {
	//	  return c.Service.GetAll()
	//}
	// Alternative way is `!ctx.IsStopped()` if middleware make use of the `ctx.StopExecution()` on failure.
	Proceed(Handler) bool
	// HandlerName returns the current handler's name, helpful for debugging.
	HandlerName() string
	// HandlerFileLine returns the current running handler's function source file and line information.
	// Useful mostly when debugging.
	HandlerFileLine() (file string, line int)
	// RouteName returns the route name that this handler is running on.
	// Note that it will return empty on not found handlers.
	RouteName() string
	// Next calls all the next handler from the handlers chain,
	// it should be used inside a middleware.
	//
	// Note: Custom context should override this method in order to be able to pass its own context.Context implementation.
	Next()
	// NextOr checks if chain has a next handler, if so then it executes it
	// otherwise it sets a new chain assigned to this Context based on the given handler(s)
	// and executes its first handler.
	//
	// Returns true if next handler exists and executed, otherwise false.
	//
	// Note that if no next handler found and handlers are missing then
	// it sends a Status Not Found (404) to the client and it stops the execution.
	NextOr(handlers ...Handler) bool
	// NextOrNotFound checks if chain has a next handler, if so then it executes it
	// otherwise it sends a Status Not Found (404) to the client and stops the execution.
	//
	// Returns true if next handler exists and executed, otherwise false.
	NextOrNotFound() bool
	// NextHandler returns (it doesn't execute) the next handler from the handlers chain.
	//
	// Use .Skip() to skip this handler if needed to execute the next of this returning handler.
	NextHandler() Handler
	// Skip skips/ignores the next handler from the handlers chain,
	// it should be used inside a middleware.
	Skip()
	// StopExecution if called then the following .Next calls are ignored,
	// as a result the next handlers in the chain will not be fire.
	StopExecution()
	// IsStopped checks and returns true if the current position of the Context is 255,
	// means that the StopExecution() was called.
	IsStopped() bool
	// OnConnectionClose registers the "cb" function which will fire (on its own goroutine, no need to be registered goroutine by the end-dev)
	// when the underlying connection has gone away.
	//
	// This mechanism can be used to cancel long operations on the server
	// if the client has disconnected before the response is ready.
	//
	// It depends on the `http#CloseNotify`.
	// CloseNotify may wait to notify until Request.Body has been
	// fully read.
	//
	// After the main Handler has returned, there is no guarantee
	// that the channel receives a value.
	//
	// Finally, it reports whether the protocol supports pipelines (HTTP/1.1 with pipelines disabled is not supported).
	// The "cb" will not fire for sure if the output value is false.
	//
	// Note that you can register only one callback for the entire request handler chain/per route.
	//
	// Look the `ResponseWriter#CloseNotifier` for more.
	OnConnectionClose(fnGoroutine func()) bool
	// OnClose registers the callback function "cb" to the underline connection closing event using the `Context#OnConnectionClose`
	// and also in the end of the request handler using the `ResponseWriter#SetBeforeFlush`.
	// Note that you can register only one callback for the entire request handler chain/per route.
	//
	// Look the `Context#OnConnectionClose` and `ResponseWriter#SetBeforeFlush` for more.
	OnClose(cb func())

	//  +------------------------------------------------------------+
	//  | Current "user/request" storage                             |
	//  | and share information between the handlers - Values().     |
	//  | Save and get named path parameters - Params()              |
	//  +------------------------------------------------------------+

	// Params returns the current url's named parameters key-value storage.
	// Named path parameters are being saved here.
	// This storage, as the whole Context, is per-request lifetime.
	Params() *RequestParams

	// Values returns the current "user" storage.
	// Named path parameters and any optional data can be saved here.
	// This storage, as the whole Context, is per-request lifetime.
	//
	// You can use this function to Set and Get local values
	// that can be used to share information between handlers and middleware.
	Values() *memstore.Store

	//  +------------------------------------------------------------+
	//  | Path, Host, Subdomain, IP, Headers, Localization etc...    |
	//  +------------------------------------------------------------+

	// Method returns the request.Method, the client's http method to the server.
	Method() string
	// Path returns the full request path,
	// escaped if EnablePathEscape config field is true.
	Path() string
	// RequestPath returns the full request path,
	// based on the 'escape'.
	RequestPath(escape bool) string
	// Host returns the host part of the current url.
	Host() string
	// Subdomain returns the subdomain of this request, if any.
	// Note that this is a fast method which does not cover all cases.
	Subdomain() (subdomain string)
	// FindClosest returns a list of "n" paths close to
	// this request based on subdomain and request path.
	//
	// Order may change.
	// Example: https://github.com/kataras/iris/tree/master/_examples/routing/not-found-suggests
	FindClosest(n int) []string
	// IsWWW returns true if the current subdomain (if any) is www.
	IsWWW() bool
	// FullRqeuestURI returns the full URI,
	// including the scheme, the host and the relative requested path/resource.
	FullRequestURI() string
	// RemoteAddr tries to parse and return the real client's request IP.
	//
	// Based on allowed headers names that can be modified from Configuration.RemoteAddrHeaders.
	//
	// If parse based on these headers fail then it will return the Request's `RemoteAddr` field
	// which is filled by the server before the HTTP handler.
	//
	// Look `Configuration.RemoteAddrHeaders`,
	//      `Configuration.WithRemoteAddrHeader(...)`,
	//      `Configuration.WithoutRemoteAddrHeader(...)` for more.
	RemoteAddr() string
	// GetHeader returns the request header's value based on its name.
	GetHeader(name string) string
	// IsAjax returns true if this request is an 'ajax request'( XMLHttpRequest)
	//
	// There is no a 100% way of knowing that a request was made via Ajax.
	// You should never trust data coming from the client, they can be easily overcome by spoofing.
	//
	// Note that "X-Requested-With" Header can be modified by any client(because of "X-"),
	// so don't rely on IsAjax for really serious stuff,
	// try to find another way of detecting the type(i.e, content type),
	// there are many blogs that describe these problems and provide different kind of solutions,
	// it's always depending on the application you're building,
	// this is the reason why this `IsAjax`` is simple enough for general purpose use.
	//
	// Read more at: https://developer.mozilla.org/en-US/docs/AJAX
	// and https://xhr.spec.whatwg.org/
	IsAjax() bool
	// IsMobile checks if client is using a mobile device(phone or tablet) to communicate with this server.
	// If the return value is true that means that the http client using a mobile
	// device to communicate with the server, otherwise false.
	//
	// Keep note that this checks the "User-Agent" request header.
	IsMobile() bool
	// IsScript reports whether a client is a script.
	IsScript() bool
	// GetReferrer extracts and returns the information from the "Referer" header as specified
	// in https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy
	// or by the URL query parameter "referer".
	GetReferrer() Referrer
	// GetLocale returns the current request's `Locale` found by i18n middleware.
	// See `Tr` too.
	GetLocale() Locale
	// Tr returns a i18n localized message based on format with optional arguments.
	// See `GetLocale` too.
	// Example: https://github.com/kataras/iris/tree/master/_examples/i18n
	Tr(format string, args ...interface{}) string

	//  +------------------------------------------------------------+
	//  | Headers helpers                                            |
	//  +------------------------------------------------------------+

	// Header adds a header to the response writer.
	Header(name string, value string)

	// ContentType sets the response writer's header key "Content-Type" to the 'cType'.
	ContentType(cType string)
	// GetContentType returns the response writer's header value of "Content-Type"
	// which may, set before with the 'ContentType'.
	GetContentType() string
	// GetContentType returns the request's header value of "Content-Type".
	GetContentTypeRequested() string

	// GetContentLength returns the request's header value of "Content-Length".
	// Returns 0 if header was unable to be found or its value was not a valid number.
	GetContentLength() int64

	// StatusCode sets the status code header to the response.
	// Look .`GetStatusCode` too.
	StatusCode(statusCode int)
	// GetStatusCode returns the current status code of the response.
	// Look `StatusCode` too.
	GetStatusCode() int

	// AbsoluteURI parses the "s" and returns its absolute URI form.
	AbsoluteURI(s string) string
	// Redirect sends a redirect response to the client
	// to a specific url or relative path.
	// accepts 2 parameters string and an optional int
	// first parameter is the url to redirect
	// second parameter is the http status should send,
	// default is 302 (StatusFound),
	// you can set it to 301 (Permant redirect)
	// or 303 (StatusSeeOther) if POST method,
	// or StatusTemporaryRedirect(307) if that's nessecery.
	Redirect(urlToRedirect string, statusHeader ...int)
	//  +------------------------------------------------------------+
	//  | Various Request and Post Data                              |
	//  +------------------------------------------------------------+

	// URLParam returns true if the url parameter exists, otherwise false.
	URLParamExists(name string) bool
	// URLParamDefault returns the get parameter from a request,
	// if not found then "def" is returned.
	URLParamDefault(name string, def string) string
	// URLParam returns the get parameter from a request, if any.
	URLParam(name string) string
	// URLParamTrim returns the url query parameter with trailing white spaces removed from a request.
	URLParamTrim(name string) string
	// URLParamEscape returns the escaped url query parameter from a request.
	URLParamEscape(name string) string
	// URLParamInt returns the url query parameter as int value from a request,
	// returns -1 and an error if parse failed.
	URLParamInt(name string) (int, error)
	// URLParamIntDefault returns the url query parameter as int value from a request,
	// if not found or parse failed then "def" is returned.
	URLParamIntDefault(name string, def int) int
	// URLParamInt32Default returns the url query parameter as int32 value from a request,
	// if not found or parse failed then "def" is returned.
	URLParamInt32Default(name string, def int32) int32
	// URLParamInt64 returns the url query parameter as int64 value from a request,
	// returns -1 and an error if parse failed.
	URLParamInt64(name string) (int64, error)
	// URLParamInt64Default returns the url query parameter as int64 value from a request,
	// if not found or parse failed then "def" is returned.
	URLParamInt64Default(name string, def int64) int64
	// URLParamFloat64 returns the url query parameter as float64 value from a request,
	// returns -1 and an error if parse failed.
	URLParamFloat64(name string) (float64, error)
	// URLParamFloat64Default returns the url query parameter as float64 value from a request,
	// if not found or parse failed then "def" is returned.
	URLParamFloat64Default(name string, def float64) float64
	// URLParamBool returns the url query parameter as boolean value from a request,
	// returns an error if parse failed or not found.
	URLParamBool(name string) (bool, error)
	// URLParams returns a map of GET query parameters separated by comma if more than one
	// it returns an empty map if nothing found.
	URLParams() map[string]string

	// FormValueDefault returns a single parsed form value by its "name",
	// including both the URL field's query parameters and the POST or PUT form data.
	//
	// Returns the "def" if not found.
	FormValueDefault(name string, def string) string
	// FormValue returns a single parsed form value by its "name",
	// including both the URL field's query parameters and the POST or PUT form data.
	FormValue(name string) string
	// FormValues returns the parsed form data, including both the URL
	// field's query parameters and the POST or PUT form data.
	//
	// The default form's memory maximum size is 32MB, it can be changed by the
	// `iris#WithPostMaxMemory` configurator at main configuration passed on `app.Run`'s second argument.
	//
	// NOTE: A check for nil is necessary.
	FormValues() map[string][]string

	// PostValueDefault returns the parsed form data from POST, PATCH,
	// or PUT body parameters based on a "name".
	//
	// If not found then "def" is returned instead.
	PostValueDefault(name string, def string) string
	// PostValue returns the parsed form data from POST, PATCH,
	// or PUT body parameters based on a "name"
	PostValue(name string) string
	// PostValueTrim returns the parsed form data from POST, PATCH,
	// or PUT body parameters based on a "name",  without trailing spaces.
	PostValueTrim(name string) string
	// PostValueInt returns the parsed form data from POST, PATCH,
	// or PUT body parameters based on a "name", as int.
	//
	// If not found returns -1 and a non-nil error.
	PostValueInt(name string) (int, error)
	// PostValueIntDefault returns the parsed form data from POST, PATCH,
	// or PUT body parameters based on a "name", as int.
	//
	// If not found returns or parse errors the "def".
	PostValueIntDefault(name string, def int) int
	// PostValueInt64 returns the parsed form data from POST, PATCH,
	// or PUT body parameters based on a "name", as float64.
	//
	// If not found returns -1 and a no-nil error.
	PostValueInt64(name string) (int64, error)
	// PostValueInt64Default returns the parsed form data from POST, PATCH,
	// or PUT body parameters based on a "name", as int64.
	//
	// If not found or parse errors returns the "def".
	PostValueInt64Default(name string, def int64) int64
	// PostValueInt64Default returns the parsed form data from POST, PATCH,
	// or PUT body parameters based on a "name", as float64.
	//
	// If not found returns -1 and a non-nil error.
	PostValueFloat64(name string) (float64, error)
	// PostValueInt64Default returns the parsed form data from POST, PATCH,
	// or PUT body parameters based on a "name", as float64.
	//
	// If not found or parse errors returns the "def".
	PostValueFloat64Default(name string, def float64) float64
	// PostValueInt64Default returns the parsed form data from POST, PATCH,
	// or PUT body parameters based on a "name", as bool.
	//
	// If not found or value is false, then it returns false, otherwise true.
	PostValueBool(name string) (bool, error)
	// PostValues returns all the parsed form data from POST, PATCH,
	// or PUT body parameters based on a "name" as a string slice.
	//
	// The default form's memory maximum size is 32MB, it can be changed by the
	// `iris#WithPostMaxMemory` configurator at main configuration passed on `app.Run`'s second argument.
	PostValues(name string) []string
	// FormFile returns the first uploaded file that received from the client.
	//
	// The default form's memory maximum size is 32MB, it can be changed by the
	//  `iris#WithPostMaxMemory` configurator at main configuration passed on `app.Run`'s second argument.
	//
	// Example: https://github.com/kataras/iris/tree/master/_examples/http_request/upload-file
	FormFile(key string) (multipart.File, *multipart.FileHeader, error)
	// UploadFormFiles uploads any received file(s) from the client
	// to the system physical location "destDirectory".
	//
	// The second optional argument "before" gives caller the chance to
	// modify the *miltipart.FileHeader before saving to the disk,
	// it can be used to change a file's name based on the current request,
	// all FileHeader's options can be changed. You can ignore it if
	// you don't need to use this capability before saving a file to the disk.
	//
	// Note that it doesn't check if request body streamed.
	//
	// Returns the copied length as int64 and
	// a not nil error if at least one new file
	// can't be created due to the operating system's permissions or
	// http.ErrMissingFile if no file received.
	//
	// If you want to receive & accept files and manage them manually you can use the `context#FormFile`
	// instead and create a copy function that suits your needs, the below is for generic usage.
	//
	// The default form's memory maximum size is 32MB, it can be changed by the
	//  `iris#WithPostMaxMemory` configurator at main configuration passed on `app.Run`'s second argument.
	//
	// See `FormFile` to a more controlled to receive a file.
	//
	//
	// Example: https://github.com/kataras/iris/tree/master/_examples/http_request/upload-files
	UploadFormFiles(destDirectory string, before ...func(Context, *multipart.FileHeader)) (n int64, err error)

	//  +------------------------------------------------------------+
	//  | Custom HTTP Errors                                         |
	//  +------------------------------------------------------------+

	// NotFound emits an error 404 to the client, using the specific custom error error handler.
	// Note that you may need to call ctx.StopExecution() if you don't want the next handlers
	// to be executed. Next handlers are being executed on iris because you can alt the
	// error code and change it to a more specific one, i.e
	// users := app.Party("/users")
	// users.Done(func(ctx context.Context){ if ctx.StatusCode() == 400 { /*  custom error code for /users */ }})
	NotFound()

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

	// SetMaxRequestBodySize sets a limit to the request body size
	// should be called before reading the request body from the client.
	SetMaxRequestBodySize(limitOverBytes int64)

	// GetBody reads and returns the request body.
	// The default behavior for the http request reader is to consume the data readen
	// but you can change that behavior by passing the `WithoutBodyConsumptionOnUnmarshal` iris option.
	//
	// However, whenever you can use the `ctx.Request().Body` instead.
	GetBody() ([]byte, error)
	// UnmarshalBody reads the request's body and binds it to a value or pointer of any type.
	// Examples of usage: context.ReadJSON, context.ReadXML.
	//
	// Example: https://github.com/kataras/iris/blob/master/_examples/http_request/read-custom-via-unmarshaler/main.go
	//
	// UnmarshalBody does not check about gzipped data.
	// Do not rely on compressed data incoming to your server. The main reason is: https://en.wikipedia.org/wiki/Zip_bomb
	// However you are still free to read the `ctx.Request().Body io.Reader` manually.
	UnmarshalBody(outPtr interface{}, unmarshaler Unmarshaler) error
	// ReadJSON reads JSON from request's body and binds it to a pointer of a value of any json-valid type.
	//
	// Example: https://github.com/kataras/iris/blob/master/_examples/http_request/read-json/main.go
	ReadJSON(jsonObjectPtr interface{}) error
	// ReadXML reads XML from request's body and binds it to a pointer of a value of any xml-valid type.
	//
	// Example: https://github.com/kataras/iris/blob/master/_examples/http_request/read-xml/main.go
	ReadXML(xmlObjectPtr interface{}) error
	// ReadYAML reads YAML from request's body and binds it to the "outPtr" value.
	//
	// Example: https://github.com/kataras/iris/blob/master/_examples/http_request/read-yaml/main.go
	ReadYAML(outPtr interface{}) error
	// ReadForm binds the formObject  with the form data
	// it supports any kind of type, including custom structs.
	// It will return nothing if request data are empty.
	// The struct field tag is "form".
	//
	// Example: https://github.com/kataras/iris/blob/master/_examples/http_request/read-form/main.go
	ReadForm(formObject interface{}) error
	// ReadQuery binds the "ptr" with the url query string. The struct field tag is "url".
	//
	// Example: https://github.com/kataras/iris/blob/master/_examples/http_request/read-query/main.go
	ReadQuery(ptr interface{}) error
	//  +------------------------------------------------------------+
	//  | Body (raw) Writers                                         |
	//  +------------------------------------------------------------+

	// Write writes the data to the connection as part of an HTTP reply.
	//
	// If WriteHeader has not yet been called, Write calls
	// WriteHeader(http.StatusOK) before writing the data. If the Header
	// does not contain a Content-Type line, Write adds a Content-Type set
	// to the result of passing the initial 512 bytes of written data to
	// DetectContentType.
	//
	// Depending on the HTTP protocol version and the client, calling
	// Write or WriteHeader may prevent future reads on the
	// Request.Body. For HTTP/1.x requests, handlers should read any
	// needed request body data before writing the response. Once the
	// headers have been flushed (due to either an explicit Flusher.Flush
	// call or writing enough data to trigger a flush), the request body
	// may be unavailable. For HTTP/2 requests, the Go HTTP server permits
	// handlers to continue to read the request body while concurrently
	// writing the response. However, such behavior may not be supported
	// by all HTTP/2 clients. Handlers should read before writing if
	// possible to maximize compatibility.
	Write(body []byte) (int, error)
	// Writef formats according to a format specifier and writes to the response.
	//
	// Returns the number of bytes written and any write error encountered.
	Writef(format string, args ...interface{}) (int, error)
	// WriteString writes a simple string to the response.
	//
	// Returns the number of bytes written and any write error encountered.
	WriteString(body string) (int, error)

	// SetLastModified sets the "Last-Modified" based on the "modtime" input.
	// If "modtime" is zero then it does nothing.
	//
	// It's mostly internally on core/router and context packages.
	//
	// Note that modtime.UTC() is being used instead of just modtime, so
	// you don't have to know the internals in order to make that works.
	SetLastModified(modtime time.Time)
	// CheckIfModifiedSince checks if the response is modified since the "modtime".
	// Note that it has nothing to do with server-side caching.
	// It does those checks by checking if the "If-Modified-Since" request header
	// sent by client or a previous server response header
	// (e.g with WriteWithExpiration or HandleDir or Favicon etc.)
	// is a valid one and it's before the "modtime".
	//
	// A check for !modtime && err == nil is necessary to make sure that
	// it's not modified since, because it may return false but without even
	// had the chance to check the client-side (request) header due to some errors,
	// like the HTTP Method is not "GET" or "HEAD" or if the "modtime" is zero
	// or if parsing time from the header failed.
	//
	// It's mostly used internally, e.g. `context#WriteWithExpiration`. See `ErrPreconditionFailed` too.
	//
	// Note that modtime.UTC() is being used instead of just modtime, so
	// you don't have to know the internals in order to make that works.
	CheckIfModifiedSince(modtime time.Time) (bool, error)
	// WriteNotModified sends a 304 "Not Modified" status code to the client,
	// it makes sure that the content type, the content length headers
	// and any "ETag" are removed before the response sent.
	//
	// It's mostly used internally on core/router/fs.go and context methods.
	WriteNotModified()
	// WriteWithExpiration works like `Write` but it will check if a resource is modified,
	// based on the "modtime" input argument,
	// otherwise sends a 304 status code in order to let the client-side render the cached content.
	WriteWithExpiration(body []byte, modtime time.Time) (int, error)
	// StreamWriter registers the given stream writer for populating
	// response body.
	//
	// Access to context's and/or its' members is forbidden from writer.
	//
	// This function may be used in the following cases:
	//
	//     * if response body is too big (more than iris.LimitRequestBodySize(if set)).
	//     * if response body is streamed from slow external sources.
	//     * if response body must be streamed to the client in chunks.
	//     (aka `http server push`).
	//
	// receives a function which receives the response writer
	// and returns false when it should stop writing, otherwise true in order to continue
	StreamWriter(writer func(w io.Writer) bool)

	//  +------------------------------------------------------------+
	//  | Body Writers with compression                              |
	//  +------------------------------------------------------------+
	// ClientSupportsGzip retruns true if the client supports gzip compression.
	ClientSupportsGzip() bool
	// WriteGzip accepts bytes, which are compressed to gzip format and sent to the client.
	// returns the number of bytes written and an error ( if the client doesn' supports gzip compression)
	// You may re-use this function in the same handler
	// to write more data many times without any troubles.
	WriteGzip(b []byte) (int, error)
	// TryWriteGzip accepts bytes, which are compressed to gzip format and sent to the client.
	// If client does not supprots gzip then the contents are written as they are, uncompressed.
	TryWriteGzip(b []byte) (int, error)
	// GzipResponseWriter converts the current response writer into a response writer
	// which when its .Write called it compress the data to gzip and writes them to the client.
	//
	// Can be also disabled with its .Disable and .ResetBody to rollback to the usual response writer.
	GzipResponseWriter() *GzipResponseWriter
	// Gzip enables or disables (if enabled before) the gzip response writer,if the client
	// supports gzip compression, so the following response data will
	// be sent as compressed gzip data to the client.
	Gzip(enable bool)

	//  +------------------------------------------------------------+
	//  | Rich Body Content Writers/Renderers                        |
	//  +------------------------------------------------------------+

	// ViewLayout sets the "layout" option if and when .View
	// is being called afterwards, in the same request.
	// Useful when need to set or/and change a layout based on the previous handlers in the chain.
	//
	// Note that the 'layoutTmplFile' argument can be set to iris.NoLayout || view.NoLayout
	// to disable the layout for a specific view render action,
	// it disables the engine's configuration's layout property.
	//
	// Look .ViewData and .View too.
	//
	// Example: https://github.com/kataras/iris/tree/master/_examples/view/context-view-data/
	ViewLayout(layoutTmplFile string)
	// ViewData saves one or more key-value pair in order to be passed if and when .View
	// is being called afterwards, in the same request.
	// Useful when need to set or/and change template data from previous hanadlers in the chain.
	//
	// If .View's "binding" argument is not nil and it's not a type of map
	// then these data are being ignored, binding has the priority, so the main route's handler can still decide.
	// If binding is a map or context.Map then these data are being added to the view data
	// and passed to the template.
	//
	// After .View, the data are not destroyed, in order to be re-used if needed (again, in the same request as everything else),
	// to clear the view data, developers can call:
	// ctx.Set(ctx.Application().ConfigurationReadOnly().GetViewDataContextKey(), nil)
	//
	// If 'key' is empty then the value is added as it's (struct or map) and developer is unable to add other value.
	//
	// Look .ViewLayout and .View too.
	//
	// Example: https://github.com/kataras/iris/tree/master/_examples/view/context-view-data/
	ViewData(key string, value interface{})
	// GetViewData returns the values registered by `context#ViewData`.
	// The return value is `map[string]interface{}`, this means that
	// if a custom struct registered to ViewData then this function
	// will try to parse it to map, if failed then the return value is nil
	// A check for nil is always a good practise if different
	// kind of values or no data are registered via `ViewData`.
	//
	// Similarly to `viewData := ctx.Values().Get("iris.viewData")` or
	// `viewData := ctx.Values().Get(ctx.Application().ConfigurationReadOnly().GetViewDataContextKey())`.
	GetViewData() map[string]interface{}
	// View renders a template based on the registered view engine(s).
	// First argument accepts the filename, relative to the view engine's Directory and Extension,
	// i.e: if directory is "./templates" and want to render the "./templates/users/index.html"
	// then you pass the "users/index.html" as the filename argument.
	//
	// The second optional argument can receive a single "view model"
	// that will be binded to the view template if it's not nil,
	// otherwise it will check for previous view data stored by the `ViewData`
	// even if stored at any previous handler(middleware) for the same request.
	//
	// Look .ViewData` and .ViewLayout too.
	//
	// Examples: https://github.com/kataras/iris/tree/master/_examples/view
	View(filename string, optionalViewModel ...interface{}) error

	// Binary writes out the raw bytes as binary data.
	Binary(data []byte) (int, error)
	// Text writes out a string as plain text.
	Text(format string, args ...interface{}) (int, error)
	// HTML writes out a string as text/html.
	HTML(format string, args ...interface{}) (int, error)
	// JSON marshals the given interface object and writes the JSON response.
	JSON(v interface{}, options ...JSON) (int, error)
	// JSONP marshals the given interface object and writes the JSON response.
	JSONP(v interface{}, options ...JSONP) (int, error)
	// XML marshals the given interface object and writes the XML response.
	// To render maps as XML see the `XMLMap` package-level function.
	XML(v interface{}, options ...XML) (int, error)
	// Problem writes a JSON or XML problem response.
	// Order of Problem fields are not always rendered the same.
	//
	// Behaves exactly like `Context.JSON`
	// but with default ProblemOptions.JSON indent of " " and
	// a response content type of "application/problem+json" instead.
	//
	// Use the options.RenderXML and XML fields to change this behavior and
	// send a response of content type "application/problem+xml" instead.
	//
	// Read more at: https://github.com/kataras/iris/wiki/Routing-error-handlers
	Problem(v interface{}, opts ...ProblemOptions) (int, error)
	// Markdown parses the markdown to html and renders its result to the client.
	Markdown(markdownB []byte, options ...Markdown) (int, error)
	// YAML parses the "v" using the yaml parser and renders its result to the client.
	YAML(v interface{}) (int, error)

	//  +-----------------------------------------------------------------------+
	//  | Content Νegotiation                                                   |
	//  | https://developer.mozilla.org/en-US/docs/Web/HTTP/Content_negotiation |                                       |
	//  +-----------------------------------------------------------------------+

	// Negotiation creates once and returns the negotiation builder
	// to build server-side available content for specific mime type(s)
	// and charset(s).
	//
	// See `Negotiate` method too.
	Negotiation() *NegotiationBuilder
	// Negotiate used for serving different representations of a resource at the same URI.
	//
	// The "v" can be a single `N` struct value.
	// The "v" can be any value completes the `ContentSelector` interface.
	// The "v" can be any value completes the `ContentNegotiator` interface.
	// The "v" can be any value of struct(JSON, JSONP, XML, YAML) or
	// string(TEXT, HTML) or []byte(Markdown, Binary) or []byte with any matched mime type.
	//
	// If the "v" is nil, the `Context.Negotitation()` builder's
	// content will be used instead, otherwise "v" overrides builder's content
	// (server mime types are still retrieved by its registered, supported, mime list)
	//
	// Set mime type priorities by `Negotiation().JSON().XML().HTML()...`.
	// Set charset priorities by `Negotiation().Charset(...)`.
	// Set encoding algorithm priorities by `Negotiation().Encoding(...)`.
	// Modify the accepted by
	// `Negotiation().Accept./Override()/.XML().JSON().Charset(...).Encoding(...)...`.
	//
	// It returns `ErrContentNotSupported` when not matched mime type(s).
	//
	// Resources:
	// https://developer.mozilla.org/en-US/docs/Web/HTTP/Content_negotiation
	// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept
	// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Charset
	// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding
	//
	// Supports the above without quality values.
	//
	// Read more at: https://github.com/kataras/iris/wiki/Content-negotiation
	Negotiate(v interface{}) (int, error)

	//  +------------------------------------------------------------+
	//  | Serve files                                                |
	//  +------------------------------------------------------------+

	// ServeContent serves content, headers are autoset
	// receives three parameters, it's low-level function, instead you can use .ServeFile(string,bool)/SendFile(string,string)
	//
	//
	// You can define your own "Content-Type" with `context#ContentType`, before this function call.
	//
	// This function doesn't support resuming (by range),
	// use ctx.SendFile or router's `HandleDir` instead.
	ServeContent(content io.ReadSeeker, filename string, modtime time.Time, gzipCompression bool) error
	// ServeFile serves a file (to send a file, a zip for example to the client you should use the `SendFile` instead)
	// receives two parameters
	// filename/path (string)
	// gzipCompression (bool)
	//
	// You can define your own "Content-Type" with `context#ContentType`, before this function call.
	//
	// This function doesn't support resuming (by range),
	// use ctx.SendFile or router's `HandleDir` instead.
	//
	// Use it when you want to serve dynamic files to the client.
	ServeFile(filename string, gzipCompression bool) error
	// SendFile sends file for force-download to the client
	//
	// Use this instead of ServeFile to 'force-download' bigger files to the client.
	SendFile(filename string, destinationName string) error

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

	// SetCookie adds a cookie.
	// Use of the "options" is not required, they can be used to amend the "cookie".
	//
	// Example: https://github.com/kataras/iris/tree/master/_examples/cookies/basic
	SetCookie(cookie *http.Cookie, options ...CookieOption)
	// SetCookieKV adds a cookie, requires the name(string) and the value(string).
	//
	// By default it expires at 2 hours and it's added to the root path,
	// use the `CookieExpires` and `CookiePath` to modify them.
	// Alternatively: ctx.SetCookie(&http.Cookie{...})
	//
	// If you want to set custom the path:
	// ctx.SetCookieKV(name, value, iris.CookiePath("/custom/path/cookie/will/be/stored"))
	//
	// If you want to be visible only to current request path:
	// ctx.SetCookieKV(name, value, iris.CookieCleanPath/iris.CookiePath(""))
	// More:
	//                              iris.CookieExpires(time.Duration)
	//                              iris.CookieHTTPOnly(false)
	//
	// Example: https://github.com/kataras/iris/tree/master/_examples/cookies/basic
	SetCookieKV(name, value string, options ...CookieOption)
	// GetCookie returns cookie's value by its name
	// returns empty string if nothing was found.
	//
	// If you want more than the value then:
	// cookie, err := ctx.Request().Cookie("name")
	//
	// Example: https://github.com/kataras/iris/tree/master/_examples/cookies/basic
	GetCookie(name string, options ...CookieOption) string
	// RemoveCookie deletes a cookie by its name and path = "/".
	// Tip: change the cookie's path to the current one by: RemoveCookie("name", iris.CookieCleanPath)
	//
	// Example: https://github.com/kataras/iris/tree/master/_examples/cookies/basic
	RemoveCookie(name string, options ...CookieOption)
	// VisitAllCookies accepts a visitor function which is called
	// on each (request's) cookies' name and value.
	VisitAllCookies(visitor func(name string, value string))

	// MaxAge returns the "cache-control" request header's value
	// seconds as int64
	// if header not found or parse failed then it returns -1.
	MaxAge() int64

	//  +------------------------------------------------------------+
	//  | Advanced: Response Recorder and Transactions               |
	//  +------------------------------------------------------------+

	// Record transforms the context's basic and direct responseWriter to a ResponseRecorder
	// which can be used to reset the body, reset headers, get the body,
	// get & set the status code at any time and more.
	Record()
	// Recorder returns the context's ResponseRecorder
	// if not recording then it starts recording and returns the new context's ResponseRecorder
	Recorder() *ResponseRecorder
	// IsRecording returns the response recorder and a true value
	// when the response writer is recording the status code, body, headers and so on,
	// else returns nil and false.
	IsRecording() (*ResponseRecorder, bool)

	// BeginTransaction starts a scoped transaction.
	//
	// You can search third-party articles or books on how Business Transaction works (it's quite simple, especially here).
	//
	// Note that this is unique and new
	// (=I haver never seen any other examples or code in Golang on this subject, so far, as with the most of iris features...)
	// it's not covers all paths,
	// such as databases, this should be managed by the libraries you use to make your database connection,
	// this transaction scope is only for context's response.
	// Transactions have their own middleware ecosystem also, look iris.go:UseTransaction.
	//
	// See https://github.com/kataras/iris/tree/master/_examples/ for more
	BeginTransaction(pipe func(t *Transaction))
	// SkipTransactions if called then skip the rest of the transactions
	// or all of them if called before the first transaction
	SkipTransactions()
	// TransactionsSkipped returns true if the transactions skipped or canceled at all.
	TransactionsSkipped() bool

	// Exec calls the `context/Application#ServeCtx`
	// based on this context but with a changed method and path
	// like it was requested by the user, but it is not.
	//
	// Offline means that the route is registered to the iris and have all features that a normal route has
	// BUT it isn't available by browsing, its handlers executed only when other handler's context call them
	// it can validate paths, has sessions, path parameters and all.
	//
	// You can find the Route by app.GetRoute("theRouteName")
	// you can set a route name as: myRoute := app.Get("/mypath", handler)("theRouteName")
	// that will set a name to the route and returns its RouteInfo instance for further usage.
	//
	// It doesn't changes the global state, if a route was "offline" it remains offline.
	//
	// app.None(...) and app.GetRoutes().Offline(route)/.Online(route, method)
	//
	// Example: https://github.com/kataras/iris/tree/master/_examples/routing/route-state
	//
	// User can get the response by simple using rec := ctx.Recorder(); rec.Body()/rec.StatusCode()/rec.Header().
	//
	// Context's Values and the Session are kept in order to be able to communicate via the result route.
	//
	// It's for extreme use cases, 99% of the times will never be useful for you.
	Exec(method, path string)

	// RouteExists reports whether a particular route exists
	// It will search from the current subdomain of context's host, if not inside the root domain.
	RouteExists(method, path string) bool

	// ReflectValue caches and returns a []reflect.Value{reflect.ValueOf(ctx)}.
	// It's just a helper to maintain variable inside the context itself.
	ReflectValue() []reflect.Value

	// Application returns the iris app instance which belongs to this context.
	// Worth to notice that this function returns an interface
	// of the Application, which contains methods that are safe
	// to be executed at serve-time. The full app's fields
	// and methods are not available here for the developer's safety.
	Application() Application

	// String returns the string representation of this request.
	// Each context has a unique string representation.
	// It can be used for simple debugging scenarios, i.e print context as string.
	//
	// What it returns? A number which declares the length of the
	// total `String` calls per executable application, followed
	// by the remote IP (the client) and finally the method:url.
	String() string
}

抽象总结

image.png

Echo

Echo

Echo 是它内部的一个结构体,类似于Beego的HttpServer和Gin的Engine:

  • 暴露了注册路由的方法,但是它并不是路由树的载体
  • 生命周期管理:如Shutdown和Start等方法

在Echo 里面有两个相似的字段:

  • Route:这其实就是代表路由树
  • Routers:这代表的是根据Host来进行分组组织,可以看做是近似于namespace之类的概念,既是一种组织方式,也是一种隔离机制
type (
	// Echo is the top-level framework instance.
	Echo struct {
		filesystem
		common
		// startupMutex is mutex to lock Echo instance access during server configuration and startup. Useful for to get
		// listener address info (on which interface/port was listener binded) without having data races.
		startupMutex     sync.RWMutex
		StdLogger        *stdLog.Logger
		colorer          *color.Color
		premiddleware    []MiddlewareFunc
		middleware       []MiddlewareFunc
		maxParam         *int
		router           *Router
		routers          map[string]*Router
		pool             sync.Pool
		Server           *http.Server
		TLSServer        *http.Server
		Listener         net.Listener
		TLSListener      net.Listener
		AutoTLSManager   autocert.Manager
		DisableHTTP2     bool
		Debug            bool
		HideBanner       bool
		HidePort         bool
		HTTPErrorHandler HTTPErrorHandler
		Binder           Binder
		JSONSerializer   JSONSerializer
		Validator        Validator
		Renderer         Renderer
		Logger           Logger
		IPExtractor      IPExtractor
		ListenerNetwork  string
	}

	// Route contains a handler and information for matching against requests.
	Route struct {
		Method string `json:"method"`
		Path   string `json:"path"`
		Name   string `json:"name"`
	}

	// HTTPError represents an error that occurred while handling a request.
	HTTPError struct {
		Code     int         `json:"-"`
		Message  interface{} `json:"message"`
		Internal error       `json:"-"` // Stores the error returned by an external dependency
	}

	// MiddlewareFunc defines a function to process middleware.
	MiddlewareFunc func(next HandlerFunc) HandlerFunc

	// HandlerFunc defines a function to serve HTTP requests.
	HandlerFunc func(c Context) error

	// HTTPErrorHandler is a centralized HTTP error handler.
	HTTPErrorHandler func(error, Context)

	// Validator is the interface that wraps the Validate function.
	Validator interface {
		Validate(i interface{}) error
	}

	// JSONSerializer is the interface that encodes and decodes JSON to and from interfaces.
	JSONSerializer interface {
		Serialize(c Context, i interface{}, indent string) error
		Deserialize(c Context, i interface{}) error
	}

	// Renderer is the interface that wraps the Render function.
	Renderer interface {
		Render(io.Writer, string, interface{}, Context) error
	}

	// Map defines a generic map of type `map[string]interface{}`.
	Map map[string]interface{}

	// Common struct for Echo & Group.
	common struct{}
)

Route和node

Route代表的就是路由树,node代表的是路由树上的节点。

type (
	// Router is the registry of all registered routes for an `Echo` instance for
	// request matching and URL path parameter parsing.
	Router struct {
		tree   *node
		routes map[string]*Route
		echo   *Echo
	}
	node struct {
		kind           kind
		label          byte
		prefix         string
		parent         *node
		staticChildren children
		originalPath   string
		methods        *routeMethods
		paramChild     *node
		anyChild       *node
		paramsCount    int
		// isLeaf indicates that node does not have child routes
		isLeaf bool
		// isHandler indicates that node has at least one handler registered to it
		isHandler bool

		// notFoundHandler is handler registered with RouteNotFound method and is executed for 404 cases
		notFoundHandler *routeMethod
	}
	kind        uint8
	children    []*node
	routeMethod struct {
		ppath   string
		pnames  []string
		handler HandlerFunc
	}
	routeMethods struct {
		connect     *routeMethod
		delete      *routeMethod
		get         *routeMethod
		head        *routeMethod
		options     *routeMethod
		patch       *routeMethod
		post        *routeMethod
		propfind    *routeMethod
		put         *routeMethod
		trace       *routeMethod
		report      *routeMethod
		anyOther    map[string]*routeMethod
		allowHeader string
	}
)

node 里面有一个很有意思的设计:

  • staticChildren 静态节点
  • paramChild 参数节点
  • anyChild 任意匹配节点

利用这种设计可以轻松实现路由优先级和路由冲突检测。
它里面还有一个字段叫做echo维护的是使用Route的是echo。这种设计形态在别的地方也能见到,比如说在sql.Tx里面维持了一个sql.DB的实例。

Context

一个大而全的接口(它希望用户可以去扩展Context),定义了处理请求和响应的各种方法。
和Beego、Gin、Iris的Context没有什么区别。

type (
	// Context represents the context of the current HTTP request. It holds request and
	// response objects, path, path parameters, data and registered handler.
	Context interface {
		// Request returns `*http.Request`.
		Request() *http.Request

		// SetRequest sets `*http.Request`.
		SetRequest(r *http.Request)

		// SetResponse sets `*Response`.
		SetResponse(r *Response)

		// Response returns `*Response`.
		Response() *Response

		// IsTLS returns true if HTTP connection is TLS otherwise false.
		IsTLS() bool

		// IsWebSocket returns true if HTTP connection is WebSocket otherwise false.
		IsWebSocket() bool

		// Scheme returns the HTTP protocol scheme, `http` or `https`.
		Scheme() string

		// RealIP returns the client's network address based on `X-Forwarded-For`
		// or `X-Real-IP` request header.
		// The behavior can be configured using `Echo#IPExtractor`.
		RealIP() string

		// Path returns the registered path for the handler.
		Path() string

		// SetPath sets the registered path for the handler.
		SetPath(p string)

		// Param returns path parameter by name.
		Param(name string) string

		// ParamNames returns path parameter names.
		ParamNames() []string

		// SetParamNames sets path parameter names.
		SetParamNames(names ...string)

		// ParamValues returns path parameter values.
		ParamValues() []string

		// SetParamValues sets path parameter values.
		SetParamValues(values ...string)

		// QueryParam returns the query param for the provided name.
		QueryParam(name string) string

		// QueryParams returns the query parameters as `url.Values`.
		QueryParams() url.Values

		// QueryString returns the URL query string.
		QueryString() string

		// FormValue returns the form field value for the provided name.
		FormValue(name string) string

		// FormParams returns the form parameters as `url.Values`.
		FormParams() (url.Values, error)

		// FormFile returns the multipart form file for the provided name.
		FormFile(name string) (*multipart.FileHeader, error)

		// MultipartForm returns the multipart form.
		MultipartForm() (*multipart.Form, error)

		// Cookie returns the named cookie provided in the request.
		Cookie(name string) (*http.Cookie, error)

		// SetCookie adds a `Set-Cookie` header in HTTP response.
		SetCookie(cookie *http.Cookie)

		// Cookies returns the HTTP cookies sent with the request.
		Cookies() []*http.Cookie

		// Get retrieves data from the context.
		Get(key string) interface{}

		// Set saves data in the context.
		Set(key string, val interface{})

		// Bind binds the request body into provided type `i`. The default binder
		// does it based on Content-Type header.
		Bind(i interface{}) error

		// Validate validates provided `i`. It is usually called after `Context#Bind()`.
		// Validator must be registered using `Echo#Validator`.
		Validate(i interface{}) error

		// Render renders a template with data and sends a text/html response with status
		// code. Renderer must be registered using `Echo.Renderer`.
		Render(code int, name string, data interface{}) error

		// HTML sends an HTTP response with status code.
		HTML(code int, html string) error

		// HTMLBlob sends an HTTP blob response with status code.
		HTMLBlob(code int, b []byte) error

		// String sends a string response with status code.
		String(code int, s string) error

		// JSON sends a JSON response with status code.
		JSON(code int, i interface{}) error

		// JSONPretty sends a pretty-print JSON with status code.
		JSONPretty(code int, i interface{}, indent string) error

		// JSONBlob sends a JSON blob response with status code.
		JSONBlob(code int, b []byte) error

		// JSONP sends a JSONP response with status code. It uses `callback` to construct
		// the JSONP payload.
		JSONP(code int, callback string, i interface{}) error

		// JSONPBlob sends a JSONP blob response with status code. It uses `callback`
		// to construct the JSONP payload.
		JSONPBlob(code int, callback string, b []byte) error

		// XML sends an XML response with status code.
		XML(code int, i interface{}) error

		// XMLPretty sends a pretty-print XML with status code.
		XMLPretty(code int, i interface{}, indent string) error

		// XMLBlob sends an XML blob response with status code.
		XMLBlob(code int, b []byte) error

		// Blob sends a blob response with status code and content type.
		Blob(code int, contentType string, b []byte) error

		// Stream sends a streaming response with status code and content type.
		Stream(code int, contentType string, r io.Reader) error

		// File sends a response with the content of the file.
		File(file string) error

		// Attachment sends a response as attachment, prompting client to save the
		// file.
		Attachment(file string, name string) error

		// Inline sends a response as inline, opening the file in the browser.
		Inline(file string, name string) error

		// NoContent sends a response with no body and a status code.
		NoContent(code int) error

		// Redirect redirects the request to a provided URL with status code.
		Redirect(code int, url string) error

		// Error invokes the registered HTTP error handler. Generally used by middleware.
		Error(err error)

		// Handler returns the matched handler by router.
		Handler() HandlerFunc

		// SetHandler sets the matched handler by router.
		SetHandler(h HandlerFunc)

		// Logger returns the `Logger` instance.
		Logger() Logger

		// SetLogger Set the logger
		SetLogger(l Logger)

		// Echo returns the `Echo` instance.
		Echo() *Echo

		// Reset resets the context after request completes. It must be called along
		// with `Echo#AcquireContext()` and `Echo#ReleaseContext()`.
		// See `Echo#ServeHTTP()`
		Reset(r *http.Request, w http.ResponseWriter)
	}

	context struct {
		request  *http.Request
		response *Response
		path     string
		pnames   []string
		pvalues  []string
		query    url.Values
		handler  HandlerFunc
		store    Map
		echo     *Echo
		logger   Logger
		lock     sync.RWMutex
	}
)

核心抽象

image.png

框架对比

Beego Gin Iris Echo
代表服务器 HttpServer Engine Application Echo
代表路由 ControllerRegister methodTree Route Route
上下文 Context Context Context Context
处理逻辑 HandlerFunc HandlerFunc HandlerFunc HandlerFunc

Web框架面试题

实际上,面整体Web框架的还是比较少的。大多数时候,面试都是聊具体的某个Web框架。
从整体上来面的话,比较高频率的问题:

  • Web框架拿来做什么?处理HTTP请求,为用户提供便捷API,为用户提供无侵入式的插件机制,提供如上传下载等默认功能
  • 为什么都已经有了http包,还要开发Web框架?高级路由功能、封装HTTP上下文以提供简单API、封装Server以提供生命周期控制、设计插件机制以提供无侵入式解决方案
  • Web框架的核心?路由树、上下文Context、Server(重要性排序)
posted @ 2022-12-21 22:31  请务必优秀  阅读(412)  评论(0编辑  收藏  举报