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

Gee教程2.上下文Context

先来看看Gin框架的简单例子

func main() {engine := gin.Default()engine.GET("/", func(c *gin.Context) {c.String(http.StatusOK, "hello World!")})//监听并启动服务,默认 http://localhost:8080/engine.Run()
}//我们自己写的
func main() {engine := gee.New()engine.GET("/", func(w http.ResponseWriter, req *http.Request) {fmt.Fprintf(w, "URL.Path = %q\n", req.URL.Path)})engine.Run("localhost:10000")
}

对比我们自己写的,Hander方法的参数变成了*gin.Context。

那么这版的主要修改策略是:

  • 路由(Router)独立出来,方便之后增强。
  • 设计上下文(Context),封装 Request 和 Response ,并提供对 JSON、HTML 等返回类型的支持。

 设计Context

必要性

1.web服务,基本就是根据客户的请求*http.Request,构造回应客户的响应http.ResponseWriter。但是这两个对象提供的接口粒度比较细。比如要构造一个完整的响应,需要考虑消息头和消息体,而消息头包含了状态码,消息类型等。那每次构造回复,都要写这些设置的代码。所以,如果不进行有效的封装,那么使用该框架的用户就需要写大量繁琐重复的代码。

通俗点说,就是要让使用框架的用户尽量少写重复的代码,少写代码可以实现同样的功能。

用返回JSON数据进行比较,来感受下封装前后的变化。

封装之前

engine.GET("/", func(w http.ResponseWriter, req *http.Request) {obj := map[string]any{"name": "abc","age":  20,}w.Header().Set("Content-Type", "application/json")w.WriteHeader(http.StatusOK)encoder := json.NewEncoder(w)if err := encoder.Encode(obj); err != nil {http.Error(w, err.Error(), 500)}})

封装后

	engine.GET("/", func(c *gee.Context) {c.JSON(http.StatusOK, gee.H{"username": c.PostForm("username"),"password": c.PostForm("password"),})})

2.针对使用场景,封装*http.Requesthttp.ResponseWriter的方法,简化了相关接口的调用,这只是设计Context的原因之一。

对应框架来说,还需要支撑额外的功能。例如,之后要解析动态路由/hello/:name,参数:name的值放在哪里?Context随着每一个请求的出现而产生,请求的结束而销毁,和当前请求的强相关的信息都应由Context承载。每一个HTTP请求就会带有一个Context。

因此,设置Context结构,扩展性和复杂性都留在了内部,从而简化了接口。而路由的处理函数,以及之后要实现的中间件,参数都统一使用Context实例。Context就像一次会话的百宝箱,可以找到任何东西。

Context的实现

type H map[string]anytype Context struct {Wrtier http.ResponseWriterReq    *http.RequestPath   stringMethod string//响应的状态码StatusCode int
}func newContext(w http.ResponseWriter, req *http.Request) *Context {return &Context{Wrtier: w,Req:    req,Path:   req.URL.Path,Method: req.Method,}
}func (c *Context) PostForm(key string) string {return c.Req.FormValue(key)
}func (c *Context) Query(key string) string {return c.Req.URL.Query().Get(key)
}func (c *Context) Status(code int) {c.StatusCode = codec.Wrtier.WriteHeader(code)
}func (c *Context) SetHeader(key string, value string) {c.Wrtier.Header().Set(key, value)
}//以下是快速构造Sring/Data/JSON/HTML响应的方法
func (c *Context) String(code int, format string, values ...any) {c.SetHeader("Content-Type", "text/plain")c.Status(code)c.Wrtier.Write([]byte(fmt.Sprintf(format, values...)))
}func (c *Context) JSON(code int, obj any) {c.SetHeader("Content-Type", "application/json")c.Status(code)encoder := json.NewEncoder(c.Wrtier)if err := encoder.Encode(obj); err != nil {http.Error(c.Wrtier, err.Error(), 500)}
}func (c *Context) Data(code int, data []byte) {c.Status(code)c.Wrtier.Write(data)
}func (c *Context) HTML(code int, html string) {c.SetHeader("Content-Type", "text/html")c.Status(code)c.Wrtier.Write([]byte(html))
}

