Giter VIP home page Giter VIP logo

go-gin-example's Introduction

Go Gin Example rcard GoDoc License

An example of gin contains many useful features

简体中文

Installation

$ go get github.com/EDDYCJY/go-gin-example

How to run

Required

  • Mysql
  • Redis

Ready

Create a blog database and import SQL

Conf

You should modify conf/app.ini

[database]
Type = mysql
User = root
Password =
Host = 127.0.0.1:3306
Name = blog
TablePrefix = blog_

[redis]
Host = 127.0.0.1:6379
Password =
MaxIdle = 30
MaxActive = 30
IdleTimeout = 200
...

Run

$ cd $GOPATH/src/go-gin-example

$ go run main.go 

Project information and existing API

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:	export GIN_MODE=release
 - using code:	gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /auth                     --> github.com/EDDYCJY/go-gin-example/routers/api.GetAuth (3 handlers)
[GIN-debug] GET    /swagger/*any             --> github.com/EDDYCJY/go-gin-example/vendor/github.com/swaggo/gin-swagger.WrapHandler.func1 (3 handlers)
[GIN-debug] GET    /api/v1/tags              --> github.com/EDDYCJY/go-gin-example/routers/api/v1.GetTags (4 handlers)
[GIN-debug] POST   /api/v1/tags              --> github.com/EDDYCJY/go-gin-example/routers/api/v1.AddTag (4 handlers)
[GIN-debug] PUT    /api/v1/tags/:id          --> github.com/EDDYCJY/go-gin-example/routers/api/v1.EditTag (4 handlers)
[GIN-debug] DELETE /api/v1/tags/:id          --> github.com/EDDYCJY/go-gin-example/routers/api/v1.DeleteTag (4 handlers)
[GIN-debug] GET    /api/v1/articles          --> github.com/EDDYCJY/go-gin-example/routers/api/v1.GetArticles (4 handlers)
[GIN-debug] GET    /api/v1/articles/:id      --> github.com/EDDYCJY/go-gin-example/routers/api/v1.GetArticle (4 handlers)
[GIN-debug] POST   /api/v1/articles          --> github.com/EDDYCJY/go-gin-example/routers/api/v1.AddArticle (4 handlers)
[GIN-debug] PUT    /api/v1/articles/:id      --> github.com/EDDYCJY/go-gin-example/routers/api/v1.EditArticle (4 handlers)
[GIN-debug] DELETE /api/v1/articles/:id      --> github.com/EDDYCJY/go-gin-example/routers/api/v1.DeleteArticle (4 handlers)

Listening port is 8000
Actual pid is 4393

Swagger doc

image

Features

  • RESTful API
  • Gorm
  • Swagger
  • logging
  • Jwt-go
  • Gin
  • Graceful restart or stop (fvbock/endless)
  • App configurable
  • Cron
  • Redis

go-gin-example's People

Contributors

canghai908 avatar donng avatar eddycjy avatar git-zjx avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

go-gin-example's Issues

大佬们好,我新手问个小疑惑,别笑我。。

因为我是搞前端的,对后端的了解仅限于MVC,所以看到这个项目就开始找controller,没找到,发现业务逻辑好像是写在了routers/api目录下,这样做是基于什么考虑呢?是go框架的惯例吗。。。我内心有点不习惯,求解脱。。

JwtSecret无法加载

配置文件go-gin-example/conf/app.ini中,设定的“JwtSecret = 233”不起作用;
可能问题出在go-gin-example/pkg/util/jwt.go中,设定全局变量JwtSecret这一句。
var jwtSecret = []byte(setting.AppSetting.JwtSecret)

应该是因为golang中是先导入包,再执行main.go中的init()
原代码执行的顺序为:

  1. jwtSecret作为util的全局变量导入main.go中,将setting.AppSetting.JwtSecret赋值给jwtSecret,此时setting.AppSetting.JwtSecret为空;

  2. 再执行main.go中的init(),给setting.AppSetting.JwtSecret赋值为233,jwtSecret一直都没有赋值成功。

不知道是否是这样,望赐教。

有个小小的疑惑,还往大佬们赐教

首先感谢这个项目的作者,给了我入门学习go语言十分有效的帮助。
我的困惑就是关于“封装度”的问题,和我接触到的前端项目比起来呢,这个项目是倾向于把各种功能封装到函数,然后再调用的。
举一处例子,比如在这里
判断文件是否存在,用到了CheckMergedImage函数,但是这个函数其实就是把判断文件是否存在做了封装,那为何不把它封装的内容直接写到if语句后面呢,至少这样代码量看起来少了,而且阅读起来也不难。
这种处处皆函数的**是一种设计模式,还是最佳实践,还是为了统一项目风格,或是作者的个人风格?
因为我个人刚接触这个,怕走弯路,所以想把代码之外的许多东西整明白。

/routers/router.go第26行报错

麻烦问下第26行
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
报错:
cannot use sse.Event literal (type sse.Event) as type render.Render in argument to c.Render: sse.Event does not implement render.Render (missing WriteContentType method)
是怎么回事呢?

win下的 endless 中的 signal 信号没有 syscall.SIGUSR1 和 syscall.SIGUSR2

vendor\github.com\fvbock\endless\endless.go:64:3: undefined: syscall.SIGUSR1
vendor\github.com\fvbock\endless\endless.go:65:3: undefined: syscall.SIGUSR2
vendor\github.com\fvbock\endless\endless.go:68:3: undefined: syscall.SIGTSTP
vendor\github.com\fvbock\endless\endless.go:111:5: undefined: syscall.SIGUSR1
vendor\github.com\fvbock\endless\endless.go:112:5: undefined: syscall.SIGUSR2
vendor\github.com\fvbock\endless\endless.go:115:5: undefined: syscall.SIGTSTP
vendor\github.com\fvbock\endless\endless.go:119:5: undefined: syscall.SIGUSR1
vendor\github.com\fvbock\endless\endless.go:120:5: undefined: syscall.SIGUSR2
vendor\github.com\fvbock\endless\endless.go:123:5: undefined: syscall.SIGTSTP

请教下关于redis 的一个问题

func Set(key string, data interface{}, time int) (bool, error) {
conn := RedisConn.Get()
defer conn.Close()

value, err := json.Marshal(data)
if err != nil {
	return false, err
}

reply, err := redis.Bool(conn.Do("SET", key, value))
conn.Do("EXPIRE", key, time)

return reply, err

}

您好,在这个方法中,conn.Do("SET", key, value) 这个返回一个ok,err ,ok一个字符串“ok”,那这里用redis.Bool()方法不是很合适吧。还是我这边测试的问题

关于middleware中jwt的一个小问题

claims, err := util.ParseToken(token)
if err != nil {
code = e.ERROR_AUTH_CHECK_TOKEN_FAIL
} else if time.Now().Unix() > claims.ExpiresAt {
code = e.ERROR_AUTH_CHECK_TOKEN_TIMEOUT
}

这块逻辑好像有点问题。如果token过期的话,err就不为空,里面也会有过期错误

if !tokenClaims.Valid {
	if ve, ok := err.(*jwt.ValidationError); ok {
		if ve.Errors&jwt.ValidationErrorMalformed != 0 {
			code = e.ERROR_AUTH_TOKEN
		} else if ve.Errors&(jwt.ValidationErrorExpired|jwt.ValidationErrorNotValidYet) != 0 {
			code = e.ERROR_AUTH_TOKEN_TIMEOUT
		}
	}
}

我现在是这样处理的

timeout的设置问题

image
image
dear 老哥:
为什么我超时时间 设置15, 请求数据的时候就返回不了数据呢? 这不是15吗?

JWT middleware 包含密码的安全问题

JWT的payload中不应该填入密码,因为这段数据只是使用了base64编码了下。鉴权时,也不需要检查密码,因为这个token一定是该系统发出的,JWT 是包含签名的

从你的segmentfault文章中拿到以下token:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InRlc3QiLCJwYXNzd29yZCI6InRlc3QxMjM0NTYiLCJleHAiOjE1MTg3MjQ2OTMsImlzcyI6Imdpbi1ibG9nIn0.KSBY6TeavV_30kfmP7HWLRYKP5TPEDgHtABe9HCsic4

python: 由于该base64编码数据长度模4为3,则后面填充一个'='

base64.b64decode("eyJ1c2VybmFtZSI6InRlc3QiLCJwYXNzd29yZCI6InRlc3QxMjM0NTYiLCJleHAiOjE1MTg3MjQ2OTMsImlzcyI6Imdpbi1ibG9nIn0=")

获取到

{"username":"test","password":"test123456","exp":1518724693,"iss":"gin-blog"}

您好,为什么我的sql语句会复用呢?models.go 配置文件一样的

第一次执行

 UPDATE `topic` SET `modified_at` = '1543033067', `modified_by` = '1', `name` = '主题名称', `state` = '1'  WHERE `topic`.`deleted_at` IS NULL AND ((id = '1' ))

第五次执行

UPDATE `topic` SET `modified_at` = '1543033071', `modified_by` = '1', `name` = '主题名称', `state` = '1'  WHERE `topic`.`deleted_at` IS NULL AND ((id = '1' ) AND (id = '1' ) AND (id = '1' ) AND (id = '1' ) AND (id = '1' ))

附带项目地址。 麻烦大佬了。谢谢。
gin-blog

invalid memory address or nil pointer dereference

我在构建models文件里的tag文件运行时报了
```invalid memory address or nil pointer dereference``
这个错误,可是我是按照做着代码编写的,请问是什么原因呢?gorm包我也引入了,使用的gomod的包管理工具

怎样实现 热更新呢

每次 改完gin的代码 都要自己重新go run 下,不太方便.
大佬知道怎样可以实现热更新吗?

replacing callback `gorm:update_time_stamp`

[info] replacing callback gorm:update_time_stamp from /data/go/src/github.com/EDDYCJY/go-gin-example/models/models.go:40
[info] replacing callback gorm:update_time_stamp from /data/go/src/github.com/EDDYCJY/go-gin-example/models/models.go:41
[info] replacing callback gorm:delete from /data/go/src/github.com/EDDYCJY/go-gin-example/models/models.go:42

这个是什么原因?

go run main.go

vendor\github.com\fvbock\endless\endless.go:64:3: undefined: syscall.SIGUSR1
vendor\github.com\fvbock\endless\endless.go:65:3: undefined: syscall.SIGUSR2
vendor\github.com\fvbock\endless\endless.go:68:3: undefined: syscall.SIGTSTP
vendor\github.com\fvbock\endless\endless.go:111:5: undefined: syscall.SIGUSR1
vendor\github.com\fvbock\endless\endless.go:112:5: undefined: syscall.SIGUSR2
vendor\github.com\fvbock\endless\endless.go:115:5: undefined: syscall.SIGTSTP
vendor\github.com\fvbock\endless\endless.go:119:5: undefined: syscall.SIGUSR1
vendor\github.com\fvbock\endless\endless.go:120:5: undefined: syscall.SIGUSR2
vendor\github.com\fvbock\endless\endless.go:123:5: undefined: syscall.SIGTSTP
vendor\github.com\fvbock\endless\endless.go:224:3: undefined: syscall.Kill
vendor\github.com\fvbock\endless\endless.go:224:3: too many errors

安装出错

package golang.org/x/image/math/fixed: unrecognized import path "golang.org/x/image/math/fixed" (https fetch: Get https://golang.org/x/image/math/fixed?go-get=1: dial tcp 216.239.3
7.1:443: connectex: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host
has failed to respond.)
package golang.org/x/image/font: unrecognized import path "golang.org/x/image/font" (https fetch: Get https://golang.org/x/image/font?go-get=1: dial tcp 216.239.37.1:443: connectex
: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to res
pond.)

redis 缓存与数据库更新的过期机制

看到比如 package article_service 里边的, 查了 Article 单个或列表都会去设置 redis 缓存, 类似:

gredis.Set(key, article, 3600)

gredis.Set(key, articles, 3600)

但是, 没有看到哪里有过期, 是只能靠这个 3600s 吗? 如果数据库更新了某个 Article 的话, 取到的是不是就是旧的数据而不是数据库最新的数据?

根据token获取用户信息

有些接口需要获取用户信息,不知道这个根据jwt怎么实现,有人提议,在加密生辰token之前查出用户信息将id缓存,不知道该如何实现

一个package下有两个main

你的仓库中 根目录下 main.go 和 cron.go 中都有main入口,导致 go get时 报错

github.com/EDDYCJY/go-gin-example

go/src/github.com/EDDYCJY/go-gin-example/main.go:21:6: main redeclared in this block
previous declaration at go/src/github.com/EDDYCJY/go-gin-example/cron.go:12:6
go/src/github.com/EDDYCJY/go-gin-example/main.go:28:23: main.func1 redeclared in this block
previous declaration at go/src/github.com/EDDYCJY/go-gin-example/cron.go:16:27

希望能有一些前端的实现就好了

这个框架及教程很棒,但我对后端和前端如何进行交互不太清楚,比如后端怎么从前端取数据及提供数据给前端,如能增加一些前端的实例页面就好了

热更新之优雅的停止程序同时如何启动新的程序监听新请求?

您好:
我使用Shutdown方法来实现热更新。当我发出信号之后即:SIGINT(ctrl+C)之后,可以正常等待正在请求的接口陆续请求完成。但是我该如何同时启动新的应用程序,来接收一些新的请求呢?

问题:正在运行的二进制文件无法覆盖,fock新进程有出现所说的守护进程问题。我该如何开启一个覆盖老的端口和二进制文件来监听新的接口请求呢?目前只完成了优雅得到停止,但是没有启动起来。从其他目录运行文件./main会提示端口被占用,导致新应用程序无法启动。

希望有时间可以透露下您的解决方案。看文章之前无接触过热更新,百度也无找到答案。谢谢您。

ok, _ := valid.Valid(&a)

routers里面验证用户名密码,这样验证会存在问题的,加入我用户名密码为空呢

预加载Scan指定struct类不执行预加载表.对于预加载和Scan结合有没有好的办法呢?

又来麻烦了,尴尬学艺不精.
开始按照 gorm:Issues1965实现了接近的效果.
半月前也提了一个Issues但似乎无人知晓解答.
看下源码心想在 Scan之前执行,Find将值全部查到. s.value Set 就会有tag表的数据,可还是不可以.
image
如果有时间麻烦帮忙救救强迫症.(去掉strcut中无用的字段)

	var topics []*MResult
	var topics2 []*Topic
	err := db.Select("id,name").Preload("Tags", func(db *gorm.DB) *gorm.DB {
		return db.Select("id,name,tag.topic_id")
	}).Where(maps).Find(&topics2).Scan(&topics).Error

我目前实现的是:
(查询结果:结构有些臃肿,想要的效果是只显示select中的内容(红箭头指向的))
gorm 查询语句

        // err := db.Select("id,name").Preload("Tags", func(db *gorm.DB) *gorm.DB {
	// 	return db.Select("id,name,tag.topic_id")
	// }).Where(maps).Find(&topics).Error

结果

在网上查找是用Scan 解决.
但是不执行查询tag表语句了. 关联效果好像因为新的struct失效了.

gorm 查询语句

	err := db.Select("id,name").Model(&Topic{}).Select("id,name").Preload("Tags", func(db *gorm.DB) *gorm.DB {
		return db.Select("id,name,tag.topic_id")
	}).Where(maps).Scan(&topics).Error

查询结果
image

重现步骤

  1. 重新pull下,将数据库数据更新下.sql文件

  2. 涉及到的方法入口.
    routers/api/v1/topic.go -> service/topic_service/topic.go -> models/topic.go->GetTopics()

  3. 测试
    切换 models/topic.go->GetTopics() 返回值 为 []*Topic

切换 service/topic_service/topic.go->GetAll() 返回值为 []*models.Topic

运行打开 http://localhost:8000/swagger/index.html
执行 获取主题列表
或者 利用 curl
curl -X GET "http://localhost:8000/api/v1/topics" -H "accept: application/json"

Thx.

几点疑惑及建议

几点建议:
1.Token获取应该使用post方式,Token验证应加在HTTP Header头部
2.Router的swagger注解有点问题,添加等POST接口应该使用body模式,直接添加
3.用户密码应该使用md5加密(看已经函数写了没用)

疑惑:
1.业务错误代码调用有问题,已提交pr #46

go-gin-example/models/tag.go 18行发现一个问题

大佬下面的 err := db.Select("id").Where("name = ? AND deleted_on = ? ", name, 0).First(&tag).Error
应该是db.Select("name")吧?
func ExistTagByName(name string) (bool, error) {
var tag Tag
err := db.Select("id").Where("name = ? AND deleted_on = ? ", name, 0).First(&tag).Error
if err != nil && err != gorm.ErrRecordNotFound {
return false, err
}

if tag.ID > 0 {
	return true, nil
}

return false, nil

}

为啥有2份struct

model和service里都有一份struct 有点困惑 可以解释下出于什么考量吗

Swagger 更新

新版的swagger 不再支持param类型 而是改为了path类型,在编写api中的文档时把param 更新为 path

关于go项目结构的问题

在response.go中第15行代码 "code": httpCode, 此处应该"code": errCode更合理吧!

另外还有个疑问:
为什么不把service中的方法直接封装到model中为结构方法,v1/api目录相当于controller, 这样在v1/api中直接调用model的方法不是更好吗?
我看到你在service里面从新定义了结构,和对应model类似,作用仅仅只是用来接收v1/api传递下来的参数, 那为什么不直接用model呢,如果service里面定义的struct意图是用于逻辑数据模型,完全可以创建一个vo目录,里面放逻辑层的model,比如UserVo.go, 因为我在看service代码时发现,struct既用来接收controller的参数,也用于返回给用户的数据模型,这样感觉有一点杂乱,这里只是个人见解,希望和你交流一下。

对目录有一点疑惑

为什么API要和Router放在一起?Models里面为什么要放查询方法?设计目的是?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.