Gee教程1.HTTP基础

标准库启动web服务

Go语言内置了 net/http库,封装了HTTP网络编程的基础的接口。这个Web 框架便是基于net/http的。我们先回顾下这个库的使用。

package mainimport ("fmt""log""net/http"
)func main() {//可以写成匿名函数(lambda表达式),handler echoes r.URL.Pathhttp.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {fmt.Fprintf(w, "URL.Path = %q\n", req.URL.Path)})http.HandleFunc("/hello", helloHandler)log.Fatal(http.ListenAndServe("localhost:10000", nil))//log.Fatal(http.ListenAndServe(":10000", nil)) 也可以写成这样的,没有写到ip
}// handler echoes r.URL.Header
func helloHandler(w http.ResponseWriter, req *http.Request) {for k, v := range req.Header {fmt.Fprintf(w, "Header[%q] = %q\n", k, v)}
}

来看看效果

 这里设置了两个路由 / 和hello,分别绑定了一个匿名函数和helloHandler。之后根据不同的http请求会调用不同的处理函数。结果如上如图。

main 函数的最后一行,是用来启动 Web 服务的。

第一个参数是ip地址,localhost:110000表示在 本地地址的10000端口监听。而第二个参数则代表处理所有的HTTP请求的实例,nil 代表使用标准库中的默认实例处理。第二个参数,则是我们基于net/http标准库实现Web框架的入口

实现http.Handler接口

来看看源码

package httptype Handler interface {ServeHTTP(w ResponseWriter, r *Request)
}func ListenAndServe(address string, h Handler) error// Serve a new connection.
func (c *conn) serve(ctx context.Context) {//...............serverHandler{c.server}.ServeHTTP(w, w.req)//.................
}

通过源码可以看到,Handler是一个接口类型,需要实现ServeHTTP方法。

http.ListenAndServe中会调用ServerHTTP方法,即是调用h.ServerHTTP。

即是说,只要传入任何实现了 ServerHTTP 接口的实例,所有的HTTP请求,就都交给了该实例处理了。所以我们要自己写一个实现该接口的实例,让所有的HTTP请求由自己来写。

那接下我们实现一个Handler。

package mainimport ("fmt""log""net/http"
)type Engine struct{}func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {switch req.URL.Path {case "/":fmt.Fprintf(w, "URL.Path = %q\n", req.URL.Path)case "/hello":for k, v := range req.Header {fmt.Fprintf(w, "Header[%q]= %q\n", k, v)}default:fmt.Fprintf(w, "404 NOT FOUND: %s\n", req.URL)}
}func main() {engine := &Engine{}log.Fatal(http.ListenAndServe("localhost:10000", engine))
}

我们定义了一个空的结构体Engine,实现了方法ServeHTTP。

ServeHTTP方法有两个参数,一是ResponseWriter,这个是用来构建对客户端的http回复响应。二是Request ,该对象包含了该HTTP请求的所有的信息,比如请求地址、请求Header和Body等信息。

而在main函数中,我们给ListenAndServe方法的第二个参数传入了engine实例。至此,我们走出了实现web框架的第一步,即是将所有的HTTP请求都转向了我们自己的处理逻辑。

而在实现Enginx之前,我们调用 http.HandleFunc实现了路由和helloHandler的映射,也就是只能针对具体的路由来写对应的处理逻辑。而在实现Engine之后,我们可以拦截所有的HTTP请求,拥有了统一的控制入口。这样我们可以自由定义路由映射的规则,也可以统一添加一些处理逻辑,例如日志、异常处理等。

Gee框架的雏形

我们再整理之前所讲的,搭建出整个框架的雏形。

 进行初始化

 主要文件是main.go和gee.go

先看下是如何使用的

mian.go

package mainimport ("fmt""geeV1/gee""net/http"
)func main() {engine := gee.New()engine.GET("/", func(w http.ResponseWriter, req *http.Request) {fmt.Fprintf(w, "URL.Path = %q\n", req.URL.Path)})engine.POST("/hello", func(w http.ResponseWriter, req *http.Request) {for k, v := range req.Header {fmt.Fprintf(w, "Header[%q] = %q\n", k, v)}})engine.Run("localhost:10000")
}

和gin框架的使用是相似的。使用New()创建 gee 的实例,使用 GET(),POST()方法添加路由,最后使用Run()启动Web服务。这里的路由,只是静态路由,不支持/hello/:name这样的动态路由,之后会实现动态路由。

接下来看看是如何实现的

gee.go

