Краткий обзор синтаксиса Go и практический контрольный список (версия 0.5)

задняя часть Go сервер Безопасность
Краткий обзор синтаксиса Go и практический контрольный список (версия 0.5)

Краткий обзор синтаксиса Go и практический контрольный список (версия 0.5)

Go CheatSheetЭто перечень грамматики и навыков в процессе изучения/практики Го, который принадлежитAwesome CheatSheetЭта серия посвящена повышению скорости обучения и эффективности исследований и разработок. Ее можно использовать в качестве краткого справочного руководства или легкого вводного учебного материала. Эта статья ссылается на множество отличных статей и демонстраций кода, а унифицированное объявление находится вGo Links; если вы хотите узнать больше об определенном аспекте, вы можете продолжить чтениеGo Development: основы синтаксиса и инженерные практики, или перейти кcoding-snippets/goПросмотрите реализации кода, которые используют Go для решения общих структур данных и алгоритмов, шаблонов проектирования и бизнес-функций.

Конфигурация среды и основы синтаксиса

можно пойти вздесьЗагрузите установочный пакет Go SDK или установите его с помощью диспетчера пакетов, например brew. Команда go полагается на переменную окружения $GOPATH для организации кода.В случае нескольких проектов ln также можно использовать для сопоставления каталогов, чтобы облегчить управление проектом. GOPATH позволяет установить несколько каталогов, и каждый каталог будет содержать три подкаталога: src используется для хранения исходного кода, pkg используется для хранения файлов, сгенерированных после компиляции, и bin используется для хранения исполняемых файлов, сгенерированных после компиляции.

После настройки среды вы можете использовать go get для получения зависимостей, go run для запуска программы и go build для компиляции проекта для создания исполняемых файлов с тем же именем пакета (именем папки). После Golang 1.8 поддерживается инструмент управления зависимостями dep.Для пустых проектов используйте dep init для инициализации конфигурации зависимостей, которая будет генерироватьGopkg.toml Gopkg.lock vendor/эти три файла (папки).

мы можем использоватьdep ensure -add github.com/pkg/errorsДобавьте зависимости, и после запуска он добавит следующие блокировки в файл toml:

[[constraint]]
  name = "github.com/pkg/errors"
  version = "0.8.0"

Простой код Hello World в Go выглядит следующим образом:

package main
import "fmt"
func main() {
    fmt.Println("hello world")
}

Также можно реализовать простой HTTP-сервер с помощью Beego:

package main
import "github.com/astaxie/beego"
func main() {
	beego.Run()
}

Go не вводит относительные пути, а определяет модули в папках.Например, мы создаем новую папку с именем math, а затем используемpackage mathобъявить модуль, которому принадлежит функция в этом файле.

import (
        mongo "mywebapp/libs/mongodb/db" // 对引入的模块重命名
        _ "mywebapp/libs/mysql/db" // 使用空白下划线表示仅调用其初始化函数

)

Внешняя ссылка на этот модуль требует использования рабочей области или относительного каталога поставщика, и его индекс каталога выглядит следующим образом:

cannot find package "sub/math" in any of:
    ${PROJECTROOT}/vendor/sub/math (vendor tree)
    /usr/local/Cellar/go/1.10/libexec/src/sub/math (from $GOROOT)
    ${GOPATH}/src/sub/math (from $GOPATH)

Go предусматривает, что в заголовке каждого исходного файла требуется объявление пакета, а исполняемый файл по умолчанию помещается в основной пакет, а функции с прописными буквами в каждом пакете используются как экспортируемые функции, видимые другим пакетам, а функции с строчными буквами невидимая по умолчанию.Частная функция.

Выражения и поток управления

Объявление и присвоение переменной

Будучи строго типизированным статическим языком, Go позволяет нам идентифицировать типы данных после переменных, а также обеспечивает автоматический вывод типов.

// 声明三个变量,皆为 bool 类型
var c, python, java bool

// 声明不同类型的变量,并且赋值
var i bool, j int = true, 2

// 复杂变量声明
var (
	ToBe   bool       = false
	MaxInt uint64     = 1<<64 - 1
	z      complex128 = cmplx.Sqrt(-5 + 12i)
)

// 短声明变量
c, python, java := true, false, "no!"

// 声明常量
const constant = "This is a constant"

В Go, если нам нужно сравнить сходство двух сложных объектов, мы можем использовать метод Reflect.DeepEqual:

