Go 中 struct tag 如何用?基于它实现字段级别的访问控制

嗨,大家好!本文是系列文章 Go 小技巧第十篇,系列文章查看:Go 语言小技巧。

在 Go 中,结构体主要是用于定义复杂数据类型。而 struct tag 则是附加在 struct 字段后的字符串,提供了一种方式来存储关于字段的元信息。然后,tag 在程序运行时不会直接影响程序逻辑。

本文将会基于这个主题展开,讨论 Go 中的结构体 tag 究竟是什么?我们该如何利用它?另外,文末还提供了一个实际案例,帮助我们进一步提升对 struct tag 的理解。

struct tag 是什么?

如下是一个定义了 tag 的结构体 Person 类型。

type Person struct {Name string `json:"name"`Age  int    `json:"age"`
}

例子中,json:"name"json:"age" 就是结构体 tag。结构体 tag 的使用非常直观。你只需要在定义结构体字段后,通过反引号 `` 包裹起来的键值对形式就可定义它们。

具体有什么用呢?

这个 tag 究竟有什么用呢?为何要定义它们。

单从这个例子中来看,假设你是在 “encoding/json” 库中使用 Person 结构体,它是告诉 Go 在处理 JSON 序列化和反序列化时,字段名称的转化规则。

让我们通过它在 “encoding/json” 的使用说明它的效果吧。

p := Person{Name: "John", Age: 30}
jsonData, err := json.Marshal(p)
if err != nil {log.Println(err)
}
fmt.Println(string(jsonData)) 

输出:

{"name":"John","age":30}

可以看到输出的 JSON 的 key 是 name 和 age,而非 Name 和 Age。

与其他语言对比的话,虽然 Go 的 struct tag 在某种程度上类似于 Java 的注解或 C# 的属性,但 Go 的 tag 更加简洁,并且主要通过反射机制在运行时被访问。

这种设计反映了Go语言的哲学:简单、直接而有效。但确实也是功能有限!

在这里插入图片描述

常见使用场景

结构体 tag 在 Go 语言中常见用途,我平时最常见有如下这些。

JSON/XML 序列反序列化

如前面的介绍的案例中,通过 encoding/json 或者其他的库如 encoding/xml 库,tag 可以控制如何将结构体字段转换为 JSON 或 XML,或者如何从它们转换回来。

数据库操作

在ORM(对象关系映射)库中,tag 可以定义数据库表的列名、类型或其他特性。

如我们在使用 Gorm 时,会看到这样的定义:

type User struct {gorm.ModelName   string `gorm:"type:varchar(100);unique_index"`Age    int    `gorm:"index:age"`Active bool   `gorm:"default:true"`
}

结构体 tag 可用于定义数据库表的列名、类型或其他特性。

数据验证

在一些库中,tag 用于验证数据,例如,确保一个字段是有效的电子邮件地址。

如下是 govalidator 使用结构体上 tag 实现定义数据验证规则的一个案例。

type User struct {Email string `valid:"email"`Age   int    `valid:"range(18|99)"`
}

在这个例子中,valid tag 定义了字段的验证规则,如 email 字段值是否是有效的 emailage 字段是否满足数值在 18 到 99 之间等。

我们只要将类型为 User 类型的变量交给 govalidator,它可以根据这些规则来验证数据,确保数据的正确性和有效性。

示例如下:

valid, err := govalidator.ValidateStruct(User{Email: "test@example.com", Age: 20})

返回的 valid:truefalse,如果发生错误,err 提供具体的错误原因。

tag 行为自定义

前面展示的都是利用标准库或三方库提供的能力,如果想自定义 tag 该如何实现?毕竟有些情况下,如果默认提供的 tag 提供的能力不满足需求,我们还是希望可以自定义 tag 的行为。

这需要了解与理解 Go 的反射机制,它为数据处理和元信息管理提供了强大的灵活性。

如下的示例代码:

type Person struct {Name string `mytag:"MyName"`
}t := reflect.TypeOf(Person{})
field, _ := t.FieldByName("Name")
fmt.Println(field.Tag.Get("mytag")) // 输出: MyName

