Golang个人web框架开发-学习流程
Golang-个人web框架
- github仓库
- 创建github仓库
- web框架学习
- 开发周期
- 第一阶段--了解
- 第一阶段思考
- 小结
- 第二阶段
- 第三阶段
github仓库
github地址:ameamezhou/golang-web-frame
后续还将继续学习更新
创建github仓库
设置免密登录
ssh-keygen 一路回车就OK 上面有告诉你密钥生成地址
红框为需要上传的公钥
web框架学习
首先明确目标–我们学习开发web框架的目的是 :
在日常的web开发中,我们经常要使用到web框架,python就有很多好用的框架,比如flask和django,前者小巧精美,后者厚重却有着齐全的功能,不同开发者在设计框架的时候会有他们不同的看法和理念,因此在不同框架之间就会有许多不同的区别。这对于Go语言来说也是一样的,我们看到有很多好用的框架,例如Beego,Gin等等。但是我们在用这些框架的时候,我们可能需要去思考一下,其实这些框架翻找源码到底其实都是http等基础库构成的,但是我们为什么要使用它们呢?我们用框架究竟目的是什么?只有我们想明白了这一点我们才能更好的去做我们的开发工作,因此我决定做一个简单的框架实现这些基础功能。
开发周期
第一阶段–了解
package mainimport ("net/http"
)func main() {http.HandleFunc("/", sayHello)http.ListenAndServe("localhost:9999", nil)
}// 最基础的功能展示, 这里函数携带的参数是根据http库里面定义的
func sayHello(w http.ResponseWriter, r *http.Request){w.Write([]byte("hello world"))
}
实现一个最简单的web功能,就是打开页面输出 hello world 这里其实可以看到所需代码量其实和用现存的 gin 或者 beego 框架差不多,这里也能看出一些web框架大概的逻辑
然后我们在里面加点功能,增加点json输出
package mainimport ("encoding/json""net/http"
)func main() {http.HandleFunc("/", sayHello)http.ListenAndServe("localhost:9999", nil)
}// 最基础的功能展示, 这里函数携带的参数是根据http库里面定义的
func sayHello(w http.ResponseWriter, r *http.Request){// 在页面输出展示jsonobj := make(map[string]interface{}, 0)obj["username"] = "xiawuyue"obj["password"] = "xiaoqizhou"// 这里是设置response 的响应头w.Header().Set("Content-Type", "application/json")// 这里是设置响应头的状态码 ok 就是 200w.WriteHeader(http.StatusOK)encoder := json.NewEncoder(w)if err := encoder.Encode(obj); err != nil {http.Error(w, err.Error(), 500)}w.Write([]byte("hello world"))
}
然后我们可以看到页面
还是非常有意思的
第一阶段思考
两个问题
1 这个demo和你常用的框架的区别
2 你觉得这个地方的重点在哪里
附加:
关于web框架 我们都用过flask框架 请问这些框架最底层的运行逻辑是如何?go实现框架的逻辑相比于python如何?
(欢迎评论讨论)
小结
其实这一阶段我们要着重关注http的路由
package mainimport ("net/http"
)func main() {http.HandleFunc("/", sayHello)http.ListenAndServe("localhost:9999", nil)
}// 最基础的功能展示, 这里函数携带的参数是根据http库里面定义的
func sayHello(w http.ResponseWriter, r *http.Request){w.Write([]byte("hello world"))
}
在这里我们可以看到http.ListenAndServe 这里我们传进去的是一个nil,在里面是需要绑定路由的,也就是我们最关键的地方在HandleFunc这里,我们可以看到路由分发是通过 http.HandleFunc(“路径”, 处理函数) 这种形式实现的
// HandleFunc registers the handler function for the given pattern
// in the DefaultServeMux.
// The documentation for ServeMux explains how patterns are matched.
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {DefaultServeMux.HandleFunc(pattern, handler)
}// HandleFunc registers the handler function for the given pattern.
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {if handler == nil {panic("http: nil handler")}mux.Handle(pattern, HandlerFunc(handler))
}
在ListenAndServe 这个函数中,我们第二个参数为nil,go会为我们分配一个默认的路由,会携带自己的路由结构体 ServeMux
// ListenAndServe always returns a non-nil error.
func ListenAndServe(addr string, handler Handler) error {server := &Server{Addr: addr, Handler: handler}return server.ListenAndServe()
}// ServeMux还负责清除URL请求路径和主机标头,剥离端口号并重定向包含的任何请求。
// 或元素或重复的斜杠转换为等效的、更干净的URL。
type ServeMux struct {mu sync.RWMutex // 这是一个互斥锁,保证并发m map[string]muxEntry // 具体的路由规则es []muxEntry // slice of entries sorted from longest to shortest.hosts bool // whether any patterns contain hostnames 查看是否包含具体的host信息
}
其中我们最需要关注的就是这个m,我们注意到它是一个map类型,是一个 string
对应一个 muxEntry
结构体,这里最重要的就是muxEntry
Handler 其实就是一个interface接口,所以我们每一个HandFunc里面对应函数的类型都是要和这个 ServeHTTP(ResponseWriter, *Request)
保持一致的
type muxEntry struct {h Handler // 具体路由对应的 handlerpattern string // 匹配字符串
}type Handler interface {ServeHTTP(ResponseWriter, *Request)
}
题外话,go和python c 等语言不一样,这一块不需要通过sokcet来搞端口监听,http一个包就囊括了这些功能,所以我们可以深挖一下源码,看看究竟是怎么做得这方面的功能
第二阶段
看了上面的源码,我们实现的关键其实就是两个,一个是 ServeMux
一个是 muxEntry
,然后具体的 Handler
其实对应的就是一个ServeHTTP,我们需要实现的具体功能就是在这一块。所以其实我们完全可以自己来实现一个,不依赖 net/http
库它内置的一些功能,用我们自己的方式写一个 ServeHTTP
我们先梳理下这次的主要思路:
- base1的重点就是简单了解http库
- 我们来尝试自己写一个handle
- 以后我们的所有的框架代码都不再放在main.go 下面 养成包开发的习惯从主函数去调用
根据第一阶段的总结,我们不难发现我们要是想要自己实现一个框架,那么核心就是要实现一个 muxEntry
和 Handler
根据需求我们可以实现:
package xiawuyueimport ("fmt""net/http"
)/*
本地包用法:
require xiawuyue v0.0.0replace xiawuyue => ./base2/xiawuyue
*/type XiaWuYue struct {router map[string]HandleFunc
}// New 直接调用New方法构建对象
func New() *XiaWuYue {return &XiaWuYue{ router: make(map[string]HandleFunc) }
}// HandleFunc 简单定义一类函数 这就是后续具体的处理方法的类型
type HandleFunc func(w http.ResponseWriter, req *http.Request)func (x *XiaWuYue)addRoute(method string, pattern string, handleFunc HandleFunc) {// 其中method 是用来区分 get post 等方法的// patter 是提到的 muxEntry 中的匹配字符串 也就是具体的路径key := method + "-" + patternx.router[key] = handleFunc
}func (x *XiaWuYue) Get(pattern string, handleFunc HandleFunc) {x.addRoute("GET", pattern, handleFunc)
}func (x *XiaWuYue) Post(pattern string, handleFunc HandleFunc) {x.addRoute("POST", pattern, handleFunc)
}func (x *XiaWuYue) Pull(pattern string, handleFunc HandleFunc) {x.addRoute("PULL", pattern, handleFunc)
}func (x *XiaWuYue) Delete(pattern string, handleFunc HandleFunc) {x.addRoute("DELETE", pattern, handleFunc)
}func (x *XiaWuYue) ServeHTTP(w http.ResponseWriter, req *http.Request) {switch req.URL.Path {case "/":fmt.Println("你访问的是根路径")w.Write([]byte("hello world"))// 这里会导致只在终端打印 所以要修改逻辑}key := req.Method + "-" + req.URL.Pathif handler, ok := x.router[key]; ok {handler(w, req)}}
这里Handler其实就是要求一个接口,这个接口它必须有 ServerHTTP
这个功能就ok,只要能理解这个,做这个逻辑的时候就会很清晰了,我们要实现的就是它的基本功能,并通过 ServerHTTP
对找到的路由提供相应的服务就行,所以这里我们新生成的 struct xiawuyue
它就需要带有这个功能接口
好的 代码看到这里我们来回忆一下第一天的内容:
package mainimport ("net/http"
)func main() {http.HandleFunc("/", sayHello)http.ListenAndServe("localhost:9999", nil)
}// 最基础的功能展示, 这里函数携带的参数是根据http库里面定义的
func sayHello(w http.ResponseWriter, r *http.Request){w.Write([]byte("hello world"))
}
这里我们看到原始的 HandleFunc
我们并没有初始化任何一个struct 对象,并且在 ListenAndServe
这里传进去的也是个 nil
, 这里的逻辑究竟是怎样的,我们为什么这样也能够去正常跑一个服务?
// serverHandler delegates to either the server's Handler or
// DefaultServeMux and also handles "OPTIONS *" requests.
type serverHandler struct {srv *Server
}func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {handler := sh.srv.Handlerif handler == nil {handler = DefaultServeMux}if req.RequestURI == "*" && req.Method == "OPTIONS" {handler = globalOptionsHandler{}}if req.URL != nil && strings.Contains(req.URL.RawQuery, ";") {var allowQuerySemicolonsInUse int32req = req.WithContext(context.WithValue(req.Context(), silenceSemWarnContextKey, func() {atomic.StoreInt32(&allowQuerySemicolonsInUse, 1)}))defer func() {if atomic.LoadInt32(&allowQuerySemicolonsInUse) == 0 {sh.srv.logf("http: URL query contains semicolon, which is no longer a supported separator; parts of the query may be stripped when parsed; see golang.org/issue/25192")}}()}handler.ServeHTTP(rw, req)
}
我们可以在 http
包的server.go
中找到这样一段,这里其实很好理解,当 svr
的 handler
为 nil
的时候,我们就会将 DefaultServeMux
导入当作这个 muxEntry
,它拥有 ServerHTTP
这个接口 可以实现相应的功能。
在我们都理解了这一块的知识之后,就写个总的 main.go 函数进行调用就ok
package mainimport ("fmt""net/http""xiawuyue/base2/xiawuyue"
)// base1的重点就是简单了解http库
// 我们来尝试自己写一个handle// 以后我们的所有的框架代码都不再放在main.go 下面 养成包开发的习惯
// 从主函数去调用//func main(){
// http.ListenAndServe(":9999", new(xiawuyue.XiaWuYue))
//}func main() {//如果没有 New 方法//r := new(xiawuyue.XiaWuYue)r := xiawuyue.New()r.Get("/get", func(w http.ResponseWriter, req *http.Request) {fmt.Println("hello world")w.Write([]byte("hello world get"))// 这里println 只会在终端输出 所以我们后续还是要包装一个w.return 的功能,其实很简单})// 请大家给斗鱼9999fg投一票 球球了http.ListenAndServe("localhost:9999", r)// 到这一步完成了然后就去启动
}
第三阶段
相关文章:

Golang个人web框架开发-学习流程
Golang-个人web框架 github仓库创建github仓库 web框架学习开发周期第一阶段--了解第一阶段思考小结 第二阶段第三阶段 github仓库 github地址:ameamezhou/golang-web-frame 后续还将继续学习更新 创建github仓库 设置免密登录 ssh-keygen 一路回车就OK 上面有告…...

java面试题(23):Spring Bean如何保证并发安全
1 问题分析 我们知道默认情况下,Spring中的Bean是单例的,所以在多线程并发访问的时候,有可能会出现线程安全问题。 2 解决方案 有几个方面的解决思路: 我们可以设置Bean的作用域设置为原型(prototype)&a…...

HarmonyOS【应用服务开发】在模块中添加Ability
Ability是应用/服务所具备的能力的抽象,一个Module可以包含一个或多个Ability。应用/服务先后提供了两种应用模型: FA(Feature Ability)模型: API 7开始支持的模型,已经不再主推。Stage模型:AP…...

根据屏幕尺寸设置html根字号fontSize大小并刷新
<script> // rem等比适配配置文件 // 基准大小 const baseSize 16 // 设置 rem 函数 function setRem() {// 当前页面宽度相对于 1920宽的缩放比例,可根据自己需要修改。const scale document.documentElement.clientWidth / 1920console.log(document.docu…...

Flutter 中的 InteractiveViewer:轻松实现交互性
在Flutter中,为了创建具有交互性的用户界面,我们通常需要使用各种手势检测和动画。然而,Flutter提供了一个强大而简便的小部件,即InteractiveViewer,它可以帮助我们轻松实现拖动、缩放和其他手势交互效果。本文将介绍I…...

UE4 添加按键输入事件 并在蓝图中使用按键输入节点
绑定按键 选择Edit/ProjectSettings/Engine/Input 在bindings中可以选择添加ActionMappings或则AxisMappings ActionMappings:按键事件,有按下和抬起两个事件,需要分别用两个键触发AxisMappings:输入事件,返回值为float,对于键盘…...

Go 语言命名规范:清晰、简洁、一致
Go 语言命名规范:清晰、简洁、一致 Go 语言是一门注重简洁和一致性的编程语言,良好的命名规范是代码可读性和维护性的关键因素之一。在本篇博客中,我们将深入探讨 Go 语言的命名规范,包括标识符、包名、常量、变量、函数等各个方…...

代码随想录训练营第三十期|第十天|栈与队列part01|理论基础● 232.用栈实现队列● 225. 用队列实现栈
232. 用栈实现队列 - 力扣(LeetCode) class MyQueue {Stack<Integer> in;Stack<Integer> out;public MyQueue() {in new Stack<>();out new Stack<>();}public void push(int x) {in.push(x);}public int pop() {move();retu…...

Backtrader 文档学习-Indicators混合时间框架
Backtrader 文档学习-Indicators混合时间周期 1.不同时间周期 如果数据源在Cerebro引擎中具有不同的时间范围和不同的长度,指示器将会终止。 比如:data0是日线,data1是月线 。 pivotpoint btind.PivotPoint(self.data1) sellsignal self…...

网络攻击与检测防御:维护数字安全的关键挑战
随着数字化时代的深入,网络攻击已成为企业和个人面临的严峻挑战之一。本文将深入探讨不同类型的网络攻击,以及有效的检测和防御策略,以确保网络系统的安全性和稳定性。 1. 常见网络攻击类型: DDoS 攻击:分布式拒绝服…...

使用 Vector 在 Kubernetes 中收集日志
多年来,我们一直在使用 Vector 在我们的 Kubernetes 平台中收集日志,并成功地将其应用于生产中以满足各种客户的需求,并且非常享受这种体验。因此,我想与更大的社区分享它,以便更多的 K8s 运营商可以看到潜力并考虑他们…...

ardupilot开发 --- 固件定制(OEM) 篇
0. 前言 固件功能定制OEM Customization: 原厂设备制造商OEM(Original Equipment Manufacturer)、代工功能勾选参数预设固件名称自定义 1. 基于某个飞控硬件来定制自己的飞控产品 可以自定义的包括:固件名称、预设参数、lua脚本…...

爬虫代理IP在电商行业的应用
随着互联网的快速发展,电商行业已经成为人们购物的主要渠道之一。在电商行业中,数据分析和挖掘至关重要。爬虫代理IP作为一种能够提供大量模拟请求和收集数据的工具,被广泛应用于电商行业。下面介绍爬虫代理IP在电商行业中的应用。 1、保护隐…...

Vue配置语法检查及关闭语法检查的说明
1. 第一种方式://eslint-disable-next-line 2. 第二种方式:/*eslint-disable*/ 3. 第三种方式:vue.config.js中配置 ,具体配置如下: const { defineConfig } require(vue/cli-service)module.exports defineConfig…...

【Linux】yum
个人主页 : zxctsclrjjjcph 文章封面来自:艺术家–贤海林 如有转载请先通知 yum 1. 什么是yum?2. Linux系统(Centos)的生态3. yum的相关操作4. yum的本地配置5. 如何安装软件 1. 什么是yum? yum是一个软件下载安装的一个客户端&a…...

安装sftpgo
1.下载安装包;选择自己需要的cpu架构和操作系统的版本 https://github.com/drakkan/sftpgo/releases/tag/v2.5.6推荐使用版本下载地址 https://github.com/drakkan/sftpgo/releases/download/v2.5.6/sftpgo_v2.5.6_linux_x86_64.tar.xz2.解压文件到某个文件夹,根据需要修改配…...

JS-元素尺寸与位置
通过js的方式,得到元素在页面中的位置 获取宽高 元素.offsetWidth 元素.offsetHeight 1)获取元素的自身宽高、包括元素自身设置的宽高paddingborder 2)获取出来的是数值,方便计算 3)注意:获取的是可视…...

