http.ServeMux多路复用器的设置

复制代码
package main

import (
    "fmt"
    "net/http"
)

func first(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "多函数-first")
}

func second(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "多函数-second")
}

func main() {
    server := http.Server{
        Addr: "localhost:8888",
        Handler: nil,
    }
    
    http.HandleFunc("/first", first)
    http.HandleFunc("/second", second)
    server.ListenAndServe()
    
}

以上代码中的 Handler: nil, 和 http.HandleFunc("/first", first)、http.HandleFunc("/second", second)会不会冲突?

在代码中,Handler: nil 和 http.HandleFunc 并不会冲突,这是因为 http.HandleFunc 实际上是在设置全局的 ServeMux,而 http.Server 结构体中的 Handler 字段,如果没有设置(或者设置为 nil),则会默认使用这个全局的 ServeMux。

以下是详细解释:

1.全局 ServeMux

http 包中有一个全局的 ServeMux 实例,名为 defaultServeMux。当使用 http.ListenAndServe 或者 http.Serve 函数时,如果没有提供特定的 Handler,就会使用这个全局的 ServeMux 来分发请求。

2.http.HandleFunc

这个函数是 http.Handle 的一个便捷版本,它将传入的函数作为处理器与指定的路径关联起来。这些处理器会被添加到全局的 defaultServeMux 中。

3.Handler: nil

在 http.Server 结构体中,Handler 字段用于指定一个自定义的 Handler 或 Handler 链。如果这个字段被设置为 nil,那么 http.Server 会回退到使用全局的 defaultServeMux。

在代码中,server 结构体的 Handler 字段被设置为 nil,这意味着服务器将使用全局defaultServeMux 来处理请求。而 http.HandleFunc 正是将处理器添加到这个全局 ServeMux 中,所以它们之间不会冲突。

当服务器接收到一个请求时,http.Server 会查找 defaultServeMux 中与请求路径匹配的处理器,并调用它来处理请求。因此,即使 Handler 字段被设置为 nil,服务器仍然能够正确地响应 /first 和 /second 路径的请求,因为这些路径已经被 http.HandleFunc 与相应的处理器关联起来了。

复制代码
package main

import (
    "fmt"
    "net/http"
)

func first(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "多函数-first")
}

func second(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "多函数-second")
}

type handle struct {
}

func (h *handle) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("同一处理"))
}

func main() {
    h := handle{}
    server := http.Server{
        Addr:    "localhost:8888",
        Handler: &h,
    }

    http.HandleFunc("/first", first)
    http.HandleFunc("/second", second)
    server.ListenAndServe()

}

如果Handler: &h,,会不会和 http.HandleFunc("/first", first)、http.HandleFunc("/second", second)冲突?

在代码中,Handler: &hhttp.HandleFunc 之间确实存在冲突。这是因为 http.Server 结构体的 Handler 字段和 http.HandleFunc 函数使用了不同的 ServeMux 实例。

  1. Handler: &h: 当在 http.Server 结构体中设置 Handler 为 &h 时,实际上是在告诉服务器使用 &h 作为其顶级处理器。这意味着所有的请求都会首先经过 &h 的 ServeHTTP 方法。如果没有在 &h 的 ServeHTTP 方法中显式地将请求分发到其他路由,那么 http.HandleFunc 设置的路由将不会被调用。

  2. http.HandleFunc: http.HandleFunc 函数将特定的 URL 路径与特定的处理器函数关联起来,并将这些路由添加到全局的 ServeMux(defaultServeMux) 中。这些路由只有在没有为 http.Server 设置自定义 Handler 时才会被使用。

在代码中,由于 Handler: &h 被设置为 http.Server 的处理器,全局的 ServeMux(即http.HandleFunc 添加的路由所在的 ServeMux) 将不会被使用。因此,任何发送到 /first 或 /second 的请求都会被 &h 的 ServeHTTP 方法处理,而不会调用 first 或 second 函数。

要解决这个问题,您需要确保 http.Server 使用全局的 ServeMux 作为其处理器,或者在 &h 的 ServeHTTP 方法中显式地将请求分发到其他路由。以下是使用全局 ServeMux 的示例:

复制代码
package main

import (
    "fmt"
    "net/http"
)

func first(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "多函数-first")
}

func second(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "多函数-second")
}

type handle struct {
}

func (h *handle) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    // 这里可以添加逻辑来处理特定的路径,或者直接使用默认的ServeMux
    http.DefaultServeMux.ServeHTTP(w, r)
}

func main() {
    h := handle{}
    server := http.Server{
        Addr:    "localhost:8888",
        Handler: &h, // 使用自定义ServeMux
    }

    http.HandleFunc("/first", first)
    http.HandleFunc("/second", second)
    server.ListenAndServe()
}

在这个修改后的代码中,handle 结构体的 ServeHTTP 方法调用 http.DefaultServeMux.ServeHTTP,这样所有的请求都会通过全局的 ServeMux 来处理,包括通过 http.HandleFunc 设置的路由。这样就不会与 http.HandleFunc 设置的路由冲突了。