m1 := map[string]int{
    "a":1,
    "b":2,
}
m2 := map[string]int{
    "a":1,
    "b":2,
}
fmt.Println(reflect.DeepEqual(m1, m2))

Условное суждение

Go предоставляет расширенный оператор if для условного суждения:

// 基础形式
if x > 0 {
	return x
} else {
	return -x
}

// 条件判断之前添加自定义语句
if a := b + c; a < 42 {
	return a
} else {
	return a - 42
}

// 常用的类型判断
var val interface{}
val = "foo"
if str, ok := val.(string); ok {
	fmt.Println(str)
}

Go также поддерживает использование оператора Switch:

// 基础格式
switch operatingSystem {
case "darwin":
	fmt.Println("Mac OS Hipster")
	// 默认 break,不需要显式声明
case "linux":
	fmt.Println("Linux Geek")
default:
	// Windows, BSD, ...
	fmt.Println("Other")
}

// 类似于 if,可以在条件之前添加自定义语句
switch os := runtime.GOOS; os {
case "darwin": ...
}

// 使用 switch 语句进行类型判断:
switch v := anything.(type) {
  case string:
    fmt.Println(v)
  case int32, int64:
    ...
  default:
    fmt.Println("unknown")
}

Сравнения также поддерживаются в Switch:

number := 42
switch {
	case number < 42:
		fmt.Println("Smaller")
	case number == 42:
		fmt.Println("Equal")
	case number > 42:
		fmt.Println("Greater")
}

Или для сопоставления нескольких условий:

var char byte = '?'
switch char {
	case ' ', '?', '&', '=', '#', '+', '%':
		fmt.Println("Should escape")
}

цикл

Go поддерживает зацикливание с оператором for, нет ни while, ни until:

for i := 1; i < 10; i++ {
}

// while - loop
for ; i < 10;  {
}

// 单条件情况下可以忽略分号
for i < 10  {
}

// ~ while (true)
for {
}

Мы также можем использовать функцию диапазона для перебора массивов и срезов:

// loop over an array/a slice
for i, e := range a {
    // i 表示下标,e 表示元素
}

// 仅需要元素
for _, e := range a {
    // e is the element
}

// 或者仅需要下标
for i := range a {
}

// 定时执行
for range time.Tick(time.Second) {
    // do it once a sec
}

Функция: функция

Определение, параметры и возвращаемые значения

// 简单函数定义
func functionName() {}

// 含参函数定义
func functionName(param1 string, param2 int) {}

// 多个相同类型参数的函数定义
func functionName(param1, param2 int) {}

// 函数表达式定义
add := func(a, b int) int {
	return a + b
}

Go поддерживает последний параметр функции как неопределенный параметр с помощью ... , то есть одно или несколько значений параметров могут быть переданы в:

func adder(args ...int) int {
	total := 0
	for _, v := range args { // Iterates over the arguments whatever the number.
		total += v
	}
	return total
}

adder(1, 2, 3) // 6
adder(9, 9) // 18

nums := []int{10, 20, 30}
adder(nums...) // 60

Мы также можем использовать функцию-заглушку в качестве параметра функции для реализации функции функции обратного вызова:

func Filter(s []int, fn func(int) bool) []int {
    var p []int // == nil
    for _, v := range s {
        if fn(v) {
            p = append(p, v)
        }
    }
    return p
}

Хотя Go не является функциональным языком, с его помощью также можно реализовать каррирующие функции:

func add(x, y int) int {
    return x+ y
}

func adder(x int) (func(int) int) {
    return func(y int) int {
        return add(x, y)
    }
}

func main() {
	add3 := adder(3)
	fmt.Println(add3(4))    // 7
}

Go поддерживает несколько возвращаемых значений:

// 返回单个值
func functionName() int {
    return 42
}

// 返回多个值
func returnMulti() (int, string) {
    return 42, "foobar"
}
var x, str = returnMulti()

// 命名返回多个值
func returnMulti2() (n int, s string) {
    n = 42
    s = "foobar"
    // n and s will be returned
    return
}
var x, str = returnMulti2()

Закрытие: Закрытие

Go также поддерживает лексическую область видимости и сохранение переменных, поэтому мы можем использовать замыкания для доступа к переменным за пределами определения функции:

func scope() func() int{
    outer_var := 2
    foo := func() int { return outer_var}
    return foo
}

Замыкания не могут напрямую изменять внешние переменные, но автоматически переопределяют новые значения переменных:

func outer() (func() int, int) {
    outer_var := 2
    inner := func() int {
        outer_var += 99
        return outer_var // => 101 (but outer_var is a newly redefined
    }
    return inner, outer_var // => 101, 2 (outer_var is still 2, not mutated by inner!)
}

