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 方法中显式地控制请求的分发逻辑。

相关推荐
岳哥说跨境1 小时前
TikTok无网络黑屏原因及解决方法
网络·tcp/ip·http·ip
cwtlw2 小时前
如何创建maven工程
java·笔记·后端·学习·maven
YGGP2 小时前
【Golang】Go语言编程思想(六):Channel,第四节,Select
golang
我命由我123452 小时前
15.Java 网络编程(网络相关概念、InetAddress、NetworkInterface、TCP 网络通信、UDP 网络通信、超时中断)
java·开发语言·网络·后端·tcp/ip·udp·java-ee
爱吃香菜---www3 小时前
Scala隐式泛型
开发语言·后端·scala
我爱写代码?3 小时前
Scala的隐式对象
开发语言·后端·scala
●VON4 小时前
go语言的成神之路-标准库篇-os标准库
linux·运维·服务器·开发语言·后端·学习·golang
一只拉古4 小时前
后端编程大师之路:在 .NET 应用中使用 ElasticSearch 和 Kibana 进行日志管理
后端·elasticsearch·架构
灼华十一4 小时前
数据结构-树状数组
数据结构·leetcode·golang·树状数组
程序员大金5 小时前
基于SpringBoot+Vue的驾校管理系统
java·vue.js·spring boot·后端·mysql·spring·intellij-idea