回首页

文中的代码片段多来自Go Web 编程 一书,有些根据自己的需求,做了修改

如添加路由,可以请求方式映射到自定义方法:

a.AddRoute("/users", map[string]string{
        "GET": "Index",
    }, &controller.UserController{})
a.AddRoute("/users/:id", map[string]string{
        "GET": "Show",
}, &controller.UserController{})

一. 路由

1 hello world

package main

import (
    "fmt"
    "log"
    "net/http"
)

func fooHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello World!") // 这个写入到 w 的是输出到客户端的
}
func main() {
    http.HandleFunc("/foo", fooHandler) //注册路由
    err := http.ListenAndServe(":9090", nil) // 设置监听的端口
    if err != nil {
        log.Fatal("ListenAndServe: ", err)
    }
}

运行

go run main.go

访问 localhost:9090/foo 输出 hello world

2 路由分发

由于go的路由不支持正则匹配,所以上面的路由比较僵硬,要满足动态转发的要求

上面的:

http.ListenAndServe(":9090", nil) 

在源码C:\Go\src\net\http\server.go 3035行

func ListenAndServe(addr string, handler Handler) error {
    server := &Server{Addr: addr, Handler: handler}
    return server.ListenAndServe()
}

最终执行

// serverHandler delegates to either the server's Handler or
// DefaultServeMux and also handles "OPTIONS *" requests.
type serverHandler struct {
    srv *Server
}

func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
    handler := sh.srv.Handler
    if handler == nil {
        handler = DefaultServeMux
    }
    if req.RequestURI == "*" && req.Method == "OPTIONS" {
        handler = globalOptionsHandler{}
    }
    handler.ServeHTTP(rw, req)
}

传入的第二个参数Handler

http.ListenAndServe(":9090", Handler) 

最终执行的是Handler的ServeHTTP方法

handler.ServeHTTP(rw, req)

因此Handler是一个结构体,并实现了ServeHTTP方法

type Hander struct {
    http.Handler
}
//实现ServeHTTP方法
func (h *Hander) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello World!") // 这个写入到 w 的是输出到客户端的
}

完整例子

package main

import (
    "fmt"
    "log"
    "net/http"
)

type Hander struct {
    http.Handler
}

func (h *Hander) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, r.URL.Path) //可根据Path 分配请求到相对应的控制器
    fmt.Fprintf(w, "\nHello World!") // 这个写入到 w 的是输出到客户端的
}

func main() {
    hander := &Hander{}
    err := http.ListenAndServe(":9090", hander) // 设置监听的端口
    if err != nil {
        log.Fatal("ListenAndServe: ", err)
    }
}

不管访问哪那个路由都返回的是 Hello World!

现在前端访问都进入到 ServeHTTP方法

然后根据页面访问的url ,正则匹配到相对应的控制器中

路由分发,就不得不提go的反射机制

反射的话,只需知道如何调用方法即可,例子:

package main

import (
    "fmt"
    "reflect"
)

type User struct {
    Name string
}

func (u *User) GetName() string {
    fmt.Println("获取成功:" + u.Name)
    return u.Name
}
func (u *User) SetName(name string) {
    u.Name = name
    fmt.Println("设置成功:" + u.Name)

}

func main() {

    user := &User{Name: "hello name0"}

    obj := reflect.ValueOf(user)

    //调用带参数的方法
    args := []reflect.Value{reflect.ValueOf("hello set Name1")}
    obj.MethodByName("SetName").Call(args) //输出设置成功:hello set Name1

    //调用非参数的方法
    args = make([]reflect.Value, 0)
    obj.MethodByName("GetName").Call(args) //获取成功:hello set Name1

}

现在我们知道如何通过反射调用结构体的方法。接下来要把路由和对应的结构体的方法对应起来,就可以路由到最终的方法。类似于这样

[
    '/home' => 'HomeController@Home'
]

路由/home可以接受任何形式的请求。比如要把Post请求路由到HomeController 下的 PostHome 方法。因此要更改下上面的结构。

[
     [
        'Regex' => '/home'
        'controllerType'=>'HomeController',
        'methods'=> [
            'Post' =>'HomePost',
            'Get'  =>'HomeGet'
        ]
    ]
]

因此整个路由信息可以用这样的数据结构表示 Routes 是一个Map

每一个Route对应一个结构体,里面包含了四个信息

Routes map[int]Route

type Route struct {
    Regex          *regexp.Regexp //正则表达式类型,匹配到该路由则执行ControllerType在Methods的对应的方法
    Methods        map[string]string
    Params         map[int]string// 比如路由/users/:id 记录["id"]
    ControllerType reflect.Type //控制器,参考
}

接下来要注册路由,为Hander 添加AddRoute方法

func (a *Hander) AddRoute(pattern string, m map[string]string, c controller.ControllerInterface) {
    parts := strings.Split(pattern, "/")

    j := 0
    params := make(map[int]string)
    for i, part := range parts {
        if strings.HasPrefix(part, ":") {
            expr := "([^/]+)"

            // a user may choose to override the defult expression
            // similar to expressjs: ‘/user/:id([0-9]+)’

            if index := strings.Index(part, "("); index != -1 {
                expr = part[index:]
                part = part[1:index]
            } else {
                part = part[1:len(part)]
            }

            params[j] = part
            parts[i] = expr
            j++
        }
    }

    // recreate the url pattern, with parameters replaced
    // by regular expressions. then compile the regex

    pattern = strings.Join(parts, "/")
    regex, regexErr := regexp.Compile(pattern)
    if regexErr != nil {

        // TODO add error handling here to avoid panic
        panic(regexErr)
        return
    }

    // now create the Route
    t := reflect.Indirect(reflect.ValueOf(c)).Type()
    route := &routes.Route{}
    route.Regex = regex
    route.Methods = m
    route.Params = params
    route.ControllerType = t

    a.routes = append(a.routes, route)

}

调用的时候

UserControllerController 介绍

a.AddRoute("/users", map[string]string{
        "GET": "Index",
    }, &controller.UserController{})

二. 控制器

参考控制器的设计

只保留三个方法

type ControllerInterface interface {
    Init(ct *contex.Context, cn string) // 初始化上下文和子类名称
    Prepare()                           // 开始执行之前的一些处理
    Finish()                            // 执行完成之后的处理
}

基类实现上面的三个方法

type Controller struct {
    Ct        *contex.Context
    ChildName string
}

func (c *Controller) Init(ct *contex.Context, cn string) {

    c.ChildName = cn
    c.Ct = ct
    fmt.Println("\n---------")
    fmt.Println("\nhello Init")

}

func (c *Controller) Prepare() {
    fmt.Println("\nhello Prepare")
}

func (c *Controller) Finish() {
    fmt.Println("\nhello Finish")
    fmt.Println("\n---------")
}

UserController 组合基类例子


type UserController struct {
    Controller
}

添加自定义方法
func (c *UserController) Show() {
    id, ok := c.Ct.Params["id"] //获取路由参数
}

//最后路由到上面的Show方法
a.AddRoute("/users/:id", map[string]string{
        "GET": "Show",
}, &controller.UserController{})

三. 模型

四. 视图