выполнение функции

Go предоставляет ключевое слово defer, которое позволяет отложить выполнение оператора до тех пор, пока функция не вернётся:

func read(...) (...) {
  f, err := os.Open(file)
  ...
  defer f.Close()
  ...
  return .. // f will be closed

Обработка исключений

В языке Go нет ключевых слов для обработки исключений, таких как try-catch.Для тех функций, которые могут возвращать исключения, необходимо только добавить дополнительное возвращаемое значение типа Error к возвращаемому значению функции:

type error interface {
    Error() string
}

Вызов функции, который может вернуть исключение, выглядит следующим образом:

import (
    "fmt"
    "errors"
)

func main() {
    result, err:= Divide(2,0)

    if err != nil {
            fmt.Println(err)
    }else {
            fmt.Println(result)
    }
}

func Divide(value1 int,value2 int)(int, error) {
    if(value2 == 0){
        return 0, errors.New("value2 mustn't be zero")
    }
    return value1/value2  , nil
}

Go также предоставляет нам функцию паники, так называемая паника означает, что ожидаемый результат не получен, и она часто используется для выдачи ненормальных результатов. Например, когда мы получаем исключение, возвращаемое функцией, но не знаем, как с ним бороться или нам не нужно с ним бороться, мы можем напрямую прервать текущую операцию через функцию паники, распечатать информацию об ошибке, горутинную трассировку информацию и вернуть ненулевой код состояния:

_, err := os.Create("/tmp/file")
if err != nil {
	panic(err)
}

Типы данных и структуры

Привязка типов и инициализация

Go — тип, ключевое слово может переименовать тип:

// IntSlice 并不等价于 []int,但是可以利用类型转换进行转换
type IntSlice []int
a := IntSlice{1, 2}

Вы можете использовать T(v) или obj.(T) для преобразования типов, obj.(T) работает только для типов interface{}:

t := obj.(T) // if obj is not T, error
t, ok := obj.(T) // if obj is not T, ok = false

// 类型转换与判断
str, ok := val.(string);

базовый тип данных

interface {} // ~ java Object
bool // true/false
string
int8  int16  int32  int64
int // =int32 on 32-bit, =int64 if 64-bit OS
uint8 uint16 uint32 uint64 uintptr
uint
byte // alias for uint8
rune // alias for int32, represents a Unicode code point
float32 float64

нить

// 多行字符串声明
hellomsg := `
 "Hello" in Chinese is 你好 ('Ni Hao')
 "Hello" in Hindi is नमस्ते ('Namaste')
`

Строка формата:

fmt.Println("Hello, 你好, नमस्ते, Привет, ᎣᏏᏲ") // basic print, plus newline
p := struct { X, Y int }{ 17, 2 }
fmt.Println( "My point:", p, "x coord=", p.X ) // print structs, ints, etc
s := fmt.Sprintln( "My point:", p, "x coord=", p.X ) // print to string variable

fmt.Printf("%d hex:%x bin:%b fp:%f sci:%e",17,17,17,17.0,17.0) // c-ish format
s2 := fmt.Sprintf( "%d %f", 17, 17.0 ) // formatted print to string variable

тип последовательности

И Array, и Slice могут использоваться для представления данных последовательности, и они также в определенной степени связаны.

Array

Массив используется для представления объектов последовательности фиксированной длины одного и того же типа, которые могут быть созданы в следующей форме:

[N]Type
[N]Type{value1, value2, ..., valueN}

// 由编译器自动计算数目
[...]Type{value1, value2, ..., valueN}

Его конкретное использование заключается в следующем:

// 数组声明
var a [10]int

// 赋值
a[3] = 42

// 读取
i := a[3]

// 声明与初始化
var a = [2]int{1, 2}
a := [2]int{1, 2}
a := [...]int{1, 2}

В Go есть встроенные функции len и cap для получения размера и емкости массива:

var arr = [3]int{1, 2, 3}
arr := [...]int{1, 2, 3}

len(arr) // 3
cap(arr) // 3

В отличие от указателя в C/C++ или ссылки на объект в Java, массив в Go — это просто значение. Это означает, что при копировании массива или передаче параметра в вызове функции по значению копируются все копии элементов, а не только указатели или ссылки. Очевидно, что такое дублирование будет стоить дороже.

Slice

Срез предоставляет нам более гибкие и легкие операции с последовательностями.Срезы можно создавать следующими способами:

// 使用内置函数创建
make([]Type, length, capacity)
make([]Type, length)

// 声明为不定长度数组
[]Type{}
[]Type{value1, value2, ..., valueN}

// 对现有数组进行切片转换
array[:]
array[:2]
array[2:]
array[2:3]

В отличие от Array, Slice можно рассматривать как более гибкий ссылочный тип (Reference Type), он фактически хранит не значения массива, а структуру, содержащую три атрибута: указатель массива (ptr), len и cap. Другими словами, Slice можно рассматривать как описание сегмента в массиве, включая указатель на массив, длину сегмента и максимально возможную длину сегмента.Его структура показана на следующем рисунке:

group 2

// 创建 len 为 5,cap 为 5 的 Slice
s := make([]byte, 5)

// 对 Slice 进行二次切片,此时 len 为 2,cap 为 3
s = s[2:4]

// 恢复 Slice 的长度
s = s[:cap(s)]

Следует отметить, что операция среза фактически не копирует значение в срезе, а только создает новый указатель на исходный массив, что гарантирует, что операция среза и индекс массива имеют одинаковую высокую эффективность. Однако, если мы изменим значение в Slice, это фактически изменит значение в базовом массиве, и это также будет отражено в исходном массиве:

d := []byte{'r', 'o', 'a', 'd'}
e := d[2:]
// e == []byte{'a', 'd'}
e[1] = 'm'
// e == []byte{'a', 'm'}
// d == []byte{'r', 'o', 'a', 'm'}

Go предоставляет встроенную функцию добавления для динамического добавления данных в срез, которая возвращает новый объект среза, содержащий исходный срез и новое значение. Если емкости исходного слайса недостаточно для хранения новой последовательности, новая память будет выделена автоматически:

// len=0 cap=0 []
var s []int

// len=1 cap=2 [0]
s = append(s, 0)

// len=2 cap=2 [0 1]
s = append(s, 1)

// len=5 cap=8 [0 1 2 3 4]
s = append(s, 2, 3, 4)

// 使用 ... 来自动展开数组
a := []string{"John", "Paul"}
b := []string{"George", "Ringo", "Pete"}
a = append(a, b...) // equivalent to "append(a, b[0], b[1], b[2])"
// a == []string{"John", "Paul", "George", "Ringo", "Pete"}

Мы также можем использовать встроенную функцию копирования для копирования срезов Эта функция поддерживает копирование срезов разной длины и автоматически использует минимальное количество элементов. В то же время функция копирования также может автоматически обрабатывать копирование фрагментов между использованием одного и того же базового массива, чтобы избежать лишней траты пространства.

func copy(dst, src []T) int

// 申请较大的空间容量
t := make([]byte, len(s), (cap(s)+1)*2)
copy(t, s)
s = t

тип карты

var m map[string]int
m = make(map[string]int)
m["key"] = 42

// 删除某个键
delete(m, "key")

// 测试该键对应的值是否存在
elem, has_value := m["key"]

// map literal
var m = map[string]Vertex{
    "Bell Labs": {40.68433, -74.39967},
    "Google":    {37.42202, -122.08408},
}

Структура и интерфейс: Структура и интерфейс

Структура: структура

В языке Go нет понятия класса, есть только структура, которую можно рассматривать как набор атрибутов, и для нее можно определить методы.

// 声明结构体
type Vertex struct {
    // 结构体的属性,同样遵循大写导出,小写私有的原则
    X, Y int
    z bool
}

// 也可以声明隐式结构体
point := struct {
	X, Y int
}{1, 2}

// 创建结构体实例
var v = Vertex{1, 2}

// 读取或者设置属性
v.X = 4;

// 显示声明键
var v = Vertex{X: 1, Y: 2}

// 声明数组
var v = []Vertex{{1,2},{5,2},{5,5}}

Объявление метода также очень лаконичное, вам нужно только объявить указатель структуры между ключевым словом func и именем функции, и структура будет скопирована между разными методами:

func (v Vertex) Abs() float64 {
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

// Call method
v.Abs()

Для тех методов, которым необходимо изменить текущий объект структуры, вам необходимо передать указатель:

func (v *Vertex) add(n float64) {
    v.X += n
    v.Y += n
}
var p *Person = new(Person) // pointer of type Person

Указатель: указатель

// p 是 Vertex 类型
p := Vertex{1, 2}  

// q 是指向 Vertex 的指针
q := &p

// r 同样是指向 Vertex 对象的指针
r := &Vertex{1, 2}

// 指向 Vertex 结构体对象的指针类型为 *Vertex
var s *Vertex = new(Vertex)

Интерфейс: интерфейс

GO позволяет нам добиться полиморфизма, определяя интерфейсы:

// 接口声明
type Awesomizer interface {
    Awesomize() string
}

// 结构体并不需要显式实现接口
type Foo struct {}

// 而是通过实现所有接口规定的方法的方式,来实现接口
func (foo Foo) Awesomize() string {
    return "Awesome!"
}
type Shape interface {
   area() float64
}

func getArea(shape Shape) float64 {
   return shape.area()
}

type Circle struct {
   x,y,radius float64
}

type Rectangle struct {
   width, height float64
}

func(circle Circle) area() float64 {
   return math.Pi * circle.radius * circle.radius
}

func(rect Rectangle) area() float64 {
   return rect.width * rect.height
}

func main() {
   circle := Circle{x:0,y:0,radius:5}
   rectangle := Rectangle {width:10, height:5}

   fmt.Printf("Circle area: %f\n",getArea(circle))
   fmt.Printf("Rectangle area: %f\n",getArea(rectangle))
}
//Circle area: 78.539816
//Rectangle area: 50.000000

Обычная идея состоит в том, чтобы сначала определить интерфейс, затем определить реализацию и, наконец, определить используемый метод:

package animals

type Animal interface {
	Speaks() string
}

// implementation of Animal
type Dog struct{}
func (a Dog) Speaks() string { return "woof" }

/** 在需要的地方直接引用 **/

package circus

import "animals"

func Perform(a animal.Animal) { return a.Speaks() }

Go также предоставляет нам другую схему реализации интерфейса, мы не можем определить интерфейс в конкретной реализации, но там, где интерфейс нужно использовать, шаблон такой:

func funcName(a INTERFACETYPE) CONCRETETYPE

Определите интерфейс:

package animals

type Dog struct{}
func (a Dog) Speaks() string { return "woof" }

/** 在需要使用实现的地方定义接口 **/
package circus

type Speaker interface {
	Speaks() string
}

func Perform(a Speaker) { return a.Speaks() }

Embedding

В языке Go нет концепции наследования подклассов, но объединение классов или интерфейсов реализуется путем встраивания.

// ReadWriter 的实现需要同时满足 Reader 与 Writer
type ReadWriter interface {
    Reader
    Writer
}

// Server 暴露了所有 Logger 结构体的方法
type Server struct {
    Host string
    Port int
    *log.Logger
}

// 初始化方式并未受影响
server := &Server{"localhost", 80, log.New(...)}

// 却可以直接调用内嵌结构体的方法,等价于 server.Logger.Log(...)
server.Log(...)

// 内嵌结构体的名词即是类型名
var logger *log.Logger = server.Logger

параллельное программирование

Goroutines

Горутины — это легкие потоки, вы можете обратиться кВведение в параллельное программированиеОбсуждение процессов, потоков и сопрограмм в этой статье; Go предоставляет нам очень удобный синтаксис Goroutines:

// 普通函数
func doStuff(s string) {
}

func main() {
    // 使用命名函数创建 Goroutine
    go doStuff("foobar")

    // 使用匿名内部函数创建 Goroutine
    go func (x int) {
        // function body goes here
    }(42)
}

Channels

Канал — это типизированный канал, который можно использовать для передачи сообщений между различными горутинами.Его основные операции таковы:

// 创建类型为 int 的信道
ch := make(chan int)

// 向信道中发送值
ch <- 42

// 从信道中获取值
v := <-ch

// 读取,并且判断其是否关闭
v, ok := <-ch

// 读取信道,直至其关闭
for i := range ch {
    fmt.Println(i)
}

Например, мы можем дождаться сообщения от горутины в основном потоке и вывести:

// 创建信道
messages := make(chan string)

// 执行 Goroutine
go func() { messages <- "ping" }()

// 阻塞,并且等待消息
msg := <-messages

// 使用信道进行并发地计算,并且阻塞等待结果
c := make(chan int)
go sum(s[:len(s)/2], c)
go sum(s[len(s)/2:], c)
x, y := <-c, <-c // 从 c 中接收

Выше были созданы небуферизованные каналы, которые являются блокирующими каналами; модуль чтения будет продолжать блокироваться, когда нет значения, а модуль записи будет блокироваться, когда нет чтения. Мы можем создать буферизованные каналы, читатели которых не будут заблокированы, пока канал не будет заполнен:

ch := make(chan int, 100)

// 发送方也可以主动关闭信道
close(ch)

Канал также можно использовать как параметр функции, и мы можем явно объявить, используется ли он для отправки или получения информации, тем самым повышая безопасность типов программы:

// ping 函数用于发送信息
func ping(pings chan<- string, msg string) {
    pings <- msg
}

// pong 函数用于从某个信道中接收信息,然后发送到另一个信道中
func pong(pings <-chan string, pongs chan<- string) {
    msg := <-pings
    pongs <- msg
}

func main() {
    pings := make(chan string, 1)
    pongs := make(chan string, 1)
    ping(pings, "passed message")
    pong(pings, pongs)
    fmt.Println(<-pongs)
}

Синхронизировать

Синхронизация является общим требованием в параллельном программировании.Здесь мы можем использовать функцию блокировки канала для достижения синхронизации между горутинами:

func worker(done chan bool) {
    time.Sleep(time.Second)
    done <- true
}

func main() {
    done := make(chan bool, 1)
    go worker(done)

	// 阻塞直到接收到消息
    <-done
}

Go также предоставляет нам ключевое слово select для ожидания результатов выполнения нескольких каналов:

// 创建两个信道
c1 := make(chan string)
c2 := make(chan string)

// 每个信道会以不同时延输出不同值
go func() {
	time.Sleep(1 * time.Second)
	c1 <- "one"
}()
go func() {
	time.Sleep(2 * time.Second)
	c2 <- "two"
}()

// 使用 select 来同时等待两个信道的执行结果
for i := 0; i < 2; i++ {
	select {
	case msg1 := <-c1:
		fmt.Println("received", msg1)
	case msg2 := <-c2:
		fmt.Println("received", msg2)
	}
}

веб-программирование

HTTP Server

package main

import (
    "fmt"
    "net/http"
)

// define a type for the response
type Hello struct{}

// let that type implement the ServeHTTP method (defined in interface http.Handler)
func (h Hello) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w, "Hello!")
}

