快速上手GO的net/http包,个人学习笔记

更多个人笔记:(仅供参考,非盈利)
gitee: https://gitee.com/harryhack/it_note
github: https://github.com/ZHLOVEYY/IT_note

针对GO中net/http包的学习笔记

基础快速了解

创建简单的GOHTTP服务

func main() {  http.HandleFunc("/hello", sayHello)  http.ListenAndServe(":8080", nil) //创建基本服务  
}  func sayHello(w http.ResponseWriter, r *http.Request) {  w.Write([]byte("Hello, World!"))  
}

访问8080/hello进行测试

Handler接口定义:(这部分后面又详细解释)

type Handler interface {  ServeHTTP(ResponseWriter, *Request)  
}
//只要有ServeHTTP方法就行

可以自己实现这个接口

同时http提供了handlerFunc结构体

type HandlerFunc func(ResponseWriter, *Request)
// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
//本质上就是调用自身,因为也是一个函数,不过serveHTTP的内容自己可以定义改动

和之前的HandleFunc区分,HandleFunc是用来给不同路径绑定方法的,少一个r

那么只要满足Handlerfunc 的签名形式,就可以进行类型转换

  • 类型转换的正常的理解例子
type yes func(int, int) int  
func (y yes) add(a, b int) int {  return a + b  
}  
func multiply(a, b int) int {  fmt.Println(a * b)return a * b  
}  
func main() {  multiply(1, 2)   //2ans := yes(multiply) //将multiply进行转换  res := ans.add(1, 2)  fmt.Println(res)   //3
}
http.HandleFunc("/hello", hello)

这个后面的签名只要是 func(ResponseWriter, *Request)就可以了
但是

http.ListenAndServe(":8080", referer)

这个后面的函数需要是满足Handler接口,有serveHTTP方法

尝试搭建检测是在query中有name = red
即http://localhost:8080/hello?name=red
发现会有重复覆盖路由的问题,因为listenandServe会拦截所有的路由,后面再解决