package geeimport ("fmt""net/http"
)type HandlerFunc func(http.ResponseWriter, *http.Request)type Engine struct {router map[string]HandlerFunc //添加对应的路由和路由函数的映射
}// 创建enginx实例
func New() *Engine {return &Engine{router: make(map[string]HandlerFunc)}
}// 添加路由
func (engine *Engine) addRoute(method string, pattern string, handler HandlerFunc) {key := method + "-" + patternengine.router[key] = handler
}func (engine *Engine) GET(pattern string, handler HandlerFunc) {engine.addRoute("GET", pattern, handler)
}
func (engine *Engine) POST(pattern string, handler HandlerFunc) {engine.addRoute("POST", pattern, handler)
}func (engine *Engine) Run(addr string) error {return http.ListenAndServe(addr, engine)
}func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {key := req.Method + "-" + req.URL.Pathfmt.Println(key)if handler, ok := engine.router[key]; ok {handler(w, req)} else {w.WriteHeader(http.StatusNotFound) //添加状态码,404fmt.Fprintf(w, "404 NOT FOUND: %s\n", req.URL.Path)}
}

那么gee.go就是重头戏了。我们重点介绍一下这部分的实现。

对比之前的空结构体Engine,其结构体添加了router 路由映射表。

key 由请求方法静态路由地址构成,例如GET-/GET-/helloPOST-/hello,这样针对相同的路由,如果请求方法不同,可以映射不同的处理方法(Handler),value 是用户映射的处理方法(用户自己写的)。

main函数中使用GET()等添加路由,其GET()等方法就是调用了addRoute,把路由和对应的handler添加到router中。

Run方法是调用了http.ListenAndServe(addr, engine)。所有的HTTP请求都会首先进入ServeHTTP中。Engine实现的 ServeHTTP 方法的作用就是,解析请求的路径,查找路由映射表,如果查到,就执行注册的处理方法。如果查不到,就返回 404 NOT FOUND 。

执行go run main.go查看效果

 至此,整个web框架的原型已经出来了。我们实现了路由映射表,提供了用户注册静态路由的方法,也包装了启动服务的函数。当然,当前我们实现的功能也没有比net/http标准库更强大。之后,会陆续将动态路由、中间件等功能添加上去。

完整代码:https://github.com/liwook/Go-projects/tree/main/gee-web/1-http-base

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

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

相关文章

【数据结构初阶】树,二叉树

树,二叉树 1.树概念及结构1.1树的概念1.2 树的相关概念1.3 树的表示1.4 树在实际中的运用(表示文件系统的目录树结构) 2.二叉树概念及结构2.1概念2.2现实中的二叉树2.3 特殊的二叉树2.4 二叉树的性质2.5 二叉树的存储结构 1.树概念及结构 1.…

STM32-SPI3控制MCP3201、MCP3202(Sigma-Delta-ADC芯片)

STM32-SPI3控制MCP3201、MCP3202(Sigma-Delta-ADC芯片) 原理图手册说明功能方框图引脚功能数字输出编码与实值的转换分辨率设置与LSB最小和最大输出代码(注) 正负符号寄存器位MSB数字输出编码数据转换的LSB值 将设备输出编码转换为…

SQL JOIN 子句:合并多个表中相关行的完整指南

SQL JOIN JOIN子句用于基于它们之间的相关列合并来自两个或更多表的行。 让我们看一下“Orders”表的一部分选择: OrderIDCustomerIDOrderDate1030821996-09-1810309371996-09-1910310771996-09-20 然后,看一下“Customers”表的一部分选择&#xff…

单片机学习5——外部中断程序

#include<reg52.h>unsigned char a; sbit lcden P3^4;void main() {lcden0;EA1;EX01;IT00;a0xF0; //点亮4位小灯while(1){P1a;} }//中断服务程序 void ext0() interrupt 0 // 0 表示的是外部中断源0 {a0x0f; // 中断处理完&#xff0c;再返回主…

2018年10月4日 Go生态洞察:参与2018年Go公司问卷调查

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

系列十八、Spring bean线程安全问题

一、概述 我们知道Spring中的bean&#xff0c;默认情况下是单例的&#xff0c;那么Spring中的bean是线程安全的吗&#xff1f;这个需要分情况考虑&#xff0c;bean中是否存在成员变量&#xff1f;bean中的成员变量是怎么处理的&#xff1f;...&#xff0c;针对bean的状态会有不…

【C++】类和对象——拷贝构造和赋值运算符重载

上一篇我们讲了构造函数&#xff0c;就是对象实例化时会自动调用&#xff0c;那么&#xff0c;我们这里的拷贝构造在形式上是构造函数的一个重载&#xff0c;拷贝构造其实也是一种构造函数&#xff0c;那么我们就可以引出这里的规则 1.拷贝构造函数的函数名必须与类名相同。 2.…

数据结构——带头循环双向链表(List)

1、带头双向循环链表介绍 在上一篇博客中我们提到了链表有三个特性&#xff0c;可以组合成为8种不同类型的链表。单链表是其中比较重要的一种&#xff0c;那么这次我们选择和带头双向循环链表会会面&#xff0c;这样我们就见识过了所有三种特性的呈现。 带头双向循环链表&#…

安卓使用MediaRecorder录制音频的详细使用

安卓使用MediaRecorder录制音频的详细使用 文章目录 安卓使用MediaRecorder录制音频的详细使用引言使用 MediaRecorder 的步骤常见问题及解决思路无法访问存储卡目录录制的音频文件没有声音录制过程中出现异常MediaRecorder无法正常启动录制的音质或者画质很差录制的文件无法播…

Object.is和====和==的区别

Object.is() 方法和比较操作符 、 用于比较两个值的相等性&#xff0c;但它们在比较方式和行为上有一些区别。Object.is() 方法是严格相等比较&#xff0c;而 操作符也是严格相等比较&#xff0c;但 操作符是相等比较。 严格相等比较&#xff08; &#xff09; 要求比较的…

HONOR荣耀MagicBook 15 2021款 锐龙版R5(BMH-WFQ9HN)原厂Windows11预装OEM系统含F10智能还原

链接&#xff1a;https://pan.baidu.com/s/1faYtC5BIDC2lsV_JSMI96A?pwdj302 提取码&#xff1a;j302 原厂系统Windows11.22H2工厂模式安装包,含F10一键智能还原&#xff0c;自带所有驱动、出厂主题壁纸、系统属性专属LOGO标志、Office办公软件、荣耀 电脑管家等预装程序 …

process control 化学工程 需要用到MATLAB的Simulink功能

process control 化学工程 需要用到MATLAB的Simulink功能 所有问题需要的matlab simulink 模型 WeChat: ye1-6688 The riser tube brings in contact the recirculating catalyst with the feed oil, which then vaporizes and splits to lighter components as it flows up th…

服务器下db(数据库)的执行

1、查看 select * from xxxx&#xff08;表名&#xff09; where xxx&#xff08;列表&#xff09;1 and.......正常写就行 2、插入 如果你想要在 SELECT INSERT INTO … SELECT 语句中将部分列保持不变,只改变一两列的值,可以在 语句中直接设置目标列的值,而其他列从源表中…

【代码随想录】算法训练计划31

贪心 1、455. 分发饼干 题目&#xff1a; 假设你是一位很棒的家长&#xff0c;想要给你的孩子们一些小饼干。但是&#xff0c;每个孩子最多只能给一块饼干。 对每个孩子 i&#xff0c;都有一个胃口值 g[i]&#xff0c;这是能让孩子们满足胃口的饼干的最小尺寸&#xff1b;并且…

【C++】类型转换 ③ ( 重新解释类型转换 reinterpret_cast | 指针类型数据转换 )

文章目录 一、重新解释类型转换 reinterpret_cast1、指针数据类型转换 - C 语言隐式类型转换报错 ( 转换失败 )2、指针数据类型转换 - C 语言显示类型强制转换 ( 转换成功 )3、指针数据类型转换 - C 静态类型转换 static_cast ( 转换失败 )4、指针数据类型转换 - C 重新解释类型…

Simulink 的代数环

代数环, 就是由于模型的输出反馈到模块或子系统先的某个输入端, 如果这个输入是直接馈入的, 那么二者在同一个采样点内需得到求解, 但又互相依赖, 哪一方都不能完成求解过程, 使得解算器无法解算导致错误产生, 这样的情况称为代数环。 一旦 Simulink 遇到代数环, 将根据 Confi…

Binlog vs. Redo Log:数据库日志的较劲【高级】

&#x1f38f;&#xff1a;你只管努力&#xff0c;剩下的交给时间 &#x1f3e0; &#xff1a;小破站 Binlog vs. Redo Log&#xff1a;数据库日志的较劲【高级】 前言第一&#xff1a;事务的生命周期事务的生命周期Binlog和Redo Log记录事务的一致性和持久性Binlog的记录过程R…

二叉树--算法题总结

1、利用层序遍历的产生的字符串来创建二叉树 /*** 使用层序遍历的字符串创建二叉树* param treeInfo* return*/public static TreeNode generateTreeNodeSecond(String treeInfo) {LinkedList<TreeNode> treeNodeLinkedList new LinkedList<>();if(StringUtils.is…

Flask 使用Jinja2模板引擎

Jinja2&#xff0c;由Flask框架的创作者开发&#xff0c;是一款功能丰富的模板引擎&#xff0c;以其完整的Unicode支持、灵活性、高效性和安全性而备受推崇。最初受Django模板引擎启发&#xff0c;Jinja2为Flask提供了强大的模板支持&#xff0c;后来也成为其他项目的首选。在本…

@JsonSerialize注解的使用

使用场景 ** 在开发中&#xff0c;有时候某些字段需要特殊处理&#xff0c;比如我们有一个日期字段&#xff0c;当日期为NULL时给前端不返回NULL而返回为其他等信息&#xff0c;就需要自定义字段的序列化。这就是JsonSerialize的用处 ** 1&#xff1a;先写一个指定的处理类 pa…