func main() {
    var h Hello
    http.ListenAndServe("localhost:4000", h)
}

// Here's the method signature of http.ServeHTTP:
// type Handler interface {
//     ServeHTTP(w http.ResponseWriter, r *http.Request)
// }

Beego

Используйте официальную рекомендацию BeegobeeИнструмент командной строки, мы можем быстро создать проект Beego, каталог которого организован следующим образом:

quickstart
├── conf
│   └── app.conf
├── controllers
│   └── default.go
├── main.go
├── models
├── routers
│   └── router.go
├── static
│   ├── css
│   ├── img
│   └── js
├── tests
│   └── default_test.go
└── views
    └── index.tpl

В файле main.go мы можем запустить экземпляр Beego и вызвать файл конфигурации инициализации маршрута:

package main

import (
        _ "quickstart/routers"
        "github.com/astaxie/beego"
)

func main() {
        beego.Run()
}

В функции инициализации маршрута мы объявим отношение сопоставления между каждым маршрутом и контроллером:

package routers

import (
        "quickstart/controllers"
        "github.com/astaxie/beego"
)

func init() {
        beego.Router("/", &controllers.MainController{})
}

Также возможно вручную указать статические сопоставления ресурсов в проектах Beego:

beego.SetStaticPath("/down1", "download1")
beego.SetStaticPath("/down2", "download2")

В конкретном контроллере вы можете установить возвращаемые данные или связанное имя шаблона:

package controllers

import (
        "github.com/astaxie/beego"
)

type MainController struct {
        beego.Controller
}

func (this *MainController) Get() {
        this.Data["Website"] = "beego.me"
        this.Data["Email"] = "astaxie@gmail.com"
        this.TplNames = "index.tpl" // version 1.6 use this.TplName = "index.tpl"
}

DevPractics: практика разработки

файл читать и писать

import (
    "io/ioutil"
)
...
datFile1, errFile1 := ioutil.ReadFile("file1")
if errFile1 != nil {
	panic(errFile1)
}
...

контрольная работа

VSCode может автоматически генерировать базовые тестовые сценарии для функций и предоставлять удобные функции выполнения сценариев использования и отладки.

/** 交换函数 */
func swap(x *int, y *int) {
	x, y = y, x
}

/** 自动生成的测试函数 */
func Test_swap(t *testing.T) {
	type args struct {
		x *int
		y *int
	}
	tests := []struct {
		name string
		args args
	}{
		// TODO: Add test cases.
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			swap(tt.args.x, tt.args.y)
		})
	}
}