## 引言
> 简单说下本章的重点
* 1、路由支持中间件
* 2、新增日志中间件
* 4、新增异常错误中间件
* 3、[代码地址 https://github.com/18211167516/go-Ebb/tree/master/day5-middleware](https://github.com/18211167516/go-Ebb/tree/master/day5-middleware)
## 1、重点
* 中间件的目的是为了让web框架灵活支持用户某些自定义行为,对外开放一个接口
* 核心的点在什么时候插入、以及中间件传入的支持参数
## 2、思路
* 1、作用于每一条路由规则
* 2、作用于Context、依次执行该路由说匹配的中间件
* 3、中间件也和控制器层一样传入Context、接入点是框架接收到请求初始化Context对象后,允许用户使用自己定义的中间件做一些额外的处理、另外通过调用(*Context).Next()函数,中间件可等待用户自己定义的 Handler处理结束后,再做一些额外的操作
## 3、Context支持
```golang
package ebb
import (
"net/http"
"fmt"
"encoding/json"
)
type Context struct{
//write and request
Writer http.ResponseWriter
Request *http.Request
//request info
Method string
Path string
Params map[string]interface{}
HttpCode int
//middleware 新增
handlers HandlersChain
index int
}
func newContext(w http.ResponseWriter,r *http.Request) *Context{
context := &Context{
Writer:w,
Request:r,
Path: r.URL.Path,
Method: r.Method,
Params: make(map[string]interface{}),
index: -1,
}
return context
}
//本次新增
func (c *Context) Next() {
c.index++
for c.index < len(c.handlers) {
c.handlers[c.index](c)
c.index++
}
}
```
## 4、routerGroup支持
```golang
package ebb
type HandlersChain []HandlerFunc //新增
type RouterGroup struct {
prefix string
middlewares HandlersChain // support middleware
parent *RouterGroup // support nesting
engine *Engine // all groups share a Engine instance
}
func (group *RouterGroup) Group(prefix string) *RouterGroup {
engine := group.engine
newGroup := &RouterGroup{
prefix: group.prefix + prefix,//支持分组嵌套
parent: group,
engine: engine,
}
engine.groups = append(engine.groups, newGroup)
return newGroup
}
//添加中间件 v5新增
func (group *RouterGroup) Use(middlewares ...HandlerFunc){
group.middlewares = append(group.middlewares, middlewares...)
}
//合并handlers
func (group *RouterGroup) combineHandlers(handlers HandlersChain) HandlersChain {
finalSize := len(group.middlewares) + len(handlers)
mergedHandlers := make(HandlersChain, finalSize)
copy(mergedHandlers, group.middlewares)
copy(mergedHandlers[len(group.middlewares):], handlers)
return mergedHandlers
}
func (group *RouterGroup) addRoute(method string, comp string, handler ...HandlerFunc) {
pattern := group.prefix + comp
//添加的路由的时候将中间件合并到 路由handles
handlers := group.combineHandlers(handler)
group.engine.router.addRoute(method, pattern, handlers)
}
// GET defines the method to add GET request
func (group *RouterGroup) GET(pattern string, handler ...HandlerFunc) {
group.addRoute("GET", pattern, handler...)
}
// POST defines the method to add POST request
func (group *RouterGroup) POST(pattern string, handler ...HandlerFunc) {
group.addRoute("POST", pattern, handler...)
}
```
## 5、路由支持
```golang
package ebb
import (
"strings"
)
type router struct{
roots map[string]*node
handlers map[string]HandlersChain //新增
}
func newRouter() *router{
return &router{
roots:make(map[string]*node),
handlers:make(map[string]HandlersChain),
}
}
func (r *router) addRoute(method string,pattern string,handlers HandlersChain){
parts := parsePattern(pattern)
key := method + "-" + pattern
_, ok := r.roots[method]
if !ok {
r.roots[method] = &node{}
}
r.roots[method].insert(pattern, parts, 0)
r.handlers[key] = append(r.handlers[key],handlers...) //合并中间件和handlers
}
```
## 6、ebb支持
```golang
package ebb
import (
"net/http"
)
//定义框架请求处理方法
type HandlerFunc func(*Context)
//核心结构体
type Engine struct{
*RouterGroup //v4新增 顶级路由组
router *router
groups []*RouterGroup // v4新增
}
//实例化结构体
func New() *Engine{
engine := &Engine{
router : newRouter(),
}
engine.RouterGroup = &RouterGroup{engine: engine}
engine.groups = []*RouterGroup{engine.RouterGroup}
return engine
}
//新增
func Default() *Engine{
engine := New()
engine.Use(Logger(),Recovery())
return engine
}
//启动服务
func (engine *Engine) Run(addr string) (err error){
return http.ListenAndServe(addr,engine)
}
//engine 实现ServeHTTP接口(所有的请求都会走到这)
//查找是否路由映射表存在,如果存在则调用,否则返回404
func (engine *Engine) ServeHTTP(w http.ResponseWriter,req *http.Request){
c := newContext(w, req)
engine.handleHTTPRequest(c)
}
//v2 新增
func (engine *Engine) handleHTTPRequest(c *Context){
node,params := engine.router.getRoute(c.Method,c.Path)
if node != nil {
c.Params = params
key := c.Method + "-" + node.pattern
//赋值
c.handlers = engine.router.handlers[key]
}else{
c.handlers = append(c.handlers, func(c *Context) {
c.String(http.StatusNotFound, "404 NOT FOUND: %s\n", c.Path)
})
}
//真正调度执行中间件以及具体handler
c.Next()
}
```
## 7、新增logger中间件
```golang
package ebb
import (
"time"
"log"
)
func Logger() HandlerFunc {
return func(c *Context) {
// Start timer
t := time.Now()
// Process request
c.Next()
// Calculate resolution time
log.Printf("[%d] %s in %v", c.HttpCode, c.Request.RequestURI, time.Since(t))
}
}
```
## 8、新增错误异常中间件
```golang
package ebb
import (
"fmt"
"log"
"net/http"
"runtime"
"strings"
)
// print stack trace for debug
func trace(message string) string {
var pcs [32]uintptr
n := runtime.Callers(3, pcs[:]) // skip first 3 caller
var str strings.Builder
str.WriteString(message + "\nTraceback:")
for _, pc := range pcs[:n] {
fn := runtime.FuncForPC(pc)
file, line := fn.FileLine(pc)
str.WriteString(fmt.Sprintf("\n\t%s:%d", file, line))
}
return str.String()
}
func Recovery() HandlerFunc {
return func(c *Context) {
defer func() {
if err := recover(); err != nil {
message := fmt.Sprintf("%s", err)
log.Printf("%s\n\n", trace(message))
c.JSON(http.StatusInternalServerError, "Internal Server Error")
}
}()
c.Next()
}
}
```
## 9、使用案例
```golang
package main
import (
"ebb"
)
func main(){
r := ebb.Default()
r.GET("/panic",func(c *ebb.Context) {
panic("err")
})
r.POST("/login/*name",func(c *ebb.Context) {
c.JSON(200, ebb.H{
"name": c.Param("name"),
})
})
r.Run(":8080")
}
```
> 到目前为止,框架的基本架构已确立,后续就是一些补充性的功能,以及优化点
> 看起来是不是和gin框架非常类似
> 下一章节我们实现对go模板的使用封装
有疑问加站长微信联系(非本文作者))