首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >golang中实现通用http参数与结构体的转换

golang中实现通用http参数与结构体的转换

原创
作者头像
衡阵
修改于 2017-06-19 11:10:37
修改于 2017-06-19 11:10:37
11.6K0
举报
文章被收录于专栏:衡阵的专栏衡阵的专栏

作者介绍:衡阵,2011年加入腾讯,先后经历webqq,qq互联,手Q后台等相关的工作,目前负责NOW直播的后台开发工作。热爱后台开发,喜欢研究新的技术。对Java/C++/Golang等都非常感兴趣。

最近基于golang 实现一个通用的http的协议代理,把来自http的请求转换成内部的通信协议。内部协议是基于pb的,所以关键就是实现pb和http请求中的参数的转换。

研究protoc生成的go源码发现,生成的go的结构体中已经自带的json的tag,可以很方便的在json和pb之间互转。

于是想到,可以以一个请求参数来传json来实现。

代码语言:txt
AI代码解释
复制
var data={name:"hello"}
var url="http://test.xx.com/cgi-bin/request?data="+urlencode(data)
http.get(url)

这样在服务端先拿到data的数据,直接用json库就可以转成相关的结构体。

这样实现虽然简单,但并不直观。对用户来说,更习惯用。

var url="http://test.xx.com/cgi-bin/request?name=hello"

这种形式来请求。由于其他语言习惯把请求参数存在一个map中,于是想golang是不是也可以这样处理。于是问题变成一个mapstringstring和json的转换的故事。搜下网上发现有人提出类似的问题,还是playgroud上的一个练习。代码如下:

代码语言:txt
AI代码解释
复制
func SetField(obj interface{}, name string, value interface{}) error {
    structValue := reflect.ValueOf(obj).Elem()
    structFieldValue := structValue.FieldByName(name)

    if !structFieldValue.IsValid() {
        return fmt.Errorf("No such field: %s in obj", name)
    }

    if !structFieldValue.CanSet() {
        return fmt.Errorf("Cannot set %s field value", name)
    }

    structFieldType := structFieldValue.Type()
    val := reflect.ValueOf(value)
    if structFieldType != val.Type() {
        return errors.New("Provided value type didn't match obj field type")
    }

    structFieldValue.Set(val)
    return nil
}

type MyStruct struct {
    Name string
    Age  int64
}

func (s *MyStruct) FillStruct(m map[string]interface{}) error {
    for k, v := range m {
        err := SetField(s, k, v)
        if err != nil {
            return err
        }
    }
    return nil
}

func main() {
    myData := make(map[string]interface{})
    myData["Name"] = "Tony"
    myData["Age"] = int64(23)

    result := &MyStruct{}
    err := result.FillStruct(myData)
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println(result)
}

这个代码实现了简单的map到struct的转换,但要求类型强一致。所以需要实现弱类型的转换。后来发现,github上已经有一个开源的实现。

https://github.com/mitchellh/mapstructure 通过很简单的代码就可以实现我们想要的功能

代码语言:txt
AI代码解释
复制
  paras := make(map[string]interface{})
	setfunc := func(k []byte, v []byte) {
		paras[string(k)] = string(v)
	}
	if ctx.IsPost() {
		ctx.PostArgs().VisitAll(setfunc)
	} else if ctx.IsGet() {
		ctx.QueryArgs().VisitAll(setfunc)
	}
	ex := mapstructure.WeakDecode(paras, pb)

到了这里,大部分场景都可以实现了。但有些请求是有消息嵌套的,虽然mapstructure是支持嵌套转换的,但我们的请求参数只是一层的mapstringstring。

这种情况mapstructure无能为力了。看下mapstructure的源码,逻辑比较简单,既然你不支持,就改到你支持。我们定义如果有结构体嵌套,二级参数要是一个json字符串。在处理结构提的地方,如果发现传入的是个字符串,就尝试用json去处理一下,然后再走后面的逻辑。

在slice的地方也同样处理

这样处理完了之后试了一下,果然处理嵌套的结构体了。但是在实际使用的时候发现,有人竟然在pb中定义普通的字符串为bytes,这样在生成的go代码中就是[]byte类型。这种情况很不巧也会走到decodeSlice的逻辑,而我们并没有考虑兼容。事实上mapstructure这个框架就没有考虑这种情形。按json定义的标准,[]byte类型要以base64编码,我们也遵循这种规范。于是加上这个代码:

至此,这个转换基本完美。但是发现一使用,发现还是有坑存在,对应proto文件中定义的带下划线的字段,生成的struct成员代码是驼峰型的。标准库中的json可以通过反射拿到tag中的原始名称正常的输出。但我们用mapstructure默认是以字段名来解析的。