代码开头,给map[string]any起了别名H,构建JSON数据时候,使用gee.H显得更加简洁。

  • Context目前包含了http.ResponseWriter*http.Request,这样为路由函数的参数可以修改成(c *gee.Context)做准备。另外也提供了对Method和Path这两个常用属性的直接访问。
  • 提供了访问Query和PostForm参数的方法
  • 提供了快速构造Sring/Data/JSON/HTML响应的方法,使用简便。

 路由(Router)

将路由抽取出来,放到一个新的文件router.go,方便我们下次对router功能的增强,录入添加动态路由的支持。而router的hander方法做了一个调整,就是handler的参数变成了前面所说的*Context

将路由单独抽离出来,那和路由相关的路由方法的操作也会在该文件内。

package geeimport "net/http"
//HandlerFunc的定义在gee.go文件中type router struct {handers map[string]HandlerFunc
}func newRouter() *router {return &router{handers: make(map[string]HandlerFunc)}
}// 添加路由
func (r *router) addRoute(method string, pattern string, handler HandlerFunc) {key := method + "-" + patternr.handers[key] = handler
}func (r *router) handle(c *Context) {key := c.Method + "-" + c.Pathif hander, ok := r.handers[key]; ok {hander(c)} else {c.String(http.StatusNotFound, "404 NOT FOUND: %s\n", c.Path)}
}

框架入口

gee.go

type Engine struct {router *router
}// 创建enginx实例
func New() *Engine {return &Engine{router: newRouter()}
}func (engine *Engine) addRoute(method string, pattern string, hander HandlerFunc) {engine.router.addRoute(method, pattern, hander)
}func (engine *Engine) GET(pattern string, handler HandlerFunc) {engine.addRoute("GET", pattern, handler)
}
func (engine *Engine) POST(pattern string, handler HandlerFunc) {engine.addRoute("POST", pattern, handler)
}func (engine *Engine) Run(addr string) error {return http.ListenAndServe(addr, engine)
}func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {c := newContext(w, req)engine.router.handle(c)
}

 在将路由router相关的代码独立之后,gee.go相对简单了不少。其中最重要的还是通过实现了ServeHTTP接口,接管了所有的HTTP请求。

这版的改变主要的改变,添加了Context,把路由router独立开来。在此基础上,gee.go中最大变化的是在ServHTTP方法中。

在Context中说了,一个HTTP请求,就带有一个Context。所以一个HTTP请求到来后,就为这个请求创建一个Context实例

独立router部分后,执行路由方法的代码也在router中了。而路由方法的参数也由(http.ResponseWriter, *http.Request)修改成(*Context)。所以,ServHTTP需要调用router.handle去执行对应的路由方法,并把*context传递进去,给该请求发送响应,由用户自己定义的,但都是调用Context中的api来实现的,例如返回string字符串,Context.String函数。

所以,Context承载了整个会话的生命周期

测试

到这里,使用的样子已经和Gin相似了。来看看使用例子。

func main() {engine := gee.New()engine.GET("/", func(c *gee.Context) {c.String(http.StatusOK, "ok..")})engine.POST("/hello", func(c *gee.Context) {c.JSON(http.StatusOK, gee.H{"name": "abc","age":  32,})})engine.Run("localhost:10000")
}

 执行 go run main.go查看效果

 完整代码:https://github.com/liwook/Go-projects/tree/main/gee-web/2-context

相关文章:

Gee教程2.上下文Context

