Golang http 标准库 http 标准库了 http 客户端和服务器的实现,注意了,客户端实现可以发出 http 请求,并解析响应。服务器可以实现 http server 功能。市面上的所有 golang web 框架都是基于 http 标准库实现的。
http 标准库客户端功能 发出 GET 请求 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package mainimport ( "fmt" "io/ioutil" "log" "net/http" "net/url" "time" ) func testGet () { url := "http://apis.juhe.cn/simpleWeather/query?key=087d7d10f700d20e27bb753cd806e40b&city=北京" r, err := http.Get(url) if err != nil { log.Fatal(err) } defer r.Body.Close() b, _ := ioutil.ReadAll(r.Body) fmt.Printf("b: %v\n" , string (b)) }
运行结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 { "reason" : "查询成功!" , "result" : { "city" : "北京" , "realtime" : { "temperature" : "3" , "humidity" : "94" , "info" : "阴" , "wid" : "02" , "direct" : "东北风" , "power" : "2级" , "aqi" : "117" }, "future" : [ { "date" : "2021-12-09" , "temperature" : "-1/7℃" , "weather" : "多云转晴" , "wid" : { "day" : "01" , "night" : "00" }, "direct" : "北风" }, { "date" : "2021-12-10" , "temperature" : "-1/8℃" , "weather" : "多云" , "wid" : { "day" : "01" , "night" : "01" }, "direct" : "北风转西南风" }, { "date" : "2021-12-11" , "temperature" : "-2/10℃" , "weather" : "多云转晴" , "wid" : { "day" : "01" , "night" : "00" }, "direct" : "北风" }, { "date" : "2021-12-12" , "temperature" : "-5/4℃" , "weather" : "晴" , "wid" : { "day" : "00" , "night" : "00" }, "direct" : "西北风转西南风" }, { "date" : "2021-12-13" , "temperature" : "-6/5℃" , "weather" : "晴" , "wid" : { "day" : "00" , "night" : "00" }, "direct" : "西南风" } ] }, "error_code" : 0 }
本实例我们使用到了:https://www.juhe.cn/box/index/id/73 天气查询 api
GET 请求,把一些参数做成变量而不是直接放到 url 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 func testGet2 () { params := url.Values{} Url, err := url.Parse("http://apis.juhe.cn/simpleWeather/query" ) if err != nil { return } params.Set("key" , "087d7d10f700d20e27bb753cd806e40b" ) params.Set("city" , "北京" ) Url.RawQuery = params.Encode() urlPath := Url.String() fmt.Println(urlPath) resp, err := http.Get(urlPath) if err != nil { log.Fatal(err) } defer resp.Body.Close() body, _ := ioutil.ReadAll(resp.Body) fmt.Println(string (body)) }
解析 JSON 类型的返回结果 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package mainimport ( "encoding/json" "fmt" "io/ioutil" "log" "net/http" "net/url" "time" ) func testParseJson () { type result struct { Args string `json:"args"` Headers map [string ]string `json:"headers"` Origin string `json:"origin"` Url string `json:"url"` } resp, err := http.Get("http://httpbin.org/get" ) if err != nil { return } defer resp.Body.Close() body, _ := ioutil.ReadAll(resp.Body) fmt.Println(string (body)) var res result _ = json.Unmarshal(body, &res) fmt.Printf("%#v" , res) }
运行结果
1 2 3 4 5 6 7 8 9 10 11 12 13 { "args" : {}, "headers" : { "Accept-Encoding" : "gzip" , "Host" : "httpbin.org" , "User-Agent" : "Go-http-client/1.1" , "X-Amzn-Trace-Id" : "Root=1-61b16029-731c99ba4591c9bd3db53edd" }, "origin" : "115.171.25.28" , "url" : "http://httpbin.org/get" } main.result{Args:"" , Headers:map [string ]string {"Accept-Encoding" :"gzip" , "Host" :"httpbin.org" , "User-Agent" :"Go-http-client/1.1" , "X-Amzn-Trace-Id" :"Root=1-61b16029-731c99ba4591c9bd3db53edd" }, Origin:"115.171.25.28" , Url:"http://httpbin.org/get" }
GET 请求添加请求头 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package mainimport ( "encoding/json" "fmt" "io/ioutil" "log" "net/http" "net/url" "time" ) func testAddHeader () { client := &http.Client{} req, _ := http.NewRequest("GET" , "http://httpbin.org/get" , nil ) req.Header.Add("name" , "老郭" ) req.Header.Add("age" , "80" ) resp, _ := client.Do(req) body, _ := ioutil.ReadAll(resp.Body) fmt.Printf(string (body)) }
运行结果
1 2 3 4 5 6 7 8 9 10 11 12 13 { "args" : {}, "headers" : { "Accept-Encoding" : "gzip" , "Age" : "3" , "Host" : "httpbin.org" , "Name" : "zhaofan" , "User-Agent" : "Go-http-client/1.1" , "X-Amzn-Trace-Id" : "Root=1-61b16107-5814e133649862c20ab1c26f" }, "origin" : "115.171.25.28" , "url" : "http://httpbin.org/get" }
发出 POST 请求 1 2 3 4 5 6 7 8 9 10 11 12 13 func testPost () { path := "http://apis.juhe.cn/simpleWeather/query" urlValues := url.Values{} urlValues.Add("key" , "087d7d10f700d20e27bb753cd806e40b" ) urlValues.Add("city" , "北京" ) r, err := http.PostForm(path, urlValues) if err != nil { log.Fatal(err) } defer r.Body.Close() b, _ := ioutil.ReadAll(r.Body) fmt.Printf("b: %v\n" , string (b)) }
另外一种方式
1 2 3 4 5 6 7 8 9 10 func testPost2 () { urlValues := url.Values{ "name" : {"老郭" }, "age" : {"80" }, } reqBody := urlValues.Encode() resp, _ := http.Post("http://httpbin.org/post" , "text/html" , strings.NewReader(reqBody)) body, _ := ioutil.ReadAll(resp.Body) fmt.Println(string (body)) }
发送 JSON 数据的 post 请求 1 2 3 4 5 6 7 8 9 func testPostJson () { data := make (map [string ]interface {}) data["site" ] = "www.duoke360.com" data["name" ] = "多课网" bytesData, _ := json.Marshal(data) resp, _ := http.Post("http://httpbin.org/post" , "application/json" , bytes.NewReader(bytesData)) body, _ := ioutil.ReadAll(resp.Body) fmt.Println(string (body)) }
使用 Client 自定义请求 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 func testClient () { client := http.Client{ Timeout: time.Second * 5 , } url := "http://apis.juhe.cn/simpleWeather/query?key=087d7d10f700d20e27bb753cd806e40b&city=北京" req, err := http.NewRequest(http.MethodGet, url, nil ) if err != nil { log.Fatal(err) } req.Header.Add("referer" , "http://apis.juhe.cn/" ) res, err2 := client.Do(req) if err2 != nil { log.Fatal(err2) } defer res.Body.Close() b, _ := ioutil.ReadAll(res.Body) fmt.Printf("b: %v\n" , string (b)) }
HTTP Server 使用 golang 实现一个 http server 非常简单,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 func testHttpServer () { f := func (resp http.ResponseWriter, req *http.Request) { io.WriteString(resp, "hello world" ) } http.HandleFunc("/hello" , f) err := http.ListenAndServe(":9999" , nil ) if err != nil { log.Fatal(err) } }
在浏览器输入:
1 http://localhost:9999/hello
运行结果:
使用 Handler 实现并发处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 type countHandler struct { mu sync.Mutex n int } func (h *countHandler) ServeHTTP (w http.ResponseWriter, r *http.Request) { h.mu.Lock() defer h.mu.Unlock() h.n++ fmt.Fprintf(w, "count is %d\n" , h.n) } func testHttpServer2 () { http.Handle("/count" , new (countHandler)) log.Fatal(http.ListenAndServe(":8080" , nil )) }
在浏览器输入:http://localhost:8080/count
,刷新查看结果
Golang 标准库 template templates 包定义了数据驱动的文本输出。生成 html 文件的模板在 html/template
包下面。模板使用插值语法 {{.var}}
格式,也可以使用一些流程控制,例如判断 if else
、循环 range
还可以使用一些函数,包括内建函数和自定义函数。
第一个模板实例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package mainimport ( "os" "text/template" ) func main () { name := "ghz" muban := "hello, {{.}}" tmpl, err := template.New("test" ).Parse(muban) if err != nil { panic (err) } err = tmpl.Execute(os.Stdout, name) if err != nil { panic (err) } }
运行结果
也可以是结构体 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package mainimport ( "os" "text/template" ) type Person struct { Name string Age int } func main () { ghz := Person{"ghz" , 80 } muban := "hello, {{.Name}}, Your age {{.Age}}" tmpl, err := template.New("test" ).Parse(muban) if err != nil { panic (err) } err = tmpl.Execute(os.Stdout, ghz) if err != nil { panic (err) } }
运行结果
html 模板 定义一个 HTML 文件
1 2 3 4 5 6 7 8 9 <!DOCTYPE html > <html lang ="en" > <head > <title > Test golang template</title > </head > <body > {{.}} </body > </html >
定义一个 HttpServer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package mainimport ( "html/template" "net/http" ) func tmpl (w http.ResponseWriter, r *http.Request) { t1, err := template.ParseFiles("test.html" ) if err != nil { panic (err) } t1.Execute(w, "hello world" ) } func main () { server := http.Server{ Addr: "127.0.0.1:8080" , } http.HandleFunc("/tmpl" , tmpl) server.ListenAndServe() }
客户端访问
1 http://localhost:8080/tmpl
运行结果
文本和空格 模板引擎在进行替换的时候,是完全按照文本格式进行替换的。除了需要评估和替换的地方,所有的行分隔符、空格等等空白都原样保留。所以,对于要解析的内容,不要随意缩进、随意换行 。
例如:
1 2 3 4 {{23 }} < {{45 }} -> 23 < 45 {{23 }} < {{- 45 }} -> 23 <45 {{23 -}} < {{45 }} -> 23 < 45 {{23 -}} < {{- 45 }} -> 23 <45
去掉后空格 xxxx -}}
,去掉前空格 {{- xxxx`
## 模板注释
注释方式:`{{/* a comment */}}
。
注释后的内容不会被引擎进行替换。但需要注意,注释行在替换的时候也会占用行,所以应该去除前缀和后缀空白,否则会多一空行。
管道 pipeline 管道就是一系列命令的链式调用。当然,也可以是一个命令,例如:计算表达式的值 {{.}}
、{{.Name}}
,或者是一个函数调用或者方法调用。
可以使用管道符号 |
链接多个命令,用法和 unix 下的管道类似:|
前面的命令将运算结果(或返回值)传递给后一个命令的最后一个位置。
需要注意的是,并非只有使用了 |
才是 pipeline。Go template 中,pipeline 的概念是传递数据,只要能产生数据的,都是 pipeline。
下面是 Pipeline 的几种示例,它们都输出 "output"
:
1 2 3 4 5 6 {{`"output"` }} {{printf "%q" "output" }} {{"output" | printf "%q" }} {{printf "%q" (print "out" "put" )}} {{"put" | printf "%s%s" "out" | printf "%q" }} {{"output" | printf "%s" | printf "%q" }}
可以在 HTML 中测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <!DOCTYPE html > <html lang ="en" > <head > <title > Test golang template</title > </head > <body > {{`"output"`}} <br /> {{printf "%q" "output"}}<br /> {{"output" | printf "%q"}}<br /> {{printf "%q" (print "out" "put")}}<br /> {{"put" | printf "%s%s" "out" | printf "%q"}}<br /> {{"output" | printf "%s" | printf "%q"}}<br /> </body > </html >
运行结果
1 2 3 4 5 6 "output" "output" "output" "output" "output" "output"
变量 变量的语法
1 2 3 4 $var := pipeline $var = pipeline
例如
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <!DOCTYPE html> <html lang="en" > <head> <title>Test golang template</title> </head> <body> {{$Name := "tom" }} {{$Name = "kite" }} {{$Name}}<br> {{$len := (len "hello,ghz" )}} {{$len }} </body> </html>
运行结果
条件判断 语法
1 2 3 4 {{if pipeline}} T1 {{end}} {{if pipeline}} T1 {{else}} T0 {{end}} {{if pipeline}} T1 {{else if pipeline}} T0 {{end}} {{if pipeline}} T1 {{else}}{{if pipeline}} T0 {{end}}{{end}}
pipeline 为 false 的情况是各种数据对象的 0 值:数值 0,指针或接口是 nil,数组、slice、map 或 string 则是 len 为 0。
可以使用如下运算符表达式
1 2 3 4 5 6 7 8 9 10 11 12 eq Returns the boolean truth of arg1 == arg2 ne Returns the boolean truth of arg1 != arg2 lt Returns the boolean truth of arg1 < arg2 le Returns the boolean truth of arg1 <= arg2 gt Returns the boolean truth of arg1 > arg2 ge Returns the boolean truth of arg1 >= arg2
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 <!DOCTYPE html > <html lang ="en" > <head > <title > Test golang template</title > </head > <body > {{$Age := 18}} {{if (ge $Age 18)}} <h3 > 你已经成年!</h3 > {{else}} <h3 > 你还未成年!</h3 > {{end}} </body > </html >
运行结果
循环迭代 语法
1 2 {{range pipeline}} T1 {{end}} {{range pipeline}} T1 {{else }} T0 {{end}}
range 可以迭代 slice、数组、map 或 channel。迭代的时候,会设置”.”为当前正在迭代的元素。对于第一个表达式,当迭代对象的值为 0 值时,则 range 直接跳过,就像 if 一样。对于第二个表达式,则在迭代到 0 值时执行 else 语句。
实例演示
go 代码
1 2 3 4 5 6 7 8 func tmpl (w http.ResponseWriter, r *http.Request) { t1, err := template.ParseFiles("test.html" ) if err != nil { panic (err) } s := []string {"多课网" , "golang 教程" , "老郭" } t1.Execute(w, s) }
html
1 2 3 4 5 6 7 8 9 <!DOCTYPE html > <html lang ="en" > <head > <title > Test golang template</title > </head > <body > {{range $x := . -}} {{println $x}} {{- end}} </body > </html >
运行结果
with…end with 用来设置 "."
的值,语法如下:
1 2 {{with pipeline}} T1 {{end}} {{with pipeline}} T1 {{else}} T0 {{end}}
对于第一种格式,当 pipeline 不为 0 值的时候,点”.”设置为 pipeline 运算的值,否则跳过。对于第二种格式,当 pipeline 为 0 值时,执行 else 语句块,否则”.”设置为 pipeline 运算的值,并执行 T1。
实例演示
1 {{with "多课网-golang-老郭"}}{{println .}}{{end}}
运行结果
内置函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 and 返回第 一个空参数或最后一个参数返回其参数的布尔值 AND ,即 “and x y”表现为“if x then y else x”。 评估所有参数。 call 返回调用第一个参数的结果,该参数 必须是一个函数,其余参数作为参数。 因此,“call .XY 1 2 ”在 Go 表示法中是 dot.XY(1 , 2 ),其中 Y 是函数值字段、映射条目等。 第一个参数必须是 产生函数类型值的评估结果(不同于 预定义的函数,如打印)。该函数必须 返回一个或两个结果值,其中第二个 是类型错误。如果参数与函数不匹配 或返回的错误值为非零,则执行停止。 html 返回等效 于其参数文本表示的转义 HTML 。此功能 在 html/template 中不可用,但有一些例外。 index 返回通过 以下参数对其第一个参数进行索引的结果。因此,在 Go 语法中,“索引 x 1 2 3 ”是 x[1 ][2 ][3 ]。每个索引项必须是映射、切片或数组。 slice slice 返回其第一个参数被 其余参数切片的结果。因此,"slice x 1 2" 在 Go 语法中是 x[1 :2 ], 而 "slice x" 是 x[:],"slice x 1" 是 x[1 :], 是 x[1 :2 :3 ]。第一个参数必须是字符串、切片或数组。 js 返回转义的 JavaScript 等效 于其参数的文本表示。 len 返回其参数的整数长度。 not 返回其单个参数的布尔否定。 or 通过返回第 一个非空参数或最后一个参数来返回其参数的布尔 OR ,即 “or x y”表现为“if x then x else y”。 评估所有参数。 print fmt.Sprint 的别名 printf fmt.Sprintf 的别名 println fmt.Sprint的别名urlquery 以适合嵌入 URL 查询的形式 返回其参数的文本表示的转义值。 此功能在 html/template 中不可用,但有一些 例外。
嵌套 template:define 和 template define 可以直接在待解析内容中定义一个模板,这个模板会加入到 common 结构组中,并关联到关联名称上。
1 2 3 {{template "name"}} {{template "name" pipeline}} {{define "name"}}
实例演示
假设我们有一个 header.html、footer.html 和 index.html,index.html 包含 header.html 和 footer.html
header.html
1 2 3 4 5 {{define "header"}} <head> <title>{{.Title}}</title> </head> {{end}}
footer.html
1 2 3 4 {{define "footer"}} 这是footer {{end}}
index.html
1 2 3 4 5 6 7 8 9 10 11 <!DOCTYPE html > <html lang ="en" > <head > <title > Document</title > </head > <body > {{template "header" .}} <h1 > 首页...</h1 > {{template "footer"}} </body > </html >
go code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package mainimport ( "html/template" "net/http" ) func tmpl (w http.ResponseWriter, r *http.Request) { t1, err := template.ParseFiles("templates/index.html" , "templates/header.html" , "templates/footer.html" ) if err != nil { panic (err) } t1.Execute(w, nil ) } func main () { server := http.Server{ Addr: "127.0.0.1:8080" , } http.HandleFunc("/tmpl" , tmpl) server.ListenAndServe() }
运行结果
HttpRouter HttpRouter 是一种轻量级高性能的,golang HTTP 请求路由器。
与 Golang 默认路由相比,此路由器支持路由模式中的变量 并匹配请求方法。它还可以更好地扩展。
该路由器针对高性能和小内存占用进行了优化。即使有很长的路径和大量的路线,它也能很好地扩展。压缩动态特里(基数树)结构用于有效匹配。
gin 框架就是以 httprouter 为基础开发的,下面我们来学习一下 httprouter。
下载安装 1 go get github.com/julienschmidt/httprouter
第一个路由实例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package mainimport ( "fmt" "log" "net/http" "github.com/julienschmidt/httprouter" ) func Index (w http.ResponseWriter, r *http.Request, _ httprouter.Params) { fmt.Fprint(w, "Welcome!\n" ) } func Hello (w http.ResponseWriter, r *http.Request, ps httprouter.Params) { fmt.Fprintf(w, "hello, %s!\n" , ps.ByName("name" )) } func main () { router := httprouter.New() router.GET("/" , Index) router.GET("/hello/:name" , Hello) log.Fatal(http.ListenAndServe(":8080" , router)) }
在浏览器输入:http://localhost:8080/
,输出:Welcome!
在浏览器输入:http://localhost:8080/hello/ghz
,输出:hello, ghz!
http router 请求方法 httprouter 为所有的 HTTP Method 提供了快捷的使用方式,只需要调用对应的方法即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 func (r *Router) GET (path string , handle Handle) { r.Handle("GET" , path, handle) } func (r *Router) HEAD (path string , handle Handle) { r.Handle("HEAD" , path, handle) } func (r *Router) OPTIONS (path string , handle Handle) { r.Handle("OPTIONS" , path, handle) } func (r *Router) POST (path string , handle Handle) { r.Handle("POST" , path, handle) } func (r *Router) PUT (path string , handle Handle) { r.Handle("PUT" , path, handle) } func (r *Router) PATCH (path string , handle Handle) { r.Handle("PATCH" , path, handle) } func (r *Router) DELETE (path string , handle Handle) { r.Handle("DELETE" , path, handle) }
restful 风格路由 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 package mainimport ( "fmt" "log" "net/http" "github.com/julienschmidt/httprouter" ) func Index (w http.ResponseWriter, r *http.Request, _ httprouter.Params) { fmt.Fprint(w, "Welcome!\n" ) } func Hello (w http.ResponseWriter, r *http.Request, ps httprouter.Params) { fmt.Fprintf(w, "hello, %s!\n" , ps.ByName("name" )) } func getuser (w http.ResponseWriter, r *http.Request, ps httprouter.Params) { uid := ps.ByName("uid" ) fmt.Fprintf(w, "you are get user %s" , uid) } func modifyuser (w http.ResponseWriter, r *http.Request, ps httprouter.Params) { uid := ps.ByName("uid" ) fmt.Fprintf(w, "you are modify user %s" , uid) } func deleteuser (w http.ResponseWriter, r *http.Request, ps httprouter.Params) { uid := ps.ByName("uid" ) fmt.Fprintf(w, "you are delete user %s" , uid) } func adduser (w http.ResponseWriter, r *http.Request, ps httprouter.Params) { uid := ps.ByName("uid" ) fmt.Fprintf(w, "you are add user %s" , uid) } func main () { router := httprouter.New() router.GET("/" , Index) router.GET("/hello/:name" , Hello) router.GET("/user/:uid" , getuser) router.POST("/adduser/:uid" , adduser) router.DELETE("/deluser/:uid" , deleteuser) router.PUT("/moduser/:uid" , modifyuser) log.Fatal(http.ListenAndServe(":8080" , router)) }
Gin 简介 Gin 官网
gin 简介 Gin 是一个 golang 的微框架,基于 httprouter,封装比较优雅,API 友好,源码注释比较明确,具有快速灵活,容错方便等特点。
gin 特征 速度快
基于基数树的路由,内存占用小。没有反射。可预测的 API 性能。
中间件支持
传入的 HTTP 请求可以由中间件链和最终操作处理。例如:Logger、Authorization、GZIP 最后在 DB 中发布一条消息。
Crash-free
Gin 可以捕获 HTTP 请求期间发生的 panic 并恢复它。这样,你的服务器将始终可用。
JSON 验证
Gin 可以解析和验证请求的 JSON - 例如,检查所需值的存在。
路由分组
更好地组织您的路线。需要授权与不需要授权,不同的 API 版本……此外,组可以无限嵌套,而不会降低性能。
错误管理
Gin 提供了一种方便的方法来收集 HTTP 请求期间发生的所有错误。最终,中间件可以将它们写入日志文件、数据库并通过网络发送它们。
内置渲染
Gin 为 JSON、XML 和 HTML 渲染提供了一个易于使用的 API。
可扩展
创建一个新的中间件非常简单,只需查看示例代码即可。
第一个 gin 安装 gin
1 go get -u github.com/gin-gonic/gin
导入项目
1 import "github.com/gin-gonic/gin"
实现代码
1 2 3 4 5 6 7 8 9 10 11 12 13 package mainimport "github.com/gin-gonic/gin" func main () { r := gin.Default() r.GET("/ping" , func (c *gin.Context) { c.JSON(200 , gin.H{ "message" : "pong" , }) }) r.Run() }
运行
浏览器输入:http://localhost:8080/ping
Gin 实现用户登录 实现步骤 创建一个文件 tempates 在项目跟目录下面创建给文件夹 tempates,用来保存静态文件
创建一个登录 html 文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" /> <meta http-equiv ="X-UA-Compatible" content ="IE=edge" /> <meta name ="viewport" content ="width=device-width, initial-scale=1.0" /> <title > Login</title > </head > <body > <form action ="/login" method ="post" > Username: <input type ="text" name ="username" /> <br /> Password: <input type ="password" name ="password" /> <br /> <input type ="submit" value ="Login" /> </form > </body > </html >
创建一个欢迎 html 页面 1 2 3 4 5 6 7 8 9 10 11 12 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" /> <meta http-equiv ="X-UA-Compatible" content ="IE=edge" /> <meta name ="viewport" content ="width=device-width, initial-scale=1.0" /> <title > Welcome</title > </head > <body > Welcome, {{.username}} </body > </html >
使用 Gin 处理逻辑 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package mainimport "github.com/gin-gonic/gin" func MyHandler (c *gin.Context) { c.JSON(200 , gin.H{ "hello" : "hello world" , }) } func Login (c *gin.Context) { c.HTML(200 , "login.html" , nil ) } func DoLogin (c *gin.Context) { username := c.PostForm("username" ) password := c.PostForm("password" ) c.HTML(200 , "welcome.html" , gin.H{ "username" : username, "password" : password, }) } func main () { e := gin.Default() e.LoadHTMLGlob("templates/*" ) e.GET("/login" , Login) e.POST("/login" , DoLogin) e.Run() }
Gin 请求参数 Get 请求参数
使用 c.Query("key")
、或者 c.DefaultQuery("key")
方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package mainimport "github.com/gin-gonic/gin" func TestQueryString (c *gin.Context) { username := c.Query("username" ) site := c.DefaultQuery("site" , "www.xiaobaibk.com" ) c.String(200 , "username:%s, site:%s" , username, site) } func main () { e := gin.Default() e.GET("/testQueryString" , TestQueryString) e.Run() }
运行结果
1 username:xiaobai, site:baidu.com
Post 参数 使用 c.PostForm("key")
、或者 c.DefaultQuery("key")
方法
1 2 3 4 5 6 7 8 9 func DoLogin (c *gin.Context) { username := c.PostForm("username" ) password := c.DefaultPostForm("password" , "123" ) c.HTML(200 , "welcome.html" , gin.H{ "username" : username, "password" : password, }) }
使用 Postman 或者 Post 表单测试
路劲参数(restful 风格) 使用 c.Param("key")
方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package mainimport "github.com/gin-gonic/gin" func TestPathParam (c *gin.Context) { s := c.Param("username" ) c.String(200 , "Username:%s" , s) } func main () { e := gin.Default() e.GET("/hello/:username" , TestPathParam) e.Run() }
既有 Get 也有 Post 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package mainimport "github.com/gin-gonic/gin" func TestGetAndPost (c *gin.Context) { page := c.DefaultQuery("page" , "0" ) key := c.PostForm("key" ) c.String(200 , "Page:%s, Key:%s" , page, key) } func main () { e := gin.Default() e.POST("/query" , TestGetAndPost) e.Run() }
使用 Postman 或者 Post 表单测试
Gin 表单处理 创建一个 HTML 表单 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" /> <meta http-equiv ="X-UA-Compatible" content ="IE=edge" /> <meta name ="viewport" content ="width=device-width, initial-scale=1.0" /> <title > 多课网,听老郭讲golang</title > </head > <body > <h1 > 用户注册页面</h1 > <form action ="/register" method ="post" > 用户名: <input type ="text" name ="username" /> <br /> 密码: <input type ="password" name ="password" /> <br /> 爱好: <input type ="checkbox" name ="hobby" value ="swiming" /> 游泳 <input type ="checkbox" name ="hobby" value ="basketball" /> 篮球 <br /> 性别:<input type ="radio" name ="gender" id ="1" value ="m" /> 男 <input type ="radio" name ="gender" id ="2" value ="f" /> 女 <br /> 城市: <select name ="city" > <option value ="beijing" > 北京</option > <option value ="shanghai" > 上海</option > </select > <br /> <input type ="submit" value ="注册" /> </form > </body > </html >
Go code 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package mainimport "github.com/gin-gonic/gin" func Regsiter (c *gin.Context) { username := c.PostForm("username" ) password := c.PostForm("password" ) hobby := c.PostFormArray("hobby" ) gender := c.PostForm("gender" ) city := c.PostForm("city" ) c.String(200 , "Username:%s, Password:%s, hobby:%s, gender:%s, city:%s" , username, password, hobby, gender, city) } func GoRegister (c *gin.Context) { c.HTML(200 , "register.html" , nil ) } func main () { e := gin.Default() e.LoadHTMLGlob("templates/*" ) e.POST("/register" , Regsiter) e.GET("/register" , GoRegister) e.Run() }
运行结果
1 Username:ghz, Password:123, hobby:[swiming basketball], gender:m, city:beijing
Gin 数据绑定 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package mainimport ( "github.com/gin-gonic/gin" ) type User struct { Username string `form:"username"` Password string `form:"password"` Hobby []string `form:"hobby"` Gender string `form:"gender"` City string `form:"city"` } func Regsiter (c *gin.Context) { var user User c.ShouldBind(&user) c.String(200 , "User:%s" , user) } func GoRegister (c *gin.Context) { c.HTML(200 , "register.html" , nil ) } func main () { e := gin.Default() e.LoadHTMLGlob("templates/*" ) e.POST("/register" , Regsiter) e.GET("/register" , GoRegister) e.Run() }
绑定查询参数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 package mainimport ( "log" "github.com/gin-gonic/gin" ) type User struct { Username string `form:"username"` Password string `form:"password"` } func TestGetBind (c *gin.Context) { var user User err := c.ShouldBind(&user) if err != nil { log.Fatal(err) } c.String(200 , "User:%s" , user) } func main () { e := gin.Default() e.GET("/testGetBind" , TestGetBind) e.Run() }
路径请求参数绑定 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 package mainimport ( "log" "github.com/gin-gonic/gin" ) type User struct { Username string `uri:"username"` Password string `uri:"password"` } func TestGetBind (c *gin.Context) { var user User err := c.ShouldBindUri(&user) if err != nil { log.Fatal(err) } c.String(200 , "User:%s" , user) } func main () { e := gin.Default() e.GET("/testGetBind/:username/:password" , TestGetBind) e.Run() }
注意:结构体和绑定方法的变化
Gin 访问静态文件集成 BootStrap 框架 下载 BootStrap 下载地址:https://getbootstrap.com/
添加 bootstrap css 和 js 文件 创建一个 assets 文件夹,将 css 和 js 文件添加到该文件夹
创建 html 文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" /> <meta http-equiv ="X-UA-Compatible" content ="IE=edge" /> <meta name ="viewport" content ="width=device-width, initial-scale=1.0" /> <link rel ="stylesheet" href ="/assets/css/bootstrap.min.css" /> <title > Login</title > </head > <body > <div class ="container" > <form > <div class ="mb-3" > <label for ="exampleInputEmail1" class ="form-label" > Email address</label > <input type ="email" class ="form-control" id ="exampleInputEmail1" aria-describedby ="emailHelp" /> <div id ="emailHelp" class ="form-text" > We'll never share your email with anyone else. </div > </div > <div class ="mb-3" > <label for ="exampleInputPassword1" class ="form-label" > Password</label > <input type ="password" class ="form-control" id ="exampleInputPassword1" /> </div > <div class ="mb-3 form-check" > <input type ="checkbox" class ="form-check-input" id ="exampleCheck1" /> <label class ="form-check-label" for ="exampleCheck1" > Check me out</label > </div > <button type ="submit" class ="btn btn-primary" > Submit</button > </form > </div > </body > </html >
Go Code 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package mainimport ( "net/http" "github.com/gin-gonic/gin" ) func Login (c *gin.Context) { c.HTML(200 , "login.html" , nil ) } func main () { e := gin.Default() e.LoadHTMLGlob("templates/*" ) e.Static("/assets" , "./assets" ) e.StaticFS("/croot" , http.Dir("c:/" )) e.StaticFile("/favicon.ico" , "./assets/favicon.ico" ) e.GET("/login" , Login) e.POST("/login" , DoLogin) e.Run() }
Gin 使用中间件 中间件听起来非常高大上的名字,实际非常简单,就是在请求中间起到拦截作用的处理函数。
Gin 默认中间件 如果你使用 Gin.Default()
实例化 gin 引擎,默认有两个中间件,Logger
和 Recovery
,分别用来处理日志和处理错误。如果使用 gin.New()
需要重新添加。
1 2 3 4 5 6 7 8 9 10 r := gin.New() r.Use(gin.Logger()) r.Use(gin.Recovery())
自定义中间件
自定义中间件非常简单,定义一个符合下面格式的处理函数
1 type HandlerFunc func (*Context)
使用Use
方法调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 package mainimport ( "fmt" "github.com/gin-gonic/gin" ) func TestMW (c *gin.Context) { c.String(200 , "hello,%s" , "ghz" ) } func MyMiddleware1 (c *gin.Context) { fmt.Println("我的第一个中间件" ) } func MyMiddleware2 (c *gin.Context) { fmt.Println("我的第二个中间件" ) } func main () { e := gin.Default() e.Use(MyMiddleware1, MyMiddleware2) e.GET("testmw" , TestMW) e.Run() }
使用 Gin BasicAuth 中间件 Gin 提供了 BasicAuth 中间件,用来对网站资源的访问保护。
示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 package mainimport ( "fmt" "net/http" "github.com/gin-gonic/gin" ) var secrets = gin.H{ "foo" : gin.H{"email" : "[email protected] " , "phone" : "123433" }, "austin" : gin.H{"email" : "[email protected] " , "phone" : "666" }, "lena" : gin.H{"email" : "[email protected] " , "phone" : "523443" }, } func main () { r := gin.Default() authorized := r.Group("/admin" , gin.BasicAuth(gin.Accounts{ "foo" : "bar" , "austin" : "1234" , "lena" : "hello2" , "manu" : "4321" , })) authorized.GET("/secrets" , func (c *gin.Context) { user := c.MustGet(gin.AuthUserKey).(string ) fmt.Println(user) 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 :(" }) } }) r.Run(":8080" ) }
测试 在浏览器输入 localhost:8080/admin/secrets
时,会弹出一个对话框,要求输入正确的用户名和密码,才能访问资源。
Gin cookie 的使用 cookie
是服务器向客户端写的一些数据,可以实现像自动登录等功能。
Gin cookie 的使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package mainimport "github.com/gin-gonic/gin" func Handler (c *gin.Context) { s, err := c.Cookie("username" ) if err != nil { s = "ghz" c.SetCookie("username" , s, 60 *60 , "/" , "localhost" , false , true ) } c.String(200 , "测试cookie" ) } func main () { e := gin.Default() e.GET("/test" , Handler) e.Run() }
基于安全的考虑,需要给 cookie 加上 Secure
和 HttpOnly
属性,HttpOnly
比较好理解,设置 HttpOnly=true
的 cookie 不能被 js 获取到,无法用 document.cookie
打出 cookie 的内容。
Secure
属性是说如果一个 cookie 被设置了 Secure=true
,那么这个 cookie 只能用 https
协议发送给服务器,用 http
协议是不发送的。
Gin 使用 Session 因为 http 是无状态、短连接,如何保存客户端和服务器直接的会话状态呢?可以使用 session。
使用 gin session 中间件 gin 本身没有对 session 的支持,可以使用第三方中间件。
1 2 go get github.com/gin-contrib/sessionsimport "github.com/gin-contrib/sessions"
该中间件提供了很多后端支持:
实例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package mainimport ( "github.com/gin-contrib/sessions" "github.com/gin-contrib/sessions/cookie" "github.com/gin-gonic/gin" ) func main () { r := gin.Default() store := cookie.NewStore([]byte ("secret" )) r.Use(sessions.Sessions("mysession" , store)) r.GET("/hello" , func (c *gin.Context) { session := sessions.Default(c) if session.Get("hello" ) != "world" { session.Set("hello" , "world" ) session.Save() } c.JSON(200 , gin.H{"hello" : session.Get("hello" )}) }) r.Run(":8000" ) }
更多内容请参考:https://pkg.go.dev/github.com/gin-contrib/sessions#section-readme
Gin 实现 restful 风格的 CRUD 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 package mainimport ( "fmt" "strconv" "github.com/gin-gonic/gin" ) type User struct { UId int `json:"uid"` Name string `json:"name"` Age int `json:"age"` } var users = make ([]User, 3 )func init () { u1 := User{1 , "tom" , 20 } u2 := User{2 , "kite" , 30 } u3 := User{3 , "rose" , 40 } users = append (users, u1) users = append (users, u2) users = append (users, u3) fmt.Println(users) } func find (uid int ) (*User, int ) { for i, u := range users { if u.UId == uid { return &u, i } } return nil , -1 } func AddUser (c *gin.Context) { u4 := User{4 , "Joe" , 50 } users = append (users, u4) c.JSON(200 , users) } func DelUser (c *gin.Context) { uid := c.Param("uid" ) id, _ := strconv.Atoi(uid) _, i := find(id) users = append (users[:i], users[i+1 :]...) c.JSON(200 , users) } func UpdateUser (c *gin.Context) { uid := c.Param("uid" ) id, _ := strconv.Atoi(uid) u, _ := find(id) u.Name = "修改的Name" c.JSON(200 , u) } func FindUser (c *gin.Context) { uid := c.Param("uid" ) id, _ := strconv.Atoi(uid) u, _ := find(id) c.JSON(200 , u) } func main () { e := gin.Default() e.GET("/user/:uid" , FindUser) e.PUT("/user/:uid" , UpdateUser) e.DELETE("/user/:uid" , DelUser) e.POST("/user/" , AddUser) e.Run() }
Gin 实现路由分组 假如你的网站有多个模块:博客、教程、视频、问答,每个模块又有多个路由,这样就可以进行路由分组,使用的方法是 router.Group(“分组名称”)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 package mainimport "github.com/gin-gonic/gin" func F1 (c *gin.Context) {}func F2 (c *gin.Context) {}func F3 (c *gin.Context) {}func F4 (c *gin.Context) {}func F5 (c *gin.Context) {}func F6 (c *gin.Context) {}func main () { router := gin.Default() v1 := router.Group("/blog" ) { v1.POST("/list" , F1) v1.POST("/post" , F2) v1.POST("/add" , F3) } v2 := router.Group("/video" ) { v2.POST("/list" , F4) v2.POST("/post" , F5) v2.POST("/add" , F6) } router.Run(":8080" ) }
Gin 输出渲染 Gin 支持很多种输出渲染,可以是简单的字符串、JSON、XML、HTML、ProtoBuf。使用的方法如下:
1 2 3 4 5 c.JSON(200 , nil ) c.XML(200 , nil ) c.HTML(200 , "" , nil ) c.String(200 , "" ) c.ProtoBuf(200 , nil )
这里像字符串、JSON、HTML 我们都用过,这里我们再总结一下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 package mainimport "github.com/gin-gonic/gin" func TestJson (c *gin.Context) { c.JSON(200 , gin.H{ "name" : "多课网" , "site" : "www.duoke360.com" , }) } func TestXML (c *gin.Context) { c.XML(200 , gin.H{ "name" : "多课网" , "site" : "www.duoke360.com" , }) } func TestHtml (c *gin.Context) { c.HTML(200 , "login.html" , nil ) } func TestString (c *gin.Context) { c.String(200 , "多课网,老郭讲golang" ) } func main () { e := gin.Default() e.GET("/test_json" , TestJson) e.GET("/test_xml" , TestXML) e.LoadHTMLGlob("templates/*" ) e.GET("/test_html" , TestHtml) e.GET("/test_string" , TestString) e.Run() }
Gin 实现文件上传 创建 html 文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" /> <meta http-equiv ="X-UA-Compatible" content ="IE=edge" /> <meta name ="viewport" content ="width=device-width, initial-scale=1.0" /> <title > Document</title > </head > <body > <form action ="/upload" method ="post" enctype ="multipart/form-data" > 请选择上传文件:<input type ="file" name ="file" id ="" /> <br /> <input type ="submit" value ="上传" /> </form > </body > </html >
Go Code 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 package mainimport ( "fmt" "log" "net/http" "github.com/gin-gonic/gin" ) func Upload (c *gin.Context) { file, _ := c.FormFile("file" ) log.Println(file.Filename) c.SaveUploadedFile(file, file.Filename) c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!" , file.Filename)) } func GoUpload (c *gin.Context) { c.HTML(200 , "upload.html" , nil ) } func main () { router := gin.Default() router.MaxMultipartMemory = 8 << 20 router.LoadHTMLGlob("templates/*" ) router.GET("/upload" , GoUpload) router.POST("/upload" , Upload) router.Run(":8080" ) }
Gin 博客项目-项目架构 规范包及目录 1 2 3 4 5 6 7 8 9 10 11 |-controller |-dao |-router |-model |-assets |-templates
初始化项目
Gin 博客项目-集成 gorm 下载包 1 2 go get -u gorm.io/gorm go get -u gorm.io/driver/mysql
创建模型 1 2 3 4 5 6 7 8 9 package modelsimport "github.com/jinzhu/gorm" type User struct { gorm.Model Username string `json:"username"` Password string `json:"passowrd"` }
创建 Dao 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package daoimport ( "log" "pro04/models" "gorm.io/driver/mysql" "gorm.io/gorm" ) type Manager interface { AddUser(user *models.User) } type manager struct { db *gorm.DB } var Mgr Managerfunc init () { dsn := "root:123456@tcp(127.0.0.1:3306)/golang_db?charset=utf8mb4&parseTime=True&loc=Local" db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) if err != nil { log.Fatal("Failed to init db:" , err) } Mgr = &manager{db: db} db.AutoMigrate(&models.User{}) } func (mgr *manager) AddUser (user *models.User) { mgr.db.Create(user) }
测试 1 2 3 4 5 6 user := models.User{ Username: username, Password: password, } dao.Mgr.AddUser(&user)
Gin 博客项目-集成 Bootstrap 创建用户表单 下载 bootstrap 1 https://getbootstrap.com/
添加 js 和 css 到 assets 目录下面 创建添加用户 html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 <!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8" > <meta http-equiv="X-UA-Compatible" content="IE=edge" > <meta name="viewport" content="width=device-width, initial-scale=1.0" > <link rel="stylesheet" href="/assets/css/bootstrap.min.css" > <title>用户管理</title> </head> <body> <div class="container" > <div class="row mt-3 justify-content-center" > <div class="col-md-4" > <form method="post" action="/users" > <div class="mb-3" > <label for ="exampleInputEmail1" class="form-label" >用户名称</label> <input type ="username" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp" > <div id="emailHelp" class="form-text" >We'll never share your email with anyone else.</div> </div> <div class="mb-3"> <label for="exampleInputPassword1" class="form-label">密码</label> <input type="password" class="form-control" name="password" id="exampleInputPassword1"> </div> <div class="mb-3 form-check"> <input type="checkbox" class="form-check-input" id="exampleCheck1"> <label class="form-check-label" for="exampleCheck1">Check me out</label> </div> <textarea name="test" id="test" cols="30" rows="10"></textarea> <button type="submit" class="btn btn-primary">添加</button> </form> <hr> <ul class="list-group list-group-flush"> <li class="list-group-item">tom 123</li> <li class="list-group-item">kite 456</li> </ul> </div> </div> </div> </body> </html>
Gin 博客项目-实现控制器和路由 控制器 controller 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package controllerimport ( "pro04/dao" "pro04/models" "github.com/gin-gonic/gin" ) func AddUser (c *gin.Context) { username := c.PostForm("username" ) password := c.PostForm("password" ) user := models.User{ Username: username, Password: password, } dao.Mgr.AddUser(&user) } func ListUser (c *gin.Context) { c.HTML(200 , "user.html" , nil ) }
路由 router 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package routersimport ( "pro04/controller" "github.com/gin-gonic/gin" ) func Start () { e := gin.Default() e.LoadHTMLGlob("templates/*" ) e.Static("/assets" , "./assets" ) e.GET("/users" , controller.ListUser) e.POST("/users" , controller.AddUser) e.Run() }
Gin 博客项目-设计静态页面 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 {{define "header"}} <header class ="p-3 bg-dark text-white" > <div class ="container" > <div class ="d-flex flex-wrap align-items-center justify-content-center justify-content-lg-start" > <a class ="navbar-brand" href ="#" > 多课网</a > <ul class ="nav col-12 col-lg-auto me-lg-auto mb-2 justify-content-center mb-md-0" > <li > <a href ="#" class ="nav-link px-2 text-secondary" > 首页</a > </li > <li > <a href ="#" class ="nav-link px-2 text-white" > 博客</a > </li > </ul > <form class ="col-12 col-lg-auto mb-3 mb-lg-0 me-lg-3" > <input type ="search" class ="form-control form-control-dark" placeholder ="Search..." aria-label ="Search" /> </form > <div class ="text-end" > <button type ="button" class ="btn btn-outline-light me-2" > 登录</button > <a type ="button" class ="btn btn-warning" href ="/register" > 注册</a > </div > </div > </div > </header > {{end}}
注册 register.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" /> <meta http-equiv ="X-UA-Compatible" content ="IE=edge" /> <meta name ="viewport" content ="width=device-width, initial-scale=1.0" /> <link rel ="stylesheet" href ="/assets/css/bootstrap.min.css" /> <title > 用户注册</title > </head > <body > <div class ="container" > {{template "header"}} <div class ="row justify-content-center mt-3" > <div class ="col-md-4" > <form > <div class ="mb-3" > <label for ="exampleInputEmail1" class ="form-label" > 用户名称</label > <input type ="email" class ="form-control" id ="exampleInputEmail1" aria-describedby ="emailHelp" /> </div > <div class ="mb-3" > <label for ="exampleInputPassword1" class ="form-label" > 用户密码</label > <input type ="password" class ="form-control" id ="exampleInputPassword1" /> </div > <div class ="mb-3 form-check" > <input type ="checkbox" class ="form-check-input" id ="exampleCheck1" /> <label class ="form-check-label" for ="exampleCheck1" > 记住我</label > </div > <button type ="submit" class ="btn btn-primary" > 添加</button > </form > </div > </div > </div > </body > </html >
登录 login.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" /> <meta http-equiv ="X-UA-Compatible" content ="IE=edge" /> <meta name ="viewport" content ="width=device-width, initial-scale=1.0" /> <link rel ="stylesheet" href ="/assets/css/bootstrap.min.css" /> <title > 用户注册</title > </head > <body > <div class ="container" > {{template "header"}} <div class ="row justify-content-center mt-3" > <div class ="col-md-4" > <form > <div class ="mb-3" > <label for ="exampleInputEmail1" class ="form-label" > 用户名称</label > <input type ="email" class ="form-control" id ="exampleInputEmail1" aria-describedby ="emailHelp" /> </div > <div class ="mb-3" > <label for ="exampleInputPassword1" class ="form-label" > 用户密码</label > <input type ="password" class ="form-control" id ="exampleInputPassword1" /> </div > <div class ="mb-3 form-check" > <input type ="checkbox" class ="form-check-input" id ="exampleCheck1" /> <label class ="form-check-label" for ="exampleCheck1" > 记住我</label > </div > <button type ="submit" class ="btn btn-primary" > 添加</button > </form > </div > </div > </div > </body > </html >
首页 index.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" /> <meta http-equiv ="X-UA-Compatible" content ="IE=edge" /> <meta name ="viewport" content ="width=device-width, initial-scale=1.0" /> <link rel ="stylesheet" href ="/assets/css/bootstrap.min.css" /> <title > Document</title > </head > <body > <div class ="container" > {{template "header"}} <div class ="row justify-content-center mt-3" > <div class ="col-md-4" > <p > 首页...</p > </div > </div > </div > </body > </html >
Gin 博客项目-用户注册 controller 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 func RegisterUser (c *gin.Context) { username := c.PostForm("username" ) password := c.PostForm("password" ) user := model.User{ Username: username, Password: password, } dao.Mgr.AddUser(&user) c.Redirect(200 , "/" ) } func GoRegister (c *gin.Context) { c.HTML(200 , "register.html" , nil ) }
router 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package routerimport ( "blog/controller" "github.com/gin-gonic/gin" ) func Start () { e := gin.Default() e.LoadHTMLGlob("templates/*" ) e.Static("/assets" , "./assets" ) e.GET("/" , controller.Index) e.POST("/register" , controller.RegisterUser) e.GET("/register" , controller.GoRegister) e.Run() }
Gin 博客项目-用户登录 dao 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 package daoimport ( "blog/model" "log" "gorm.io/driver/mysql" "gorm.io/gorm" ) type Manager interface { AddUser(user *model.User) Login(username string ) model.User } type manager struct { db *gorm.DB } var Mgr Managerfunc init () { dsn := "root:123456@tcp(127.0.0.1:3306)/golang_db?charset=utf8mb4&parseTime=True&loc=Local" db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) if err != nil { log.Fatal("Failed to init db:" , err) } Mgr = &manager{db: db} db.AutoMigrate(&model.User{}) } func (mgr *manager) AddUser (user *model.User) { mgr.db.Create(user) } func (mgr *manager) Login (username string ) model .User { var user model.User mgr.db.Where("username=?" , username).First(&user) return user }
controller 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 package controllerimport ( "blog/dao" "blog/model" "fmt" "github.com/gin-gonic/gin" ) func RegisterUser (c *gin.Context) { username := c.PostForm("username" ) password := c.PostForm("password" ) user := model.User{ Username: username, Password: password, } dao.Mgr.AddUser(&user) c.Redirect(200 , "/" ) } func GoRegister (c *gin.Context) { c.HTML(200 , "register.html" , nil ) } func GoLogin (c *gin.Context) { c.HTML(200 , "login.html" , nil ) } func Login (c *gin.Context) { username := c.PostForm("username" ) password := c.PostForm("password" ) fmt.Println(username) u := dao.Mgr.Login(username) if u.Username == "" { c.HTML(200 , "login.html" , "用户名不存在!" ) fmt.Println("用户名不存在!" ) } else { if u.Password != password { fmt.Println("密码错误" ) c.HTML(200 , "login.html" , "密码错误" ) } else { fmt.Println("登录成功" ) c.Redirect(301 , "/" ) } } } func Index (c *gin.Context) { c.HTML(200 , "index.html" , nil ) } func ListUser (c *gin.Context) { c.HTML(200 , "index.html" , nil ) }
router 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package routerimport ( "blog/controller" "github.com/gin-gonic/gin" ) func Start () { e := gin.Default() e.LoadHTMLGlob("templates/*" ) e.Static("/assets" , "./assets" ) e.GET("/login" , controller.GoLogin) e.POST("/login" , controller.Login) e.GET("/" , controller.Index) e.POST("/register" , controller.RegisterUser) e.GET("/register" , controller.GoRegister) e.Run() }
login.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" /> <meta http-equiv ="X-UA-Compatible" content ="IE=edge" /> <meta name ="viewport" content ="width=device-width, initial-scale=1.0" /> <link rel ="stylesheet" href ="/assets/css/bootstrap.min.css" /> <title > 用户注册</title > </head > <body > <div class ="container" > {{template "header"}} <div class ="row justify-content-center mt-3" > <div class ="col-md-4" > <p style ="background-color: red;" > {{.}}</p > <form method ="post" action ="/login" > <div class ="mb-3" > <label for ="exampleInputEmail1" class ="form-label" > 用户名称</label > <input type ="text" name ="username" class ="form-control" id ="exampleInputEmail1" aria-describedby ="emailHelp" /> </div > <div class ="mb-3" > <label for ="exampleInputPassword1" class ="form-label" > 用户密码</label > <input type ="password" name ="password" class ="form-control" id ="exampleInputPassword1" /> </div > <button type ="submit" class ="btn btn-primary" > 登录</button > </form > </div > </div > </div > </body > </html >
Gin 博客项目-集成 markdown 编辑器 下载 mdeditor 1 https://pandao.github.io/editor.md/
集成 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" /> <meta http-equiv ="X-UA-Compatible" content ="IE=edge" /> <meta name ="viewport" content ="width=device-width, initial-scale=1.0" /> <link rel ="stylesheet" href ="/assets/css/bootstrap.min.css" /> <link rel ="stylesheet" href ="/assets/editormd/css/editormd.css" /> <title > Document</title > </head > <body > <div class ="container" > {{template "header"}} <div id ="test-editormd" > <textarea style ="display:none;" > </textarea > </div > </div > var testEditor; $(function() { testEditor = editormd("test-editormd", { width : "100%", height : 640, syncScrolling : "single", path : "assets/editormd/lib/" }); }); </body > </html >
Gin 博客项目-创建博客模型和 DAO 创建模型 1 2 3 4 5 6 type Post struct { gorm.Model Title string Content string `gorm:"type:text"` Tag string }
创建 DAO 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 package daoimport ( "blog/model" "log" "gorm.io/driver/mysql" "gorm.io/gorm" ) type Manager interface { AddUser(user *model.User) Login(username string ) model.User AddPost(post *model.Post) GetAllPost() []model.Post getPost(pid int ) model.Post } type manager struct { db *gorm.DB } var Mgr Managerfunc init () { dsn := "root:123456@tcp(127.0.0.1:3306)/golang_db?charset=utf8mb4&parseTime=True&loc=Local" db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) if err != nil { log.Fatal("Failed to init db:" , err) } Mgr = &manager{db: db} db.AutoMigrate(&model.User{}) db.AutoMigrate(&model.Post{}) } func (mgr *manager) AddUser (user *model.User) { mgr.db.Create(user) } func (mgr *manager) Login (username string ) model .User { var user model.User mgr.db.Where("username=?" , username).First(&user) return user } func (mgr *manager) AddPost (post *model.Post) { mgr.db.Create(post) } func (mgr *manager) GetAllPost () []model .Post { var posts = make ([]model.Post, 10 ) mgr.db.Find(&posts) return posts } func (mgr *manager) getPost (pid int ) model .Post { var post model.Post mgr.db.First(&post, pid) return post }
Gin 博客项目-创建博客控制器和路由 创建控制器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 package controllerimport ( "blog/dao" "blog/model" "fmt" "github.com/gin-gonic/gin" ) func RegisterUser (c *gin.Context) { username := c.PostForm("username" ) password := c.PostForm("password" ) user := model.User{ Username: username, Password: password, } dao.Mgr.AddUser(&user) c.Redirect(200 , "/" ) } func GoRegister (c *gin.Context) { c.HTML(200 , "register.html" , nil ) } func GoLogin (c *gin.Context) { c.HTML(200 , "login.html" , nil ) } func Login (c *gin.Context) { username := c.PostForm("username" ) password := c.PostForm("password" ) fmt.Println(username) u := dao.Mgr.Login(username) if u.Username == "" { c.HTML(200 , "login.html" , "用户名不存在!" ) fmt.Println("用户名不存在!" ) } else { if u.Password != password { fmt.Println("密码错误" ) c.HTML(200 , "login.html" , "密码错误" ) } else { fmt.Println("登录成功" ) c.Redirect(301 , "/" ) } } } func Index (c *gin.Context) { c.HTML(200 , "index.html" , nil ) } func ListUser (c *gin.Context) { c.HTML(200 , "index.html" , nil ) } func GetPostIndex (c *gin.Context) { posts := dao.Mgr.GetAllPost() c.HTML(200 , "postIndex.html" , posts) } func AddPost (c *gin.Context) { title := c.PostForm("title" ) tag := c.PostForm("tag" ) content := c.PostForm("content" ) post := model.Post{ Title: title, Tag: tag, Content: content, } dao.Mgr.AddPost(&post) c.Redirect(302 , "/post_index" ) } func GoAddPost (c *gin.Context) { c.HTML(200 , "post.html" , nil ) }
创建路由 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package routerimport ( "blog/controller" "github.com/gin-gonic/gin" ) func Start () { e := gin.Default() e.LoadHTMLGlob("templates/*" ) e.Static("/assets" , "./assets" ) e.GET("/login" , controller.GoLogin) e.POST("/login" , controller.Login) e.GET("/" , controller.Index) e.POST("/register" , controller.RegisterUser) e.GET("/register" , controller.GoRegister) e.GET("/post_index" , controller.GetPostIndex) e.POST("/post" , controller.AddPost) e.GET("/post" , controller.GoAddPost) e.Run() }
Gin 博客项目-添加博客 添加博客页面 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 <!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8" > <meta http-equiv="X-UA-Compatible" content="IE=edge" > <meta name="viewport" content="width=device-width, initial-scale=1.0" > <link rel="stylesheet" href="/assets/css/bootstrap.min.css" > <link rel="stylesheet" href="/assets/editormd/css/editormd.css" > <title>添加博客</title> </head> <body> <div class="container" > {{template "header" }} <form action="/post" method="post" > <div class="row" > <div class="col-md-8" > <div id="test-editormd" > <textarea style="display:none;" name="content" ></textarea> </div> </div> <div class="col-md-4 mt-3" > <label for ="title" class="form-label" >请输入标题</label> <input type ="text" name="title" class="form-control" id="title" ><br> <label for ="tag" class="form-label" >请输入标签</label> <input type ="text" name="tag" class="form-control" id="tag" ><br> <button type ="submit" class="btn btn-primary" >添加</button> </div> </div> </form> </div> var testEditor; $(function() { testEditor = editormd("test-editormd" , { width : "100%" , height : 450 , syncScrolling : "single" , path : "assets/editormd/lib/" , watch : false , }); }); </body> </html>
Gin 博客项目-实现博客列表 博客列表 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 <!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8" > <meta http-equiv="X-UA-Compatible" content="IE=edge" > <meta name="viewport" content="width=device-width, initial-scale=1.0" > <link rel="stylesheet" href="/assets/css/bootstrap.min.css" > <title>博客列表</title> </head> <body> <div class="container" > {{template "header" }} <div class="row mt-3" > {{range $post := . -}} <div class="col-md-6" > <div class="row g-0 border rounded overflow-hidden flex-md-row mb-4 shadow-sm h-md-250 position-relative" > <div class="col p-4 d-flex flex-column position-static" > <strong class="d-inline-block mb-2 text-primary" >分类</strong> <h3 class="mb-0" >{{$post.Title}}</h3> <div class="mb-1 text-muted" >Nov 12 </div> <p class="card-text mb-auto" >{{$post.Content}}</p> <a href="#" class="stretched-link" >阅读更多...</a> </div> <div class="col-auto d-none d-lg-block" > <svg class="bd-placeholder-img" width="200" height="250" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Placeholder: Thumbnail" preserveAspectRatio="xMidYMid slice" focusable="false" ><title>Placeholder</title><rect width="100%" height="100%" fill="#55595c" ></rect><text x="50%" y="50%" fill="#eceeef" dy=".3em" >博客封面</text></svg> </div> </div> </div> {{- end}} </div> </div> </body> </html>
Gin 博客项目-实现博客详细 博客详细页面 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" /> <meta http-equiv ="X-UA-Compatible" content ="IE=edge" /> <meta name ="viewport" content ="width=device-width, initial-scale=1.0" /> <link rel ="stylesheet" href ="/assets/css/bootstrap.min.css" /> <link rel ="stylesheet" href ="/assets/editormd/css/editormd.css" /> <title > 博客详细</title > </head > <body > <div class ="container" > {{template "header"}} <div class ="row" > <div class ="col-md-12" > <h1 > {{.Title}}</h1 > {{.Content}} </div > </div > </div > </body > </html >
博客详细控制器 1 2 3 4 5 6 7 8 9 10 11 12 func PostDetail (c *gin.Context) { s := c.Query("pid" ) pid, _ := strconv.Atoi(s) p := dao.Mgr.GetPost(pid) content := blackfriday.Run([]byte (p.Content)) c.HTML(200 , "detail.html" , gin.H{ "Title" : p.Title, "Content" : template.HTML(content), }) }
beego 起步 下载安装 beego 1 go get github.com/astaxie/beego
beego 命令行工具 bee 1 go get github.com/beego/bee
第一个 beego 项目
使用 bee 命令创建项目
项目目录结构1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 myproject ├── conf │ └── app.conf ├── controllers │ └── default .go ├── main.go ├── models ├── routers │ └── router.go ├── static │ ├── css │ ├── img │ └── js ├── tests │ └── default_test.go └── views └── index.tpl
运行项目
beego restful api 创建 restful api 项目
创建模型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 package modelsimport ( "errors" "strconv" "time" ) var ( UserList map [string ]*User ) func init () { UserList = make (map [string ]*User) u := User{"user_11111" , "astaxie" , "11111" , Profile{"male" , 20 , "Singapore" , "[email protected] " }} UserList["user_11111" ] = &u } type User struct { Id string Username string Password string Profile Profile } type Profile struct { Gender string Age int Address string Email string } func AddUser (u User) string { u.Id = "user_" + strconv.FormatInt(time.Now().UnixNano(), 10 ) UserList[u.Id] = &u return u.Id } func GetUser (uid string ) (u *User, err error) { if u, ok := UserList[uid]; ok { return u, nil } return nil , errors.New("User not exists" ) } func GetAllUsers () map [string ]*User { return UserList } func UpdateUser (uid string , uu *User) (a *User, err error) { if u, ok := UserList[uid]; ok { if uu.Username != "" { u.Username = uu.Username } if uu.Password != "" { u.Password = uu.Password } if uu.Profile.Age != 0 { u.Profile.Age = uu.Profile.Age } if uu.Profile.Address != "" { u.Profile.Address = uu.Profile.Address } if uu.Profile.Gender != "" { u.Profile.Gender = uu.Profile.Gender } if uu.Profile.Email != "" { u.Profile.Email = uu.Profile.Email } return u, nil } return nil , errors.New("User Not Exist" ) } func Login (username, password string ) bool { for _, u := range UserList { if u.Username == username && u.Password == password { return true } } return false } func DeleteUser (uid string ) { delete (UserList, uid) }
创建控制器 controller 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 package controllersimport ( "apiproject/models" "encoding/json" "github.com/astaxie/beego" ) type UserController struct { beego.Controller } func (u *UserController) Post () { var user models.User json.Unmarshal(u.Ctx.Input.RequestBody, &user) uid := models.AddUser(user) u.Data["json" ] = map [string ]string {"uid" : uid} u.ServeJSON() } func (u *UserController) GetAll () { users := models.GetAllUsers() u.Data["json" ] = users u.ServeJSON() } func (u *UserController) Get () { uid := u.GetString(":uid" ) if uid != "" { user, err := models.GetUser(uid) if err != nil { u.Data["json" ] = err.Error() } else { u.Data["json" ] = user } } u.ServeJSON() } func (u *UserController) Put () { uid := u.GetString(":uid" ) if uid != "" { var user models.User json.Unmarshal(u.Ctx.Input.RequestBody, &user) uu, err := models.UpdateUser(uid, &user) if err != nil { u.Data["json" ] = err.Error() } else { u.Data["json" ] = uu } } u.ServeJSON() } func (u *UserController) Delete () { uid := u.GetString(":uid" ) models.DeleteUser(uid) u.Data["json" ] = "delete success!" u.ServeJSON() } func (u *UserController) Login () { username := u.GetString("username" ) password := u.GetString("password" ) if models.Login(username, password) { u.Data["json" ] = "login success" } else { u.Data["json" ] = "user not exist" } u.ServeJSON() } func (u *UserController) Logout () { u.Data["json" ] = "logout success" u.ServeJSON() }
创建路由
路由在控制器方法中有注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package routersimport ( "apiproject/controllers" "github.com/astaxie/beego" ) func init () { ns := beego.NewNamespace("/v1" , beego.NSNamespace("/object" , beego.NSInclude( &controllers.ObjectController{}, ), ), beego.NSNamespace("/user" , beego.NSInclude( &controllers.UserController{}, ), ), ) beego.AddNamespace(ns) }
运行并生成 swagger 1 bee run -gendoc=true -downdoc=true
测试 1 http://localhost:8080/swagger/
beego 从零开始写一个 beego 项目 为了更好的理解 beego 框架,我们不使用 bee 工具,从零开始来写一个项目,实现步骤如下:
1. 创建项目结构 创建一个文件夹,例如:beegopro01,并初始化。
项目结构如下:
1 2 3 |-controller |-router |-views
2. 创建 controller 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package controllerimport ( "github.com/astaxie/beego" ) type UserControler struct { beego.Controller } func (c *UserControler) Get () { c.Data["name" ] = "老郭" c.Data["site" ] = "多课网" c.TplName = "index.tpl" }
3. 创建 router 路由 1 2 3 4 5 6 7 8 9 10 package routersimport ( "myproject/controllers" "github.com/astaxie/beego" ) func init () { beego.Router("/" , &controllers.MainController{}) }
4. 创建模板文件 index.tpl 1 2 3 4 5 6 7 8 9 10 <!DOCTYPE html > <html lang ="en" > <head > <title > beego 项目</title > </head > <body > Name: {{.name}} <br /> Site:{{.site}} </body > </html >
5. main.go 1 2 3 4 5 6 7 8 9 10 11 package mainimport ( _ "beegopro01/router" "github.com/astaxie/beego" ) func main () { beego.Run() }
beego orm 起步 安装库 1 go get github.com/astaxie/beego/orm
简单示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 package mainimport ( "fmt" "github.com/astaxie/beego/orm" _ "github.com/go-sql-driver/mysql" ) type User struct { Id int Name string `orm:"size(100)"` } func init () { orm.RegisterDataBase("default" , "mysql" , "root:123456@/beego_db?charset=utf8" ) orm.RegisterModel(new (User)) orm.RunSyncdb("default" , false , true ) } func add () { o := orm.NewOrm() o.Insert(&User{Name: "老郭" }) } func find () { o := orm.NewOrm() user := User{Id: 101 } o.Read(&user) fmt.Printf("user: %v\n" , user) } func update () { o := orm.NewOrm() user := User{Id: 101 } o.Read(&user) user.Name = "多课网-老郭" o.Update(&user) } func del () { o := orm.NewOrm() user := User{Id: 101 } o.Read(&user) o.Delete(&user) } func main () { del() }