本来以为要自己处理一下,在修改一下mapstructure的源码,然而阅读代码的时候发现,mapstructure支持指定要处理的tag。并且tag处理逻辑是兼容pb生成的json tag的。我们只要在解析时指定一下tag即可。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
golang如何使用原生RPC及微服务简述
1、对于公司间的系统调用, 如果性能要求在100ms以上的服务,基于XML的SOAP协议 是一个值得考虑的方案。
花落花相惜
2021/11/21
9490
玩转golang——JSON高性能自动字段名
golang最近在中国非常火爆,尤其是后端服务开发场景。原生并发支持、优秀的性能、统一的风格,极大提升了开发效率。笔者用golang独立开发过不少小中型系统,写了几万行代码,确实很爽。
大福加冰
2019/08/04
3.5K0
玩转golang——JSON高性能自动字段名
Go 处理yaml类型的配置文件
先说一下,这里用到了很多关于反射类型的功能,可能刚开始看代码,如果对反射不熟悉的可能会不是非常清晰,但是同时也是为了更好的理解golang中的反射,同时如果后面想在代码中可以直接从我的git地址get: go get github.com/pythonsite/config_yaml 直接上代码: // 可以用于处理读yaml格式的配置文件,同时也可以用于理解golang中的反射 package config_yaml import ( "strings" "errors" "io
coders
2018/05/28
3.7K0
golang源码分析:encoding/json(2)
在golang源码分析:encoding/json(1)分析完序列化方法后,我们来分析下Unmarshal函数,它的源码位于encoding/json/decode.go,同样,我先看下函数的注释:
golangLeetcode
2023/09/06
3180
golang源码分析:encoding/json(2)
浅谈Go语言中的结构体struct & 接口Interface & 反射
结构体struct struct 用来自定义复杂数据结构,可以包含多个字段(属性),可以嵌套;
李海彬
2018/07/26
1.1K0
浅谈Go语言中的结构体struct & 接口Interface & 反射
手把手教你用 reflect 包解析 Go 的结构体 - Step 1: 参数类型检查
Go 原生的 encoding/json 的 Unmarshal 和 Marshal 函数的入参为 interface{},并且能够支持任意的 struct 或 map 类型。这种函数模式,具体是如何实现的呢?本文便大略探究一下这种实现模式的基础:reflect 包。
amc
2021/06/28
1.7K0
手把手教你用 reflect 包解析 Go 的结构体 - Step 1: 参数类型检查
Golang包——net
请求http://localhost:8080/hello会打印hello world
羊羽shine
2019/05/29
4950
Go 语言怎么处理三方接口返回数据?
在使用 Go 语言调用三方RESTful接口时,因为无法直接操作 json 字符串,所以我们需要先将 json 字符串转换为 map 或 struct。
frank.
2022/10/28
1.2K0
Golang高效实践之interface、reflection、json实践
反射是程序校验自己数据结构和类型的一种机制。文章尝试解释Golang的反射机制工作原理,每种编程语言的反射模型都是不同的,有很多语言甚至都不支持反射。
用户2937493
2019/08/29
1K0
[Golang]从0到1写一个web服务(上)
学生时代曾和几个朋友做了一个笔记本小应用,当时我的角色是pm + dba,最近心血来潮,想把这个玩意自己实现一遍,顺便写一篇文章记录整个过程。
薯条的编程修养
2022/08/10
7700
[Golang]从0到1写一个web服务(上)
grafana后台源码分析(一)
Grafana是监控领域比较出名的开源可视化套件,笔者最近在阅读grafana后台源码,里面有很多值得我们学习借鉴的地方,这里通过文章记录下来。
哈利哥
2022/11/22
2.3K0
grafana后台源码分析(一)
Go结构体&接口&反射
Golang中通过type关键词定义一个结构体,需要注意的是,数组和结构体都是值类型
用户9645905
2023/10/28
5840
Go结构体&接口&反射
Golang —— 反射
bill := &User{"bill", "bill@163.com"}
舒琪
2019/04/13
7260
golang的一个err不判断引起的血案(json.Marshal的error到底要不要判断?)
很多同学都认为如果我知道json.marshal的值,我就不用判断它执行之后返回错误,包过工作5,6年的经验的一些高工也是这么认为的。然而到底要不要判断呢?我这里先不给结论,我们先来看下我们业务中出现的问题。
公众号-利志分享
2022/04/25
8100
golang的一个err不判断引起的血案(json.Marshal的error到底要不要判断?)
[Go基础]Json在Go中的使用
本文主要根据Go语言Json包[1]、官方提供的Json and Go[2]和go-and-json[3]整理的。
TOMOCAT
2020/06/09
9.8K0
Golang框架实战-KisFlow流式计算框架(12)-基于反射自适应注册FaaS形参类型
接下来我们来增强KisFlow中Function对业务数据处理的聚焦,将之前Function的写法:
刘丹冰Aceld
2024/07/23
1570
gin中validator模块的源码分析
众所周知,在api层需要使用gin.Context中的ShouldBindJSON方法来对request中的json字段进行校验,例子如下:
编程黑洞
2023/03/06
4590
Traefik Plugins 全面解析
Traefik v2.3 及以上版本允许开发人员使用 Plugins 插件向 Traefik 添加新功能或定义新行为。例如,可以修改请求或标头、重定向、添加身份验证等,提供与 Traefik 中间件类似的功能。
gopher云原生
2021/10/18
2.1K0
golang实现动态调用不同struct中不同的方法
在我们的业务中,尤其涉及到后台业务,在我们不用考虑性能的情况下,我们写后台框架的时候,可能会遇到这样的一些情况,如何通过某些struct名和方法名传递进来执行不同的逻辑。这个时候我想的是go的反射是最好的实现这种功能,当然在go里面也可以通过定义配置来实现进入动态进入不同的struct名和方法名,或者其他方式(如果你有更好的方式,可以互相交流)。
公众号-利志分享
2022/04/25
2K0
Go 每日一库之 mapstructure
mapstructure用于将通用的map[string]interface{}解码到对应的 Go 结构体中,或者执行相反的操作。很多时候,解析来自多种源头的数据流时,我们一般事先并不知道他们对应的具体类型。只有读取到一些字段之后才能做出判断。这时,我们可以先使用标准的encoding/json库将数据解码为map[string]interface{}类型,然后根据标识字段利用mapstructure库转为相应的 Go 结构体以便使用。
用户7731323
2020/09/08
2.9K0
相关推荐
golang如何使用原生RPC及微服务简述
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档