使用Go语言打造轻量级Web框架
前言
Web框架是Web开发中不可或缺的组件。它们的主要目标是抽象出HTTP请求和响应的细节,使开发人员可以更专注于业务逻辑的实现。在本篇文章中,我们将使用Go语言实现一个简单的Web框架,类似于Gin框架。
功能
我们的Web框架需要实现以下功能:
- 路由:处理HTTP请求的路由,并支持路径参数和通配符。
- 上下文:封装HTTP请求和响应,并提供访问请求参数的方法。
- 中间件:在请求处理之前或之后运行的函数。
- HTTP请求和响应:支持GET、POST等HTTP方法。
实现
首先,我们需要定义一个HandlerFunc
类型,表示处理HTTP请求的函数。这个函数需要接受一个Context
类型的参数,用于访问请求和响应。
type HandlerFunc func(Context)
接下来,我们需要定义一个Context
类型,封装HTTP请求和响应,并提供访问请求参数的方法。我们可以使用Go语言标准库中的http.ResponseWriter
和http.Request
类型分别表示响应和请求。
type Context struct {
Response http.ResponseWriter
Request *http.Request
Params map[string]string
}
Params
字段用于存储路径参数。例如,如果路由路径为/users/:id
,则可以使用c.Params["id"]
访问路径参数id
的值。
现在,我们可以开始实现路由。我们需要定义一个Route
类型,表示一个路由,包含HTTP方法、路径和处理函数。我们还需要一个Router
类型,表示整个应用程序的路由器。它应该包含所有的路由,包含需要的中间件,并能够处理HTTP请求。
type Route struct {method stringpath stringhandler HandlerFunc
}type Router struct {routes []*Routemiddlewares []MiddlewareFunc}
我们可以使用Handle
方法将路由添加到路由器中。
func (r *Router) Handle(method, path string, handler HandlerFunc) {r.routes = append(r.routes, &Route{method, path, handler})
}
当HTTP请求到达时,需要遍历所有的路由,并找到匹配的路由。如果找到了一个匹配的路由,我们就调用它的处理函数,并且如果有中间件,需要遍历所有中间件执行中间件处理逻辑。否则,我们返回HTTP 404错误。
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {var match *Routeparams := make(map[string]string)for _, route := range r.routes {if req.Method == route.method {if ok, p := matchPath(route.path, req.URL.Path); ok {match = routeparams = pbreak}}}if match != nil {handler := match.handlerfor i := len(r.middlewares) - 1; i >= 0; i-- {handler = r.middlewares[i](handler)}handler(Context{w, req, params})} else {http.NotFound(w, req)}
}
在上面的代码中,我们使用了matchPath
函数来比较HTTP请求的路径和路由的路径,以确定是否匹配。这个函数还会返回路径参数的值,以便我们可以在Context
中访问它们。
现在,我们可以实现中间件。中间件是在请求处理前或处理后运行的函数,它们可以修改请求或响应,或执行其他任务。我们可以定义一个MiddlewareFunc
类型,表示中间件函数。
type MiddlewareFunc func(handler HandlerFunc) HandlerFunc
接下来,我们可以在Router
中添加一个Use
方法,用于注册中间件。这个方法会往路由中添加一个中间件,后面处理函数时候会要遍历使用。
func (r *Router) Use(middleware MiddlewareFunc) {r.middlewares = append(r.middlewares, middleware)
}
最后,我们需要添加HTTP方法的支持。我们可以为每个HTTP方法定义一个快捷方法,它们分别调用Handle
方法并传递正确的HTTP方法和路径。
例如,对于GET方法,我们可以定义一个GET
方法,如下所示:
func (r *Router) GET(path string, handler HandlerFunc) {r.Handle("GET", path, handler)
}
现在,我们已经完成了一个简单的Web框架的实现。下面是完整的代码:
完整的代码
package mainimport ("fmt""net/http""strings""time"
)type HandlerFunc func(Context)type Context struct {Response http.ResponseWriterRequest *http.RequestParams map[string]string
}type Route struct {method stringpath stringhandler HandlerFunc
}type Router struct {routes []*Routemiddlewares []MiddlewareFunc
}type MiddlewareFunc func(handler HandlerFunc) HandlerFuncfunc NewRouter() *Router {return &Router{}
}func (r *Router) Handle(method, path string, handler HandlerFunc) {r.routes = append(r.routes, &Route{method, path, handler})
}func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {var match *Routeparams := make(map[string]string)for _, route := range r.routes {if req.Method == route.method {if ok, p := matchPath(route.path, req.URL.Path); ok {match = routeparams = pbreak}}}if match != nil {handler := match.handlerfor i := len(r.middlewares) - 1; i >= 0; i-- {handler = r.middlewares[i](handler)}handler(Context{w, req, params})} else {http.NotFound(w, req)}
}func (r *Router) Use(middleware MiddlewareFunc) {r.middlewares = append(r.middlewares, middleware)
}func (r *Router) GET(path string, handler HandlerFunc) {r.Handle("GET", path, handler)
}func (r *Router) POST(path string, handler HandlerFunc) {r.Handle("POST", path, handler)
}func (r *Router) PUT(path string, handler HandlerFunc) {r.Handle("PUT", path, handler)
}func (r *Router) DELETE(path string, handler HandlerFunc) {r.Handle("DELETE", path, handler)
}func matchPath(path, pattern string) (bool, map[string]string) {parts1 := strings.Split(path, "/")parts2 := strings.Split(pattern, "/")if len(parts1) != len(parts2) {return false, nil}params := make(map[string]string)for i, part := range parts1 {if part != parts2[i] {if strings.HasPrefix(part, ":") {params[part[1:]] = parts2[i]} else if strings.HasPrefix(part, "*") {params[part[1:]] = strings.Join(parts2[i:], "/")break} else {return false, nil}}}return true, params
}
使用案例
func main() {router := NewRouter()router.Use(func(handler HandlerFunc) HandlerFunc {return func(ctx Context) {start := time.Now()handler(ctx)fmt.Printf("%s cost %s\n", ctx.Request.RequestURI, time.Now().Sub(start))}})router.GET("/", func(c Context) {fmt.Fprintf(c.Response, "欢迎使用我的web框架!")})router.GET("/users/:id", func(c Context) {fmt.Fprintf(c.Response, "User ID: %s", c.Params["id"])})router.GET("/users/:id/friends", func(c Context) {fmt.Fprintf(c.Response, "User ID: %s, list all friends.", c.Params["id"])})router.GET("/*path", func(c Context) {fmt.Fprintf(c.Response, "User path: %s", c.Params["path"])})http.ListenAndServe(":8080", router)
}
在上面的代码中,我们添加了一个中间件函数记录请求耗时,它用于记录每个HTTP请求的执行。我们还添加了几个路由,以演示路径参数和通配符的用法。
运行结果
$ curl localhost:8080/users
User path: users$ curl localhost:8080/users/1001
User ID: 1001$ curl localhost:8080/users/1001/friends
User ID: 1001, list all friends.$ curl localhost:8080/users/1001/friends/xxx
404 page not found$ curl localhost:8080/xxxx
User path: xxxx---------------------------------------------------
/users cost 51.917µs
/users/1001 cost 2.75µs
/users/1001/friends cost 5.833µs
/xxxx cost 6.875µs
总结
在本文中,我们使用Go语言实现了一个简单的Web框架。我们实现了路由、上下文、中间件、HTTP请求和响应等功能。还演示了如何使用路径参数和通配符来匹配不同的路径。这个Web框架虽然比不上流行的框架,但它可以作为学习Web框架实现的好起点。
欢迎关注,学习不迷路
相关文章:
使用Go语言打造轻量级Web框架
前言 Web框架是Web开发中不可或缺的组件。它们的主要目标是抽象出HTTP请求和响应的细节,使开发人员可以更专注于业务逻辑的实现。在本篇文章中,我们将使用Go语言实现一个简单的Web框架,类似于Gin框架。 功能 我们的Web框架需要实现以下功能…...