type CheckQueryName struct {wantname stringhandler  http.Handler
}func (this *CheckQueryName) ServeHTTP(w http.ResponseWriter, r *http.Request) {queryParams := r.URL.Query() //获取get请求的queryname := queryParams.Get("name")if name == "red" {this.handler.ServeHTTP(w, r) //其实就是调用本身,下面变为checkforname了} else {w.Write([]byte("not this name"))}
}func checkforname(w http.ResponseWriter, r *http.Request) {w.Write([]byte("check is ok"))
}func hello(w http.ResponseWriter, r *http.Request) {w.Write([]byte("hello"))
}func main() {thecheck := &CheckQueryName{ //用&因为serveHTTP方法定义在指针接收器上wantname: "red",handler:  http.HandlerFunc(checkforname),}http.HandleFunc("/hello", hello)       //满足func(ResponseWriter, *Request)签名就可以http.ListenAndServe(":8080", thecheck) //直接监视8080的所有端口,拦截所有路由
}
编写简单的GET请求客户端

利用defaultclient或者自己定义client都可以

func main() {resp, err := http.DefaultClient.Get("https://api.github.com")if err != nil {panic(err)}defer resp.Body.Close()body, err := ioutil.ReadAll(resp.Body)if err != nil {panic(err)}fmt.Println(string(body))time.Sleep(time.Second * 2)  //等待获取请求
}

但是如果把网址换成baidu.com就会获取不到,这是因为转发,以及没有User-agent的问题

编写自定义的GET请求客户端

利用http.Client可以进行自定义

func main() {client := &http.Client{// 允许重定向CheckRedirect: func(req *http.Request, via []*http.Request) error {return nil},}req, err := http.NewRequest("GET", "https://www.baidu.com", nil)if err != nil {panic(err)}// 添加请求头req.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36")resp, err := client.Do(req) //do执行HTTP请求的整个周期包括请求准备,建立连接,发送请求,请求重定向,接收响应等等defer resp.Body.Close()body, err := ioutil.ReadAll(resp.Body)if err != nil {panic(err)}fmt.Println(string(body))time.Sleep(time.Second * 2)
}

发现可以接受到baidu的网页html信息

编写默认的post请求客户端
func main() {postData := strings.NewReader(`{"name": "张三", "age": 25}`)resp, err := http.DefaultClient.Post("http://localhost:8080/users","application/json",  postData,)if err != nil {fmt.Printf("POST请求失败: %v\n", err)return}defer resp.Body.Close()body, _ := ioutil.ReadAll(resp.Body)fmt.Printf("POST响应: %s\n", string(body))
}

string.NewReader是一种方法将格式改为io reader可以读取的形式,接收的话也可以postData.Read读取。
这一类的方式比较多,不一一汇总

对应的server.go (需要在终端中go run server.go )两个终端分别运行服务端,客户端

func main() {// 处理 /users 路径的 POST 请求http.HandleFunc("/users", func(w http.ResponseWriter, r *http.Request) {// 只允许 POST 方法if r.Method != http.MethodPost {w.WriteHeader(http.StatusMethodNotAllowed)fmt.Fprintf(w, "只支持 POST 方法")return}// 读取请求体body, err := ioutil.ReadAll(r.Body)if err != nil {w.WriteHeader(http.StatusBadRequest)fmt.Fprintf(w, "读取请求失败: %v", err)return}defer r.Body.Close()// 解析 JSON 数据,放入user中var user Userif err := json.Unmarshal(body, &user); err != nil {w.WriteHeader(http.StatusBadRequest) //写入状态码fmt.Fprintf(w, "JSON 解析失败: %v", err)return}// 设置响应头w.Header().Set("Content-Type", "application/json")// 构造响应数据response := map[string]interface{}{"message": "success","data": map[string]interface{}{"name": user.Name,"age":  user.Age,},}// 返回 JSON 响应json.NewEncoder(w).Encode(response) //将 response 对象转换为 JSON 格式并写入响应//等价于:// jsonData, err := json.Marshal(response)// if err != nil {// 	w.WriteHeader(http.StatusInternalServerError)// 	return// }// w.Write(jsonData)})// 启动服务器fmt.Println("服务器启动在 :8080 端口...")if err := http.ListenAndServe(":8080", nil); err != nil {panic(err)}
}

多路复用器

DefaultServeMux一般不会使用,因为会有冲突等等问题,所以一般用NewServeMux直接创建

type apiHandler struct{}func (apiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {w.Header().Set("Content-Type", "application/json")fmt.Fprintf(w, `{"message": "API response"}`)
}func main() {mux := http.NewServeMux()mux.Handle("/api/", apiHandler{})  //多引入结构体,后面会知道有好处// mux.HandleFunc("/api/", func(w http.ResponseWriter, req *http.Request) {//     w.Header().Set("Content-Type", "application/json")//     fmt.Fprintf(w, `{"message": "API response from HandleFunc"}`)// })   //和上面等效mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {if req.URL.Path != "/" {http.NotFound(w, req)return}fmt.Fprintf(w, "Welcome to the home page!")})fmt.Println("Server running on :8080")http.ListenAndServe(":8080", mux)//用server:= &http.Server创建地址和handler,然后server.ListenAndServe也是一种表现方式}

mux和http.HandleFunc()的区别:
mux 可以创建多个路由器实例,用于不同的目的。同时可以为不同的路由器配置不同的中间件(用着先)

第三方有库httprouter,比如可以解决url不能是变量代表的问题,了解就行
更多都是使用restful API进行开发的~目前的了解有个概念就行

处理器函数

Handle

注册处理器过程中调用的函数:Handle

type username struct {name string
}func (this *username) ServeHTTP(w http.ResponseWriter, r *http.Request) {fmt.Fprintf(w, "%s", this.name)
}func main() {mux := http.NewServeMux()mux.Handle("/jack", &username{name: "jack"}) //会调用对应的serveHTTP方法mux.Handle("/lily", &username{name: "lily"})//可以为不同的路径使用相同的处理器结构体,但传入不同的参数//这就是比用handleFunc()更灵活的地方server := &http.Server{Addr:    ":8080",Handler: mux,}if err := server.ListenAndServe(); err != nil { //防止错误panic(err)}
}
HandlleFunc

处理器函数:HandleFunc
(注意不是HandlerFunc,没有r !! HandleFunc 是处理器函数)
之前已经学习过,定义就是
func HandleFunc(pattern string, handler func(ResponseWriter, *Request))

深入源代码会发现内部也是借助serveMux对象,从而实现了Handler的ServeHTTP()方法的

Handler

Handler就是处理器接口,实现ServeHTTP方法的,之前展示过

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

HandlerFunc是结构体,用于实现接口的
定义:

type HandlerFunc func(ResponseWriter, *Request)

用于连接处理器Handle和处理器函数HandleFunc,它实现了 Handler 接口,使得函数可以直接当作处理器使用:

  • 理解“连接连接处理器Handle和处理器函数HandleFunc”:(之前也学过)
// 方式一:普通函数
func hello(w http.ResponseWriter, r *http.Request) {fmt.Fprintf(w, "Hello!")
}
// 注册方式一:将函数转换为 HandlerFunc
http.Handle("/hello", http.HandlerFunc(hello))// 方式二:直接使用 HandleFunc
http.HandleFunc("/hello", hello)

处理请求

请求分为:请求头,请求URL,请求体等

html表单的enctype属性

在postman的body部分可以查看

  • application/x-www-form-urlencode
    url方式编码,较为通用,get和post都可以用
  • multipart/form-data
    通常适配post方法提交
  • text/plain
    适合传递大量数据

ResponseWriter接口涉及方法

  • 补充:
    fmt.Fprint和fmt.Fprintln能够写入ResponseWriter是因为ResponseWriter实现了io.Writer接口,fmt.Fprint/Fprintln将数据按格式转换为字节流(如字符串、数字等),最终调用io.Writer的Write方法
Writeheader

curl -i localhost:8080/noAuth 或者使用postman进行验证

func noAuth(w http.ResponseWriter, r *http.Request) {w.WriteHeader(401)fmt.Fprint(w, "没有授权,你需要认证后访问")
}func main() {http.HandleFunc("/noAuth", noAuth)err := http.ListenAndServe(":8080", nil)if err != nil {fmt.Println(err)}
}
Header

调用了Writeheader后的话就不能对响应头进行修改了
curl -i http://localhost:8081/redirect (可以直接看到301)或者postman验证

  • 重定向代码
func Redirect(w http.ResponseWriter, r *http.Request) {w.Header().Set("Location", "http://localhost:8080/hello")// 必须使用包括http的完整的URL!w.WriteHeader(301)
}
func main() {http.HandleFunc("/redirect", Redirect)if err := http.ListenAndServe(":8081", nil); err != nil {panic(err)}
}
  • 主服务代码
func sayHello(w http.ResponseWriter, r *http.Request) {w.Write([]byte("hello !!"))
}func main() {http.HandleFunc("/hello", sayHello)http.ListenAndServe(":8080", nil) //创建基本服务
}
write

之前都有demo,就是写入返回,注意需要是[]byte()这样的表示形式
如果不知道content-type格式可以通过数据的前512 比特进行确认

除了一般的文本字符串之外,还可以返回html和json,下面给出json的示范

type language struct {Language string `json:"language"` //反引号// 字段名首字母需要大写才能被 JSON 序列化!!!!
}func uselanguage(w http.ResponseWriter, r *http.Request) {uselanguageis := language{Language: "en"}message, err := json.Marshal(uselanguageis)if err != nil {http.Error(w, err.Error(), http.StatusInternalServerError)return}w.Header().Set("Content-Type", "application/json") //通过json形式传递w.Write(message)
}func main() {http.HandleFunc("/lan", uselanguage)if err := http.ListenAndServe(":8080", nil); err != nil {panic(err)}
}

注意一些格式上的细节,比如字段名首字母需要大写才能被 JSON 序列化

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

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

相关文章

AI-Browser适用于 ChatGPT、Gemini、Claude、DeepSeek、Grok的客户端开源应用程序,集成了 Monaco 编辑器。

一、软件介绍 文末提供程序和源码下载学习 AI-Browser适用于 ChatGPT、Gemini、Claude、DeepSeek、Grok、Felo、Cody、JENOVA、Phind、Perplexity、Genspark 和 Google AI Studio 的客户端应用程序,集成了 Monaco 编辑器。使用 Electron 构建的强大桌面应用程序&a…

Dify框架面试内容整理-Dify如何处理知识库的集成?

Dify 在知识库集成方面采用了“检索增强生成(RAG)”的技术架构,核心实现思路如下: 一、知识库集成的整体流程 Dify处理知识库集成通常包括以下关键步骤: 文档上传↓

Laravel 模型使用全局作用域和局部作用域

一. 需要解决什么问题 最近Laravel 项目中遇到一个需求,我有一个客户表,每个员工都有自己的客户,但是自己只能看自己的客户。 项目中,有很多功能需要查询客户列表,客户详情,查询客户入口很多,…

【Nova UI】十二、打造组件库之按钮组件(上):迈向功能构建的关键一步

序言 在上一篇文章中,我们深入探索了 icon 组件从测试到全局注册的全过程🎯,成功为其在项目中稳定运行筑牢了根基。此刻,组件库的建设之旅仍在继续,我们将目光聚焦于另一个关键组件 —— 按钮组件。按钮作为用户与界面…

鸿蒙OSS文件(视频/图片)压缩上传组件-能够增删改查

一、鸿蒙实现处理-压缩上传整体代码处理逻辑 转沙箱压缩获取凭证并上传文件 文件准备(拿到文件流)获取上传凭证(调接口1拿到file_name和upload_url)执行文件上传(向阶段2拿到的upload_url上传文件)更新列表…

河道流量监测,雷达流量计赋能水安全智慧守护

在蜿蜒的河道之上,水流的脉搏始终与人类文明的兴衰紧密相连。从农田灌溉的水量调配到城市防洪的精准预警,从生态保护的水质溯源到水资源管理的决策,河道流量监测如同大地的 “血管检测”,是守护水安全的第一道防线。传统监测手段在…

CSS3 基础(边框效果)

一、边框效果 属性功能示例值说明border-radius创建圆角border-radius: 20px;设置元素的圆角半径,支持像素(px)或百分比(%)。值为 50% 时可变为圆形。box-shadow添加阴影box-shadow: 5px 5px 15px rgba(0, 0, 0, 0.5)…

零基础小白如何上岸数模国奖

零基础小白如何上岸数模国奖 我自己本人第一次参加数模国赛顺利上岸国奖,当然那段经历也是比较痛苦了,差不多也是从当年四月开始接触数学建模,第一次参加妈妈杯成绩并不理想,后面不断参加数模比赛进行模拟,最后顺利上岸…

SQL学习-常用函数

常见SQL函数使用 (注意:不同的数据库类型使用的语法不同) 以下是MySQL和PostgreSQL在实现替换、抽取、拼接、分列四个常见字符串操作功能时的核心区别总结,按功能分类对比: 1. 替换(Replace) …

rt-linux下的cgroup cpu的死锁bug

一、背景 rt-linux系统有其非常大的实时性的优势,但是与之俱来的是该系统上有一些天然的缺陷。由于rt-linux系统允许进程在内核态执行的逻辑里,在持锁期间,甚至持spinlock锁期间,都能被其他进程抢占。这一特性能带来实时性的好处…

java—12 kafka

目录 一、消息队列的优缺点 二、常用MQ 1. Kafka 2. RocketMQ 3. RabbitMQ 4. ActiveMQ 5. ZeroMQ 6. MQ选型对比 适用场景——从公司基础建设力量角度出发 适用场景——从业务场景角度出发 四、基本概念和操作 1. kafka常用术语 2. kafka常用指令 3. 单播消息&a…

14【模块学习】74HC595:使用学习

74HC595 1、74HC595简介2、代码演示2.1、驱动8位流水灯 3、74HC595级联3.1、驱动16位流水灯3.2、驱动8位数码管3.3、驱动8x8点阵屏幕3.4、8x8点阵屏幕滚动显示 1、74HC595简介 在51单片机中IO引脚资源十分的紧缺,所以常常需要使用75HC595芯片进行驱动那些需要占用多…

JAVA后端开发常用的LINUX命令总结

一、Linux常用命令大全(2025年最新版) 常用 Linux 命令 文件和目录管理: cd:用于切换当前工作目录,如cd /home/user。mkdir:创建新目录,mkdir -p /home/user/mydir可递归创建多级目录。pwd&am…

uniapp-商城-40-shop 购物车 选好了 进行订单确认4 配送方式3 地址编辑

前面说了配送 和地址页面 当地址页面为空或需要添加地址时&#xff0c;需要添加地址。 我的地址页面有个按钮 就是添加地址 点击 添加地址 按钮 后&#xff0c;就会跳转到地址添加的页面 1、添加地址页面 2、添加地址文件夹以及文件的创建 3、添加地址的代码 <template…

现场问题排查-postgresql某表索引损坏导致指定数据无法更新影响卷宗材料上传

问题现象 今天突然被拉进一个群&#xff0c;说某地区友商推送编目结果报错&#xff0c;在我们自己的卷宗系统上传材料也一直转圈&#xff0c;也删除不了案件卷宗&#xff0c;重置模板也没用&#xff0c;只有个别案件有问题。虽然这事儿不属于我负责&#xff0c;但还是抽时间给…

Redis01-基础-入门

零、文章目录 Redis01-基础-入门 1、认识 NoSQL NoSQL 知识请参考&#xff1a;https://blog.csdn.net/liyou123456789/article/details/132612444 2、认识 Redis &#xff08;1&#xff09;简介 Redis&#xff08;Remote Dictionary Server&#xff0c;远程字典服务&…

【嘉立创EDA】如何在更新或转换原理图到PCB时,保留已有布局器件

文章路标👉 :one: 文章解决问题:two: 主题内容:three: 参考方法be end..1️⃣ 文章解决问题 操作环境:嘉立创EDA专业版 V2.2.37 本文使用嘉立创EDA,描述在更新或转换原理图到PCB时,保留已有布局器件的方法。本文将此过程记录,以供有需要的读者参考。 2️⃣ 主题内容 …

03 APQC PROCESS CLASSIFICATION FRAMEWORK (PCF)

APQC流程分类框架&#xff08;APQC Process Classification Framework, PCF&#xff09;最初由美国生产力与质量中心&#xff08;American Productivity & Quality Center, APQC&#xff09;开发&#xff0c;旨在用于跨组织的流程性能基准比较。现在&#xff0c;它也常被用…

分析型数据库入门指南:如何选择适合你的实时分析工具?

一、什么是分析型数据库&#xff1f;为什么需要它&#xff1f; 据Gartner最新报告显示&#xff0c;超过75%的企业现已在关键业务部门部署了专门的分析型数据库&#xff0c;这一比例还在持续增长。 随着数据量呈指数级增长&#xff0c;传统数据库已无法满足复杂分析场景的需求…

body Param Query 三个 不同的入参 分别是什么意思 在前端 要怎么传 这三种不同的参数

在 NestJS 中&#xff0c;Body()、Param() 和 Query() 用于处理不同类型的请求参数。以下是它们的含义及前端传递方式&#xff1a; Body()&#xff1a;请求体参数 • 含义&#xff1a;用于获取请求体中的数据&#xff08;如 POST/PUT 请求中提交的 JSON、表单数据等&#xff09…