2024-01-15(SpringMVCMybatis)
1.拦截器:如果我们想在多个handler方法(controller中的方法)执行之前或者之后都进行一些处理,甚至某些情况下需要拦截掉,不让handler方法执行,那么就可以使用SpringMVC为我们提供的拦截器。 拦截器和过滤器的区别:过滤…...

Node+Express编写接口---前端
前端页面 vue_node_admin: 第一个以node后端,vue为前端的后台管理项目https://gitee.com/ah-ah-bao/vue_node_admin.git...

防火墙技术
防火墙(英语:Firewall)技术是通过有机结合各类用于安全管理与筛选的软件和硬件设备,帮助计算机网络于其内、外网之间构建一道相对隔绝的保护屏障,以保护用户资料与信息安全性的一种技术。 防火墙技术的功能主要在于及…...

图灵日记之java奇妙历险记--String类
目录 String常用方法字符串构造String对象的比较字符串查找char charAt(int index)int indexOf(int ch)int indexOf(int ch, int fromIndex)int indexOf(String str)int indexOf(String str, int fromIndex)int lastIndexOf(String str)int lastIndexOf(String str, int fromIn…...

代码随想录算法训练营第六天| 242 有效的字母异位词 349 两个数组的交集 202 快乐数 1 两数之和
目录 242 有效的字母异位词 349 两个数组的交集 202 快乐数 1 两数之和 242 有效的字母异位词 排序 class Solution { public:bool isAnagram(string s, string t) {sort(s.begin(),s.end());sort(t.begin(),t.end());return t s;} }; 时间复杂度O(nlogn) 空间复杂度O(l…...

数学建模--比赛
内容来自数学建模BOOM:【快速入门】北海:数模建模基础MATLAB入门论文写作数学模型与算法(推荐数模美赛国赛小白零基础必看教程)_哔哩哔哩_bilibili 目录 1.学习内容 2.参赛须知 1)参赛作品的组成 2)参赛作品的提交 3.软件安装 4.注意…...