在这个例子中,我们的 Person 的字段 Name 有一个自定义的 tag - mytag,我们直接通过反射就可以访问它。

这只是简单的演示如何访问到 tag。如何使用它呢?

这就要基于实际的场景了,当然,这通常也离不开与反射配合。下面我们来通过一个实际的例子介绍。

案例:结构体字段访问控制

让我们考虑一个实际的场景:一个结构访问控制系统。

这个系统中,我们可以根据用户的角色(如 admin、user)或者请求的来源(admin、web)控制对结构体字段的访问。具体而言,假设我定义了一个包含敏感信息的结构体,我可以使用自定义 tag 来标记每个字段的访问权限。

是不是想到,这或许可用在 API 接口范围字段的控制上,防止泄露敏感数据给用户。

接下来,具体看看如何做吧?

定义结构体

我们首先定义一个UserProfile结构体,其中包含用户的各种信息。每个信息字段都有一个自定义的 access tag,用于标识字段访问权限(adminuser)。

type UserProfile struct {Username    string `access:"user"`  // 所有用户可见Email       string `access:"user"`  // 所有用户可见PhoneNumber string `access:"admin"` // 仅管理员可见Address     string `access:"admin"` // 仅管理员可见
}

其中,PhoneNumberAddress 是敏感字段,它只对 admin 角色可见。而 UserNameEmail 则是所有用户可见。

到此,结构体 UserProfile 定义完成。

实现权限控制

接下来就是要实现一个函数,实现根据 UserProfile 定义的 access tag 决定字段内容的可见性。

假设函数名称为 FilterFieldsByRole,它接受一个 UserProfile 类型变量和用户角色,返回内容一个过滤后的 map(由 fieldname 到 fieldvalue 组成的映射),其中只包含角色有权访问的字段。

