设计Context
context,顾名思义,就是存储我们在进行路由的时候所需要的各种信息,有路由请求request
的信息,也有返回的responseWriter
里的信息等
由于信息过于杂碎,很多不重要的信息要重复机械地填写,如果不进行封装,会让代码看的拥挤冗长
设计context,可以将一些复杂的信息保留在context内部,对外进行路由处理的时候简化接口
day2-context/gee/context.go
package gee
import (
"encoding/json"
"fmt"
"net/http"
)
type H map[string]interface{}
type Context struct {
// origin objects
Writer http.ResponseWriter
Req *http.Request
// request info
Path string
Method string
// response info
StatusCode int
}
func newContext(w http.ResponseWriter, req *http.Request) *Context {
return &Context{
Writer: w,
Req: req,
Path: req.URL.Path,
Method: req.Method,
}
}
func (c *Context) PostForm(key string) string {
return c.Req.FormValue(key)
}
//URl可以理解为网址,例如 http://example.com:8080/path/to/resource?key1=value1&key2=value2#section1
func (c *Context) Query(key string) string {
return c.Req.URL.Query().Get(key)
}
func (c *Context) Status(code int) {
c.StatusCode = code
c.Writer.WriteHeader(code)
}
func (c *Context) SetHeader(key string, value string) {
c.Writer.Header().Set(key, value)
}
//上面的是一些封装好的工具,接下来要对string json html利用工具进行处理
func (c *Context) String(code int, format string, values ...interface{}) {
c.SetHeader("Content-Type", "text/plain")
c.Status(code)
c.Writer.Write([]byte(fmt.Sprintf(format, values...)))
}
func (c *Context) JSON(code int, obj interface{}) {
c.SetHeader("Content-Type", "application/json")
c.Status(code)
encoder := json.NewEncoder(c.Writer)
if err := encoder.Encode(obj); err != nil {
http.Error(c.Writer, err.Error(), 500)
}
}
func (c *Context) Data(code int, data []byte) {
c.Status(code)
c.Writer.Write(data)
}
func (c *Context) HTML(code int, html string) {
c.SetHeader("Content-Type", "text/html")
c.Status(code)
c.Writer.Write([]byte(html))
}
代码当中对状态码,头部信息等常用的填写,利用方法进行了封装,通过使用封装好的方法,来包装处理String
,JSON
等格式的信息
并且还提供了访问PostForm和Query的方法,提高的代码的扩展能力
在下文当中添加路由的时候,只需要调用String( )
,JSON( )
等方法,输入状态码和我们想要返回给客户端的内容,就可以自动添加路由
路由
day2-context/gee/router.go
package gee
import (
"fmt"
"net/http"
)
type router struct {
handlers map[string]HandlerFunc
}
func newRouter() *router {
return &router{handlers: make(map[string]HandlerFunc)}
}
func (r *router) addRoute(method string, pattern string, handler HandlerFunc) {
key := method + "-" + pattern
r.handlers[key] = handler
}
func (r *router) handle(c *Context) {
key := c.Method + "-" + c.Path
fmt.Println(key)
if handler, ok := r.handlers[key]; ok {
handler(c)
} else {
c.String(http.StatusNotFound, "404 NOT FOUND: %s\n", c.Path)
}
}
通过addRoute
方法可以添加路由进路由表当中
通过handle
方法,会根据我们传入的context当中的信息,来查找路由表,调用对应的路由方法
框架入口
package gee
import (
"log"
"net/http"
)
// HandlerFunc defines the request handler used by gee
type HandlerFunc func(*Context)
// Engine implement the interface of ServeHTTP
type Engine struct {
router *router
}
// New is the constructor of gee.Engine
func New() *Engine {
return &Engine{router: newRouter()}
}
func (engine *Engine) addRoute(method string, pattern string, handler HandlerFunc) {
log.Printf("Route %4s - %s", method, pattern)
engine.router.addRoute(method, pattern, handler)
}
// GET defines the method to add GET request
func (engine *Engine) GET(pattern string, handler HandlerFunc) {
engine.addRoute("GET", pattern, handler)
}
// POST defines the method to add POST request
func (engine *Engine) POST(pattern string, handler HandlerFunc) {
engine.addRoute("POST", pattern, handler)
}
// Run defines the method to start a http server
func (engine *Engine) Run(addr string) (err error) {
return http.ListenAndServe(addr, engine)
}
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
c := newContext(w, req)
engine.router.handle(c)
}
addRoute
方法通过调用route里的addRoute
来添加路由type HandlerFunc func(*Context),
HandlerFunc被我们改写成了一个参数为context的匿名函数在调用
GET
和POST
方法的时候,只需要传入我们的路径信息pattern和一个匿名函数,通过对函数的context参数利用context.go
里封装好的方法来写入信息类似于下面这样,就可以很轻松的达到添加路由的目的了
day2-context/main.go
package main
import (
"net/http"
"gee"
)
func main() {
r := gee.New()
r.GET("/", func(c *gee.Context) {
c.HTML(http.StatusOK, "<h1>Hello 大沙包</h1>")
})
r.GET("/hello", func(c *gee.Context) {
// expect /hello?name=大沙包
c.String(http.StatusOK, "hello %s, you're at %s\n", c.Query("name"), c.Path)
})
r.POST("/login", func(c *gee.Context) {
c.JSON(http.StatusOK, gee.H{
"username": c.PostForm("username"),
"password": c.PostForm("password"),
})
})
r.Run(":9090")
}
运行之后,可以利用postman和curl等工具来进行验证
7.15号曾经写过这个帖子,后来忘记保存了,码了半天的字,图文并茂,很伤心,今天就简单写一下?