JVM工作原理与实战(十六):运行时数据区-Java虚拟机栈
专栏导航 JVM工作原理与实战 RabbitMQ入门指南 从零开始了解大数据 目录 专栏导航 前言 一、运行时数据区 二、Java虚拟机栈 1.栈帧的组成 2.局部变量表 3.操作数栈 4.帧数据 总结 前言 JVM作为Java程序的运行环境,其负责解释和执行字节码,管理…...

DC-4靶机刷题记录
靶机下载地址: 链接:https://pan.baidu.com/s/1YbPuSw_xLdkta10O9e2zGw?pwdn6nx 提取码:n6nx 参考: 【【基础向】超详解vulnhub靶场DC-4-爆破反弹shell信息收集】 https://www.bilibili.com/video/BV1Le4y1o7Sx/?share_sourc…...

【前端学习笔记1】css基础
css可以使页面更漂亮,即美化网页 css:层叠样式表 标签选择器: 类选择器: id只能单次调用,类似人的身份证 css里只要是word里面有的功能,他们都有对应的,不会的时候查一下就行 实现垂直居中:h…...

CVE-2023-46226 Apache iotdb远程代码执行漏洞
项目介绍 Apache IoTDB 是针对时间序列数据收集、存储与分析一体化的数据管理引擎。它具有体量轻、性能高、易使用的特点,完美对接 Hadoop 与 Spark 生态,适用于工业物联网应用中海量时间序列数据高速写入和复杂分析查询的需求。 项目地址 https://io…...

Redis实战之-分布式锁
一、基本原理和实现方式对比 分布式锁:满足分布式系统或集群模式下多进程可见并且互斥的锁。 分布式锁的核心思想就是让大家都使用同一把锁,只要大家使用的是同一把锁,那么我们就能锁住线程,不让线程进行,让程序串行…...

Cookie同源策略
同源策略(Same-Origin Policy)是浏览器安全机制的一部分,用于限制一个源(域名、协议和端口的组合)的文档或脚本如何与来自另一个源的资源进行交互。这个策略帮助防止潜在的恶意网站在用户浏览器中执行恶意操作。 关于C…...

6、Numpy形状操纵
目录 1. 使用 reshape 改变形状 2. 使用 resize 改变大小和形状 3. 使用 ravel 或 flatten 展平数组 4. 使用 -1 推断尺寸 5. 使用 newaxis 增加维度 6. 使用 squeeze 移除单维度条目 1. 使用 reshape 改变形状 对于任何 NumPy 数组,你可以使用 reshape 方法来…...