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

05 封装

在对 context 的封装中,我们只是将 request、response 结构直接放入 context 结构体中,对应的方法并没有很好的封装。

函数封装并不是一件很简单、很随意的事情。相反,如何封装出易用、可读性高的函数是非常需要精心考量的,框架中每个函数的参数、返回值、命名,都代表着我们作为作者在某个事情上的思考。想要针对某个功能,封装出一系列比较完美的接口,更要我们从系统性的角度思考。

如何封装请求和返回

我们的目标是尽量在 context 这个数据结构中,封装“读取请求数据”和“封装返回数据”中的方法。

读取请求数据

头部:业务无关、传输相关的信息,如请求地址、编码格式、缓存时长
body:与业务相关的信息

Header 信息中,包含 HTTP 的一些基础信息,比如请求地址、请求方法、请求 IP、请求域名、Cookie 信息等,是经常读取使用的,为了方便,我们需要一一提供封装。

而另外一些更细节的内容编码格式、缓存时长等,由于涉及的 HTTP 协议细节内容比较多,我们很难将每个细节都封装出来,但是它们都是以 key=value 的形式传递到服务端的,所以这里也考虑封装一个通用的方法。

Body 信息中,HTTP 是已经以某种形式封装好的,可能是 JSON 格式、XML 格式,也有可能是 Form 表单格式。其中 Form 表单注意一下,它可能包含 File 文件,请求参数和返回值肯定和其他的 Form 表单字段是不一样的,需要我们对其单独封装一个函数。

封装返回数据

Header 头部,我们经常要设置的是返回状态码和 Cookie,所以单独为其封装。其他的 Header 同样是 key=value 形式设置的,设置一个通用的方法即可。

返回数据的 Body 体是有不同形式的,比如 JSON、JSONP、XML、HTML 或者其他文本格式,所以我们要针对不同的 Body 体形式,进行不同的封装。

在这里插入图片描述

定义接口让封装更明确

对于比较完整的功能模块,先定义接口,再具体实现,这样好处是实现解耦、开发清晰。

定义两个接口,IRequest 和 IResponse,分别对应“读取请求数据”和“封装返回数据” 这两个功能模块。

IRequest 接口定义


// 代表请求包含的方法
type IRequest interface {// 请求地址 url 中带的参数// 形如: foo.com?a=1&b=bar&c[]=barQueryInt(key string, def int) (int, bool)QueryInt64(key string, def int64) (int64, bool)QueryFloat64(key string, def float64) (float64, bool)QueryFloat32(key string, def float32) (float32, bool)QueryBool(key string, def bool) (bool, bool)QueryString(key string, def string) (string, bool)QueryStringSlice(key string, def []string) ([]string, bool)Query(key string) interface{}// 路由匹配中带的参数// 形如 /book/:idParamInt(key string, def int) (int, bool)ParamInt64(key string, def int64) (int64, bool)ParamFloat64(key string, def float64) (float64, bool)ParamFloat32(key string, def float32) (float32, bool)ParamBool(key string, def bool) (bool, bool)ParamString(key string, def string) (string, bool)Param(key string) interface{}// form 表单中带的参数FormInt(key string, def int) (int, bool)FormInt64(key string, def int64) (int64, bool)FormFloat64(key string, def float64) (float64, bool)FormFloat32(key string, def float32) (float32, bool)FormBool(key string, def bool) (bool, bool)FormString(key string, def string) (string, bool)FormStringSlice(key string, def []string) ([]string, bool)FormFile(key string) (*multipart.FileHeader, error)Form(key string) interface{}// json bodyBindJson(obj interface{}) error// xml bodyBindXml(obj interface{}) error// 其他格式GetRawData() ([]byte, error)// 基础信息Uri() stringMethod() stringHost() stringClientIp() string// headerHeaders() map[string][]stringHeader(key string) (string, bool)// cookieCookies() map[string]stringCookie(key string) (string, bool)
}

QueryXXX表示从URL后缀中获取参数,ParamXXX表示从路由匹配获取参数,FormXXX表示从Body的form表单获取参数。
这三类方法统一了参数与返回值:

  • 参数: key和默认值
  • 返回值:匹配值和bool

具体实现

  • Query请求

