Go中的HTTP请求处理概述

使用 Go 处理 HTTP 请求主要涉及两件事:ServeMuxes 和 Handlers。
ServeMux本质上是一个 HTTP 请求路由器(或多路复用器)。它将传入的请求与预定义的 URL 路径列表进行比较,并在找到匹配时调用路径的关联 handler。
handler 负责写入响应头和响应体。几乎任何对象都可以是 handler,只要它满足http.Handler接口即可。在非专业术语中,这仅仅意味着它必须是一个拥有以下签名的ServeHTTP方法:
ServeHTTP(http.ResponseWriter, *http.Request)
Go 的 HTTP 包附带了一些函数来生成常用的 handler,例如FileServer,NotFoundHandler和RedirectHandler。让我们从一个简单的例子开始:

$ mkdir handler-example
$ cd handler-example
$ touch main.go

File: main.go

package mainimport ("log""net/http"
)func main() {mux := http.NewServeMux()rh := http.RedirectHandler("http://example.org", 307)mux.Handle("/foo", rh)log.Println("Listening...")http.ListenAndServe(":3000", mux)
}

让我们快速介绍一下:

  • 在main函数中,我们使用http.NewServeMux函数创建了一个空的ServeMux。
  • 然后我们使用http.RedirectHandler函数创建一个新的handler。该handler将其接收的所有请求307重定向到http://example.org。
  • 接下来我们使用mux.Handle函数向我们的新ServeMux注册它,因此它充当URL路径/foo的所有传入请求的handler。
  • 最后,我们创建一个新服务并使用http.ListenAndServe函数开始监听传入的请求,并传入ServeMux给这个方法以匹配请求。

继续运行应用程序:

$ go run main.go
Listening...

并在浏览器中访问http://localhost:3000/foo。你会发现请求已经被成功重定向。
你可能已经注意到了一些有趣的东西:ListenAndServe 函数的签名是ListenAndServe(addr string, handler Handler),但我们传递了一个 ServeMux 作为第二个参数。
能这么做是因为 ServeMux 类型也有一个 ServeHTTP 方法,这意味着它也满足 Handler 接口。
对我而言,它只是将 ServeMux 视为一种特殊的 handler,而不是把响应本身通过第二个 handler 参数传递给请求。这不像刚刚听说时那么惊讶 - 将 handler 链接在一起在 Go 中相当普遍。

自定义 handler

我们创建一个自定义 handler,它以当前本地时间的指定格式响应:

type timeHandler struct {format string
}func (th *timeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {tm := time.Now().Format(th.format)w.Write([]byte("The time is: " + tm))
}

这里确切的代码并不太重要。
真正重要的是我们有一个对象(在该示例中它是一个timeHandler结构,它同样可以是一个字符串或函数或其他任何东西),并且我们已经实现了一个带有签名ServeHTTP(http.ResponseWriter, *http.Request)的方法。这就是我们实现一个 handler 所需的全部内容。
让我们将其嵌入一个具体的例子中:

File: main.go

package mainimport ("log""net/http""time"
)type timeHandler struct {format string
}func (th *timeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {tm := time.Now().Format(th.format)w.Write([]byte("The time is: " + tm))
}func main() {mux := http.NewServeMux()th := &timeHandler{format: time.RFC1123}mux.Handle("/time", th)log.Println("Listening...")http.ListenAndServe(":3000", mux)
}

在main函数中,我们使用&符号生成指针,用与普通结构完全相同的方式初始化timeHandler。然后,与前面的示例一样,我们使用mux.Handle函数将其注册到我们的ServeMux。
现在,当我们运行应用程序时,ServeMux会将任何通过/time路径的请求直接传递给我们的timeHandler.ServeHTTP方法。
试一试:http://localhost:3000/time。
另请注意,我们可以轻松地在多个路径中重复使用timeHandler:

func main() {mux := http.NewServeMux()th1123 := &timeHandler{format: time.RFC1123}mux.Handle("/time/rfc1123", th1123)th3339 := &timeHandler{format: time.RFC3339}mux.Handle("/time/rfc3339", th3339)log.Println("Listening...")http.ListenAndServe(":3000", mux)
}

普通函数作为 handler

对于简单的情况(如上例),定义新的自定义类型和 ServeHTTP 方法感觉有点啰嗦。让我们看看另一个方法,我们利用 Go 的http.HandlerFunc类型来使正常的函数满足 Handler 接口。
任何具有签名func(http.ResponseWriter, *http.Request)的函数都可以转换为 HandlerFunc 类型。这很有用,因为 HandleFunc 对象带有一个内置的ServeHTTP方法 - 这非常巧妙且方便 - 执行原始函数的内容。
如果这听起来令人费解,请尝试查看相关的源代码。你将看到它是一种让函数满足 Handler 接口的非常简洁的方法。
我们使用这种方法来重写 timeHandler 应用程序:

File: main.go

package mainimport ("log""net/http""time"
)func timeHandler(w http.ResponseWriter, r *http.Request) {tm := time.Now().Format(time.RFC1123)w.Write([]byte("The time is: " + tm))
}func main() {mux := http.NewServeMux()// Convert the timeHandler function to a HandlerFunc typeth := http.HandlerFunc(timeHandler)// And add it to the ServeMuxmux.Handle("/time", th)log.Println("Listening...")http.ListenAndServe(":3000", mux)
}

事实上,将函数转换为HandlerFunc类型,然后将其添加到ServeMux的情况比较常见,Go提供了一个快捷的转换方法:mux.HandleFunc方法。

如果我们使用这个转换方法,main()函数将是这个样子:

func main() {mux := http.NewServeMux()mux.HandleFunc("/time", timeHandler)log.Println("Listening...")http.ListenAndServe(":3000", mux)
}

大多数时候使用这样的 handler 很有效。但是当事情变得越来越复杂时,将会受限。
你可能已经注意到,与之前的方法不同,我们必须在timeHandler函数中对时间格式进行硬编码。当我们想要将信息或变量从main()传递给 handler 时会发生什么?
一个简洁的方法是将我们的 handler 逻辑放入一个闭包中,把我们想用的变量包起来:

File: main.go

package mainimport ("log""net/http""time"
)func timeHandler(format string) http.Handler {fn := func(w http.ResponseWriter, r *http.Request) {tm := time.Now().Format(format)w.Write([]byte("The time is: " + tm))}return http.HandlerFunc(fn)
}func main() {mux := http.NewServeMux()th := timeHandler(time.RFC1123)mux.Handle("/time", th)log.Println("Listening...")http.ListenAndServe(":3000", mux)
}

timeHandler函数现在有一点点不同。现在使用它来返回handler,而不是将函数强制转换为handler(就像我们之前所做的那样)。能这么做有两个关键点。
首先它创建了一个匿名函数fn,它访问形成闭包的format变量。无论我们如何处理闭包,它总是能够访问它作用域下所创建的局部变量 - 在这种情况下意味着它总是可以访问format变量。
其次我们的闭包有签名为func(http.ResponseWriter, *http.Request)的函数。你可能还记得,这意味着我们可以将其转换为HandlerFunc类型(以便它满足Handler接口)。然后我们的timeHandler函数返回这个转换后的闭包。
在这个例子中,我们仅仅将一个简单的字符串传递给handler。但在实际应用程序中,您可以使用此方法传递数据库连接,模板映射或任何其他应用程序级的上下文。它是全局变量的一个很好的替代方案,并且可以使测试的自包含handler变得更整洁。
你可能还会看到相同的模式,如下所示:

func timeHandler(format string) http.Handler {return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {tm := time.Now().Format(format)w.Write([]byte("The time is: " + tm))})
}

或者在返回时使用隐式转换为 HandlerFunc 类型:

func timeHandler(format string) http.HandlerFunc {return func(w http.ResponseWriter, r *http.Request) {tm := time.Now().Format(format)w.Write([]byte("The time is: " + tm))}
}

DefaultServeMux

你可能已经看到过很多地方提到的 DefaultServeMux,包括最简单的 Hello World 示例到 Go 源代码。
我花了很长时间才意识到它并不特别。 DefaultServeMux 只是一个普通的 ServeMux,就像我们已经使用的那样,默认情况下在使用 HTTP 包时会实例化。以下是 Go 源代码中的相关行:

var DefaultServeMux = NewServeMux()

通常,你不应使用 DefaultServeMux,因为它会带来安全风险。
由于 DefaultServeMux 存储在全局变量中,因此任何程序包都可以访问它并注册路由 - 包括应用程序导入的任何第三方程序包。如果其中一个第三方软件包遭到破坏,他们可以使用 DefaultServeMux 向 Web 公开恶意 handler。
因此,根据经验,避免使用 DefaultServeMux 是一个好主意,取而代之使用你自己的本地范围的 ServeMux,就像我们到目前为止一样。但如果你决定使用它……
HTTP 包提供了一些使用 DefaultServeMux 的便捷方式:http.Handle和http.HandleFunc。这些与我们已经看过的同名函数完全相同,不同之处在于它们将 handler 添加到 DefaultServeMux 而不是你自己创建的 handler。
此外,如果没有提供其他 handler(即第二个参数设置为nil),ListenAndServe 将退回到使用 DefaultServeMux。
因此,作为最后一步,让我们更新我们的 timeHandler 应用程序以使用 DefaultServeMux:

File: main.go

package mainimport ("log""net/http""time"
)func timeHandler(format string) http.Handler {fn := func(w http.ResponseWriter, r *http.Request) {tm := time.Now().Format(format)w.Write([]byte("The time is: " + tm))}return http.HandlerFunc(fn)
}func main() {// Note that we skip creating the ServeMux...var format string = time.RFC1123th := timeHandler(format)// We use http.Handle instead of mux.Handle...http.Handle("/time", th)log.Println("Listening...")// And pass nil as the handler to ListenAndServe.http.ListenAndServe(":3000", nil)
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/824207.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

原生实现ajax

1 什么是ajax AJAX Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)。 AJAX 不是新的编程语言,而是一种使用现有标准的新方法。 AJAX 最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网…

unity学习(85)——同步节奏(tcp架构确实有问题)

挂的时间长了,就出现其他下线本地不destroy的情况了,而且此时再登录,新渲染中已经没有已经下线的玩家 unity这边就没有收到126!!!125的问题是多种多样的!!! 化简服务器w…

设计循环队列(队列oj)

1.设计循环队列 设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。 循环队列的一个好处是我们可以利用这个队列之前用过的空间。…

网站结构规范对于独立站的重要性

网站结构是指通过优化网站的内部结构和布局,以提高其在搜索引擎结果页面(SERP)中的排名和可见性的过程。 涉及到网站内部链接、导航菜单、网站url路径、网站地图等细节。 网站结构的优化可以更好地帮助搜索引擎了索引网站的内容&#xff0c…

【笔记】vscode debug进入site-packages包源码

选择左侧栏第三个图标,点击创建 launch.json 文件 选择 Python Debugger 选择Python文件 这里可以看到launch.json 文件 在configurations中添加键值对 "justMyCode": false在文件中打上断点,点击"三角符"号开始调试 按F11或者红框…

reportlab 生成pdf文件 (python)

1 安装 pip install reportlab2 应用场景 通过网页动态生成PDF文档大量的报告和数据发布用XML一步生成PDF 官网案例 3 PLATYPUS Platypus是“Page Layout and Typography Using Scripts”,是使用脚本的页面布局和印刷术的缩写,这是一个高层次页面布局…

【面试题】MySQL 事务的四大特性说一下?

事务是一个或多个 SQL 语句组成的一个执行单元,这些 SQL 语句要么全部执行成功,要么全部不执行,不会出现部分执行的情况。事务是数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成。 事务的主要作用是保证数…

Golang最快的Web框架

一、概述 Fiber是在Go最快的HTTP引擎Fasthttp之上构建的Go web框架。它的设计目的是在考虑到零内存分配和性能的情况下简化快速开发。它的特性: 稳健的路由提供静态文件极致性能内存占用空间低API终点中间件和Next支持快速服务器端编程模板引擎WebSocket支持Socke…

记一次kafkakerberos认证问题

1,报错信息 排查思路:检查kerberos配置文件 kerberos.kafka.principalkafka/huawe_baseSECURITY.COM kerberos.kafka.keytabPath/etc/huawe_base.keytab kerberos.kafka.krb5ConfPath/etc/krb5.conf但是查看kafka_client_jass.conf文件,发现…

网络基础-TCP/IP和OSI协议模型

一、OSI和TCP/IP模型 二、OSI七层模型 三、TCP/IP模型 参考:https://www.cnblogs.com/f-ck-need-u/p/7623252.html

由于bug发现的现象

//********************************* 示例1 ******************************* $flag (float)2; var_dump($flag); if ($flag 2) { } var_dump($flag);//输出结果 float(2) int(2)//********************************* 示例2 ******************************* $flag (floa…

关联规则挖掘(二)

目录 三、FP-增长算法(一)算法的背景(二)构造FP-树(三)生成频繁项集 四、关联规则的评价(一)支持度和置信度的不足(二)相关性分析 三、FP-增长算法 &#xf…

.NET 发布,部署和运行应用程序

.NET应用发布 发布.Net应用有很多种方式,下面列举三种发布方式: 单文件发布跨平台发布Docker发布 单文件发布 右键工程,选择“发布”,部署模式选择“独立”,目标运行时选择自己想要部署到的系统,我这里用…

问题:react函数中的state是上一次的值

场景 有一个聊天输入框组件,输入框上面有表情包组件。 通过redux创建了store,存储一个message的状态,用于表情包和输入框共享状态。 输入框通过设置value和onClick做了一个简单双向绑定,其中value的值为store里的message。 impor…

ChatGPT写作术:高效撰写顶级论文

ChatGPT无限次数:点击直达 ChatGPT写作术:高效撰写顶级论文 在当今信息爆炸的时代,如何撰写出高质量的论文成为许多研究者和学生的重要课题。借助人工智能技术的不断发展,像ChatGPT这样的语言生成模型能够为撰写论文提供有效的帮助。本文将介…

【Java框架】Spring框架(三)——Spring整合Mybatis及Spring声明式事务

目录 回顾Mybatis和新对象思路整理 Spring和MyBatis的整合步骤1. 创建Web工程,导入Spring和MyBatis的相关依赖2. 建立开发目录结构,创建实体类3. 创建数据访问接口和SQL映射语句文件4. 使用Spring配置文件配置数据源4.1 database.properties4.2spring配置…

全国产化无风扇嵌入式车载电脑在救护车远端诊断的行业应用

救护车远端诊断的行业应用 背景介绍 更加快速的为急症病人在第一时间开始进行诊断和治疗,是提高病人救助成功率的关键。因此,先进的救护系统正在思考,如何在病人进入救护车之后,立刻能够将救护车中各种检查仪器的信息快速的传回医院&#xf…

aws云靶场和一些杂记

aws靶场 在AWS靶场中,存在三个安全问题:1) 一个S3存储桶政策配置错误,允许公共访问,通过访问特定域名可获取flag。2) SQS消息队列的政策没有限制角色,允许发送和接收消息,通过aws sqs命令行工具的receive-…

Javaweb知识之AJAX的概念的通俗理解(包含axios)

AJAX 一.概念: AJAX(Asynchronous JavaScript And XML):异步的JavaScript和XML 异步 JavaScript的理解:就像你给朋友发了一条消息,然后继续做其他事情一样。你不需要等待朋友回复,可以继续做自…

JS-42-Node.js01-Node.js介绍

一、浏览器大战 众所周知,在Netscape设计出JavaScript后的短短几个月,JavaScript事实上已经是前端开发的唯一标准。 后来,微软通过IE击败了Netscape后一统桌面,结果几年时间,浏览器毫无进步。(2001年推出…