内容简介:以上摘自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有深层次的理解,不仅仅局限于表面。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Don't Make Me Think
Steve Krug / New Riders Press / 18 August, 2005 / $35.00
Five years and more than 100,000 copies after it was first published, it's hard to imagine anyone working in Web design who hasn't read Steve Krug's "instant classic" on Web usability, but people are ......一起来看看 《Don't Make Me Think》 这本书的介绍吧!