一直用postman测试接口,用golang写接口性能测试的testcase,还从未自己动手写过gin框架,利用稍空闲的时间研究了gin的框架,参考之前测试过的研发童鞋写的gin框架项目代码,简单写了一套自己的框架,希望以后可以用在以后的测试中
背景:模拟项目的设备(device)的入库和查询
先看我的基本框架:
controller:是我的接口
dao:是db的操作
model:封装的数据模型
server:是我的组路由服务
middlewear:中间件
步骤:
1.models
package models
import(
"aiotdevice/dao"
)
type Resp struct{
Code int
Msg string
Data dao.DeviceInfo
}
2…数据库服务
package dao
import (
"github.com/jinzhu/gorm"
"fmt"
_ "github.com/jinzhu/gorm/dialects/mysql"
)
type DeviceInfo struct{
Mac string `gorm:column:mac`
Sn string `gorm:column:sn`
Model string `gorm:column:model`
Device_id string `gorm:column:device_id`
}
var dataBase = "root:123456@unix(/var/run/mysqld/mysqld.sock)/"
func openDb() *gorm.DB {
db, err := gorm.Open("mysql", dataBase+"?charset=utf8&parseTime=True&loc=Local")
//defer db.Close()
if err != nil {
fmt.Println(err)
panic("连接数据库失败")
}
db.SingularTable(true)
db.LogMode(false)
return db
}
func (D *DeviceInfo)InsertDB() {
DB :=openDb()
defer DB.Close()
DB.Exec("insert into concurrent_test.device set mac=?,sn=?,model=?,device_id=?",D.Mac,D.Sn,D.Model,D.Device_id )
if DB.Error != nil {
fmt.Println(DB.Error)
panic("insert db fail")
}
}
func (D *DeviceInfo)GetdevByDeviceid() DeviceInfo{
// 删除数据
db:=openDb()
var Devices DeviceInfo
sql := "select mac,sn,device_id,model from concurrent_test.device where device_id="+"\""+D.Device_id+"\""
fmt.Println(sql)
db.Raw(sql).Scan(&Devices)
db.Close()
fmt.Println(Devices)
return Devices
}
func (D *DeviceInfo)GetdevByMac() DeviceInfo{
// 删除数据
db:=openDb()
var Devices DeviceInfo
sql := "select mac,sn,device_id,model from concurrent_test.device where mac="+"\""+D.Mac+"\""
fmt.Println(sql)
db.Raw(sql).Scan(&Devices)
db.Close()
fmt.Println(Devices)
fmt.Println(sql)
return Devices
}
func (D *DeviceInfo)GetdevBySn() DeviceInfo{
// 删除数据
db:=openDb()
var Devices DeviceInfo
sql := "select mac,sn,device_id,model from concurrent_test.device where sn="+"\""+D.Sn+"\""
fmt.Println(sql)
db.Raw(sql).Scan(&Devices)
db.Close()
fmt.Println(Devices)
return Devices
}
3.接口
package controller
import (
"github.com/gin-gonic/gin"
// "net/http"
"aiotdevice/dao"
)
//get postform and url param
func Adddeviceapi(c *gin.Context){
mac := c.PostForm("mac")
sn := c.PostForm("sn")
model := c.PostForm("model")
device_id := c.PostForm("device_id")
if mac == "" && sn =="" && model =="" && device_id ==""{
c.JSON(300, gin.H{"code":30000,"msg":"need parameter"})
}else{
dev:=dao.DeviceInfo{
Mac : mac,
Sn:sn,
Model:model,
Device_id:device_id,
}
dev.InsertDB()
c.JSON(200, gin.H{"code":10000,"msg":"OK"})
}
}
注意看源码“ type H map[string]interface{}” H是一个map
package controller
import (
"github.com/gin-gonic/gin"
// "net/http"
"aiotdevice/dao"
"aiotdevice/models"
)
// type Resp struct{
// Code int
// Msg string
// Data dao.DeviceInfo
// }
func Getdeviceapi(c *gin.Context){
mac := c.Query("mac")
sn := c.Query("sn")
model := c.Query("model")
device_id := c.Query("device_id")
dev:=dao.DeviceInfo{
Mac : mac,
Sn:sn,
Model:model,
Device_id:device_id,
}
var temp dao.DeviceInfo
if dev.Mac == "" && dev.Sn =="" && dev.Device_id ==""{
c.JSON(300, gin.H{"code":30000,"msg":"need parameter"})
}else{
if dev.Mac !=""{
temp=dev.GetdevByMac()
resp := models.Resp{
Code:10000,
Msg:"ok",
Data:temp,
}
c.JSON(200, gin.H{"code":resp.Code,"msg":resp.Msg,"data":resp.Data})
}
if dev.Sn !=""{
temp=dev.GetdevBySn()
resp := models.Resp{
Code:10000,
Msg:"ok",
Data:temp,
}
c.JSON(200, gin.H{"code":resp.Code,"msg":resp.Msg,"data":resp.Data})
}
if dev.Device_id !=""{
temp=dev.GetdevByDeviceid()
resp := models.Resp{
Code:10000,
Msg:"ok",
Data:temp,
}
c.JSON(200, gin.H{"code":resp.Code,"msg":resp.Msg,"data":resp.Data})
}
}
}
注意接收参数的姿势:
form 过来的用PostForm()
query 过来的用Query()
url 的param用Param()
file 文件过来的用 FormFile()
4.组服务
package server
import (
"aiotdevice/controller"
mw "aiotdevice/middlewear"
"github.com/gin-gonic/gin"
)
func DeviceServer() {
root := "aiot/v1"
// Engin
gin.SetMode(gin.ReleaseMode)
router := gin.Default() //new()*Engine
router.Use(mw.Cors())//中间件在组路由之前启动
_index := router.Group(root)
{
_index.GET("/device", controller.Getdeviceapi)
_index.POST("/device", controller.Adddeviceapi)
}
// // 指定地址和端口号
// apidoc.RouterInfos(router, root, "dev")
// // _ = router.Run(":9111")
// _ = router.Run()
go router.Run()
// check.Check()
}
5.中间件:
package middlewear
import (
"github.com/gin-gonic/gin"
"strings"
"fmt"
"net/http"
)
func Cors() gin.HandlerFunc {
return func(c *gin.Context) {
method := c.Request.Method //请求方法
origin := c.Request.Header.Get("Origin") //请求头部
var headerKeys []string // 声明请求头keys
for k, _ := range c.Request.Header {
headerKeys = append(headerKeys, k)
}
headerStr := strings.Join(headerKeys, ", ")
if headerStr != "" {
headerStr = fmt.Sprintf("access-control-allow-origin, access-control-allow-headers, %s", headerStr)
} else {
headerStr = "access-control-allow-origin, access-control-allow-headers"
}
if origin != "" {
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Origin", "*") // 这是允许访问所有域
c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE,UPDATE") //服务器支持的所有跨域请求的方法,为了避免浏览次请求的多次'预检'请求
// header的类型
c.Header("Access-Control-Allow-Headers", "Authorization, Content-Length, X-CSRF-Token, Token,session,X_Requested_With,Accept, Origin, Host, Connection, Accept-Encoding, Accept-Language,DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Pragma")
// 允许跨域设置 可以返回其他子段
c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers,Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma,FooBar") // 跨域关键设置 让浏览器可以解析
c.Header("Access-Control-Max-Age", "172800") // 缓存请求信息 单位为秒
c.Header("Access-Control-Allow-Credentials", "false") // 跨域请求是否需要带cookie信息 默认设置为true
c.Set("content-type", "application/json") // 设置返回格式是json
}
//放行所有OPTIONS方法
if method == "OPTIONS" {
c.JSON(http.StatusOK, "Options Request!")
}
// 处理请求
c.Next() // 处理请求
}
}
6.main函数
package main
import (
// "aiotdevice/dao"
"aiotdevice/server"
"time"
)
func main(){
server.DeviceServer()
for {
time.Sleep(3*time.Second)
}
}
测试:
1.get:
2.post:
实际项目中要远远复杂于上面的代码,接口的数据非空验证,参数检查,数据库验证;还要达到性能的要求;用户多的业务要支持线程安全、多并发;等等。都需要大量的代码,实际需要结合业务进行开发, 欢迎指正
参考:https://studygolang.com/articles/14503
https://blog.csdn.net/love666666shen/article/details/88901785