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.hqwc.cn/news/220860.html

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

相关文章

持续集成部署-k8s-配置与存储-存储类:动态创建NFS-PV案例

动态创建NFS-PV案例 1. 前置条件2. StorageClass 存储类的概念和使用3. RBAC 配置4. storageClass 配置5. 创建应用,测试 PVC 的自动配置6. 解决 PVC 为 Pending 状态问题7. 单独测试自动创建 PVC 1. 前置条件 这里使用 NFS 存储的方式,来演示动态创建 …

Java王者荣耀小游戏

Background类 package LX;import java.awt.*; //背景类 public class Background extends GameObject{public Background(GameFrame gameFrame) {super(gameFrame);}Image bg Toolkit.getDefaultToolkit().getImage("C:\\Users\\ASUS\\Desktop\\王者荣耀图片\\Map.jpg&…

【JavaEE初阶】Thread 类及常见方法、线程的状态

目录 1、Thread 类及常见方法 1.1 Thread 的常见构造方法 1.2 Thread 的几个常见属性 1.3 启动⼀个线程 - start() 1.4 中断⼀个线程 1.5 等待⼀个线程 - join() 1.6 获取当前线程引用 1.7 休眠当前线程 2、线程的状态 2.1 观察线程的所有状态 2.2 线程状态和状…

P17C++析构函数

目录 前言 01 什么是析构函数 1.1 举个栗子 02 为什么要写析构函数 前言 今天我们要讨论一下它的“孪生兄弟”,析构函数,它们在某些方面非常相似。 与构造函数相反,当对象结束其生命周期,如对象所在的函数已调用完毕时&…

Redis:持久化RDB和AOF

目录 概述RDB持久化流程指定备份文件的名称指定备份文件存放的目录触发RDB备份redis.conf 其他一些配置rdb的备份和恢复优缺点停止RDB AOF持久化流程AOF启动/修复/恢复AOF同步频率设置rewrite压缩原理触发机制重写流程no-appendfsync-on-rewrite 优缺点 如何选择 概述 Redis是…

An example of a function uniformly continuous on R but not Lipschitz continuous

See https://math.stackexchange.com/questions/69457/an-example-of-a-function-uniformly-continuous-on-mathbbr-but-not-lipschitz?noredirect1

jetpack的简单使用

Jetpack ViewModel: 什么是ViewModel? ViewModel 是 Android 架构组件的一部分,用于帮助开发者管理 UI 数据的持久性和生命周期感知。ViewModel 的主要目的是将 UI 数据与界面控制逻辑分离,以便更好地管理数据的生命周期&#…

单片机学习7——定时器/计数器编程

#include<reg52.h>unsigned char a, num; sbit LED1 P1^0;void main() {num0;EA1;ET01;//IT00;//设置TMOD的工作模式TMOD0x01;//给定时器装初值&#xff0c;50000,50ms中断20次&#xff0c;就得到1sTH0(65536-50000)/256;TL0(65536-50000)%256;TR01; // 定时器/计数器启…

高阶JAVA篇-深入了解字符集

&#x1f525;博客主页&#xff1a; 小扳_-CSDN博客 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 字符集的说明 1.1 ASCII 字符集 1.2 GBK 字符集 1.3 UTF-8字符集 2.0 字符集的编码与解码 2.1 编码提供了常见的方法 2.2 解码提供了常见的方法 1.0 字符集的说明 字…

推荐6款本周 yyds 的开源项目

&#x1f525;&#x1f525;&#x1f525;本周GitHub项目圈选: 主要包含 链接管理、视频总结、有道音色情感合成、中文文本格式校正、GPT爬虫、深度学习推理 等热点项目。 1、Dub 一个开源的链接管理工具&#xff0c;可自定义域名将繁杂的长链接生成短链接&#xff0c;便于保…

18. Python 数据处理之 Numpy

目录 1. 简介2. 安装和导入Numpy3. ndarray 对象4. 基本运算5. 索引、切片和迭代6. 条件和布尔数组7. 变换形状8. 操作数组 1. 简介 数据分析的流程概括起来主要是&#xff1a;读写、处理计算、分析建模和可视化4个部分。 Numpy 是Python 进行科学计算&#xff0c;数据分析时…

Dockerfile-CentOS7.9+Python3.11.2

本文为CentOS7.9下安装Python3.11.2环境的Dockerfile # CentOS with Python3.11.2 # Author xxmail.com# build a new image with basic centos FROM centos:centos7.9.2009 # who is the author MAINTAINER xxmail.comRUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/…