当前位置: 首页 > news >正文

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)// 到这一步完成了然后就去启动
}

第三阶段

相关文章:

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宽的缩放比例&#xff0c;可根据自己需要修改。const scale document.documentElement.clientWidth / 1920console.log(document.docu…...

Flutter 中的 InteractiveViewer:轻松实现交互性

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

UE4 添加按键输入事件 并在蓝图中使用按键输入节点

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

Go 语言命名规范:清晰、简洁、一致

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

代码随想录训练营第三十期|第十天|栈与队列part01|理论基础● 232.用栈实现队列● 225. 用队列实现栈

232. 用栈实现队列 - 力扣&#xff08;LeetCode&#xff09; 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引擎中具有不同的时间范围和不同的长度&#xff0c;指示器将会终止。 比如&#xff1a;data0是日线&#xff0c;data1是月线 。 pivotpoint btind.PivotPoint(self.data1) sellsignal self…...

网络攻击与检测防御:维护数字安全的关键挑战

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

使用 Vector 在 Kubernetes 中收集日志

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

ardupilot开发 --- 固件定制(OEM) 篇

0. 前言 固件功能定制OEM Customization&#xff1a; 原厂设备制造商OEM&#xff08;Original Equipment Manufacturer&#xff09;、代工功能勾选参数预设固件名称自定义 1. 基于某个飞控硬件来定制自己的飞控产品 可以自定义的包括&#xff1a;固件名称、预设参数、lua脚本…...

爬虫代理IP在电商行业的应用

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

Vue配置语法检查及关闭语法检查的说明

1. 第一种方式&#xff1a;//eslint-disable-next-line 2. 第二种方式&#xff1a;/*eslint-disable*/ 3. 第三种方式&#xff1a;vue.config.js中配置 &#xff0c;具体配置如下&#xff1a; const { defineConfig } require(vue/cli-service)module.exports defineConfig…...

【Linux】yum

个人主页 &#xff1a; zxctsclrjjjcph 文章封面来自&#xff1a;艺术家–贤海林 如有转载请先通知 yum 1. 什么是yum&#xff1f;2. Linux系统(Centos)的生态3. yum的相关操作4. yum的本地配置5. 如何安装软件 1. 什么是yum&#xff1f; 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的方式&#xff0c;得到元素在页面中的位置 获取宽高 元素.offsetWidth 元素.offsetHeight 1&#xff09;获取元素的自身宽高、包括元素自身设置的宽高paddingborder 2&#xff09;获取出来的是数值&#xff0c;方便计算 3&#xff09;注意&#xff1a;获取的是可视…...

2024-01-15(SpringMVCMybatis)

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

Node+Express编写接口---前端

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

防火墙技术

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

Debian系统简介

目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版&#xff…...

visual studio 2022更改主题为深色

visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中&#xff0c;选择 环境 -> 常规 &#xff0c;将其中的颜色主题改成深色 点击确定&#xff0c;更改完成...

Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)

目录 1.TCP的连接管理机制&#xff08;1&#xff09;三次握手①握手过程②对握手过程的理解 &#xff08;2&#xff09;四次挥手&#xff08;3&#xff09;握手和挥手的触发&#xff08;4&#xff09;状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...

LeetCode - 394. 字符串解码

题目 394. 字符串解码 - 力扣&#xff08;LeetCode&#xff09; 思路 使用两个栈&#xff1a;一个存储重复次数&#xff0c;一个存储字符串 遍历输入字符串&#xff1a; 数字处理&#xff1a;遇到数字时&#xff0c;累积计算重复次数左括号处理&#xff1a;保存当前状态&a…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具

文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...

基于数字孪生的水厂可视化平台建设:架构与实践

分享大纲&#xff1a; 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年&#xff0c;数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段&#xff0c;基于数字孪生的水厂可视化平台的…...

CMake控制VS2022项目文件分组

我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...

Java多线程实现之Thread类深度解析

Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...

【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习

禁止商业或二改转载&#xff0c;仅供自学使用&#xff0c;侵权必究&#xff0c;如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...