http.DefaultServeMux.ServeHTTP(w, r)这行代码发挥什么作用?

http.DefaultServeMux.ServeHTTP(w, r) 这行代码的作用是将接收到的 HTTP 请求 r 以及用于响应的 http.ResponseWriter 对象 w 传递给默认的 ServeMux 实例进行处理。

在 Go 的 net/http 包中,http.DefaultServeMux 是一个全局的 ServeMux 实例,它负责根据请求的 URL 路径分发请求到注册的处理器(handler)。当你调用 http.HandleFunc 或 http.Handle 时,实际上是在向这个全局的 ServeMux 注册路由。

当你在自定义的 http.Handler(如你的 handle 结构体中调 http.DefaultServeMux.ServeHTTP(w, r),你实际上是在告诉 Go 的 HTTP 服务器使用全局的路由规则来处理请求。这意味着,即使你的服务器使用了自定义的 http.Handler,它仍然会遵循通过 http.HandleFunc 或 http.Handle 设置的路由规则。

这样做的好处是,你可以在自定义的 http.Handler 中添加一些通用的处理逻辑,比如日志记录、认证、请求预处理等,然后再将请求交给全局的 ServeMux 来根据具体的路由规则进行处理。这样既保持了自定义处理的灵活性,又能够利用全局 ServeMux 的路由功能。

简而言之,http.DefaultServeMux.ServeHTTP(w, r) 允许你的自定义 http.Handler 与全局的路由规则协同工作,确保所有的路由设置都能被正确地应用到传入的请求上。

如何在 &h 的 ServeHTTP 方法中显式地将请求分发到其他路由?

在 Go 中,如果你想在自定义的 ServeHTTP 方法中显式地将请求分发到其他路由,你需要在你的 ServeHTTP 方法中实现一个逻辑,该逻辑会检查请求的 URL 并决定使用哪个处理器(handler)。以下是如何实现这一点的步骤:

1.检查请求路径:在你的 ServeHTTP 方法中,首先检查请求的 URL 路径。

2.调用相应的处理器:根据路径匹配结果,调用相应的处理器函数或结构体的 ServeHTTP 方法。

3.使用全局 ServeMux:如果你不想完全重写路由逻辑,可以使用全局的 ServeMu(http.DefaultServeMux)来处理请求,这可以通过调http.DefaultServeMux.ServeHTTP(w, r) 实现。

以下是一个示例代码,展示了如何在自定义 ServeHTTP 方法中显式分发请求:

复制代码
package main

import (
    "fmt"
    "net/http"
)

// 定义两个处理器函数
func first(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "多函数-first")
}

func second(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "多函数-second")
}

// 自定义ServeMux结构体
type handle struct{}

// 实现ServeHTTP方法
func (h *handle) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    // 检查请求路径并分发请求
    if r.URL.Path == "/first" {
        first(w, r)
    } else if r.URL.Path == "/second" {
        second(w, r)
    } else {
        // 如果没有匹配的路径,使用默认的ServeMux处理
        http.DefaultServeMux.ServeHTTP(w, r)
    }
}

func main() {
    h := handle{}
    server := http.Server{
        Addr:    "localhost:8888",
        Handler: &h,
    }
    server.ListenAndServe()
}

在这个例子中,我们创建了一个自定义的 handle 结构体,并实现了 ServeHTTP 方法。在这个方法中,我们检查了请求的 URL 路径,并根据路径调用了相应的处理器函数。如果请求的路径不匹配任何已知路由,我们使用 http.DefaultServeMux.ServeHTTP(w, r) 将请求分发给全局的 ServeMux 处理,这样就能处理通过 http.HandleFunc 注册的路由。这种方法允许你在自定义的 ServeHTTP 方法中显式地控制请求的分发逻辑。

相关推荐
Clown9519 分钟前
go-zero(十九)使用Prometheus监控ES指标
elasticsearch·golang·prometheus
wowocpp1 小时前
spring boot Controller 和 RestController 的区别
java·spring boot·后端
后青春期的诗go1 小时前
基于Rust语言的Rocket框架和Sqlx库开发WebAPI项目记录(二)
开发语言·后端·rust·rocket框架
freellf1 小时前
go语言学习进阶
后端·学习·golang
全栈派森3 小时前
云存储最佳实践
后端·python·程序人生·flask
CircleMouse3 小时前
基于 RedisTemplate 的分页缓存设计
java·开发语言·后端·spring·缓存
獨枭4 小时前
使用 163 邮箱实现 Spring Boot 邮箱验证码登录
java·spring boot·后端
矿工学编程4 小时前
.NET 8 kestrel 配置PEM,实现内网https
网络协议·http·https
维基框架5 小时前
Spring Boot 封装 MinIO 工具
java·spring boot·后端
秋野酱5 小时前
基于javaweb的SpringBoot酒店管理系统设计与实现(源码+文档+部署讲解)
java·spring boot·后端