当前位置: 首页 > news >正文

2.2.1goweb内置的 HTTP 处理程序

net/http 使用源码分析

在 Go 语言的 HTTP 服务器里,HTTP handler 是实现了http.Handler接口的对象。该接口定义如下:

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

ServeHTTP方法接收两个参数:

  • http.ResponseWriter:用于向客户端发送 HTTP 响应。
  • *http.Request:代表客户端的 HTTP 请求。
示例代码
package mainimport ("fmt""net/http"
)// 自定义的HTTP处理程序
type HelloHandler struct{}// 实现http.Handler接口的ServeHTTP方法
func (h HelloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {fmt.Fprintf(w, "Hello, World!")
}func main() {// 创建自定义handler的实例hello := HelloHandler{}// 注册handler到默认的ServeMuxhttp.Handle("/", hello)// 启动HTTP服务器,监听8080端口fmt.Println("Starting server on :8080")http.ListenAndServe(":8080", nil)
}

在上述代码中,HelloHandler结构体实现了http.Handler接口,其ServeHTTP方法会向客户端发送 "Hello, World!" 消息。

源码分析

http.Handler接口定义在net/http/server.go文件中:

// Handler is an interface implemented by an HTTP handler.
type Handler interface {ServeHTTP(ResponseWriter, *Request)
}

该接口仅有一个方法ServeHTTP,任何实现了这个方法的类型都可作为 HTTP handler 使用。

http.ServeMux

http.ServeMux是一个 HTTP 请求多路复用器,它会将不同的请求路径映射到对应的 handler。其定义如下:

// ServeMux is an HTTP request multiplexer.
// It matches the URL of each incoming request against a list of registered
// patterns and calls the handler for the pattern that most closely matches the
// URL.
type ServeMux struct {mu    sync.RWMutexm     map[string]muxEntryes    []muxEntry // slice of entries sorted from longest to shortest.hosts bool       // whether any patterns contain hostnames
}type muxEntry struct {h       Handlerpattern string
}

ServeMux结构体包含一个映射m,用于存储路径模式和对应的 handler。http.Handle函数会向ServeMux注册 handler:

// Handle registers the handler for the given pattern.
// If a handler already exists for pattern, Handle panics.
func (mux *ServeMux) Handle(pattern string, handler Handler) {mux.mu.Lock()defer mux.mu.Unlock()if pattern == "" {panic("http: invalid pattern")}if handler == nil {panic("http: nil handler")}if _, exist := mux.m[pattern]; exist {panic("http: multiple registrations for " + pattern)}if mux.m == nil {mux.m = make(map[string]muxEntry)}e := muxEntry{h: handler, pattern: pattern}mux.m[pattern] = eif pattern[len(pattern)-1] == '/' {mux.es = appendSorted(mux.es, e)}if pattern[0] != '/' {mux.hosts = true}
}

ServeMuxServeHTTP方法会根据请求的路径查找对应的 handler 并调用其ServeHTTP方法:

// ServeHTTP dispatches the request to the handler whose
// pattern most closely matches the request URL.
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {if r.RequestURI == "*" {if r.ProtoAtLeast(1, 1) {w.Header().Set("Connection", "close")}w.WriteHeader(StatusBadRequest)return}h, _ := mux.Handler(r)h.ServeHTTP(w, r)
}
http.ListenAndServe

http.ListenAndServe函数用于启动一个 HTTP 服务器,它会监听指定的地址和端口,并处理 HTTP 请求:

// ListenAndServe listens on the TCP network address addr and then calls
// Serve with handler to handle requests on incoming connections.
// Accepted connections are configured to enable TCP keep-alives.
//
// The handler is typically nil, in which case the DefaultServeMux is used.
//
// ListenAndServe always returns a non-nil error.
func ListenAndServe(addr string, handler Handler) error {server := &Server{Addr: addr, Handler: handler}return server.ListenAndServe()
}

http.ListenAndServe函数创建了一个http.Server实例,并调用其ListenAndServe方法启动服务器。

内置的 HTTP 处理程序

http.NotFoundHandler 功能概述

http.NotFoundHandler 是 net/http 包提供的一个内置处理程序,当客户端请求的资源不存在时,使用这个处理程序可以返回一个标准的 404 响应。它通常用于处理那些没有匹配到任何路由规则的请求。

使用示例
package mainimport ("net/http"
)func main() {// 注册一个空的路由,这里没有具体的处理逻辑http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {// 处理其他特定逻辑})// 对于所有未匹配的路由,使用 NotFoundHandler 返回 404 响应http.HandleFunc("/notfound", http.NotFoundHandler().ServeHTTP)// 启动服务器,监听 8080 端口http.ListenAndServe(":8080", nil)
}

在这个示例中,当客户端请求 /notfound 路径时,会触发 http.NotFoundHandler,返回 404 状态码和相应的提示信息。

源码分析

http.NotFoundHandler 的定义和实现位于 net/http/server.go 文件中,下面是其源码:

// NotFoundHandler returns a simple request handler
// that replies to each request with a ``404 page not found'' reply.
func NotFoundHandler() Handler { return NotFoundHandlerFunc }// NotFoundHandlerFunc is the default handler for http.NotFoundHandler.
func NotFoundHandlerFunc(w ResponseWriter, r *Request) {http.Error(w, "404 page not found", http.StatusNotFound)
}

  • NotFoundHandler 是一个函数,它返回一个实现了 http.Handler 接口的处理程序。在这个例子中,返回的是 NotFoundHandlerFunc
  • NotFoundHandlerFunc 是一个具体的函数,它实现了 http.Handler 接口的 ServeHTTP 方法所需的签名。当调用这个函数时,它会使用 http.Error 函数向客户端发送一个 404 状态码和相应的错误信息。

http.Error 函数

http.Error 函数用于向客户端发送一个带有指定错误信息和状态码的响应,其源码如下:

// Error replies to the request with the specified error message and HTTP code.
// It does not otherwise end the request; the caller should ensure no further
// writes are done to w.
// The error message should be plain text.
func Error(w ResponseWriter, error string, code int) {w.Header().Set("Content-Type", "text/plain; charset=utf-8")w.Header().Set("X-Content-Type-Options", "nosniff")w.WriteHeader(code)io.WriteString(w, error)
}
  • http.Error 函数首先设置响应头,指定内容类型为纯文本(text/plain)并设置字符集为 utf-8,同时设置 X-Content-Type-Options 为 nosniff 以防止浏览器进行内容类型嗅探。
  • 然后,使用 w.WriteHeader(code) 设置 HTTP 状态码。
  • 最后,使用 io.WriteString 函数将错误信息写入响应体。

RedirectHandler 

http.RedirectHandler的定义和实现位于net/http/server.go文件中,下面是其源码:

// RedirectHandler returns a request handler that redirects
// each request it receives to the given url using the given
// status code.
//
// The provided code should be in the 3xx range and is usually
// StatusMovedPermanently, StatusFound or StatusSeeOther.
func RedirectHandler(url string, code int) Handler {return redirectHandler{url, code}
}type redirectHandler struct {url  stringcode int
}func (rh redirectHandler) ServeHTTP(w ResponseWriter, r *Request) {Redirect(w, r, rh.url, rh.code)
}
// Redirect replies to the request with a redirect to url,
// which may be a path relative to the request path.
//
// The provided code should be in the 3xx range and is usually
// StatusMovedPermanently, StatusFound or StatusSeeOther.
func Redirect(w ResponseWriter, r *Request, url string, code int) {if u, err := urlparse.Parse(url); err == nil {// If url was relative, make absolute by// combining with request path.// The browser would probably do this for us,// but doing it ourselves is more reliable.//// RFC 2616 says that the Location header must be an absolute URI,// but we obey the spirit of the RFC and allow relative URIs so// long as they can be made absolute relative to the request URI.if u.Scheme == "" && u.Host == "" {oldpath := r.URL.Pathif oldpath == "" { // should not happen, but avoid a crash if it doesoldpath = "/"}// no leading http://serverif url == "" || url[0] != '/' {// make relative path absolutelast := len(oldpath) - 1if last >= 0 && oldpath[last] != '/' {oldpath = oldpath + "/"}url = oldpath + url}var query stringif i := strings.Index(url, "?"); i != -1 {url, query = url[:i], url[i:]}// clean up but preserve trailing slashtrailing := strings.HasSuffix(url, "/")url = path.Clean(url)if trailing && !strings.HasSuffix(url, "/") {url = url + "/"}url = url + query}}w.Header().Set("Location", hexEscapeNonASCII(url))w.WriteHeader(code)// RFC 2616 recommends that a short note "SHOULD" be included in the// response because older user agents may not understand 301/307.// Shouldn't send the response for POST or HEAD; that leaves GET.if r.Method == "GET" {body := "<a href=\"" + html.EscapeString(url) + "\">" + http.StatusText(code) + "</a>.\n"io.WriteString(w, body)}
}
  • http.Redirect函数首先会处理相对 URL,将其转换为绝对 URL。
  • 然后,设置响应头的Location字段为目标 URL,并使用w.WriteHeader(code)设置重定向状态码。
  • 最后,如果请求方法是GET,会在响应体中返回一个简单的 HTML 链接,提示用户重定向信息

相关文章:

  • uniapp做app,使用v-for遍历渲染第二层的时候,打包到手机上渲染不出第二层的数据
  • 5G与边缘计算:协同发展,开启智慧世界新篇章
  • (云计算HCIP)HCIP全笔记(十三)本篇介绍虚拟化技术,内容包含:虚拟化资源、虚拟化过程、I/O虚拟化、虚拟化架构KVM和Xen介绍、主流虚拟化技术介绍
  • 终端管理系统如何助力企业简化IT管理?
  • stm32wb55rg (2) 阅读资料手册
  • 近地卫星网络 (Low Earth Orbit Satellite Networks)入门学习笔记
  • C++23 std::bind_back:一种调用包装器 (P2387R3)
  • Scratch——第20课 辗转相除法/绳子算法
  • FTP-网络文件服务器
  • Docker 容器虚拟化技术和自动化部署
  • Java面试:Spring及Spring Cloud技术深度剖析
  • 基于Springboot + vue + 爬虫实现的高考志愿智能推荐系统
  • Nacos源码—1.Nacos服务注册发现分析二
  • 驱动开发硬核特训 │ 深度解析 fixed regulator 驱动与 regulator_ops
  • Linux 命令行利用 speedtest 测速
  • MySQL 的覆盖索引是什么?
  • 8.Android(通过Manifest配置文件传递数据(meta-data))
  • 【lammps】后处理 log.lammps
  • 如何在idea 中写spark程序
  • Linux学习笔记(一):Linux下的基本指令
  • 俄宣布停火三天,外交部:希望各方继续通过对话谈判解决危机
  • “麒麟王”亮相上海彩市,体彩即开票“瑞兽家族”迎来新成员
  • 王毅:为改革完善全球治理作出金砖贡献
  • 外交部官方公众号发布视频:不跪!
  • 船只深夜撞上海上风机后沉没1死1失踪,调查报告公布
  • 湖南华容县通报“大垱湖水质受污染”,爆料者:现场已在灌清水