// 获取请求地址中所有参数
func (ctx *Context) QueryAll() map[string][]string {if ctx.request != nil {return map[string][]string(ctx.request.URL.Query())}return map[string][]string{}
}// 获取 Int 类型的请求参数
func (ctx *Context) QueryInt(key string, def int) (int, bool) {params := ctx.QueryAll()if vals, ok := params[key]; ok {if len(vals) > 0 {// 使用 cast 库将 string 转换为 Intreturn cast.ToInt(vals[0]), true}}return def, false
}
  • Param 请求
    找到路由节点后,根据uri的分段,网上寻找父节点,找到其中的参数,并存储到ctx中。

// 将 uri 解析为 params
func (n *node) parseParamsFromEndNode(uri string) map[string]string {ret := map[string]string{}segments := strings.Split(uri, "/")cnt := len(segments)cur := nfor i := cnt - 1; i >= 0; i-- {if cur.segment == "" {break}// 如果是通配符节点if isWildSegment(cur.segment) {// 设置 paramsret[cur.segment[1:]] = segments[i]}cur = cur.parent}return ret
}// 所有请求都进入这个函数, 这个函数负责路由分发
func (c *Core) ServeHTTP(response http.ResponseWriter, request *http.Request) {// 封装自定义 contextctx := NewContext(request, response)// 寻找路由node := c.FindRouteNodeByRequest(request)...// 设置路由参数params := node.parseParamsFromEndNode(request.URL.Path)ctx.SetParams(params)...
}// 获取路由参数
func (ctx *Context) Param(key string) interface{} {if ctx.params != nil {if val, ok := ctx.params[key]; ok {return val}}return nil
}// 路由匹配中带的参数
// 形如 /book/:id
func (ctx *Context) ParamInt(key string, def int) (int, bool) {if val := ctx.Param(key); val != nil {// 通过 cast 进行类型转换return cast.ToInt(val), true}return def, false
}
  • Bind 请求

// 将 body 文本解析到 obj 结构体中
func (ctx *Context) BindJson(obj interface{}) error {if ctx.request != nil {// 读取文本body, err := ioutil.ReadAll(ctx.request.Body)if err != nil {return err}// 重新填充 request.Body,为后续的逻辑二次读取做准备ctx.request.Body = ioutil.NopCloser(bytes.NewBuffer(body))// 解析到 obj 结构体中err = json.Unmarshal(body, obj)if err != nil {return err}} else {return errors.New("ctx.request empty")}return nil
}

IResponse 接口定义

type IResponse interface {// 将obj对象转成json并发送出去?Json(obj interface{}) IResponseJsonp(obj interface{}) IResponseXml(obj interface{}) IResponseHtml(template string, obj interface{}) IResponseText(format string, values ...interface{}) IResponseRedirect(path string) IResponseSetHeader(key string, val string)SetCookie(key string, val string, maxAge int, path, domin string, secure, httpOnly bool) IResponseSetStatus(code int) IResponseSetOkStatus() IResponse}

对于 Header 部分,我们设计了状态码的设置函数 SetStatus/SetOkStatus/Redirect,还设计了 Cookie 的设置函数 SetCookie,同时,我们提供了通用的设置 Header 的函数 SetHeader。

对于 Body 部分,我们设计了 JSON、JSONP、XML、HTML、Text 等方法来输出不同格式的 Body。

这里注意下,很多方法的返回值使用 IResponse 接口本身, 这个设计能允许使用方进行链式调用。链式调用的好处是,能很大提升代码的阅读性,比如在业务逻辑代码 controller.go 里这个调用方法:

c.SetOkStatus().Json("ok, UserLoginController: " + foo)

【小结】

  1. 对请求和响应进行封装,提供使用的便利性
  2. 请求封装有获取参数(url路径后缀、动态参数、表单参数)、获取header
  3. 返回封装有设置header、设备body

相关文章:

05 封装

在对 context 的封装中,我们只是将 request、response 结构直接放入 context 结构体中,对应的方法并没有很好的封装。 函数封装并不是一件很简单、很随意的事情。相反,如何封装出易用、可读性高的函数是非常需要精心考量的,框架中…...

clean

clean code 记得以前写过这题&#xff0c;写的乱七八糟&#xff0c;分析来分析去。 后悔应该早点写代码&#xff0c;leetcode大一就该刷了。 https://leetcode.cn/problems/plus-one/submissions/ class Solution { public:vector<int> plusOne(vector<int>&…...

佛科院计算机软件技术基础——线性表

一、基础知识了解&#xff1a;结构体的理解&#xff1a;我们知道整型是由1位符号位和15位数值位组成&#xff0c;而就可以把结构体理解为我们定义的数据类型&#xff0c;如&#xff1a;typedef struct {int data[2]; //存储顺序表中的元素int len; …...

linux下终端操作mysql数据库

目录 一&#xff0e;检查mysql是否安装 1. 查看文件安装路径 2. 查询运行文件所在路径(文件夹地址) 二&#xff0e;登录mysql 三&#xff0e;列出mysql全部用户 四&#xff0e;常用指令 &#xff11;&#xff0e;查看全部数据库 &#xff12;&#xff0e;选择数据库 …...

MySQL参数优化之thread_cache_size

1.thread_cache_size简介 每建立一个连接&#xff0c;都需要一个线程来与之匹配&#xff0c;此参数用来缓存空闲的线程&#xff0c;以至不被销毁&#xff0c;如果线程缓存中有空闲线程&#xff0c;这时候如果建立新连接&#xff0c;MYSQL就会很快的响应连接请求。 show statu…...

gRPC服务健康检查(二):gRPC健康检查协议详解

gRPC健康检查协议健康检查用于检测服务端能否正常处理rpc请求&#xff0c;客户端对服务端的健康检查可以点对点进行&#xff0c;也可以通过某些控制系统&#xff08;如负载平衡&#xff09;进行。客户端可以根据服务端返回的状态执行对应的策略。因为GRPC服务可以用于简单的客户…...

Android系统10 RK3399 init进程启动(四十七) Android init 进程整体代码逻辑简述

配套系列教学视频链接&#xff1a;安卓系列教程之ROM系统开发-百问100ask说明系统&#xff1a;Android10.0设备&#xff1a; FireFly RK3399 &#xff08;ROC-RK3399-PC-PLUS&#xff09;前言本文简单描述一下android init祖先进程启动的基本执行流程&#xff0c;让大家有一个整…...

CSDN 编程竞赛三十二期题解

竞赛总览 CSDN 编程竞赛三十二期&#xff1a;比赛详情 (csdn.net) 竞赛题解 题目1、传奇霸业 传奇霸业&#xff0c;是兄弟就来干。小春&#xff08;HP为a&#xff09;遇到了一只黄金哥布林&#xff08;HP为x&#xff09;。小春每次能对哥布林造成b点伤害&#xff0c;哥布林…...

Kubernetes 中的 Pod Hook

Pod Hook 我们知道Pod是Kubernetes集群中的最小单元&#xff0c;而 Pod 是有容器组组成的&#xff0c;所以在讨论 Pod 的生命周期的时候我们可以先来讨论下容器的生命周期。 实际上 Kubernetes 为我们的容器提供了生命周期钩子的&#xff0c;就是我们说的Pod Hook&#xff0c…...

Linux操作系统安装MySQL(rpm安装)

Linux操作系统安装MySQL&#xff08;rpm安装&#xff09;1 背景2 环境说明3 准备工作3.1 端口查看3.2 检查安装3.3 创建MySQL用户和组4 MySQL安装4.1 下载MySQL4.2 解压安装包4.3 安装MySQL4.4 初始化MySQL4.5 启动MySQL4.6 设置MySQL初始密码4.6.1 查看数据库初始密码4.6.2 更…...

MySQL高级第二讲

目录 二、MySQL高级02 2.1 触发器 2.1.1 触发器介绍 2.1.2 创建触发器 2.2 MySQL的体系结构 2.3 存储引擎 2.3.1 存储引擎概述 2.3.2 各种存储引擎特性 2.3.3 InnoDB 2.3.4 MyISAM 2.3.5 MEMORY 2.3.6 MERGE 2.3.7 存储引擎的选择 2.4 优化sql 2.4.1 查看sql执行…...

凸优化专题1

多变量函数的求导与求梯度/矩阵求导 1. 导数 定义: 设f:Rn→Rm,且x∈intdomf,则f在点x的导数(或称Jacobian)记为矩阵Df(x)∈Rmnf:\R^n \rightarrow \R^m, 且x\in \mathbf{int}\ \mathbf{dom} f, 则f 在点x的导\\数(或称Jacobian)记为矩阵 Df(x) \in \R^{m\times n}f:Rn→Rm,且…...

【蓝桥杯每日一题】递推算法

&#x1f34e; 博客主页&#xff1a;&#x1f319;披星戴月的贾维斯 &#x1f34e; 欢迎关注&#xff1a;&#x1f44d;点赞&#x1f343;收藏&#x1f525;留言 &#x1f347;系列专栏&#xff1a;&#x1f319; 蓝桥杯 &#x1f319;我与杀戮之中绽放&#xff0c;亦如黎明的花…...

Unity性能优化: 性能优化之内存篇

前言 本文和传统的内存优化不一样&#xff0c;不是讲如何降低内存占用&#xff0c;而是讲编程开发中要注意的内存问题以及一些内存技术的演变与原理。 对惹&#xff0c;这里有一个游戏开发交流小组&#xff0c;希望大家可以点击进来一起交流一下开发经验呀 1: Application进程…...

华为OD机试题,用 Java 解【内存资源分配】问题

最近更新的博客 华为OD机试题,用 Java 解【停车场车辆统计】问题华为OD机试题,用 Java 解【字符串变换最小字符串】问题华为OD机试题,用 Java 解【计算最大乘积】问题华为OD机试题,用 Java 解【DNA 序列】问题华为OD机试 - 组成最大数(Java) | 机试题算法思路 【2023】使…...

微服务之Nacos注册与配置

&#x1f3e0;个人主页&#xff1a;阿杰的博客 &#x1f4aa;个人简介&#xff1a;大家好&#xff0c;我是阿杰&#xff0c;一个正在努力让自己变得更好的男人&#x1f468; 目前状况&#x1f389;&#xff1a;24届毕业生&#xff0c;奋斗在找实习的路上&#x1f31f; &#x1…...

Android 动画详解

Android动画的分类与使用学习Android必不可少的就是动画的使用了&#xff0c;在Android版本迭代的过程中&#xff0c;出现了很多动画框架&#xff0c;这里做一个总结。Android动画类型分类逐帧动画【Frame Animation】&#xff0c;即顺序播放事先准备的图片。补间动画【Tween A…...

Linux -- 程序 进程 线程 概念引入

程序与进程 &#xff1a;程序 &#xff1a;什么是程序 &#xff1f;&#xff1f;&#xff1f;伪官方 &#xff1a; 二进制文件&#xff0c;文件存储在磁盘中&#xff0c;例如 /usr/bin 目录下 。 是静态。 简单讲 &#xff1a;# 我们都学习了语言&#xff0c;比如下面这串代…...

Android ART dex2oat

一、什么是dex2oat Dex2oat (dalvik excutable file to optimized art file) &#xff0c;是一个对 dex 文件进行编译优化的程序&#xff0c;在我们的 Android 手机中的位置是 /system/bin/dex2oat&#xff0c;对应的源码路径为 android/art/dex2oat/dex2oat.cc&#xff0c;通…...

「RISC-V Arch」RISC-V 规范结构

日期&#xff1a;20230228 规范分类 根据 RISC-V 设计哲学&#xff0c;其规范文档也是高度模块化的&#xff1a; ISA 规范&#xff08;2 篇&#xff09; 非特权规范特权规范 非 ISA 规范&#xff08;6篇&#xff09; Trace规范ABI 规范外部调试规范PLIC 规范SBI 规范UEFI 协…...

day52 ResNet18 CBAM

在深度学习的旅程中&#xff0c;我们不断探索如何提升模型的性能。今天&#xff0c;我将分享我在 ResNet18 模型中插入 CBAM&#xff08;Convolutional Block Attention Module&#xff09;模块&#xff0c;并采用分阶段微调策略的实践过程。通过这个过程&#xff0c;我不仅提升…...

Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件

今天呢&#xff0c;博主的学习进度也是步入了Java Mybatis 框架&#xff0c;目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学&#xff0c;希望能对大家有所帮助&#xff0c;也特别欢迎大家指点不足之处&#xff0c;小生很乐意接受正确的建议&…...

iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版​分享

平时用 iPhone 的时候&#xff0c;难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵&#xff0c;或者买了二手 iPhone 却被原来的 iCloud 账号锁住&#xff0c;这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...

ElasticSearch搜索引擎之倒排索引及其底层算法

文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...

涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战

“&#x1f916;手搓TuyaAI语音指令 &#x1f60d;秒变表情包大师&#xff0c;让萌系Otto机器人&#x1f525;玩出智能新花样&#xff01;开整&#xff01;” &#x1f916; Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制&#xff08;TuyaAI…...

网站指纹识别

网站指纹识别 网站的最基本组成&#xff1a;服务器&#xff08;操作系统&#xff09;、中间件&#xff08;web容器&#xff09;、脚本语言、数据厍 为什么要了解这些&#xff1f;举个例子&#xff1a;发现了一个文件读取漏洞&#xff0c;我们需要读/etc/passwd&#xff0c;如…...

人机融合智能 | “人智交互”跨学科新领域

本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...

20个超级好用的 CSS 动画库

分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码&#xff0c;而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库&#xff0c;可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画&#xff0c;可以包含在你的网页或应用项目中。 3.An…...

深度学习水论文:mamba+图像增强

&#x1f9c0;当前视觉领域对高效长序列建模需求激增&#xff0c;对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模&#xff0c;以及动态计算优势&#xff0c;在图像质量提升和细节恢复方面有难以替代的作用。 &#x1f9c0;因此短时间内&#xff0c;就有不…...