Go 语言反射和范型在 API 服务中的应用
- 为何需要使用 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")
}

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
-
上一篇
图片人脸检测——Dlib版(四)
上几篇给大家讲了OpenCV的图片人脸检测,而本文给大家带来的是比OpenCV更加精准的图片人脸检测Dlib库。 往期目录 视频人脸检测——Dlib版(六)OpenCV添加中文(五)图片人脸检测——Dlib版(四)视频人脸检测——OpenCV版(三)图片人脸检测——OpenCV版(二)OpenCV环境搭建(一)更多更新,欢迎访问我的github:https://github.com/vipstone/faceai dlib与OpenCV对比 识别精准度:Dlib >= OpenCV Dlib更多的人脸识别模型,可以检测脸部68甚至更多的特征点 效果展示 人脸的68个特征点 安装dlib 下载地址:https://pypi.org/simple/dlib/选择适合你的版本,本人配置: Window 10 + Python 3.6.4 我现在的版本是:dlib-19.8.1-cp36-cp36m-win_amd64.whl 使用命令安装: pip3 install D:\soft\py\dlib-19.8.1-cp36-cp36m-win_amd64.whl 显示结果: Proce...
-
下一篇
ThinkPHP搭建博客
博客源码:https://github.com/pleated/blog 源码地址:https://github.com/xialeistudio/thinkphp-inaction/tree/master/blog功能 管理员登陆,修改密码,退出登录 文章分类添加,编辑,删除。 文章添加,编辑,删除。 发表,管理评论 添加,删除,展示友情链接 创建数据表 CREATE TABLE blog_admin (`adminId` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(20) NOT NULL COMMENT '账号', `password` char(32) NOT NULL COMMENT '密码', `createdAt` int(10) NOT NULL COMMENT '添加时间',`loginAt` int(11) NOT NULL DEFAULT '0' COMMENT '最近登录时间',`loginIp`varchar(15) NOT NULL DEFAULT '' COMMENT '最近登录IP',PRIMA...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- MySQL8.0.19开启GTID主从同步CentOS8
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- MySQL数据库在高并发下的优化方案
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- CentOS7,8上快速安装Gitea,搭建Git服务器
- SpringBoot2全家桶,快速入门学习开发网站教程
- Dcoker安装(在线仓库),最新的服务器搭配容器使用