http.HandleFunc 解析

Go语言是一个神奇的语言,尤其是函数式编程,

func HandleFunc(pattern string, handler func(ResponseWriter, *Request))

就是一个典型。这个函数就是注册默认路由。
HandleFunc 的第一个参数指的是请求路径,第二个参数是一个函数类型,表示这个请求需要处理的事情。没有处理复杂的逻辑,而是直接给DefaultServeMux处理,如源码:

func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
    DefaultServeMux.HandleFunc(pattern, handler)
}

DefaultServeMux 是ServeMux一个全局实例,

// DefaultServeMux is the default ServeMux used by Serve.
var DefaultServeMux = &defaultServeMux
var defaultServeMux ServeMux

这个实例在被申明的时候初始化了,只要使用了DefaultServeMux这个变量,其实就是同一个指针而已,也就是独一份。
DefaultServeMux.HandleFunc(pattern, handler) 函数其实也没有做什么,直接调用路由注册:

// HandleFunc registers the handler function for the given pattern.
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
    mux.Handle(pattern, HandlerFunc(handler))
}

把自定义处理业务的函数进行路由注册,HandlerFunc(handler) 这个处理其实是类型转化,把函数handler func 类型转化成HandlerFunc类型,是一个强制转化,只要函数的参数类型一致,即可转化。我们来看一个HandlerFunc是如何定义的:

type HandlerFunc func(ResponseWriter, *Request)

// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    f(w, r)
}

从type可以看出 HandlerFunc 就是一个自定义类型,被定义成

func(ResponseWriter, *Request)

函数。所以只要符合此标准的函数,都可以转化成HandlerFunc,而这个函数实现了接口:

type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}

这个接口就是路由中,匹配的处理方法。

根据go对接口的定义,只有实现了接口的所有方法,就实现了这个接口。

我们来看是如何注册路由的,需要理解如下两个结构

  • ServeMux 多路路由器
  • muxEntry 具体路由
type ServeMux struct {
    mu    sync.RWMutex //锁
    m     map[string]muxEntry //路由集合
    hosts bool // whether any patterns contain hostnames
}

type muxEntry struct {
    explicit bool //是否已经存在
    h        Handler  //路由处理逻辑 是一个接口实例  在每次匹配的时候,调用此接口的方法
    pattern  string//请求路径
}

处逻辑源码如下:

// Handle 根据给定的请求路劲进行注册路由
// 如果Handle已经存在,就直接报错
func (mux *ServeMux) Handle(pattern string, handler Handler) {
 //进行加锁,高并发处理
    mux.mu.Lock()
    //释放锁
    defer mux.mu.Unlock()
 //请求路径为空,直接报错
    if pattern == "" {
        panic("http: invalid pattern " + pattern)
    }
    //Handle不存在,直接报错
    if handler == nil {
        panic("http: nil handler")
    }
    //如果Handle已经存在,就直接报错
    if mux.m[pattern].explicit {
        panic("http: multiple registrations for " + pattern)
    }
//路由表不存在,创建一个,这个路由表是一个映射MAP
//key 就是请求路径  value是muxEntry 包含具体路由信息
    if mux.m == nil {
        mux.m = make(map[string]muxEntry)
    }
    mux.m[pattern] = muxEntry{explicit: true, h: handler, pattern: pattern}
//如果首字母不是‘/’ 包含hostName
    if pattern[0] != '/' {
        mux.hosts = true
    }

    // Helpful behavior:
    // If pattern is /tree/, insert an implicit permanent redirect for /tree.
    // It can be overridden by an explicit registration.
    n := len(pattern)
    //如何是像/tree/ 请求注册路由,而且/tree 还没有注册过
    if n > 0 && pattern[n-1] == '/' && !mux.m[pattern[0:n-1]].explicit {
        // If pattern contains a host name, strip it and use remaining
        // path for redirect.
        path := pattern //保存原始路径
        if pattern[0] != '/' { //如果首字母不是 ‘/’
            // In pattern, at least the last character is a '/', so
            // strings.Index can't be -1.
            path = pattern[strings.Index(pattern, "/"):] //返回最近的/之后的串作为请求路径
        }
        //构建请求URL 直接重定向 而且注册的注册路径有/tree/  /tree
        url := &url.URL{Path: path}
        mux.m[pattern[0:n-1]] = muxEntry{h: RedirectHandler(url.String(), StatusMovedPermanently), pattern: pattern}
    }
}

以上是对函数

func HandleFunc(pattern string, handler func(ResponseWriter, *Request))

调用过程分析

转载于:https://blog.51cto.com/8184160/2052853

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要将路径修改为相对路径,您需要使用`http.Dir`函数将文件夹路径转换为`http.FileServer`类型。这样可以指定要提供给用户的静态文件所在的文件夹。以下是对代码的修改: ```go package main import ( "fmt" "html/template" "log" "net/http" "strings" ) func login(w http.ResponseWriter, r *http.Request) { fmt.Println("method:", r.Method) if r.Method == "GET" { t, err := template.ParseFiles("login.html") if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } if err := t.Execute(w, nil); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } } else { _ = r.ParseForm() fmt.Println("username:", r.Form["username"]) fmt.Println("password:", r.Form["password"]) if pwd := r.Form.Get("password"); pwd == "123456" { fmt.Fprintf(w, "欢迎登陆,Hello %s!", r.Form.Get("username")) } else { fmt.Fprintf(w, "密码错误!") } } } func sayHello(w http.ResponseWriter, r *http.Request) { _ = r.ParseForm() //3 解析参数,默认不解析 fmt.Println(r.Form) //4 输出到服务器端的打印信息 fmt.Println("Path:", r.URL.Path) for k, v := range r.Form { fmt.Println("key:", k) fmt.Println("value", strings.Join(v, "")) } fmt.Fprintf(w, "Hello aoho") //5 写入到w的是输出到客户端的内容 } func main() { http.HandleFunc("/", login) http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static")))) http.HandleFunc("/sayHello", sayHello) //1 设置访问的路由 err := http.ListenAndServe(":8080", nil) //2 设置监听的端口 if err != nil { log.Fatal("ListenSever:", err) } } ``` 这里假设您的静态资源文件夹名为`static`,并且在同一级目录下。使用`http.Handle`和`http.StripPrefix`函数来处理静态文件路由,并指定静态文件夹的相对路径。修改后,您应该能够使用相对路径来访问静态文件。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值