Go 语言反射和范型在 API 服务中的应用

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: Go reflect为何需要使用 reflect 获取:减少重复代码1. API 接口中抽取参数的逻辑大量重复  API 接口自然是要获取传过来的数据,不同接口要获取的数据自然也不一样,如果不做特殊处理,必然是每个接口都有一堆功能重复的从 request 里获取参数的代码。
img_4a211a244946d57d596491438917a358.jpe
Go reflect
  1. 为何需要使用 reflect 获取:减少重复代码

1. API 接口中抽取参数的逻辑大量重复

  API 接口自然是要获取传过来的数据,不同接口要获取的数据自然也不一样,如果不做特殊处理,必然是每个接口都有一堆功能重复的从 request 里获取参数的代码。

2. API 框架提供的抽取参数的方式并不满足需求

  当然 API 框架会提供这些功能,不过有些情况不能满足需求,比如gin-gonic,提供了将将 request 转为对应结构体的函数,但存在两个问题,第一个问题是参数区分大小写,我觉得应该实现大小写的通配,这样健壮性更高;第二是结构体直接对应数据库表结构,部分数据是不应该从接口请求中读取的,比如创建时间和删除标志,全转换的方式就很有问题。
  不过也有可能是因为我对 gin 不熟悉,不知道更好的用法,才自己造轮子,如果大家有更简洁漂亮的写法,请不吝赐教。

3. Golang 强类型语言的限制

  Go 语言是强类型语言,函数间传递参数或者返回值,必须有特定的类型,如果要实现这种范类型的处理相对 Python 等弱类型的语言要困难一些。
  Python 对于 struct 参数没有严格的限制,传什么内容都行,Golang 就没那么友好了,这部分要靠范型来处理。

# struct 是要获得的数据结果,params 是要抽取的参数名称数组,request 是接口的请求结构体。
def ExtractParamFromBody(struct, params, request):
    ...

  还有一点就是要能获取到 struct 结构体中每个参数的类型,并且给其赋值,Golang 提供的 reflect 机制可以很好的完成这项功能。

4. 实例

  以下代码先是建立了数据库连接(请注意,数据的连接需要提前建立好,并按照代码中的用户名、密码、地址、端口和数据库名称建立,不然代码无法运行成功);之后在数据库中建立了一个叫 User 的表;之后有一个创建用户的接口 "POST /users",对应的函数为 CreateUser。
  ExtractParamFromBody 是通用的参数抽取函数,不光是 User 类型,interface{} 是 Golang 中范型,可以对应任何结构体。

package main

import (
    "fmt"
    "reflect"
    "strconv"
    "strings"

    "github.com/gin-gonic/gin"
    "github.com/jinzhu/gorm"
)

type User struct {
    ID              uint   `json:"id" gorm:"PRIMARY_KEY;AUTO_INCREMENT"`
    Name            string `json:"name" gorm:"INDEX:name;UNIQUE;NOT NULL;type:varchar(100)"`
    Password        string `json:"password" gorm:"NOT NULL"`
    Mobile          string `json:"mobile"`
    Email           string `json:"email"`
    Role_id         uint   `json:"role_id"`
    Create_Time     uint   `json:"create_time"`
    Login_Time      uint   `json:"login_time"`
    Last_Login_Time uint   `json:"last_login_time`
    Login_Count     uint   `json:"login_count"`
    Deleted         bool   `json:"deleted" gorm:"DEFAULT:0"`
}

var db *gorm.DB
var err error

func ExtractParamFromBody(s interface{}, params []string, c *gin.Context) {
    var typ reflect.Type
    var val reflect.Value

    ptyp := reflect.TypeOf(s)

    if ptyp.Kind() == reflect.Ptr {
        val = reflect.ValueOf(s).Elem()
        typ = reflect.TypeOf(s).Elem()
    } else {
        val = reflect.ValueOf(s)
    }

    for _, param := range params {
        ret := c.PostForm(param)
        if ret != "" {
            for i := 0; i < typ.NumField(); i++ {
                if strings.ToLower(typ.Field(i).Name) == param {
                    if val.Field(i).Kind() == reflect.String && val.CanSet() {
                        val.Field(i).SetString(ret)
                    } else if val.Field(i).Kind() == reflect.Uint && val.CanSet() {
                        ret_int, _ := strconv.Atoi(ret)
                        val.Field(i).SetUint(uint64(ret_int))
                    }
                }
            }
        }
    }
}

func InitMysql() *gorm.DB {
    mysql_connection_string := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8&parseTime=True&loc=Local",
        "root",
        "mysql",
        "127.0.0.1",
        "3306",
        "test_db")
    db, err = gorm.Open("mysql", mysql_connection_string)

    if err != nil {
        log.Logger.Critical("Fail to connect MySQL: %s. Exit.", err)
        os.Exit(5)
    }

    db.AutoMigrate(&User{})
    return db
}

func CreateUser(c *gin.Context) {
    var user mysql.User

    params := []string{"name", "password", "email", "mobile", "role_id"}
    ExtractParamFromBody(&user, params, c)

    if err := db.Create(&user).Error; err != nil {
        c.JSON(200, gin.H{
            "code":   3,
            "result": "failed",
            "msg":    "Fail to create user",
        })
    } else {
        c.JSON(200, gin.H{
            "code":       0,
            "result":     "success",
            "msg":        "success",
            "resultBean": user,
        })
    }
}

