获取
go get github.com/gin-gonic/gin
你好
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
//gin.Default() 创建一个路由handle
r := gin.Default()
//添加/hello路由,method为get,r.GET第一个参数为相对路径,第二个参数为处理函数
r.GET("/hello", func(c *gin.Context) {
//使用c.JSON,第一个参数为状态码,第二个为gin.H
c.JSON(200, gin.H{
"message": "你好",
})
})
//启动,监听8000
r.Run(":8000")
}
使用GET、POST、PUT、PATCH、DELETE和OPTIONS
func main() {
// 取消控制台debug颜色
// gin.DisableConsoleColor()
// 创建一个默认的路由handle
router := gin.Default()
router.GET("/someGet", getting)
router.POST("/somePost", posting)
router.PUT("/somePut", putting)
router.DELETE("/someDelete", deleting)
router.PATCH("/somePatch", patching)
router.HEAD("/someHead", head)
router.OPTIONS("/someOptions", options)
// 默认监听端口为8000,router.Run(":3000"),修改为监听3000端口
router.Run()
}
func getting(c *gin.Context) {
}
func posting(c *gin.Context) {
}
func putting(c *gin.Context) {
}
func deleting(c *gin.Context) {
}
func patching(c *gin.Context) {
}
func head(c *gin.Context) {
}
func options(c *gin.Context) {
}
路径中的参数
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
//创建一个路由handle
r := gin.Default()
//匹配/user/john,但不匹配/user/或者/user
r.GET("/user/:name", func(c *gin.Context) {
name := c.Param("name")
c.String(200, "name = %s", name)
})
//如果没有路由匹配/user/john,将重定向到/user/john/
r.GET("/user/:name/*action", func(c *gin.Context) {
name := c.Param("name")
action := c.Param("action")
c.String(200, "name = %s, action = %s", name, action)
})
//启动,监听8000
r.Run(":8000")
}
请求中的参数
func main() {
//创建一个路由handle
r := gin.Default()
//DefaultQuery可以设置默认值,当参数不存在时使用默认值
r.GET("/user/", func(c *gin.Context) {
name := c.DefaultQuery("name", "john")
action := c.Query("action")
c.String(200, "name = %s, action = %s", name, action)
})
//启动,监听8000
r.Run(":8000")
}
Post form
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
func main() {
//创建一个路由handle
r := gin.Default()
//适用于Content-Type为form-data、x-www-form-urlencoded
r.POST("/form_post", func(c *gin.Context) {
message := c.PostForm("message")
nick := c.DefaultPostForm("nick", "anonymous")
fmt.Println(message, nick)
c.JSON(200, gin.H{
"status": "posted",
"message": message,
"nick": nick,
})
})
//启动,监听8000
r.Run(":8000")
}
请求 + Post form
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
//创建一个路由handle
r := gin.Default()
//query + post form
r.POST("/form_post", func(c *gin.Context) {
first := c.Query("first")
last := c.DefaultQuery("last", "last")
message := c.PostForm("message")
nick := c.DefaultPostForm("nick", "anonymous")
c.JSON(200, gin.H{
"status": "posted",
"message": message,
"nick": nick,
"first": first,
"last": last,
})
})
//启动,监听8000
r.Run(":8000")
}
文件上传
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"log"
"net/http"
)
func main() {
//创建一个路由handle
r := gin.Default()
//文件上传,打印文件名
r.POST("/upload", func(c *gin.Context) {
file, _ := c.FormFile("file")
log.Println(file.Filename)
c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename))
})
//启动,监听8000
r.Run(":8000")
}
curl请求
curl.exe 127.0.0.1:8000/upload -F "file=@./a.txt" -H "Content-Type: multipart/form-data"
多文件上传
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"log"
"net/http"
)
func main() {
//创建一个路由handle
r := gin.Default()
//多文件上传,打印文件数
r.POST("/upload", func(c *gin.Context) {
form, _ := c.MultipartForm()
files := form.File["uploads"]
for _, file := range files {
log.Println(file.Filename)
}
c.String(http.StatusOK, fmt.Sprintf("%d files uploaded!", len(files)))
})
//启动,监听8000
r.Run(":8000")
}
路由组
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
//创建一个路由handle
r := gin.Default()
//组1
g1 := r.Group("/g1")
{
g1.GET("/login", func(c *gin.Context) {
})
g1.POST("/form", func(c *gin.Context) {
})
}
//组2
g2 := r.Group("/g2")
{
g2.GET("/login", func(c *gin.Context) {
})
g2.POST("/form", func(c *gin.Context) {
})
}
//启动,监听8000
r.Run(":8000")
}
中间件
package main
import (
"github.com/gin-gonic/gin"
)
//自定义中间件
func MyMiddle() gin.HandlerFunc {
return func(c *gin.Context) {
}
}
func AuthRequired() gin.HandlerFunc {
return func(c *gin.Context) {
}
}
func main() {
//关闭控制台颜色
gin.DisableConsoleColor()
//创建一个路由handle
r := gin.New()
//加载日志模块,记录gin.DefaultWriter,默认指向os.Stdout
r.Use(gin.Logger())
//错误后,不退出main进程,相当于对panic做了处理
r.Use(gin.Recovery())
//对路径添加中间件
r.GET("/mpath", MyMiddle(), func(c *gin.Context) {
})
//对组添加中间件
authorized := r.Group("/")
authorized.Use(AuthRequired())
{
authorized.POST("/login", func(c *gin.Context){
})
authorized.POST("/submit", func(c *gin.Context){
})
authorized.POST("/read", func(c *gin.Context){
})
// 嵌套组
testing := authorized.Group("/testing")
testing.GET("/analytics", func(c *gin.Context){
})
}
//启动,监听8000
r.Run(":8000")
}
将日志写入文件
package main
import (
"io"
"os"
//"fmt"
"github.com/gin-gonic/gin"
//"log"
//"net/http"
)
func main() {
//关闭控制台颜色
gin.DisableConsoleColor()
//创建日志文件
f, _ := os.Create("a.log")
//需在创建路由handle前
gin.DefaultWriter = io.MultiWriter(f)
//创建一个路由handle
r := gin.Default()
r.GET("/hello", func(c *gin.Context) {
c.String(200, "nihao")
})
//启动,监听8000
r.Run(":8000")
}
格式绑定
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
// 绑定JSON
type Login struct {
User string `form:"user" json:"user" binding:"required"`
Password string `form:"password" json:"password" binding:"required"`
}
func main() {
router := gin.Default()
// 绑定JSON ({"User": "manu", "Password": "123"})
router.POST("/loginJSON", func(c *gin.Context) {
var json Login
if err := c.ShouldBindJSON(&json); err == nil {
if json.User == "manu" && json.Password == "123" {
c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
} else {
c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
}
} else {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
}
})
// 绑定HTML表格(User=manu&Password=123)
router.POST("/loginForm", func(c *gin.Context) {
var form Login
if err := c.ShouldBind(&form); err == nil {
if form.User == "manu" && form.Password == "123" {
c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
} else {
c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
}
} else {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
}
})
//启动,监听8080
router.Run(":8080")
}
curl请求
curl -v http://localhost:8080/loginJSON -H 'content-type: application/json' -d '{"User": "manu", "Password": "123"}'
复选框绑定
package main
import (
"github.com/gin-gonic/gin"
)
type myForm struct {
Colors []string `form:"colors[]"`
}
func main() {
r := gin.Default()
r.LoadHTMLGlob("views/*")
r.GET("/", indexHandler)
r.POST("/", formHandler)
r.Run(":8000")
}
func indexHandler(c *gin.Context) {
c.HTML(200, "form.html", nil)
}
func formHandler(c *gin.Context) {
var fakeForm myForm
c.Bind(&fakeForm)
c.JSON(200, gin.H{"color": fakeForm.Colors})
}
<form action="/" method="POST">
<p>Check some colors</p>
<label for="red">Red</label>
<input type="checkbox" name="colors[]" value="red" id="red" />
<label for="green">Green</label>
<input type="checkbox" name="colors[]" value="green" id="green" />
<label for="blue">Blue</label>
<input type="checkbox" name="colors[]" value="blue" id="blue" />
<input type="submit" />
</form>
表格绑定
package main
import (
"github.com/gin-gonic/gin"
)
type LoginForm struct {
User string `form:"user" binding:"required"`
Password string `form:"password" binding:"required"`
}
func main() {
router := gin.Default()
router.POST("/login", func(c *gin.Context) {
// you can bind multipart form with explicit binding declaration:
// c.ShouldBindWith(&form, binding.Form)
// or you can simply use autobinding with ShouldBind method:
var form LoginForm
// in this case proper binding will be automatically selected
if c.ShouldBind(&form) == nil {
if form.User == "user" && form.Password == "password" {
c.JSON(200, gin.H{"status": "you are logged in"})
} else {
c.JSON(401, gin.H{"status": "unauthorized"})
}
}
})
router.Run(":8000")
}
curl -v --form user=user --form password=password http://localhost:8000/login
XML、JSON、YAML的渲染
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
// gin.H is a shortcut for map[string]interface{}
r.GET("/someJSON", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
})
r.GET("/moreJSON", func(c *gin.Context) {
// You also can use a struct
var msg struct {
Name string `json:"user"`
Message string
Number int
}
msg.Name = "Lena"
msg.Message = "hey"
msg.Number = 123
// Note that msg.Name becomes "user" in the JSON
// Will output : {"user": "Lena", "Message": "hey", "Number": 123}
c.JSON(http.StatusOK, msg)
})
r.GET("/someXML", func(c *gin.Context) {
c.XML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
})
r.GET("/someYAML", func(c *gin.Context) {
c.YAML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
})
// Listen and serve on 0.0.0.0:8000
r.Run(":8000")
}
SecureJSON
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
// You can also use your own secure json prefix
// r.SecureJsonPrefix(")]}',\n")
r.GET("/someJSON", func(c *gin.Context) {
names := []string{"lena", "austin", "foo"}
// Will output : while(1);["lena","austin","foo"]
c.SecureJSON(http.StatusOK, names)
})
// Listen and serve on 0.0.0.0:8080
r.Run(":8000")
}
静态文件
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
router.Static("/assets", "./assets")
router.StaticFS("/more_static", http.Dir("my_file_system"))
router.StaticFile("/favicon.ico", "./resources/favicon.ico")
// Listen and serve on 0.0.0.0:8080
router.Run(":8000")
}
HTML渲染
例1
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
router.LoadHTMLGlob("templates/*")
//router.LoadHTMLFiles("templates/template1.tmpl", "templates/template2.html")
router.GET("/index", func(c *gin.Context) {
c.HTML(http.StatusOK, "index.html", gin.H{
"title": "Main website",
})
})
router.Run(":8000")
}
<html>
<h1>
{{ .title }}
</h1>
</html>
例2
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
router.LoadHTMLGlob("templates/**/*")
router.GET("/posts/index", func(c *gin.Context) {
c.HTML(http.StatusOK, "posts/index.tmpl", gin.H{
"title": "Posts",
})
})
router.GET("/users/index", func(c *gin.Context) {
c.HTML(http.StatusOK, "users/index.tmpl", gin.H{
"title": "Users",
})
})
router.Run(":8000")
}
{{ define "posts/index.tmpl" }}
<html><h1>
{{ .title }}
</h1>
<p>Using posts/index.tmpl</p>
</html>
{{ end }}
{{ define "users/index.tmpl" }}
<html><h1>
{{ .title }}
</h1>
<p>Using users/index.tmpl</p>
</html>
{{ end }}
自定义模板渲染
package main
import (
"html/template"
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
html := template.Must(template.ParseFiles("templates/index.tmpl"))
router.SetHTMLTemplate(html)
//在index.tmpl中定义为test后可在调用时直接使用test
router.GET("/test",func(c *gin.Context) {
c.HTML(200, "test", gin.H{
"title": "Users",
})
})
router.Run(":8000")
}
{{ define "test" }}
<html>
<h1>
{{ .title }}
</h1>
</html>
{{ end }}
自定义分隔符
r := gin.Default()
//templates中,使用{[{}]}代替原来的{[]}
r.Delims("{[{", "}]}")
r.LoadHTMLGlob("/path/to/templates"))
自定义模板函数
import (
"fmt"
"html/template"
"net/http"
"time"
"github.com/gin-gonic/gin"
)
func formatAsDate(t time.Time) string {
year, month, day := t.Date()
return fmt.Sprintf("%d%02d/%02d", year, month, day)
}
func main() {
router := gin.Default()
router.Delims("{[{", "}]}")
router.SetFuncMap(template.FuncMap{
"formatAsDate": formatAsDate,
})
router.LoadHTMLFiles("templates/raw.tmpl")
router.GET("/raw", func(c *gin.Context) {
c.HTML(http.StatusOK, "raw.tmpl", map[string]interface{}{
"now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC),
})
})
router.Run(":8000")
}
Date: {[{.now | formatAsDate}]}
多模板继承
https://github.com/gin-contrib/multitemplate
重定向
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
router.GET("/test", func(c *gin.Context) {
c.Redirect(http.StatusMovedPermanently, "http://www.google.com/")
})
router.Run(":8000")
}
自定义中间件
package main
import (
"log"
"time"
"github.com/gin-gonic/gin"
)
func Logger() gin.HandlerFunc {
return func(c *gin.Context) {
t := time.Now()
// Set example variable
c.Set("example", "12345")
// before request
c.Next()
// after request
latency := time.Since(t)
log.Print(latency)
// access the status we are sending
status := c.Writer.Status()
log.Println(status)
}
}
func main() {
r := gin.New()
r.Use(Logger())
r.GET("/test", func(c *gin.Context) {
example := c.MustGet("example").(string)
// it would print: "12345"
log.Println(example)
c.String(200, "abc")
})
// Listen and serve on 0.0.0.0:8080
r.Run(":8000")
}
基础认证中间件
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
// simulate some private data
var secrets = gin.H{
"foo": gin.H{"email": "foo@bar.com", "phone": "123433"},
"austin": gin.H{"email": "austin@example.com", "phone": "666"},
"lena": gin.H{"email": "lena@guapa.com", "phone": "523443"},
}
func main() {
r := gin.Default()
// Group using gin.BasicAuth() middleware
// gin.Accounts is a shortcut for map[string]string
authorized := r.Group("/admin", gin.BasicAuth(gin.Accounts{
"foo": "bar",
"austin": "1234",
"lena": "hello2",
"manu": "4321",
}))
// /admin/secrets endpoint
// hit "127.0.0.1:8000/admin/secrets
authorized.GET("/secrets", func(c *gin.Context) {
// get user, it was set by the BasicAuth middleware
user := c.MustGet(gin.AuthUserKey).(string)
if secret, ok := secrets[user]; ok {
c.JSON(http.StatusOK, gin.H{"user": user, "secret": secret})
} else {
c.JSON(http.StatusOK, gin.H{"user": user, "secret": "NO SECRET :("})
}
})
// Listen and serve on 0.0.0.0:8000
r.Run(":8000")
}
并发
func main() {
r := gin.Default()
r.GET("/long_async", func(c *gin.Context) {
// create copy to be used inside the goroutine
cCp := c.Copy()
go func() {
// simulate a long task with time.Sleep(). 5 seconds
time.Sleep(5 * time.Second)
// note that you are using the copied context "cCp", IMPORTANT
log.Println("Done! in path " + cCp.Request.URL.Path)
}()
})
r.GET("/long_sync", func(c *gin.Context) {
// simulate a long task with time.Sleep(). 5 seconds
time.Sleep(5 * time.Second)
// since we are NOT using a goroutine, we do not have to copy the context
log.Println("Done! in path " + c.Request.URL.Path)
})
// Listen and serve on 0.0.0.0:8080
r.Run(":8080")
}
自定义http配置
func main() {
router := gin.Default()
http.ListenAndServe(":8000", router)
}
func main() {
router := gin.Default()
s := &http.Server{
Addr: ":8000",
Handler: router,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20,
}
s.ListenAndServe()
}
支持加密
package main
import (
"log"
"github.com/gin-gonic/autotls"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
// Ping handler
r.GET("/ping", func(c *gin.Context) {
c.String(200, "pong")
})
log.Fatal(autotls.Run(r, "example1.com", "example2.com"))
}
package main
import (
"log"
"github.com/gin-gonic/autotls"
"github.com/gin-gonic/gin"
"golang.org/x/crypto/acme/autocert"
)
func main() {
r := gin.Default()
// Ping handler
r.GET("/ping", func(c *gin.Context) {
c.String(200, "pong")
})
m := autocert.Manager{
Prompt: autocert.AcceptTOS,
HostPolicy: autocert.HostWhitelist("example1.com", "example2.com"),
Cache: autocert.DirCache("/var/www/.cache"),
}
log.Fatal(autotls.RunWithManager(r, &m))
}
多服务端
package main
import (
"log"
"net/http"
"time"
"github.com/gin-gonic/gin"
"golang.org/x/sync/errgroup"
)
var (
g errgroup.Group
)
func router01() http.Handler {
e := gin.New()
e.Use(gin.Recovery())
e.GET("/", func(c *gin.Context) {
c.JSON(
http.StatusOK,
gin.H{
"code": http.StatusOK,
"error": "Welcome server 01",
},
)
})
return e
}
func router02() http.Handler {
e := gin.New()
e.Use(gin.Recovery())
e.GET("/", func(c *gin.Context) {
c.JSON(
http.StatusOK,
gin.H{
"code": http.StatusOK,
"error": "Welcome server 02",
},
)
})
return e
}
func main() {
server01 := &http.Server{
Addr: ":8080",
Handler: router01(),
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
}
server02 := &http.Server{
Addr: ":8081",
Handler: router02(),
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
}
g.Go(func() error {
return server01.ListenAndServe()
})
g.Go(func() error {
return server02.ListenAndServe()
})
if err := g.Wait(); err != nil {
log.Fatal(err)
}
}
优雅的重启和关闭
https://github.com/fvbock/endless
测试
net/http/httptest
package main
func setupRouter() *gin.Engine {
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
c.String(200, "pong")
})
return r
}
func main() {
r := setupRouter()
r.Run(":8080")
}
代码测试
package main
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
)
func TestPingRoute(t *testing.T) {
router := setupRouter()
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/ping", nil)
router.ServeHTTP(w, req)
assert.Equal(t, 200, w.Code)
assert.Equal(t, "pong", w.Body.String())
}
浙公网安备 33010602011771号