内容简介:以上摘自Gin简介,可以说Gin是众多Go Web框架中非常好用的微框架,简洁、易用、强大。当然,框架之间的对比没有太大的意义,仁者见仁智者见智。Gin具体使用方法参考由于Gin提供的只是骨架,并不像gin-learning
前言
Gin is a HTTP web framework written in Go (Golang). It features a Martini-like API with much better performance -- up to 40 times faster. If you need smashing performance, get yourself some Gin.
以上摘自Gin简介,可以说Gin是众多Go Web框架中非常好用的微框架,简洁、易用、强大。当然,框架之间的对比没有太大的意义,仁者见仁智者见智。Gin具体使用方法参考 https://github.com/gin-gonic/gin ,文档还是蛮详细的。
目录结构
由于Gin提供的只是骨架,并不像 Beego 一样 bee new quickstart
可以生成应用的目录结构,不过我们可以参考其方式组织目录结构,如下:
gin-learning
|-- conf
| -- app.ini
| -- database.ini
|-- controllers
|-- models
|-- routers
| -- router.go
|-- static
| -- css
| -- js
|-- templates
| -- layout
| -- directory
|-- main.go
conf
保存配置文件, controllers models templates
对应MVC, routers
为路由目录, static
保存静态文件, main.go
为入口文件。
Content
- 解析ini格式的配置文件 : https://github.com/go-ini/ini
- ORM : https://github.com/jinzhu/gorm
- REDIS https://github.com/gomodule/redigo
- MYSQL DRIVER https://github.com/go-sql-driver/mysql
配置文件 app.ini
;develop or testing or product app_mode = develop http_port = :8080
配置文件database.ini
[develop] redis.host = 10.64.144.3 redis.port = 6380 redis.password = redis.max_idle_conns = 5 redis.max_open_conns = 10 mysql.host = 127.0.0.1 mysql.port = 3306 mysql.username = kimi mysql.password = 123456 mysql.dbname = gin mysql.max_idle_conns = 5 mysql.max_open_conns = 10 [testing]...
入口文件
package main import ( "fmt" "gin-learning/routers" "github.com/gin-gonic/gin" "github.com/go-ini/ini" "os" ) func main() { // 加载配置 cfg, err := ini.Load("conf/app.ini") if err != nil { fmt.Printf("Fail to read file: %v", err) os.Exit(1) } // 运行模式 mode := cfg.Section("").Key("app_mode").String() if mode == "develop" { gin.SetMode(gin.DebugMode) } else { gin.SetMode(gin.ReleaseMode) } // 注册路由 r := routers.Register() // 加载模板文件 r.LoadHTMLGlob("templates/**/*") // 加载静态文件 r.Static("/static", "static") http_port := cfg.Section("").Key("http_port").String() r.Run(http_port) }
- 使用
r.LoadHTMLGlob("templates/**/*")
或r.LoadHTMLGlob("templates/*")
加载模板文件,区别是前者加载templates
下子目录中的模板文件,后者加载templates
目录中的模板文件。 -
r.Static("/static", "static")
。开启一个静态服务器加载static
目录中的静态文件,否则无法访问localhost:8080/css/xx.css。
注册路由
package routers import ( "gin-learning/controllers" "github.com/gin-gonic/gin" ) func Register() *gin.Engine { r := gin.New() r.Use(gin.Recovery()) articles := new(controllers.Articles) v1 := r.Group("/") { v1.GET("/articles", articles.Index) v1.GET("/article/create", articles.Create) v1.GET("/article/edit/:id", articles.Edit) v1.GET("/article/del/:id", articles.Del) v1.POST("/article/store", articles.Store) } return r }
路由中的articles controller
package controllers import ( "gin-learning/models" "github.com/gin-gonic/gin" "net/http" "strconv" ) type Articles struct { } func (_ *Articles) Index(ctx *gin.Context) { articleModel := new(models.Articles) list := articleModel.List() ctx.HTML(http.StatusOK, "articles/index.html", gin.H{ "list": list, }) } func (_ *Articles) Create(ctx *gin.Context) { ctx.HTML(http.StatusOK, "articles/create-edit.html", nil) } func (_ *Articles) Edit(ctx *gin.Context) { id, err := strconv.Atoi(ctx.Param("id")) if err != nil { ctx.Redirect(http.StatusFound, "/articles") return } articleModel := new(models.Articles) article := articleModel.First(id) ctx.HTML(http.StatusOK, "articles/create-edit.html", gin.H{ "article": article, }) } func (_ *Articles) Store(ctx *gin.Context) { id, _ := strconv.Atoi(ctx.PostForm("id")) title := ctx.PostForm("title") author := ctx.PostForm("author") content := ctx.PostForm("content") articleModel := new(models.Articles) if id == 0 { articleModel.Insert(title, author, content) } else { articleModel.Edit(id, title, author, content) } ctx.Redirect(http.StatusFound, "/articles") } func (_ *Articles) Del(ctx *gin.Context) { id, err := strconv.Atoi(ctx.Param("id")) if err != nil { ctx.Redirect(http.StatusFound, "/articles") return } articleModel := new(models.Articles) articleModel.Del(id) ctx.Redirect(http.StatusFound, "/articles") }
为了方便将orm redis封装放在models包中
mysql连接池:
package models import ( "fmt" "github.com/go-ini/ini" "github.com/jinzhu/gorm" _ "github.com/jinzhu/gorm/dialects/mysql" "os" "time" ) var orm *gorm.DB func init() { var err error var cfg *ini.File var maxIdleConns int var maxOpenConns int // load配置 cfg, err = ini.Load("conf/database.ini", "conf/app.ini") if err != nil { fmt.Printf("%v", err) os.Exit(1) } // 运行模式 mode := cfg.Section("").Key("app_mode").String() // 主机 host := cfg.Section(mode).Key("mysql.host").String() // 端口 port := cfg.Section(mode).Key("mysql.port").String() // 用户名 username := cfg.Section(mode).Key("mysql.username").String() // 密码 password := cfg.Section(mode).Key("mysql.password").String() // 数据库名称 dbname := cfg.Section(mode).Key("mysql.dbname").String() // 最大空闲连接数 maxIdleConns, err = cfg.Section(mode).Key("mysql.max_idle_conns").Int() if err != nil { fmt.Printf("%v", err) os.Exit(1) } // 最大打开的连接数 maxOpenConns, err = cfg.Section(mode).Key("mysql.max_open_conns").Int() if err != nil { fmt.Printf("%v", err) os.Exit(1) } dsn := username + ":" + password + "@tcp(" + host + ":" + port + ")/" + dbname + "?charset=utf8&parseTime=true&loc=Local" orm, err = gorm.Open("mysql", dsn) if err != nil { fmt.Printf("Fail to open mysql: %v", err) os.Exit(1) } orm.DB().SetMaxIdleConns(maxIdleConns) orm.DB().SetMaxOpenConns(maxOpenConns) orm.DB().SetConnMaxLifetime(time.Hour) } func GetGorm() *gorm.DB { return orm }
redis连接池:
package models import ( "fmt" "github.com/go-ini/ini" "github.com/gomodule/redigo/redis" "os" "time" ) var redisPool *redis.Pool func init() { var err error var cfg *ini.File var maxIdleConns int var maxOpenConns int // load配置 cfg, err = ini.Load("conf/database.ini", "conf/app.ini") if err != nil { fmt.Printf("%v", err) os.Exit(1) } // 运行模式 mode := cfg.Section("").Key("app_mode").String() // 主机 host := cfg.Section(mode).Key("redis.host").String() // 端口 port := cfg.Section(mode).Key("redis.port").String() // 密码 password := cfg.Section(mode).Key("redis.password").String() // 最大空闲连接数 maxIdleConns, err = cfg.Section(mode).Key("redis.max_idle_conns").Int() if err != nil { fmt.Printf("%v", err) os.Exit(1) } // 最大打开的连接数 maxOpenConns, err = cfg.Section(mode).Key("redis.max_open_conns").Int() if err != nil { fmt.Printf("%v", err) os.Exit(1) } redisPool = &redis.Pool{ MaxIdle: maxIdleConns, MaxActive: maxOpenConns, IdleTimeout: 240 * time.Second, Dial: func() (redis.Conn, error) { c, err := redis.Dial("tcp", host+":"+port) if err != nil { fmt.Printf("%v", err) os.Exit(1) } if password != "" { if _, err := c.Do("AUTH", password); err != nil { c.Close() fmt.Printf("%v", err) os.Exit(1) } } return c, nil }, } } func GetRedisPool() *redis.Pool { return redisPool }
models,并没有使用GORM在应用启动时检测创建表,需提前创建表,表结构: Articles
GORM 用法 。
package models import ( "time" ) type Articles struct { ID int Title string Author string Content string Click int // 避免时区问题,时间简单使用string // time.ParseInLocation("2006-01-02 15:04:05",time.Now().Format("2006-01-02 15:04:05"),time.Local) CreateTime string UpdateTime string } // 用id查询一条记录 func (article *Articles) First(id int) *Articles { orm.Where(&Articles{ID: id}).First(article) return article } // 获取文章列表 func (_ *Articles) List() []Articles { var articles []Articles orm.Select("id,title,author,content,click,create_time").Order("id desc").Find(&articles) return articles } // 返回数据插入成功后的ID func (_ *Articles) Insert(title, author, content string) int { createTime := time.Now().Format("2006-01-02 15:04:05") article := &Articles{Title: title, Author: author, Content: content, CreateTime: createTime} orm.Create(article) return article.ID } // 返回受影响行数 func (article *Articles) Edit(id int, title, author, content string) int64 { ret := article.First(id) // 查无结果 ret为空的Article if ret.ID == 0 { return 0 } updateTime := time.Now().Format("2006-01-02 15:04:05") rowsAffected := orm.Model(ret).Updates(map[string]interface{}{"title": title, "author": author, "content": content, "update_time": updateTime}).RowsAffected return rowsAffected } // 返回受影响行数 func (article *Articles) Del(id int) int64 { ret := article.First(id) if ret.ID == 0 { return 0 } rowsAffected := orm.Delete(ret).RowsAffected return rowsAffected }
GORM创建表的用法
if !db.HasTable(&Articles{}) { if err := db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8").CreateTable(&Articles{}).Error; err != nil { fmt.Println(err) os.Exit(1) } }
应用中免不了请求第三方接口,简单封装一些HTTP请求常用方法,参考(COPY)自 Beego httplib
package httplib import ( "bytes" "crypto/tls" "encoding/json" "encoding/xml" "io/ioutil" "net/http" "net/url" "strings" "time" ) type HttpRequest struct { header map[string]string req *http.Request } // 获取http client func httpClient() *http.Client { trans := &http.Transport{ // 不验证证书 TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, } client := &http.Client{ Timeout: 10 * time.Second, Transport: trans, } return client } func Get(url string) (*HttpRequest, error) { req, err := http.NewRequest("GET", url, nil) if err != nil { return nil, err } return &HttpRequest{ req: req, header: map[string]string{}, }, nil } func Post(url string) (*HttpRequest, error) { req, err := http.NewRequest("POST", url, nil) if err != nil { return nil, err } return &HttpRequest{ req: req, header: map[string]string{}, }, nil } // 向请求中添加header func (r *HttpRequest) Header(key, value string) *HttpRequest { r.header[key] = value return r } // string []byte写入请求body func (r *HttpRequest) Body(data interface{}) *HttpRequest { switch t := data.(type) { case string: bf := bytes.NewBufferString(t) r.req.Body = ioutil.NopCloser(bf) r.req.ContentLength = int64(len(t)) case []byte: bf := bytes.NewBuffer(t) r.req.Body = ioutil.NopCloser(bf) r.req.ContentLength = int64(len(t)) } return r } // form写入请求body func (r *HttpRequest) FormBody(values url.Values) (*HttpRequest, error) { if r.req.Body == nil && values != nil { r.req.Body = ioutil.NopCloser(strings.NewReader(values.Encode())) r.req.Header.Set("Content-Type", "application/x-www-form-urlencoded") } return r, nil } // json写入请求body func (r *HttpRequest) JsonBody(v interface{}) (*HttpRequest, error) { if r.req.Body == nil && v != nil { byts, err := json.Marshal(v) if err != nil { return r, err } r.req.Body = ioutil.NopCloser(bytes.NewReader(byts)) r.req.ContentLength = int64(len(byts)) r.req.Header.Set("Content-Type", "application/json") } return r, nil } // xml写入请求body func (r *HttpRequest) XmlBody(v interface{}) (*HttpRequest, error) { if r.req.Body == nil && v != nil { byts, err := xml.Marshal(v) if err != nil { return r, err } r.req.Body = ioutil.NopCloser(bytes.NewReader(byts)) r.req.ContentLength = int64(len(byts)) r.req.Header.Set("Content-Type", "application/xml") } return r, nil } // 获取响应对象 func (r *HttpRequest) Response() (*http.Response, error) { for k, v := range r.header { r.req.Header.Set(k, v) } client := httpClient() resp, err := client.Do(r.req) if err != nil { return nil, err } return resp, nil } // 获取响应体(string) func (r *HttpRequest) String() (string, error) { resp, err := r.Response() if err != nil { return "", err } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { return "", err } return string(body), nil } func (r *HttpRequest) ParseJson(v interface{}) error { body, err := r.String() if err != nil { return err } return json.Unmarshal([]byte(body), v) } func (r *HttpRequest) ParseXml(v interface{}) error { body, err := r.String() if err != nil { return err } return xml.Unmarshal([]byte(body), v) }
req, err := httplib.Post("https://www.so.com") if err != nil { return } resp, err := req.Header("User-Agent","Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36").String() if err != nil { return } fmt.Println(resp)
Usage
go run main.go
访问 localhost:8080/articles
list.jpg
edit.jpg
结语
源码地址: https://github.com/kimistar/gin-learning 。千里之行始于脚下,这仅仅是学习golang的开始,以此记录学习golang的经历与体会,希望日后回顾此文章时,对golang有深层次的理解,不仅仅局限于表面。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
爱上Arduino
Massimo Banzi / 于欣龙、郭浩赟 / 人民邮电出版社 / 2012-10 / 38.00元
《硬件开源电子设计平台:爱上Arduino(第2版)》全面透彻地介绍了arduino的相关内容,它会给你带来许多项目的点子,并帮助你顺利地实现从开始策划直到完成安装的全过程。由于《硬件开源电子设计平台:爱上Arduino(第2版)》是arduino项目合作创始人massimobanzi所著,其中一定融入了创始人对开源硬件的独到见解。《硬件开源电子设计平台:爱上Arduino(第2版)》内容完全考虑......一起来看看 《爱上Arduino》 这本书的介绍吧!