Skip to content

window下安装

// 查看版本
go version

查看go环境变量

shell
go env

vscode安装go插件

切换源

go env -w GOPROXY=https://mirrors.aliyun.com/goproxy/,direct

第一个go web程序

新建main.go

输入helloweb按回车

go
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

然后命令行访问服务

shell
curl http://localhost:8080

得到以下的结果,那么第一个golang web就执行成功了

go
StatusCode        : 200
StatusDescription : OK
Content           : Hello World! 2022-02-09 20:25:07.5051433 +0800 CST m=+55.242234801

restful接口定义

导入依赖

shell
go mod init  example/web-service-gin

产生模块管理文件

ini
module example/web-service-gin
go 1.15

main.go导入github.com/gin-gonic/gin

go
import (
  "net/http"
  "github.com/gin-gonic/gin"
)

执行

shell
go get .

go.mod(类似nodejs的package.json)

ini
module example/web-service-gin
go 1.15
require github.com/gin-gonic/gin v1.7.7

自动扫码导入依赖同时会发现目录产生了go.sum文件,其类似nodejs的package.json.lock文件

ini
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=
……

编写代码

go
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文件

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

go
package models

type Album struct {
	ID     string  `json:"id"`
	Title  string  `json:"title"`
	Artist string  `json:"artist"`
	Price  float64 `json:"price"`
}

此时main.go就变成了这样子

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
go install golang.org/dl/go1.18beta1@latest

单应测试样例

创建test文件夹,创建api_test.go,注意名称要以_test结尾

go
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访问

go
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
}

单元测试

go
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))
}

得到下面的结果

go
=== 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)

接口

接口定义

go
package interfacedemo

import "example/web-service-gin/models"

type IAlbumService interface {
	GetAlbum() []models.Album
}

实现接口,AlbumService在代码上,不需要引入IAlbumService,只要AlbumService实现了所有的方法即代表AlbumService实现了接口IAlbumService(目前来看,接口没有字段的定义),如果没实现所有的方法编译器将会提示declaration: missing method GetAlbum

go
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}}
}

调用

go
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))
}

指针

&取址操作,*声明指针类型的变量或者取指针变量指向的值

go
num := 10
//*声明指针的变量
var numPtr *int = nil
//&取num变量的地址
numPtr = &num
//*取指针变量numPtr指向的值
ptrValue := *numPtr

指针是强类型的,不匹配的类型不能赋值,比如下面的语句就是错误的

var
var floatPtr *float32=numPtr

不过对于struct的赋值,具有隐式转换,比如下面的例子

go
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
}

如果是结构体本身,那么不存在隐式转换

go
var point *Pointer = new(Pointer)
var point1 Pointer = *point//正确
var point2 *Pointer = point//正确
var point3 Pointer = point//错误

再看下面的例子

go
package interfacedemo

import "example/web-service-gin/models"

type IAlbumService interface {
	GetAlbum() []models.Album
}

AlbumService和AlbumServiceImpl实现了接口IAlbumService

go
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}}
}

通过以下的方式调用

go
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,没办法实例化

go
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++的有所不同

go
type Pointer struct {}
func main() {
	var instance Pointer = Pointer{}
	fmt.Println(instance,instancePtr,&instancePtr)
}

得到的结果是

go
{} &{} 0xc000006028

&instance并不是得到一个地址,更像一个表示对instance取址的操作,&instancePtr则是instancePtr的地址,也就是指针的指针

那如果输出修改成

go
fmt.Println(instance,&instance,&(&instance))

&(&instance)编译器报错:invalid operation: cannot take address of (&instance) (value of type *Pointer)

&instancePtr与&(&instance)不相同,&(&instance)这样并没有给指向&instance分配地址

未完待续…… -->