func main() {
    InitMysql()

    r := gin.Default()
    r.POST("/v1/users", CreateUser)
    r.Run(":8080")
}
相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
2月前
|
存储 监控 算法
员工上网行为监控中的Go语言算法:布隆过滤器的应用
在信息化高速发展的时代,企业上网行为监管至关重要。布隆过滤器作为一种高效、节省空间的概率性数据结构,适用于大规模URL查询与匹配,是实现精准上网行为管理的理想选择。本文探讨了布隆过滤器的原理及其优缺点,并展示了如何使用Go语言实现该算法,以提升企业网络管理效率和安全性。尽管存在误报等局限性,但合理配置下,布隆过滤器为企业提供了经济有效的解决方案。
95 8
员工上网行为监控中的Go语言算法:布隆过滤器的应用
|
4天前
|
缓存 Java 应用服务中间件
java语言后台管理若依框架-登录提示404-接口异常-系统接口404异常如何处理-登录验证码不显示prod-api/captchaImage 404 (Not Found) 如何处理-解决方案优雅草卓伊凡
java语言后台管理若依框架-登录提示404-接口异常-系统接口404异常如何处理-登录验证码不显示prod-api/captchaImage 404 (Not Found) 如何处理-解决方案优雅草卓伊凡
27 5
|
3月前
|
JSON 关系型数据库 测试技术
使用Python和Flask构建RESTful API服务
使用Python和Flask构建RESTful API服务
144 2
|
3月前
|
缓存 监控 前端开发
在 Go 语言中实现 WebSocket 实时通信的应用,包括 WebSocket 的简介、Go 语言的优势、基本实现步骤、应用案例、注意事项及性能优化策略,旨在帮助开发者构建高效稳定的实时通信系统
本文深入探讨了在 Go 语言中实现 WebSocket 实时通信的应用,包括 WebSocket 的简介、Go 语言的优势、基本实现步骤、应用案例、注意事项及性能优化策略,旨在帮助开发者构建高效稳定的实时通信系统。
198 1
|
3月前
|
监控 Go API
Go语言在微服务架构中的应用实践
在微服务架构的浪潮中,Go语言以其简洁、高效和并发处理能力脱颖而出,成为构建微服务的理想选择。本文将探讨Go语言在微服务架构中的应用实践,包括Go语言的特性如何适应微服务架构的需求,以及在实际开发中如何利用Go语言的特性来提高服务的性能和可维护性。我们将通过一个具体的案例分析,展示Go语言在微服务开发中的优势,并讨论在实际应用中可能遇到的挑战和解决方案。
|
3月前
|
Go 数据处理 API
Go语言在微服务架构中的应用与优势
本文摘要采用问答形式,以期提供更直接的信息获取方式。 Q1: 为什么选择Go语言进行微服务开发? A1: Go语言的并发模型、简洁的语法和高效的编译速度使其成为微服务架构的理想选择。 Q2: Go语言在微服务架构中有哪些优势? A2: 主要优势包括高性能、高并发处理能力、简洁的代码和强大的标准库。 Q3: 文章将如何展示Go语言在微服务中的应用? A3: 通过对比其他语言和展示Go语言在实际项目中的应用案例,来说明其在微服务架构中的优势。
|
4月前
|
Cloud Native Go API
Go语言在微服务架构中的创新应用与实践
本文深入探讨了Go语言在构建高效、可扩展的微服务架构中的应用。Go语言以其轻量级协程(goroutine)和强大的并发处理能力,成为微服务开发的首选语言之一。通过实际案例分析,本文展示了如何利用Go语言的特性优化微服务的设计与实现,提高系统的响应速度和稳定性。文章还讨论了Go语言在微服务生态中的角色,以及面临的挑战和未来发展趋势。
|
3月前
|
SQL 监控 算法
为Go应用无侵入地添加任意代码
这篇文章旨在提供技术深度和实践指南,帮助开发者理解并应用这项创新技术来提高Golang应用的监控与服务治理能力。在接下来的部分,我们将通过一些实际案例,进一步展示如何在不同场景中应用这项技术,提供更多实践启示。
|
3月前
|
NoSQL 测试技术 Go
自动化测试在 Go 开源库中的应用与实践
本文介绍了 Go 语言的自动化测试及其在 `go mongox` 库中的实践。Go 语言通过 `testing` 库和 `go test` 命令提供了简洁高效的测试框架,支持单元测试、集成测试和基准测试。`go mongox` 库通过单元测试和集成测试确保与 MongoDB 交互的正确性和稳定性,使用 Docker Compose 快速搭建测试环境。文章还探讨了表驱动测试、覆盖率检查和 Mock 工具的使用,强调了自动化测试在开源库中的重要性。
104 0
|
4天前
|
API PHP 开发者
速卖通商品详情接口(速卖通API系列)
速卖通(AliExpress)是阿里巴巴旗下的跨境电商平台,提供丰富的商品数据。通过速卖通开放平台(AliExpress Open API),开发者可获取商品详情、订单管理等数据。主要功能包括商品搜索、商品详情、订单管理和数据报告。商品详情接口aliexpress.affiliate.productdetail.get用于获取商品标题、价格、图片等详细信息。开发者需注册账号并创建应用以获取App Key和App Secret,使用PHP等语言调用API。该接口支持多种请求参数和返回字段,方便集成到各类电商应用中。

热门文章

最新文章