【开源项目】BallCat 项目脚手架
简介 🎉🎉🎉 基于 React 和 Ant Design 版本的前端 ballcat-ui-react 已发布,欢迎大家尝鲜使用 BallCat 组织旨在为项目快速开发提供一系列的基础能力,方便使用者根据项目需求快速进行功能拓展。 在以前使用其他后台管…...

KlayGE-004-InputCaps 例子分析
InputCaps处理外部输入的事件 该例子主要由两部分内容: 外部输入事件获取 可以处理keyboard、mouse、joystick、touch、sensor的输入事件 显示一个ui图标按钮 Input 定义监听事件类型: KlayGE::InputActionDefine actions[] {InputActionDefin…...
组装机经验、软硬件故障排除、网络问题
目录 主板 CPU 内存 显卡 判断显卡好坏的步骤 新买的显卡安装后显示器不亮 电源 其他 网络问题 主板 1.不同主板对于不同数量的内存条安装的位置有要求,要按照主板规定的位置安装不同数量的内存条,特别是服务器主板,否则系统可能起…...

【行为型模式】责任链模式
文章目录1、简介2、结构3、实现方式3.1、案例引入3.2、结构分析3.3、具体实现4、责任链优缺点5、应用场景1、简介 责任链模式(Chain of Responsibility)是一种行为型设计模式,它允许对象在链上依次处理请求,用户只需要将请求发送到责任链上即可…...
C++命令模式 指挥家:掌控命令模式之美
C指挥家:掌控命令模式之美 (C Conductor: Master the Beauty of Command Pattern一、引言 (Introduction)1.1 命令模式概述 (Overview of Command Pattern)1.2 命令模式的应用场景 (Application Scenarios of Command Pattern)二、命令模式的基本概念 (Basic Concep…...

学会 制作极简搜索浏览器 —— 并将 ChatGPT 接入浏览器
前期回顾 Vue3 Ts Vite pnpm 项目中集成 —— eslint 、prettier、stylelint、husky、commitizen_0.活在风浪里的博客-CSDN博客搭建VIte Ts Vue3项目并集成eslint 、prettier、stylelint、huskyhttps://blog.csdn.net/m0_57904695/article/details/129950163?spm1001.2…...

NumPy 秘籍中文第二版:六、特殊数组和通用函数
原文:NumPy Cookbook - Second Edition 协议:CC BY-NC-SA 4.0 译者:飞龙 在本章中,我们将介绍以下秘籍: 创建通用函数查找勾股三元组用chararray执行字符串操作创建一个遮罩数组忽略负值和极值使用recarray函数创建一…...
各种交叉编译工具链的区别
目录 1 命名规则 2 实例 2.1 arm-none-eabi-gcc 2.2 arm-none-linux-gnueabi-gcc 2.3 arm-eabi-gcc 2.4 armcc 2.5 arm-none-uclinuxeabi-gcc 和 arm-none-symbianelf-gcc 3 gnueabi和gnueabihf的区别(硬浮点、软浮点) 4 Linaro公司出品的交叉编译工具链 5 ARM公司出…...

密度聚类算法(DBSCAN)实验案例
密度聚类算法(DBSCAN)实验案例 描述 DBSCAN是一种强大的基于密度的聚类算法,从直观效果上看,DBSCAN算法可以找到样本点的全部密集区域,并把这些密集区域当做一个一个的聚类簇。DBSCAN的一个巨大优势是可以对任意形状…...

第07章_面向对象编程(进阶)
第07章_面向对象编程(进阶) 讲师:尚硅谷-宋红康(江湖人称:康师傅) 官网:http://www.atguigu.com 本章专题与脉络 1. 关键字:this 1.1 this是什么? 在Java中,this关键字不算难理解…...
异常的讲解(2)
目录 throws异常处理 基本介绍 throws异常处理注意事项和使用细节 自定义异常 基本概念 自定义异常的步骤 throw 和throws的区别 本章作业 第一题 第二题 第三题 第四题 throws异常处理 基本介绍 1)如果一个方法(中的语句执行时)可能生成某种异常,但是…...

jvm内存结构
1. 栈 程序计数器 2. 虚拟机栈 3. 本地方法栈 4. 堆 5. 方法区 1.2栈内存溢出 栈帧过多导致栈内存溢出 /*** 演示栈内存溢出 java.lang.StackOverflowError* -Xss256k*/ public class Demo1_2 {private static int count;public static void main(String[] args) {try {meth…...

要刹车?生成式AI迎新规、行业连发ChatGPT“警报”、多国考虑严监管
4月13日消息,据中国移动通信联合会元宇宙产业工作委员会网站,中国移动通信联合会元宇宙产业工作委员会、中国通信工业协会区块链专业委员会等,共同发布“关于元宇宙生成式人工智能(类 ChatGPT)应用的行业提示”。提示内…...
轻松掌握Qt FTP 机制:实现高效文件传输
轻松掌握Qt FTP:实现高效文件传输一、简介(Introduction)1.1 文件传输协议(FTP)Qt及其网络模块(Qt and its Network Module)QNetwork:二、QNetworkAccessManager上传实例(Qt FTP Upl…...

用AI帮我写一篇关于FPGA的文章,并推荐最热门的FPGA开源项目
FPGA定义 FPGA(Field Programmable Gate Array)是一种可编程逻辑器件,可以在硬件电路中实现各种不同的逻辑功能。与ASIC(Application Specific Integrated Circuit,特定应用集成电路)相比,FPGA…...
从兴趣或问题出发
当我们还沉寂在移动互联网给生活带来众多便利中,以 ChartGPT 为代表的 AI 时代已彻底到来。科技的发展,时刻在改变着我们的生活,我们需要不断地学习新知识和掌握新技能才能享受变化带来的便利,以及自身不被社会淘汰。 因此&#…...

C++ | 探究拷贝对象时的一些编译器优化
👑作者主页:烽起黎明 🏠学习社区:烈火神盾 🔗专栏链接:C 文章目录前言一、传值传参二、传引用传参三、传值返回拷贝构造和赋值重载的辨析四、传引用返回【❌】五、传匿名对象返回六、总计与提炼前言 在传参…...

linux工具gcc/g++/gdb/git的使用
目录 gcc/g 基本概念 指令集 函数库 (重要) gdb使用 基本概念 指令集 项目自动化构建工具make/makefile 进度条小程序 编辑 git三板斧 创建仓库 git add git commit git push git status git log gcc/g 基本概念 gcc/g称为编译器…...

Direct3D 12——纹理——纹理
纹理不同于缓冲区资源,因为缓冲区资源仅存储数据数组,而纹理却可以具有多个mipmap层级(后 文有介绍),GPU会基于这个层级进行相应的特殊操作,例如运用过滤器以及多重采样。支持这些特殊 的操作纹理资源都被限定为一些特定的数据格式…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...

(二)原型模式
原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...
使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度
文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...
return this;返回的是谁
一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请,不同级别的经理有不同的审批权限: // 抽象处理者:审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...

RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill
视觉语言模型(Vision-Language Models, VLMs),为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展,机器人仍难以胜任复杂的长时程任务(如家具装配),主要受限于人…...

GO协程(Goroutine)问题总结
在使用Go语言来编写代码时,遇到的一些问题总结一下 [参考文档]:https://www.topgoer.com/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/goroutine.html 1. main()函数默认的Goroutine 场景再现: 今天在看到这个教程的时候,在自己的电…...
NPOI操作EXCEL文件 ——CAD C# 二次开发
缺点:dll.版本容易加载错误。CAD加载插件时,没有加载所有类库。插件运行过程中用到某个类库,会从CAD的安装目录找,找不到就报错了。 【方案2】让CAD在加载过程中把类库加载到内存 【方案3】是发现缺少了哪个库,就用插件程序加载进…...
为什么要创建 Vue 实例
核心原因:Vue 需要一个「控制中心」来驱动整个应用 你可以把 Vue 实例想象成你应用的**「大脑」或「引擎」。它负责协调模板、数据、逻辑和行为,将它们变成一个活的、可交互的应用**。没有这个实例,你的代码只是一堆静态的 HTML、JavaScript 变量和函数,无法「活」起来。 …...
在golang中如何将已安装的依赖降级处理,比如:将 go-ansible/v2@v2.2.0 更换为 go-ansible/@v1.1.7
在 Go 项目中降级 go-ansible 从 v2.2.0 到 v1.1.7 具体步骤: 第一步: 修改 go.mod 文件 // 原 v2 版本声明 require github.com/apenella/go-ansible/v2 v2.2.0 替换为: // 改为 v…...

rm视觉学习1-自瞄部分
首先先感谢中南大学的开源,提供了很全面的思路,减少了很多基础性的开发研究 我看的阅读的是中南大学FYT战队开源视觉代码 链接:https://github.com/CSU-FYT-Vision/FYT2024_vision.git 1.框架: 代码框架结构:readme有…...