核心:实现net/http库中handler接口的ServeHTTP方法的实例,通过http.ListenAndServe注册后,所有的请求都会打到该实例的ServeHTTP方法里。Context是对请求对象和响应对象的封装,实现了获取请问请求参数、设置状态码、设置响应头、设置响应类型、设置响应体等方法。
1 项目结构
2 constant.go
package constantconst (DefaultMaxMemory = 32 << 20 // 32 MBSeparator = "_"
)
3 context.go
package contextimport ("encoding/json""mini_gee/constant""net/http"
)type HandleFunc func(context *Context)type Context struct {w http.ResponseWriterr *http.RequestPath stringMethod stringHandlers []HandleFuncindex int
}func NewContext(w http.ResponseWriter, r *http.Request) Context {return Context{w: w,r: r,Path: r.URL.Path,Method: r.Method,index: -1,}
}func (c *Context) Next() {c.index++for c.index < len(c.Handlers) {c.Handlers[c.index](c)c.index++}
}func (c *Context) SetHeader(key, value string) {c.w.Header().Set(key, value)
}func (c *Context) SetStatusCode(code int) {c.w.WriteHeader(code)
}func (c *Context) GetPostForm(key string) (string, bool) {if c.r.PostForm == nil {c.r.ParseMultipartForm(constant.DefaultMaxMemory)}has := c.r.PostForm.Has(key)if !has {return "", false}return c.r.PostForm.Get(key), true
}func (c *Context) PostForm(key string) string {return c.r.PostFormValue(key)
}func (c *Context) GetQuery(key string) (string, bool) {has := c.r.URL.Query().Has(key)if !has {return "", false}return c.r.URL.Query().Get(key), true
}func (c *Context) Query(key string) string {return c.r.URL.Query().Get(key)
}func (c *Context) String(code int, text string) {c.SetHeader("Content-Type", "text/plain")c.SetStatusCode(code)_, err := c.w.Write([]byte(text))if err != nil {http.Error(c.w, err.Error(), http.StatusInternalServerError)}
}func (c *Context) JSON(code int, data interface{}) {c.SetHeader("Content-Type", "application/json")c.SetStatusCode(code)jsonData, err := json.Marshal(data)if err != nil {http.Error(c.w, err.Error(), http.StatusInternalServerError)}_, err = c.w.Write(jsonData)if err != nil {http.Error(c.w, err.Error(), http.StatusInternalServerError)}
}
4 engine.go
package engineimport ("fmt""mini_gee/constant""mini_gee/context""net/http"
)type Engine struct {middlewares []context.HandleFunchandlers map[string]context.HandleFunc
}func New() *Engine {return &Engine{handlers: make(map[string]context.HandleFunc),}
}func (e *Engine) ServeHTTP(w http.ResponseWriter, r *http.Request) {ctx := context.NewContext(w, r)defer func() {if err := recover(); err != nil {message := fmt.Sprintf("catch error: %v", err)fmt.Println(message)ctx.String(200, message)}}()method := r.Methodpath := r.URL.Pathkey := makeHandlerKey(method, path)handler, ok := e.handlers[key]if !ok {ctx.String(http.StatusNotFound, "handler not found")return}var handlers []context.HandleFunchandlers = append(handlers, e.middlewares...)handlers = append(handlers, handler)ctx.Handlers = handlersctx.Next()
}func (e *Engine) Use(middlewares ...context.HandleFunc) {for _, middleware := range middlewares {e.middlewares = append(e.middlewares, middleware)}
}func (e *Engine) POST(path string, handler context.HandleFunc) {key := makeHandlerKey(http.MethodPost, path)e.handlers[key] = handler
}func (e *Engine) GET(path string, handler context.HandleFunc) {key := makeHandlerKey(http.MethodGet, path)e.handlers[key] = handler
}func (e *Engine) Run(addr string) error {return http.ListenAndServe(addr, e)
}func makeHandlerKey(method, path string) string {return method + constant.Separator + path
}
5 example.go
package mainimport ("fmt""mini_gee/context""mini_gee/engine""time"
)func main() {e := engine.New()e.Use(func(context *context.Context) {start := time.Now()defer func() {fmt.Println(context.Method, context.Path, time.Since(start).Microseconds(), "us")}()context.Next()})e.POST("/hello", func(context *context.Context) {name, ok := context.GetPostForm("name")if !ok {context.JSON(200, map[string]interface{}{"code": 1,"message": "field name not found",})return}context.JSON(200, map[string]interface{}{"code": 1,"message": "hello " + name,"data": name,})})e.GET("/bonjour", func(context *context.Context) {food, ok := context.GetQuery("food")if !ok {context.JSON(200, map[string]interface{}{"code": 1,"message": "field food not found",})return}context.String(200, food)})e.GET("/test_error", func(context *context.Context) {var arr []intfmt.Println(arr[10])})if err := e.Run(":9090"); err != nil {panic(err)}
}
6 docker部署
6.1 编写dockerfile
FROM ubuntu
ENV MYPATH=/usr/local
WORKDIR $MYPATH
ADD main ./
CMD ./main
6.2 定制镜像
6.3 后台启动容器
7 测试