func FilterFieldsByRole(profile UserProfile, role string) map[string]string {result := make(map[string]string)val := reflect.ValueOf(profile)typ := val.Type()for i := 0; i < val.NumField(); i++ {field := typ.Field(i)accessTag := field.Tag.Get("access")if accessTag == "user" || accessTag == role {// 获取字段名称fieldName := strings.ToLower(field.Name) // 获取字段值fieldValue := val.Field(i).String() // 组织返回结果 resultresult[fieldName] = fieldValue}}return result
}

权限控制的重点逻辑部分,就是 if accessTag == "user" || accessTag == role 这段判断条件。当满足条件之后,接下来要做的就是通过反射获取字段名称和值,并组织目标的 Map 类变量 result了。

使用演示

让我们来使用下 FilterFieldsByRole 函数,检查下是否满足按角色访问特定的用户信息的功能。

示例代码如下:

func main() {profile := UserProfile{Username:    "johndoe",Email:       "johndoe@example.com",PhoneNumber: "123-456-7890",Address:     "123 Elm St",}// 假设当前用户是普通用户userInfo := FilterFieldsByRole(profile, "user")fmt.Println(userInfo)// 假设当前用户是管理员adminInfo := FilterFieldsByRole(profile, "admin")fmt.Println(adminInfo)
}

输出:

map[username:johndoe email:johndoe@example.com]
map[username:johndoe email:johndoe@example.com phonenumber:123-456-7890 address:123 Elm St]

这个场景,通过自定义结构体 tag,给予指定角色,很轻松地就实现了一个基于角色的权限控制。

毫无疑问,这个代码更加清晰和可维护,而且具有极大灵活性、扩展性。如果想扩展更多角色,也是更加容易。

不过还是要说明下,如果在 API 层面使用这样的能力,还是要考虑反射可能带来的性能影响。

总结

这篇博文介绍了Go语言中结构体 tag 的基础知识,如是什么,如何使用。另外,还介绍了它们在不同场景下的应用。通过简单的例子和对比,我们看到了 Go 中结构体 tag 的作用。

文章的最后,通过一个实际案例,演示了如何使用 struct tag 使我们代码更加灵活强大。虽然 struct tag 的使用非常直观,但正确地利用这些 tag 可以极大提升我们程序的功能和效率。

博文地址:Go 中 struct tag 如何用?基于它实现字段级别的访问控制

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

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

相关文章

香蕉派BPI-M7 瑞芯微RK3588 人工智能开源硬件开发板公开发售

香蕉派(Banana Pi) BPI-M7瑞芯微K3588开源硬件单板计算机公开销售&#xff0c;支持WiFi 6和BT5.2&#xff0c;硬件有3个版本:8G Ram64G eMMC, 16G Ram128 eMMC和32G Ram128 eMMC 香蕉派BPI-M7采用睿芯最新旗舰RK3588八核64位处理器&#xff0c;最高频率为2.4GHz, 6 TOPS NPU&…

高通GAIA V3命令参考手册的研读学习(13):GAIA通知、示例以及制造商命令扩展

如前文《高通GAIA V3命令参考手册的研读学习&#xff08;四&#xff09;》所述&#xff0c;PDU一共有四种&#xff0c;前面已经讲了命令、回应以及错误码&#xff0c;现在来看最后一种&#xff1a;通知。 4. QTIL GAIA通知 通知发送的方向&#xff0c;是由设备发送到移动应用…

为客户解决痛点,电子纸增加制表功能

为客户解决痛点&#xff0c;电子纸增加制表功能 部分客户购买我们的电子纸后反馈效果很好&#xff0c;但是在配套组态软件制作电子纸模板时&#xff0c;遇到需要制作表格的时候比较麻烦。像是在画板作画一样&#xff0c;比较费时&#xff0c;而且效果不是很好&#xff0c;没办…

2024最新云渲染100使用方法,渲染100邀请码1a12,抢先体验免费渲染平台吧!

随着科技的进步&#xff0c;越来越多的设计师开始使用云渲染来提高工作效率&#xff0c;加快渲染进度&#xff0c;那么多的云渲染平台到底用哪家呢&#xff1f;今天&#xff0c;我就为大家介绍最具性价比的一家-渲染100&#xff0c;并说下它的使用方法。 一、性价比最高的云渲染…

结构体的学习

结构体与共用体&#xff0c;枚举 1.数据类型复习&#xff1a; 2结构体. eg&#xff1b;统计全校同学信息 需要记录的点--- 姓名&#xff0c;班级&#xff0c;性别&#xff0c;成绩&#xff0c;年龄 统计名字&#xff1a;char s[ ] [ 100 ] { "Tmo" } …

2024年美赛F题Problem F Reducing Illegal Wildlife Trade减少非法野生动物贸易的完整思路代码分享

非法的野生动物贸易会对我们的环境产生负面影响&#xff0c;并威胁到全球的生物多样性。据估计&#xff0c;它每年涉及高达265亿美元&#xff0c;被认为是全球第四大非法交易。[1]你将开发一个由数据驱动的5年项目&#xff0c;旨在显著减少非法野生动物贸易。你的目标是说服一个…

【数据结构 02】队列

一、原理 队列通常是链表结构&#xff0c;只允许在一端进行数据插入&#xff0c;在另一端进行数据删除。 队列的特性是链式存储&#xff08;随机增删&#xff09;和先进先出&#xff08;FIFO&#xff1a;First In First Out&#xff09;。 队列的缺陷&#xff1a; 不支持随机…

病历管理系统

技术架构&#xff1a; StrutsSpringHibernate 有需要该项目的小伙伴可以私信我你的Q。 功能描述&#xff1a; 企业财务管理系统主要用于电子病历来提高医院各项工作的效率和质量&#xff0c;促进医学科研、教学&#xff1b;减轻各类事务性工作的劳动强度&#xff0c;使他们…

HTML+CSS:3D轮播卡片

效果演示 实现了一个3D翻转的卡片动画&#xff0c;其中每个卡片都有不同的图片和不同的旋转角度。整个动画循环播放&#xff0c;无限次。整个页面的背景是一个占据整个屏幕的背景图片&#xff0c;并且页面内容被隐藏在背景图片之下。 Code <div class"container"…

小程序:类型三级分类

一、效果图片 二、代码 <template><view class"customPosition"><!-- header --><navBar :border"false" :hasBack"true" :title"titleName"></navBar><!-- 查询 --><view class"search…

springboot完成一个线上图片存放地址+实现前后端上传图片+回显

1.路径 注意路径 2.代码&#xff1a;&#xff08;那个imagePath没什么用&#xff0c;懒的删了&#xff09;&#xff0c;注意你的本地文件夹要有图片&#xff0c;才可以在线上地址中打开查看 package com.xxx.common.config;import org.springframework.beans.factory.annotat…

【Spark实践6】特征转换FeatureTransformers实践Scala版--补充算子

本节介绍了用于处理特征的算法&#xff0c;大致可以分为以下几组&#xff1a; 提取&#xff08;Extraction&#xff09;&#xff1a;从“原始”数据中提取特征。转换&#xff08;Transformation&#xff09;&#xff1a;缩放、转换或修改特征。选择&#xff08;Selection&…

go基础-垃圾回收+混合写屏障GC全分析

垃圾回收(Garbage Collection&#xff0c;简称GC)是编程语言中提供的自动的内存管理机制&#xff0c;自动释放不需要的对象&#xff0c;让出存储器资源&#xff0c;无需程序员手动执行。 Golang中的垃圾回收主要应用三色标记法&#xff0c;GC过程和其他用户goroutine可并发运行…

【每日一题】6.LeetCode——轮转数组

&#x1f4da;博客主页&#xff1a;爱敲代码的小杨. ✨专栏&#xff1a;《Java SE语法》|《数据结构与算法》 ❤️感谢大家点赞&#x1f44d;&#x1f3fb;收藏⭐评论✍&#x1f3fb;&#xff0c;您的三连就是我持续更新的动力❤️ &#x1f64f;小杨水平有限&#xff0c;欢…

go并发编程-runtime、Channel与Goroutine

1. runtime包 1.1.1. runtime.Gosched() 让出CPU时间片&#xff0c;重新等待安排任务(大概意思就是本来计划的好好的周末出去烧烤&#xff0c;但是你妈让你去相亲,两种情况第一就是你相亲速度非常快&#xff0c;见面就黄不耽误你继续烧烤&#xff0c;第二种情况就是你相亲速度…

unity WebGL发布游戏生成WebGL

1.unty Hub中安装WEBGL支持 2.项目平台的切换 color space需要根据项目选择 ColorSpace&#xff0c;是指玩家设置的颜色空间。 伽马颜色空间是历史悠久的标准格式&#xff0c;但线性颜色空间渲染可提供更精确的结果。 具体区别&#xff1a;ColorSpace 3.由于没有自己服务器…

软件工程(最简式总结)

目录 第一章:概述 1.软件危机的表现原因 2.常见的软件开发方法包括&#xff1a; 3.软件工程基本原则 4.软件工程三要素 5.设计模式的分类 6.针对变换型数据流设计步骤 7.针对事务型数据流设计步骤 第二章&#xff1a;软件过程 1.软件生命周期 2.软件过程模型 &…

ElementUI 组件:Container 布局容器

ElementUI安装与使用指南 Container 布局容器 点击下载learnelementuispringboot项目源码 效果图 el-container.vue&#xff08;Container 布局容器&#xff09;页面效果图 项目里el-container.vue代码 <script> import PagePath from "/components/PagePat…

【动态规划】【C++算法】1340. 跳跃游戏 V

作者推荐 【动态规划】【字符串】【表达式】2019. 解出数学表达式的学生分数 本文涉及知识点 动态规划汇总 LeetCode1340跳跃游戏 V 给你一个整数数组 arr 和一个整数 d 。每一步你可以从下标 i 跳到&#xff1a; i x &#xff0c;其中 i x < arr.length 且 0 < x…

git修改密码后mac使用sourceTree出现Authentication failed错误

1、退出sourceTree 2、在钥匙串中删除git对应站点Access Key 3、执行命令&#xff1a;git config --system --unset credential.helper 4、重新启动sourceTree&#xff0c;这时会弹出输入密码框&#xff0c;重新输入密码即可