先来看看Gin框架的简单例子 func main() {engine : gin.Default()engine.GET("/", func(c *gin.Context) {c.String(http.StatusOK, "hello World!")})//监听并启动服务,默认 http://localhost:8080/engine.Run() }//我们自己写的 func main()…...

【从浅识到熟知Linux】基本指定之cat、more和less

🎈归属专栏:从浅学到熟知Linux 🚗个人主页:Jammingpro 🐟每日一句:写完这篇我要去吃晚饭啦!! 文章前言:本文介绍cat、more和less指令三种查看文件的用法并给出示例和截图…...

2018年7月24日 Go生态洞察:Go Cloud实现便携式云编程

🌷🍁 博主猫头虎(🐅🐾)带您 Go to New World✨🍁 🦄 博客首页——🐅🐾猫头虎的博客🎐 🐳 《面试题大全专栏》 🦕 文章图文…...

storyBook常见踩坑报错 和 解决

用StoryBook官网的代码&#xff0c;但报错&#xff0c;Unexpected token’<’ 在js文件中// Button.stories.js|jsx import { Button } from ‘./Button’; export default { component: Button, }; /* *&#x1f447; Render functions are a framework specific featur…...

python 笔记 根据用户轨迹+基站位置,估计基站轨迹+RSRP

1 问题描述 已知用户实际的轨迹&#xff0c;和基站的位置&#xff0c;能不能得到用户所连接的基站&#xff0c;以及基站的信号强度RSRP&#xff1f; 1.1 几个假设 这里我们做几个假设&#xff1a; 每个用户有80%的概率连接最近的基站&#xff0c;有20%的概率选择其他的基站连…...

RocketMQ 安装部署及应用场景记录

文章目录 前言一、RocketMQ简介1.1 整体架构 二、RocketMQ安装部署2.1 RocketMQ 下载2.2 修改 JVM 参数2.3 启动 NameServer 和 Broker2.4 验证发送和接受消息2.5 停止 NameServer 和 Broker2.6 配置全局环境 三、RocketMQ应用场景3.1 异步处理3.2 应用解耦3.3 流量削峰 前言 …...

人工智能面面观

人工智能简介 人工智能&#xff08;Artificial Intelligence&#xff0c;简称AI&#xff09;是一门研究如何使计算机能够模拟和执行人类智能任务的科学和技术领域。它致力于开发能够感知、理解、学习、推理、决策和与人类进行交互的智能系统。人工智能的背景可以追溯到上世纪50…...

vue-router的使用技巧

一、安装 npm install vue-router 二、引入 main.ts引入 import { createApp } from vue import App from ./App.vue import router from ./routerconst app createApp(App)app.use(router) app.mount(#app)三、定义路由文件 路由的参数 meta添加路由的其他参数 redire…...

CV计算机视觉每日开源代码Paper with code速览-2023.11.21

点击CV计算机视觉&#xff0c;关注更多CV干货 论文已打包&#xff0c;点击进入—>下载界面 点击加入—>CV计算机视觉交流群 1.【基础网络架构&#xff1a;Transformer】Multi-entity Video Transformers for Fine-Grained Video Representation Learning 论文地址&…...

人工智能对当代生活的影响

人工智能&#xff08;AI&#xff09;是指通过模拟人类智能的方式&#xff0c;使机器能够执行某些需要智能的任务。随着技术的快速发展和应用的广泛推广&#xff0c;人工智能已经深入到我们的日常生活中&#xff0c;对我们的生活和社会产生了深远的影响。本文将探讨人工智能对当…...

笔记:如何搭建一套前端监控系统?(持续更新中)

数据敏感处理 数据加密&#xff0c;对涉及用户隐私的数据做到加密防护 独立部署&#xff0c;不和其它应用共享监控系统 不采集具体数据&#xff0c;只采集用户操作数据 错误采集 Runtime Error: JS运行错误&#xff0c;可通过error监听器捕获 load Error: 资源加载错误&#x…...

在 Ubuntu 上安装最新版的 Calibre

目录 前言 方法1&#xff1a;从 Ubuntu 的仓库安装 Calibre 卸载 Calibre 方法2&#xff1a;获取最新版本的 Calibre 卸载 Calibre 结语 前言 Calibre 是一款自由开源的电子书软件。下面介绍如何在 Ubuntu Linux 上安装它。 作为电子书管理的瑞士军刀&#xff0c;Calibre …...

docker基础学习笔记

文章目录 Docker简介Linux下安装DockerDocker常用命令Docker网络Docker存储docker-composedockerfile制作镜像私有仓库镜像导入导出参考 Docker简介 定义&#xff1a;Docker是一个开源的应用容器引擎优势&#xff1a; 一键部署&#xff0c;开箱即用&#xff1a;容器使用基于im…...

Could not resolve all files for configuration ‘:app:androidJdkImage‘.

在使用./gradlew build编译项目时候遇到了该问题&#xff0c;整体错误如下: * What went wrong: Configuration cache state could not be cached: field generatedModuleFile of com.android.build.gradle.tasks.JdkImageInput bean found in field compilerArgumentProvider…...

GLP-1 , GLP-1R

-- 6VCB_GLP-1R G_protein, GLP-1 peptidea positive allosteric modulator...

【数据结构】F : 道路建设 (Ver. I)

F : 道路建设 (Ver. I) Description 有N个村庄&#xff0c;编号从1到N&#xff0c;你应该建造一些道路&#xff0c;使每个村庄都可以相互连接。 两个村A和B是相连的&#xff0c;当且仅当A和B之间有一条道路&#xff0c;或者存在一个村C使得在A和C之间有一条道路&#xff0c;并…...

flutter 无法从H5 WebView 访问摄像头和录音权限

AndroidManifest.xml需要在 中添加以下权限&#xff1a; <uses-permission android:name"android.permission.INTERNET"/> <uses-permission android:name"android.permission.CAMERA" /> <uses-permission android:name"android.per…...

electron27-react-mateos:基于electron+react18仿matePad桌面系统

基于Electron27React18ArcoDesign搭建桌面版OS管理系统。 electron-react-mateos 基于最新前端跨端技术栈electron27.xreact18arco-designzustand4sortablejs构建的一款仿制matePad界面多层级路由管理OS系统。 ElectronReactOS支持桌面多路由配置&#xff0c;新开窗口弹窗开启路…...

高精度算法总结

高精度加法 题目链接&#xff1a; https://www.acwing.com/activity/content/problem/content/825/ 代码模版&#xff1a; #include <iostream> #include <vector>using namespace std;// C A B vector<int> add(vector<int> &A, vector<…...

EMQX-5.3.1单机集群部署并基于Nginx实现负载均衡

本例单机集群部署使用三个节点&#xff0c;分别为node1、node2、node3 一、安装与配置 1 创建数据目录 mkdir -p node1/data node1/logs mkdir -p node2/data node2/logs mkdir -p mode3/data node3/logs 2 数据目录授权 chown 1000 node1/ node2/ node3/ chown 1000 n…...

C++:std::is_convertible

C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...

3.3.1_1 检错编码(奇偶校验码)

从这节课开始&#xff0c;我们会探讨数据链路层的差错控制功能&#xff0c;差错控制功能的主要目标是要发现并且解决一个帧内部的位错误&#xff0c;我们需要使用特殊的编码技术去发现帧内部的位错误&#xff0c;当我们发现位错误之后&#xff0c;通常来说有两种解决方案。第一…...

【配置 YOLOX 用于按目录分类的图片数据集】

现在的图标点选越来越多&#xff0c;如何一步解决&#xff0c;采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集&#xff08;每个目录代表一个类别&#xff0c;目录下是该类别的所有图片&#xff09;&#xff0c;你需要进行以下配置步骤&#x…...

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列&#xff0c;以便知晓哪些列包含有价值的数据&#xff0c;…...

JAVA后端开发——多租户

数据隔离是多租户系统中的核心概念&#xff0c;确保一个租户&#xff08;在这个系统中可能是一个公司或一个独立的客户&#xff09;的数据对其他租户是不可见的。在 RuoYi 框架&#xff08;您当前项目所使用的基础框架&#xff09;中&#xff0c;这通常是通过在数据表中增加一个…...

JVM虚拟机:内存结构、垃圾回收、性能优化

1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...

破解路内监管盲区:免布线低位视频桩重塑停车管理新标准

城市路内停车管理常因行道树遮挡、高位设备盲区等问题&#xff0c;导致车牌识别率低、逃费率高&#xff0c;传统模式在复杂路段束手无策。免布线低位视频桩凭借超低视角部署与智能算法&#xff0c;正成为破局关键。该设备安装于车位侧方0.5-0.7米高度&#xff0c;直接规避树枝遮…...

Ubuntu系统多网卡多相机IP设置方法

目录 1、硬件情况 2、如何设置网卡和相机IP 2.1 万兆网卡连接交换机&#xff0c;交换机再连相机 2.1.1 网卡设置 2.1.2 相机设置 2.3 万兆网卡直连相机 1、硬件情况 2个网卡n个相机 电脑系统信息&#xff0c;系统版本&#xff1a;Ubuntu22.04.5 LTS&#xff1b;内核版本…...

轻量级Docker管理工具Docker Switchboard

简介 什么是 Docker Switchboard &#xff1f; Docker Switchboard 是一个轻量级的 Web 应用程序&#xff0c;用于管理 Docker 容器。它提供了一个干净、用户友好的界面来启动、停止和监控主机上运行的容器&#xff0c;使其成为本地开发、家庭实验室或小型服务器设置的理想选择…...

云原生安全实战:API网关Envoy的鉴权与限流详解

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关 作为微服务架构的统一入口&#xff0c;负责路由转发、安全控制、流量管理等核心功能。 2. Envoy 由Lyft开源的高性能云原生…...