window下安装
- https://golang.google.cn/dl/
- 下载msi文件,点击安装即可。
// 查看版本
go version
查看go环境变量
go env
vscode安装go插件
切换源
go env -w GOPROXY=https://mirrors.aliyun.com/goproxy/,direct
第一个go web程序
新建main.go
输入helloweb按回车
package main
import (
"fmt"
"net/http"
"time"
)
func greet(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello World! %s", time.Now())
}
func main() {
http.HandleFunc("/", greet)
http.ListenAndServe(":8080", nil)
}
命令行执行go run main.go
然后命令行访问服务
curl http://localhost:8080
得到以下的结果,那么第一个golang web就执行成功了
StatusCode : 200
StatusDescription : OK
Content : Hello World! 2022-02-09 20:25:07.5051433 +0800 CST m=+55.242234801
restful接口定义
导入依赖
go mod init example/web-service-gin
产生模块管理文件
module example/web-service-gin
go 1.15
main.go导入github.com/gin-gonic/gin
import (
"net/http"
"github.com/gin-gonic/gin"
)
执行
go get .
go.mod(类似nodejs的package.json)
module example/web-service-gin
go 1.15
require github.com/gin-gonic/gin v1.7.7
自动扫码导入依赖同时会发现目录产生了go.sum文件,其类似nodejs的package.json.lock文件
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs=
github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
……
编写代码
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
type album struct {
ID string `json:"id"`
Title string `json:"title"`
Artist string `json:"artist"`
Price float64 `json:"price"`
}
var albums = []album{
{ID: "1", Title: "Blue Train", Artist: "John Coltrane", Price: 56.99},
{ID: "2", Title: "Jeru", Artist: "Gerry Mulligan", Price: 17.99},
{ID: "3", Title: "Sarah Vaughan and Clifford Brown", Artist: "Sarah Vaughan", Price: 39.99},
}
func getAlbums(ctx *gin.Context) {
//返回带缩减的JSON数据
ctx.IndentedJSON(http.StatusOK, albums)
}
func main() {
router := gin.Default()
router.GET("/albums", getAlbums)//method:get
router.POST("/albums", getAlbums)//method:get
router.Run("localhost:8080")
}
拆分文件
把GetAlbums放到controller,新建controller目录,这也是package的名称,新建albumController.go文件
package controller
import (
"example/web-service-gin/models"
"net/http"
"github.com/gin-gonic/gin"
)
func GetAlbums(ctx *gin.Context) {
albums := []models.Album{
{ID: "1", Title: "Blue Train", Artist: "John Coltrane", Price: 56.99},
{ID: "2", Title: "Jeru", Artist: "Gerry Mulligan", Price: 17.99},
{ID: "3", Title: "Sarah Vaughan and Clifford Brown", Artist: "Sarah Vaughan", Price: 39.99},
}
ctx.IndentedJSON(http.StatusOK, albums)
}
把Albums结构体独立到models的album.go,go导出方法和结构,都是通过首字母大写的是公开的,其他的不导出,所以album改成Album
package models
type Album struct {
ID string `json:"id"`
Title string `json:"title"`
Artist string `json:"artist"`
Price float64 `json:"price"`
}
此时main.go就变成了这样子
package main
import (
"example/web-service-gin/controller"
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
router.GET("/albums", controller.GetAlbums)
router.POST("/albums", controller.GetAlbums)
router.Run("localhost:8080")
}
单元测试
安装依赖
golang的版本有要求,go install 1.15.x的版本没有对go install 的支持,建议升级到最新的版本
go install golang.org/dl/go1.18beta1@latest
单应测试样例
创建test文件夹,创建api_test.go,注意名称要以_test结尾
package test
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"testing"
)
func TestApi(t *testing.T) {
res, err := http.Get("http://localhost:8080/albums")
if err != nil {
panic(err)
}
//defer 在作用域内最后执行
defer res.Body.Close()
result, _ := ioutil.ReadAll(res.Body)
fmt.Println(string(result))
}
func TestPost(t *testing.T) {
data, _ := json.Marshal(struct{ Name, Age string }{})
res, err := http.Post("http://localhost:8080/albums", "application/json", bytes.NewBuffer(data))
if err != nil {
panic(err)
}
defer res.Body.Close()
result, _ := ioutil.ReadAll(res.Body)
fmt.Println(string(result))
}
mysql访问
package db
import (
"database/sql"
"example/web-service-gin/models"
"fmt"
"log"
"github.com/go-sql-driver/mysql"
)
func QueryData () (albumArr []models.Album,queryErr error){
var db *sql.DB;
cft :=mysql.Config{
User:"root",
Passwd: "123456",
Net: "tcp",
Addr: "127.0.0.1:3306",
DBName: "recodings",
AllowNativePasswords:true,
}
db, err := sql.Open("mysql", cft.FormatDSN())
if err != nil {
log.Fatal(err)
}
pingErr := db.Ping()
if pingErr != nil {
log.Fatal(pingErr)
}
fmt.Println("Connected!")
rows,dbErr:= db.Query("select * from album");
if(dbErr!=nil){
}
defer rows.Close()
var albums []models.Album
for rows.Next() {
var alb models.Album
if err := rows.Scan(&alb.ID, &alb.Title, &alb.Artist, &alb.Price); err != nil {
return nil, nil
}
albums = append(albums, alb)
}
return albums,nil
}
单元测试
package test
import (
"encoding/json"
"example/web-service-gin/db"
"fmt"
"testing"
)
func TestDB(t *testing.T) {
albums, err := db.QueryData()
if err != nil {
fmt.Println(err)
}
data, _ := json.MarshalIndent(albums, "", " ")
fmt.Println(string(data))
}
得到下面的结果
=== RUN TestDB
Connected!
[
{
"id": "1",
"title": "Blue Train",
"artist": "John Coltrane",
"price": 56.99
},
{
"id": "2",
"title": "Giant Steps",
"artist": "John Coltrane",
"price": 63.99
},
{
"id": "3",
"title": "Jeru",
"artist": "Gerry Mulligan",
"price": 17.99
},
{
"id": "4",
"title": "Sarah Vaughan",
"artist": "Sarah Vaughan",
"price": 34.98
}
]
--- PASS: TestDB (0.00s)
PASS
ok example/web-service-gin/test (cached)
接口
接口定义
package interfacedemo
import "example/web-service-gin/models"
type IAlbumService interface {
GetAlbum() []models.Album
}
实现接口,AlbumService在代码上,不需要引入IAlbumService,只要AlbumService实现了所有的方法即代表AlbumService实现了接口IAlbumService(目前来看,接口没有字段的定义),如果没实现所有的方法编译器将会提示declaration: missing method GetAlbum
package interfacedemo
import "example/web-service-gin/models"
type AlbumService struct {
}
func (service *AlbumService) GetAlbum() []models.Album {
return []models.Album{{ID: "10", Title: "Blue Train", Artist: "John Coltrane", Price: 100.99}}
}
调用
package test
import (
"encoding/json"
"example/web-service-gin/interfacedemo"
"example/web-service-gin/models"
"fmt"
"testing"
)
func TestInterface(t *testing.T) {
//接口接收实现的实例
var service interfacedemo.IAlbumService =new(interfacedemo.AlbumService)
var albums []models.Album = service.GetAlbum()
data, _ := json.MarshalIndent(albums, "", " ")
fmt.Println("data:", string(data))
}
指针
&取址操作,*声明指针类型的变量或者取指针变量指向的值
num := 10
//*声明指针的变量
var numPtr *int = nil
//&取num变量的地址
numPtr = &num
//*取指针变量numPtr指向的值
ptrValue := *numPtr
指针是强类型的,不匹配的类型不能赋值,比如下面的语句就是错误的
var floatPtr *float32=numPtr
不过对于struct的赋值,具有隐式转换,比如下面的例子
type IPointer interface{GetPoint()}
type Pointer struct {}
func (pointer Pointer) GetPoint() {}
func main() {
var pt *Pointer = new(Pointer)
var pt1 IPointer = *point
var pt2 IPointer = point
var pointer Pointer = Pointer{}
var pointer1 IPointer = &pointer
var pointer2 IPointer = pointer
}
如果是结构体本身,那么不存在隐式转换
var point *Pointer = new(Pointer)
var point1 Pointer = *point//正确
var point2 *Pointer = point//正确
var point3 Pointer = point//错误
再看下面的例子
package interfacedemo
import "example/web-service-gin/models"
type IAlbumService interface {
GetAlbum() []models.Album
}
AlbumService和AlbumServiceImpl实现了接口IAlbumService
package interfacedemo
import "example/web-service-gin/models"
type AlbumService struct {}
// 通过AlbumService实现方法GetAlbum,通过该方式实现,指针(隐式转换)和实例均可给接口进行赋值
func (service AlbumService) GetAlbum() []models.Album {
return []models.Album{{ID: "10", Title: "Blue Train", Artist: "John Coltrane", Price: 100.99}}
}
type AlbumServiceImpl struct{}
// 通过AlbumServiceImpl的指针类型实现GetAlbum,只能通过指针给接口进行赋值
func (service *AlbumServiceImpl) GetAlbum() []models.Album {
return []models.Album{{ID: "10", Title: "Blue Train Impl", Artist: "John Coltrane", Price: 100.99}}
}
通过以下的方式调用
var service *interfacedemo.AlbumService = new(interfacedemo.AlbumService)
var service1 interfacedemo.IAlbumService = service//正确,隐式转换
service1 = *service//正确,AlbumService实例实现了GetAlbum,比较规范的写法
var instance interfacedemo.AlbumService = interfacedemo.AlbumService{}
var instance1 interfacedemo.IAlbumService = service//正确,AlbumService实例实现了GetAlbum,比较规范的写法
instance1 = &instance//正确,隐式转换
var serviceImpl interfacedemo.AlbumServiceImpl = interfacedemo.AlbumServiceImpl{}
var serviceImpl1 interfacedemo.IAlbumService = &serviceImpl//正确
serviceImpl1 = serviceImpl//错误,AlbumServiceImpl的实例并没有实现GetAlbum
var implInstance *interfacedemo.AlbumServiceImpl = new(interfacedemo.AlbumServiceImpl)
var implInstance1 interfacedemo.IAlbumService = implInstance//正确
implInstance1 = *implInstance//错误,AlbumServiceImpl的实例并没有实现GetAlbum
接口本身声明指针类型是可以的,除了赋值为nil,没办法实例化
var service *interfacedemo.IAlbumService =nil//正确
service = new(interfacedemo.AlbumService)//错误
会抛异常:cannot use new(interfacedemo.AlbumService) (value of type *interfacedemo.AlbumService) as *interfacedemo.IAlbumService value in assignment
golang的指针类型跟C++的有所不同
type Pointer struct {}
func main() {
var instance Pointer = Pointer{}
fmt.Println(instance,instancePtr,&instancePtr)
}
得到的结果是
{} &{} 0xc000006028
&instance并不是得到一个地址,更像一个表示对instance取址的操作,&instancePtr则是instancePtr的地址,也就是指针的指针
那如果输出修改成
fmt.Println(instance,&instance,&(&instance))
&(&instance)编译器报错:invalid operation: cannot take address of (&instance) (value of type *Pointer)
&instancePtr与&(&instance)不相同,&(&instance)这样并没有给指向&instance分配地址
未完待续…… -->