GO自研微服务框架-路由实现

路由实现

1.不用框架

不用框架的路由实现

package mainimport ("fmt""log""net/http"
)func main() {http.HandleFunc("/hello", func(writer http.ResponseWriter, request *http.Request) {fmt.Fprintf(writer, "%s 欢迎来到goweb教程", "lisus.com")})err := http.ListenAndServe(":8111", nil)if err != nil {log.Fatal(err)}
}

2.路由实现

package mainimport ("log""net/http"
)type HandleFunc func(w http.ResponseWriter, r *http.Request)
//定义路由结构体
type router struct {//定义一个路由处理函数maphandleFuncMap map[string]HandleFunc
}
//定义引擎
type Engine struct {router
}func (r *router) Add(name string, handleFunc HandleFunc) {r.handleFuncMap[name] = handleFunc
}func New() *Engine {return &Engine{router: router{handleFuncMap: make(map[string]HandleFunc)},}
}
func (e *Engine) Run() {//循环处理路由for key, value := range e.handleFuncMap {http.HandleFunc(key, value)}err := http.ListenAndServe(":8111", nil)if err != nil {log.Fatal(err)}
}

测试代码

package mainimport ("fmt""net/http"
)func main() {engine := New()engine.Add("/hello", func(w http.ResponseWriter, r *http.Request) {fmt.Fprintf(w, "%s 欢迎学习GO自研框架", "lisus2000")})engine.Run()
}

3.实现分组路由

