Golang个人web框架开发-学习流程

Golang-个人web框架

  • github仓库
    • 创建github仓库
  • web框架学习
  • 开发周期
    • 第一阶段--了解
    • 第一阶段思考
      • 小结
    • 第二阶段
    • 第三阶段

github仓库

github地址:ameamezhou/golang-web-frame
后续还将继续学习更新

创建github仓库

在这里插入图片描述
设置免密登录
在这里插入图片描述
ssh-keygen 一路回车就OK 上面有告诉你密钥生成地址
在这里插入图片描述
红框为需要上传的公钥
在这里插入图片描述

web框架学习

首先明确目标–我们学习开发web框架的目的是

在日常的web开发中,我们经常要使用到web框架,python就有很多好用的框架,比如flaskdjango,前者小巧精美,后者厚重却有着齐全的功能,不同开发者在设计框架的时候会有他们不同的看法和理念,因此在不同框架之间就会有许多不同的区别。这对于Go语言来说也是一样的,我们看到有很多好用的框架,例如BeegoGin等等。但是我们在用这些框架的时候,我们可能需要去思考一下,其实这些框架翻找源码到底其实都是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 下面 养成包开发的习惯从主函数去调用

根据第一阶段的总结,我们不难发现我们要是想要自己实现一个框架,那么核心就是要实现一个 muxEntryHandler
根据需求我们可以实现:

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 中找到这样一段,这里其实很好理解,当 svrhandlernil 的时候,我们就会将 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)// 到这一步完成了然后就去启动
}

第三阶段

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

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

相关文章

flutter使用get依赖实现全局loading效果,弹窗loading状态

get dialog的官网文档:GetDialogRoute class - dialog_route library - Dart API 可以使用Get.dialog()方法来创建一个自定义的加载弹窗,get框架是支持自定义弹窗效果的,所以我们就使用这个方式来自定义一个弹窗效果,并且点击遮罩…

Ubuntu使用QtCreator + CMake 开发C/C++程序

平台 OS: Ubuntu 20.04 cmake: 3.16.3 IDE: Qt Creator 4.11.1 Based on Qt 5.14.1 (GCC 5.3.1 20160406 (Red Hat 5.3.1-6), 64 bit) Built on Feb 5 2020 12:48:30 From revision b2ddeacfb5 Copyright 2008-2019 The Qt Company Ltd. All rights reserved. The program …

运维工具之iptables命令

运维工具之iptables命令 1.iptables防火墙介绍 ​ iptables其实并不是真正的防火墙,我们可以理解成一个客户端代理,用户通过 IPTables这个代理,将用户的安全设定执行到对应的"安全框架"中,这个"安全框架"才…

【C++】string的基本使用

从这篇博客开始,我们的C部分就进入到了STL,STL的出现可以说是C发展历史上非常关键的一步,自此C和C语言有了较为明显的差别。那么什么是STL呢? 后来不断的演化,发展成了知名的两个版本,一个叫做P.J.版本&am…

c语言案例双色球

系列文章目录 c语言案例双色球 c语言案例双色球 系列文章目录c语言案例双色球 c语言案例双色球 int main() {srand((unsigned int)time(NULL));//双色球两种原色 红球蓝球(61)红球1-33 蓝球1-16 打印双色球中奖信息//红色球不能重复 int ball[6];//红球f…

为什么 macOS 比 Windows 稳定?

在计算机操作系统领域,macOS 和 Windows 分别是苹果公司和微软公司的主打产品。尽管两者都拥有大量的用户群体,但在稳定性和用户体验方面,macOS 常常被认为优于 Windows。那么,为什么 macOS 比 Windows 更稳定呢? 我们…

HTML前端CSS实现只显示1行或者2行、3行剩余显示省略号

想要做的效果: 文本只一行显示 /**实现思路:1.设置inline-block属相2.强制不换行3.固定高度4.隐藏超出部分5.显示“……”*/ {display: inline-block;white-space: nowrap; width: 100%; overflow: hidden;text-overflow:ellipsis; }文本只多行显示 /** 实现思路&…

【Debian】非图形界面Debian10.0.0安装xfce和lxde桌面

一、安装 1. Debian10.0.0安装xfce桌面 sudo apt update sudo apt install xfce4 startxfce4 2. Debian10.0.0安装lxde桌面 sudo apt-get install lxde安装后重启电脑。 二、说明 XFCE、LXDE 和 GNOME 是三个流行的桌面环境,它们都是为类 Unix 操作系统设计…

springboot108精品在线试题库系统

简介 【毕设源码推荐 javaweb 项目】基于springbootvue 的精品在线试题库系统 适用于计算机类毕业设计,课程设计参考与学习用途。仅供学习参考, 不得用于商业或者非法用途,否则,一切后果请用户自负。 看运行截图看 第五章 第四章 …

SCI 2区论文:医疗保健中心训练有素的脑膜瘤分割模型的性能测试-基于四个回顾性多中心数据集的二次分析

基本信息 标题:Performance Test of a Well-Trained Model for Meningioma Segmentation in Health Care Centers: Secondary Analysis Based on Four Retrospective Multicenter Data Sets中文标题:医疗保健中心训练有素的脑膜瘤分割模型的性能测试&am…

arthas(阿尔萨斯)日常java代码调优使用命令

官方项目文档:https://gitee.com/arthas/arthas (最权威的教学还是得官网,这里仅作简单记录) 1:启动 java -jar arthas-boot.jar 2:查看cpu占用排名前三 thread -3 3:查看指定id thread 203 4:查…

【音视频原理】图像相关概念 ③ ( RGB 色彩简介 | RGB 排列 | YUV 色彩简介 | YUV 编码好处 )

文章目录 一、RGB 色彩1、RGB 色彩简介2、RGB 排列 二、YUV 色彩1、YUV 色彩简介2、YUV 编码好处 一、RGB 色彩 1、RGB 色彩简介 RGB 是 计算机 中的 颜色编码方法 , 红 ( R ) / 绿 ( G ) / 蓝 ( B ) 三个颜色通道 可以设置不同的值 , 每个 通道 的 颜色值都可以取值 0 ~ 255 ,…