package mainimport ("log""net/http"
)type HandleFunc func(w http.ResponseWriter, r *http.Request)// 定义路由分组结构
type routerGroup struct {name          stringhandleFuncMap map[string]HandleFunc
}type router struct {routerGroups []*routerGroup
}// Group 分组方法
func (r *router) Group(name string) *routerGroup {routerGroup := &routerGroup{name:          name,handleFuncMap: make(map[string]HandleFunc),}r.routerGroups = append(r.routerGroups, routerGroup)return routerGroup
}// Add 添加路由
func (r *routerGroup) Add(name string, handleFunc HandleFunc) {r.handleFuncMap[name] = handleFunc
}type Engine struct {router
}func New() *Engine {return &Engine{router: router{},}
}
func (e *Engine) Run() {//user key:get value funcfor _, group := range e.routerGroups {for key, value := range group.handleFuncMap {http.HandleFunc("/"+group.name+key, value)}}err := http.ListenAndServe(":8111", nil)if err != nil {log.Fatal(err)}

测试代码

package mainimport ("fmt""net/http"
)func main() {engine := New()g := engine.Group("user")g.Add("/hello", func(w http.ResponseWriter, r *http.Request) {fmt.Fprintf(w, "%s 欢迎学习GO自研框架", "lisus2000")})g.Add("/info", func(w http.ResponseWriter, r *http.Request) {fmt.Fprintf(w, "%s info功能", "lisus2000")})engine.Run()
}

4.支持不同的请求方式

package mainimport ("fmt""log""net/http"
)type HandleFunc func(w http.ResponseWriter, r *http.Request)// 定义路由分组结构
type routerGroup struct {name             stringhandleFuncMap    map[string]HandleFunchandlerMethodMap map[string][]string
}type router struct {routerGroups []*routerGroup
}// Group 分组方法
func (r *router) Group(name string) *routerGroup {routerGroup := &routerGroup{name:             name,handleFuncMap:    make(map[string]HandleFunc),handlerMethodMap: make(map[string][]string),}r.routerGroups = append(r.routerGroups, routerGroup)return routerGroup
}// Add 添加路由
func (r *routerGroup) Add(name string, handleFunc HandleFunc) {r.handleFuncMap[name] = handleFunc
}
func (r *routerGroup) Any(name string, handleFunc HandleFunc) {r.handleFuncMap[name] = handleFuncr.handlerMethodMap["ANY"] = append(r.handlerMethodMap["ANY"], name)
}
func (r *routerGroup) Get(name string, handleFunc HandleFunc) {r.handleFuncMap[name] = handleFuncr.handlerMethodMap[http.MethodGet] = append(r.handlerMethodMap[http.MethodGet], name)
}
func (r *routerGroup) Post(name string, handleFunc HandleFunc) {r.handleFuncMap[name] = handleFuncr.handlerMethodMap[http.MethodPost] = append(r.handlerMethodMap[http.MethodPost], name)
}type Engine struct {router
}func New() *Engine {return &Engine{router: router{},}
}
func (e *Engine) ServeHTTP(w http.ResponseWriter, r *http.Request) {method := r.Methodfor _, group := range e.routerGroups {for name, methodHandle := range group.handleFuncMap {url := "/" + group.name + nameif r.RequestURI == url {routers, ok := group.handlerMethodMap["ANY"]if ok {for _, routerName := range routers {if routerName == name {methodHandle(w, r)return}}}//method 进行匹配routers, ok = group.handlerMethodMap[method]if ok {for _, routerName := range routers {if routerName == name {methodHandle(w, r)return}}}w.WriteHeader(http.StatusMethodNotAllowed)fmt.Fprintf(w, "%s %s not allow\n", r.RequestURI, method)return}}}w.WriteHeader(http.StatusNotFound)fmt.Fprintf(w, "%s not found\n", r.RequestURI)
}
func (e *Engine) Run() {http.Handle("/", e)err := http.ListenAndServe(":8111", nil)if err != nil {log.Fatal(err)}
}

测试代码

package mainimport ("fmt""net/http"
)func main() {engine := New()g := engine.Group("user")g.Get("/hello", func(w http.ResponseWriter, r *http.Request) {fmt.Fprintf(w, "%s 欢迎学习GO自研框架", "lisus2000")})g.Post("/info", func(w http.ResponseWriter, r *http.Request) {fmt.Fprintf(w, "%s info功能", "lisus2000")})engine.Run()
}

5.支持同一个路径不同请求方式

package mainimport "net/http"type Context struct {W http.ResponseWriterR *http.Request
}
package mainimport ("fmt""log""net/http"
)const ANY = "ANY"type HandleFunc func(ctx *Context)// 定义路由分组结构
type routerGroup struct {name             stringhandleFuncMap    map[string]map[string]HandleFunchandlerMethodMap map[string][]string
}type router struct {routerGroups []*routerGroup
}// Group 分组方法
func (r *router) Group(name string) *routerGroup {routerGroup := &routerGroup{name:             name,handleFuncMap:    make(map[string]map[string]HandleFunc),handlerMethodMap: make(map[string][]string),}r.routerGroups = append(r.routerGroups, routerGroup)return routerGroup
}func (r *routerGroup) handle(name string, method string, handleFunc HandleFunc) {_, ok := r.handleFuncMap[name]if !ok {r.handleFuncMap[name] = make(map[string]HandleFunc)}_, ok = r.handleFuncMap[name][method]if ok {panic("有重复的路由")}r.handleFuncMap[name][method] = handleFunc//r.handlerMethodMap[method] = append(r.handlerMethodMap[method], name)
}func (r *routerGroup) Any(name string, handleFunc HandleFunc) {r.handle(name, ANY, handleFunc)
}
func (r *routerGroup) Get(name string, handleFunc HandleFunc) {r.handle(name, http.MethodGet, handleFunc)
}
func (r *routerGroup) Post(name string, handleFunc HandleFunc) {r.handle(name, http.MethodPost, handleFunc)
}type Engine struct {router
}func New() *Engine {return &Engine{router: router{},}
}
func (e *Engine) ServeHTTP(w http.ResponseWriter, r *http.Request) {method := r.Methodfor _, group := range e.routerGroups {for name, methodHandle := range group.handleFuncMap {url := "/" + group.name + nameif r.RequestURI == url {ctx := &Context{W: w,R: r,}handle, ok := methodHandle[ANY]if ok {handle(ctx)return}handle, ok = methodHandle[method]if ok {handle(ctx)return}//method 进行匹配w.WriteHeader(http.StatusMethodNotAllowed)fmt.Fprintf(w, "%s %s not allow\n", r.RequestURI, method)return}}}w.WriteHeader(http.StatusNotFound)fmt.Fprintf(w, "%s not found\n", r.RequestURI)
}
func (e *Engine) Run() {http.Handle("/", e)err := http.ListenAndServe(":8111", nil)if err != nil {log.Fatal(err)}
}

测试代码

package mainimport ("fmt"
)func main() {engine := New()g := engine.Group("user")g.Get("/hello", func(ctx *Context) {fmt.Fprintf(ctx.W, "%s Get欢迎学习GO自研框架", "lisus2000")})g.Post("/hello", func(ctx *Context) {fmt.Fprintf(ctx.W, "%s Post欢迎学习GO自研框架", "lisus2000")})g.Post("/info", func(ctx *Context) {fmt.Fprintf(ctx.W, "%s info功能", "lisus2000")})engine.Run()
}

6.前缀树

在前面实现的时候,我们的路径匹配实现的很简陋,不能实现更为复杂的需求,比如/user/get/:id 这种带有参数的,这种带有参数的路径,我们称之为动态路由

除了带有参数的,一般情况下,我们可能还希望支持通配符**,比如/static/**, 可以匹配/static/vue.js或者/static/css/index.css这些。

在这里插入图片描述

实现代码

package msgoimport "strings"type treeNode struct {name       stringchildren   []*treeNoderouterName stringisEnd      bool
}//put path: /user/get/:idfunc (t *treeNode) Put(path string) {root := tstrs := strings.Split(path, "/")for index, name := range strs {if index == 0 {continue}children := t.childrenisMatch := falsefor _, node := range children {if node.name == name {isMatch = truet = nodebreak}}if !isMatch {isEnd := falseif index == len(strs)-1 {isEnd = true}node := &treeNode{name: name, children: make([]*treeNode, 0), isEnd: isEnd}children = append(children, node)t.children = childrent = node}}t = root
}//get path:/user/get/11
// hellofunc (t *treeNode) Get(path string) *treeNode {strs := strings.Split(path, "/")routerName := ""for index, name := range strs {if index == 0 {continue}children := t.childrenisMatch := falsefor _, node := range children {if node.name == name || node.name == "*" ||strings.Contains(node.name, ":") {isMatch = truerouterName += "/" + node.namenode.routerName = routerNamet = node//到达最尾部结点if index == len(strs)-1 {return node}break}}if !isMatch {for _, node := range children {// /user/**// /user/get/userinfo// /user/aa/bbif node.name == "**" {routerName += "/" + node.namenode.routerName = routerNamereturn node}}}}return nil
}

测试代码

package msgoimport ("fmt""testing"
)func TestTreeNode(t *testing.T) {root := &treeNode{name:     "/",children: make([]*treeNode, 0),}root.Put("/user/get/:id")root.Put("/user/create/hello")root.Put("/user/create/aaa")root.Put("/order/get/aaa")node := root.Get("/user/get/1")fmt.Println(node)node = root.Get("/user/create/hello")fmt.Println(node)node = root.Get("/user/create/aaa")fmt.Println(node)node = root.Get("/order/get/aaa")fmt.Println(node)}

6.1适配前缀树

实现代码

package msgoimport ("fmt""log""net/http"
)const ANY = "ANY"type HandleFunc func(ctx *Context)// 定义路由分组结构
type routerGroup struct {name             stringhandleFuncMap    map[string]map[string]HandleFunchandlerMethodMap map[string][]stringtreeNode         *treeNode
}type router struct {routerGroups []*routerGroup
}// Group 分组方法
func (r *router) Group(name string) *routerGroup {routerGroup := &routerGroup{name:             name,handleFuncMap:    make(map[string]map[string]HandleFunc),handlerMethodMap: make(map[string][]string),treeNode:         &treeNode{name: "/", children: make([]*treeNode, 0)},}r.routerGroups = append(r.routerGroups, routerGroup)return routerGroup
}func (r *routerGroup) handle(name string, method string, handleFunc HandleFunc) {_, ok := r.handleFuncMap[name]if !ok {r.handleFuncMap[name] = make(map[string]HandleFunc)}_, ok = r.handleFuncMap[name][method]if ok {panic("有重复的路由")}r.handleFuncMap[name][method] = handleFuncr.treeNode.Put(name)
}func (r *routerGroup) Any(name string, handleFunc HandleFunc) {r.handle(name, ANY, handleFunc)
}
func (r *routerGroup) Get(name string, handleFunc HandleFunc) {r.handle(name, http.MethodGet, handleFunc)
}
func (r *routerGroup) Post(name string, handleFunc HandleFunc) {r.handle(name, http.MethodPost, handleFunc)
}
func (r *routerGroup) Delete(name string, handlerFunc HandleFunc) {r.handle(name, http.MethodDelete, handlerFunc)
}
func (r *routerGroup) Put(name string, handlerFunc HandleFunc) {r.handle(name, http.MethodPut, handlerFunc)
}
func (r *routerGroup) Patch(name string, handlerFunc HandleFunc) {r.handle(name, http.MethodPatch, handlerFunc)
}
func (r *routerGroup) Options(name string, handlerFunc HandleFunc) {r.handle(name, http.MethodOptions, handlerFunc)
}
func (r *routerGroup) Head(name string, handlerFunc HandleFunc) {r.handle(name, http.MethodHead, handlerFunc)
}type Engine struct {router
}func New() *Engine {return &Engine{router: router{},}
}
func (e *Engine) ServeHTTP(w http.ResponseWriter, r *http.Request) {method := r.Methodfor _, group := range e.routerGroups {routerName := SubStringLast(r.RequestURI, "/"+group.name)// get/1node := group.treeNode.Get(routerName)if node != nil && node.isEnd {//路由匹配上了ctx := &Context{W: w,R: r,}handle, ok := group.handleFuncMap[node.routerName][ANY]if ok {handle(ctx)return}handle, ok = group.handleFuncMap[node.routerName][method]if ok {handle(ctx)return}//method 进行匹配w.WriteHeader(http.StatusMethodNotAllowed)fmt.Fprintf(w, "%s %s not allow\n", r.RequestURI, method)return}}w.WriteHeader(http.StatusNotFound)fmt.Fprintf(w, "%s not found\n", r.RequestURI)
}
func (e *Engine) Run() {http.Handle("/", e)err := http.ListenAndServe(":8111", nil)if err != nil {log.Fatal(err)}
}

测试代码

package mainimport ("fmt""msgo"
)func main() {engine := msgo.New()g := engine.Group("user")//g.Get("/hello", func(ctx *msgo.Context) {//	fmt.Fprintf(ctx.W, "%s Get欢迎学习GO自研框架", "lisus2000")//})g.Get("/hello/get", func(ctx *msgo.Context) {fmt.Fprintf(ctx.W, "%s Get欢迎学习GO自研框架", "lisus2000")})g.Post("/hello", func(ctx *msgo.Context) {fmt.Fprintf(ctx.W, "%s Post欢迎学习GO自研框架", "lisus2000")})g.Post("/info", func(ctx *msgo.Context) {fmt.Fprintf(ctx.W, "%s info功能", "lisus2000")})g.Get("/get/:id", func(ctx *msgo.Context) {fmt.Fprintf(ctx.W, "%s get user 路径变量的值", "lisus2000")})engine.Run()
}

il)
if err != nil {
log.Fatal(err)
}
}


测试代码```go
package mainimport ("fmt""msgo"
)func main() {engine := msgo.New()g := engine.Group("user")//g.Get("/hello", func(ctx *msgo.Context) {//	fmt.Fprintf(ctx.W, "%s Get欢迎学习GO自研框架", "lisus2000")//})g.Get("/hello/get", func(ctx *msgo.Context) {fmt.Fprintf(ctx.W, "%s Get欢迎学习GO自研框架", "lisus2000")})g.Post("/hello", func(ctx *msgo.Context) {fmt.Fprintf(ctx.W, "%s Post欢迎学习GO自研框架", "lisus2000")})g.Post("/info", func(ctx *msgo.Context) {fmt.Fprintf(ctx.W, "%s info功能", "lisus2000")})g.Get("/get/:id", func(ctx *msgo.Context) {fmt.Fprintf(ctx.W, "%s get user 路径变量的值", "lisus2000")})engine.Run()
}

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

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

相关文章

基于3D Gaussian Splatting与NeRF实现三维重建(使用IPhone创建数据集)

基于Spectacular AI与NeRF实现三维重建-使用IPhone创建数据集 前言项目简介创建数据集扫描处理数据集 解析数据集Python环境 Windows ffmpeg 环境搭建数据集处理安装Nerfstudio需要CUDA环境 依次安装依赖pip install nerfstudio Nerfstudio实现效果开始训练参数配置实时训练浏览…

[足式机器人]Part3 机构运动学与动力学分析与建模 Ch00-4(2) 刚体的速度与角速度

本文仅供学习使用,总结很多本现有讲述运动学或动力学书籍后的总结,从矢量的角度进行分析,方法比较传统,但更易理解,并且现有的看似抽象方法,两者本质上并无不同。 2024年底本人学位论文发表后方可摘抄 若有…

关于树结构的数据的权限控制的算法

树结构的权限控制分两种:1、逐层加载;2、一次性加载 一、逐层加载 涉及的表结构 表名 T_PLAN 表字段 字段类型 是否必 须字段 说明 ID VARCHAR2(50) Y 主键 PARENT_ID VARCHAR2(50) Y 父项节点ID,默认根节点的父节点ID’-1’ TREEPATH VA…

前端moa gif json显示动画

JSON动画 第三方库或插件: 使用lottie-web库:将JSON文件导入,并在HTML中添加一个空的div作为容器,再调用lottie.loadAnimation()方法加载JSON文件,然后将其渲染到div容器中。使用bodymovin插件:将JSON文件…

搜索与图论第二期 BFS

前言 BFS跟DFS同样重要,也一定要熟练的掌握!!! 一、BFS的基本内容 BFS是从根节点开始,沿着树(图)的宽度遍历树(图)的节点。 如果所有节点均被访问,则算法中止。 BFS同样属于盲目搜索。 一般用队列数据结…

shell mapfile命令(readarray命令)介绍(读取行到数组变量)(进程替换+重定向< <()、()> >)

文章目录 shell mapfile命令(readarray命令)介绍mapfile --help英文中文 简单使用教程创建一个测试文件使用mapfile命令读取文件打印数组内容 各选项详解1. -d delim:使用DELIM来终止行,而不是换行原理示例 2. -n count&#xff1…

Agisoft Metashape 基于影像的外部点云着色

Agisoft Metashape 基于影像的外部点云着色 文章目录 Agisoft Metashape 基于影像的外部点云着色前言一、添加照片二、对齐照片三、导入外部点云四、为点云着色五、导出彩色点云前言 本教程介绍了在Agisoft Metashape Professional中,将照片中的真实颜色应用于从不同源获取的…

一键替换SQL中gt;或lt;为<>大于小于符号或一键转换<或>为gt;lt;

一键替换SQL中>或<为<>大于小于符号或一键转换<或>为>< 一、转义字符的困扰二、解决方法三、一键替换转义符教程四、Notepad的宏功能进行转义字符的转换具有以下优点&#xff1a; 一、转义字符的困扰 在日常开发中&#xff0c;处理大量的SQL数据时&am…

使用推测解码 (Speculative Decoding) 使 Whisper 实现 2 倍的推理加速

Open AI 推出的 Whisper 是一个通用语音转录模型&#xff0c;在各种基准和音频条件下都取得了非常棒的结果。最新的 large-v3 模型登顶了 OpenASR 排行榜&#xff0c;被评为最佳的开源英语语音转录模型。该模型在 Common Voice 15 数据集的 58 种语言中也展现出了强大的多语言性…

【期末不挂科-C++考前速过系列P1】大二C++第1次过程考核(3道简述题&7道代码题)【解析,注释】

前言 大家好吖&#xff0c;欢迎来到 YY 滴C复习系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过C的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; YY的《C》专栏YY的《C11》专栏YY的《Lin…

GitHub SSH 身份验证原理

HTTPS 跟 SSH 方式连接远程仓库的区别是什么&#xff1f; HTTPS和SSH是两种不同的协议&#xff0c;用于连接到远程Git仓库。它们的主要区别在于身份验证方式和连接方式&#xff1a; HTTPS&#xff1a;使用用户名和密码或者个人访问令牌&#xff08;Personal Access Token&…

SQL-修改数据

目录 DML-修改数据 删除数据 总结 &#x1f389;欢迎您来到我的MySQL基础复习专栏 ☆* o(≧▽≦)o *☆哈喽~我是小小恶斯法克&#x1f379; ✨博客主页&#xff1a;小小恶斯法克的博客 &#x1f388;该系列文章专栏&#xff1a;重拾MySQL &#x1f4dc;其他专栏&#xff1a;…

python爬虫实战(8)--获取虎pu热榜

1. 需要的类库 import requests from bs4 import BeautifulSoup import pandas as pd2. 请求地址 def fetch_data():url "https://bbs.xxx.com/" # Replace with the actual base URLresponse requests.get(url)if response.status_code 200:return response.c…

【python 的各种模块】(9) 在python使用PIL,即pillow模块

目录 1 导入PIL模块&#xff08;pillow&#xff09; 1.1 导入PIL模块 1.1.1 可用的导入形式 1.1.2 常用的导入形式 1.1.3 PIL下面的常用子模块 2 用 PIL读入&#xff0c;生成和显示图片 2.1 用 PIL.Image.open() 可以读入图片 2.2 用PIL.Image.new() 生成新图片 2.3 …

如何从多个文件夹里各提取相应数量的文件放一起到新文件夹中形成多文件夹组合

首先&#xff0c;需要用到的这个工具&#xff1a; 百度 密码&#xff1a;qwu2蓝奏云 密码&#xff1a;2r1z 说明一下情况 文件夹&#xff1a;1、2、3里面分别放置了各100张动物的图片&#xff0c;模拟实际情况的各种文件 操作&#xff1a;这里演示的是从3个文件夹里各取2张图…

移动安全-certutil

1 需求 需求1&#xff1a;获取应用文件的MD5 CertUtil -hashfile 文件路径 MD5 2 语法 C:\>certutil -?动词:-dump -- 转储配置信息或文件-dumpPFX -- 转储 PFX 结构-asn -- 分析 ASN.1 文件-decodehex -- 解码十六进制编码的…

MySQL 按日期流水号 条码 分布式流水号

有这样一个场景&#xff0c;有多台终端&#xff0c;要获取唯一的流水号&#xff0c;流水号格式是 日期0001形式&#xff0c;使用MySQL的存储过程全局锁实现这个需求。 以下是代码示例。 注&#xff1a;所有的终端连接到MySQL服务器获取流水号&#xff0c;如果获取到的是 “-1”…

软件测试|好用的pycharm插件推荐(二)—— JSON Parser

简介 PyCharm是一款强大的Python集成开发环境&#xff08;IDE&#xff09;&#xff0c;它提供了许多插件来增强开发体验。其中一个非常有用的插件是"JSON Parser"&#xff0c;它允许你在PyCharm中轻松解析和处理JSON数据。在本文中&#xff0c;我们将详细介绍如何安…

RTMO 姿态识别

RTMO 姿态识别 预测示例: # Copyright (c) OpenMMLab. All rights reserved. from argparse import ArgumentParser from typing import Dictfrom mmpose.apis.inferencers import MMPoseInferencer, get_model_aliasesfilter_args = dict(bbox_thr=0.3, nms_thr=0.3, pose_…

文件操作(与缓存相关的)相关笔记

与缓存相关就是一行一行进行读写&#xff0c;或者直接读写整个文件 1.BufferedInputStream&#xff0c;字节 1.构造方法&#xff1a; new BufferedInputStream(FileInputStream对象)&#xff1b; 2.读取文件 除了基础的read方法之外还有一个readAlllBytes方法可以返回这个文件…