
Привет, Хабр!
Beego – это фреймворк для разработки веб-приложений на языке Go, ориентированный на быстрое развертывание и простоту использования. В его основе лежит идея создания полнофункциональных приложений с минимум усилиям на настройку и кодирование. Это достигается за счет широкого выбора инструментов, включая ORM, систему маршрутизации, интерфейс кмд и многое другое. Beego придерживается принципов RESTful и MVC.
Установим и создадим первый проект
Установить Beego проще пареной репы:
go get -u github.com/beego/beego/v2
Команда загрузит Beego и добавит его в рабочую директорию GOPATH.
Также потребуется установить Bee от создателей Beego, который является инструментом командной строки:
go get -u github.com/beego/bee/v2
После установки Beego и Bee можно уже приступать к работе.
В терминале переходим в директорию, где хочется разместить новый проект, и выполняем команду:
bee new <имя_проекта>
После создания проекта, в директории проекта можно увидеть стандартную структуру каталогов Beego:
controllers — для файлов контроллеров
models — для моделей данных
routers — для настройки маршрутов
views — для шаблонов представлений
main.go — точка входа в приложение
Запуск выглядит достаточно просто:
bee run
Так запустим веб-сервер на localhost с портом по умолчанию 8080 и можно увидеть результаты работы приложения, перейдя по адресу http://localhost:8080 в браузере.
Работа с роутером
Маршрутизация в Beego определяется в файле routers/router.go. В этом файле можно указать какие контроллеры должны обрабатывать различные URL-пути. Beego поддерживает статическую и параметризованную маршрутизацию, а также группировку маршрутов. Например, пример статической маршрутизации:
package routers import ( "myapp/controllers" "github.com/beego/beego/v2/server/web" ) func init() { web.Router("/", &controllers.MainController{}) }
Для обработки запросов к корню сайта / используется MainController.
Пример параметризованной маршрутизации:
web.Router("/user/:id", &controllers.UserController{})
Для обработки запросов к /user/123, где 123 — это динамический параметр id, используется UserController.
Контроллеры в Beego наследуются от beego.Controller и содержат методы, соответствующие HTTP-методам Get, Post, Delete и т.д., которые вызываются при обращении к связанным с ними маршрутам. Пример контроллера:
package controllers import "github.com/beego/beego/v2/server/web" type MainController struct { web.Controller } func (c *MainController) Get() { c.Data["Website"] = "Beego.me" c.Data["Email"] = "contact@beego.me" c.TplName = "index.tpl" }
MainController обрабатывает GET-запросы. Данные, передаваемые в представление index.tpl, устанавливаются через c.Data.
Beego позволяет настраивать маршруты гибче, используя функцииweb.NSRouter, web.Include, и web.NSNamespace для группировки маршрутов и в целом более удобной организации приложений.
Пример группировки маршрутов:
ns := web.NewNamespace("/v1", web.NSRouter("/user", &controllers.UserController{}, "get:ListUsers"), web.NSRouter("/user/:id", &controllers.UserController{}, "get:GetUser"), ) web.AddNamespace(ns)
Создали пространство имен /v1, в котором группируются маршруты, относящиеся к пользователям.
Также существуют перехватчики, которые позволяют выполнять код до или после определенных контроллеров:
web.InsertFilter("/user/*", web.BeforeExec, InterceptorFunc)
InterceptorFunc будет вызываться перед каждым выполнением методов контроллера, обрабатывающего пути, соответствующие шаблону /user/*.
Также есть механизм авто-роутинга, который автоматически связывает URL-адреса с методами контроллеров на основе их имен:
web.AutoRouter(&controllers.YourController{})
Так Beego будет автоматически маршрутизировать запросы типа /yourcontroller/methodname к соответствующим методам в YourController.
Для решения более специфических задач, связанных с маршрутизацией, Beego позволяет настраивать роутер с помощью регулярных выражений и условий:
web.Router("/user/:id([0-9]+)", &controllers.UserController{})
Запросы к /user/123 будут обрабатываться UserController, при этом :id должен соответствовать регулярному выражению [0-9]+, что означает одну или более цифр.
Beego суперски подходит для создания RESTful API благодаря своей системе маршрутизации. Можно определить маршруты для каждого из HTTP-методов (GET, POST, PUT, DELETE и т.д.) и связать их с соответствующими методами в контроллерах:
web.Router("/api/user", &controllers.UserController{}, "get:GetUsers;post:CreateUser") web.Router("/api/user/:id", &controllers.UserController{}, "get:GetUser;put:UpdateUser;delete:DeleteUser")
Используя перехватчики, авто-роутинг, кастомные пути с регулярными выражениями и поддержку RESTful URL, можно идеально управлять потоками.
Работа с БД
ORM Beego позволяет отображать таблицы БД на структуры Go, превращая строки таблиц в объекты Go.
Подключение к БД на примере MySQL
Установим:
go get -u github.com/go-sql-driver/mysql
В файле конфигурации приложения Beego conf/app.conf указываем параметры подключения к MySQL:
[database] dbDriver = mysql dbUser = ваш_пользователь dbPass = ваш_пароль dbName = имя_базы_данных dbHost = 127.0.0.1 dbPort = 3306
В приложении (например, в main.go) юзаем информацию из конфига для инициализации подключения к БД:
import ( "github.com/beego/beego/v2/client/orm" _ "github.com/go-sql-driver/mysql" ) func init() { orm.RegisterDriver("mysql", orm.DRMySQL) // строка подключения: пользователь:пароль@tcp(хост:порт)/имя_базы_данных orm.RegisterDataBase("default", "mysql", "user:password@tcp(127.0.0.1:3306)/dbname") }
Прочие БД подключаются аналогично.
Определение моделей
Для взаимодействия с БД понадобится определить модели. В Beego модели — это структуры Go, которые отражают структуру таблиц в базе данных. Пример простой модели пользователя:
type User struct { Id int Name string Password string }
Чтобы использовать эту модель в Beego ORM её нужно зарегистрировать в системе ORM. Это делается так:
func init() { orm.RegisterModel(new(User)) }
После настройки подключения к БД и определения моделей можно приступить к операциям с данными.
Чтобы создать новую запись в базе данных, используем Insert объекта ORM:
o := orm.NewOrm() user := User{Name: "ivan", Password: "securepassword"} id, err := o.Insert(&user) if err != nil { // обработка ошибки }
Для чтения есть метод Read:
o := orm.NewOrm() user := User{Id: 1} err := o.Read(&user) if err == orm.ErrNoRows { // зЗапись не найдена } else if err == nil { // запись найдена, `user` заполнен данными }
Для обновления записи апдейт:
o := orm.NewOrm() user := User{Id: 1, Name: "ivan Updated"} _, err := o.Update(&user, "Name") if err != nil { // обработка ошибки }
Для удаления записи Delete:
o := orm.NewOrm() user := User{Id: 1} _, err := o.Delete(&user) if err != nil { // обработка ошибки }
Также есть транзакции, составные запросы, ленивая загрузка и т.д. Например, для выполнения транзакции:
o := orm.NewOrm() err := o.Begin() user := User{Name: "Ivan Transaction", Password: "transpassword"} _, err = o.Insert(&user) if err != nil { o.Rollback() } else { o.Commit() }
Также Beego ORM поддерживает работу с запросами через QueryBuilder:
var users []User qb, _ := orm.NewQueryBuilder("mysql") qb.Select("id", "name").From("user").Where("id > ?").OrderBy("id").Desc().Limit(10) orm.NewOrm().Raw(qb.String(), 1).QueryRows(&users)
Создание пользовательского интерфейса
Beego использует систему шаблонов Go html/template для динамической генерации HTML. Шаблоны позволяют разделять логику приложения и его представление.
Шаблоны обычно хранятся в каталоге views и имеют расширение .tpl или .html. В шаблонах можно использовать конструкции Go Template для вставки данных, выполнения условий и циклов:
<!-- views/index.tpl --> <html> <body> <h1>{{.Title}}</h1> {{range .Items}} <div>{{ . }}</div> {{else}} <div>No items found.</div> {{end}} </body> </html>
В контроллерах Beego можно передавать данные в шаблон, используя мапу Data.
func (this *MainController) Get() { this.Data["Title"] = "My Page Title" this.Data["Items"] = []string{"Item 1", "Item 2", "Item 3"} this.TplName = "index.tpl" }
Beego автоматически компилирует шаблоны и рендерит их при вызове метода ServeHTTP. Имя шаблона указывается в свойстве TplName контроллера.
Также есть возможность локализации с помощью поддержки i18n в пакете github.com/beego/beego/v2/server/web/i18n.
Файлы локализации хранятся в формате INI или JSON и содержат пары ключ-значение для переводов. По дефолту они размещаются в каталоге conf/locale:
// conf/locale/en-US.ini hello = "Hello" // conf/locale/ru-RU.ini hello = "Привет"
В main.go или в начальной функции приложения можно инициализровать i18n и указать директорию с файлами локализации:
import "github.com/beego/beego/v2/server/web" func init() { web.SetStaticPath("/static", "static") web.AddFuncMap("i18n", web.I18n) web.LoadAppConfig("ini", "conf/app.conf") if err := web.LoadI18n("conf/locale"); err != nil { log.Fatal("Failed to load i18n files:", err) } }
Для использования локализации в шаблонах можно юзать функцию i18n.Tr.
<body> <h1>{{i18n.Tr "en-US" "hello"}}</h1> </body>
И в контроллерах можно использовать ту же функцию для получения локализованных строк программно:
codefunc (this *MainController) Get() { locale := "ru-RU" // Обычно это значение извлекается из настроек пользователя или заголовков запроса greeting := web.I18n(locale, "hello") this.Data["Greeting"] = greeting this.TplName = "index.tpl" }
Язык для пользователя обычно определяется автоматически на основе заголовков HTTP-запроса, но его также можно задать явно, например, в зависимости от выбора пользователя на сайте.
Тестирование
Тесты в Beego по дефолту размещаются в папке tests проекта.
Допустим, у нас есть контроллер MainController с методом Get, который возвращает простое сообщение. Пример теста для этого метода:
Определение контроллера:
// controllers/main.go package controllers import ( "github.com/beego/beego/v2/server/web" ) type MainController struct { web.Controller } func (c *MainController) Get() { c.Ctx.WriteString("Hello, Beego!") }
Создаем файл теста в папке tests, например, main_test.go:
// tests/main_test.go package test import ( "net/http" "net/http/httptest" "testing" "github.com/beego/beego/v2/server/web" "path/filepath" "runtime" _ "yourapp/routers" "github.com/stretchr/testify/assert" ) func init() { _, file, _, _ := runtime.Caller(0) apppath, _ := filepath.Abs(filepath.Dir(filepath.Join(file, ".." + string(filepath.Separator)))) web.TestBeegoInit(apppath) } func TestMainPage(t *testing.T) { r, _ := http.NewRequest("GET", "/", nil) w := httptest.NewRecorder() web.BeeApp.Handlers.ServeHTTP(w, r) assert.Equal(t, 200, w.Code) assert.Equal(t, "Hello, Beego!", w.Body.String()) }
Юзаем пакет assert библиотеки testify для проверки, что ответ от сервера соответствует ожиданиям. Создаем HTTP-запрос к главной странице и проверяем, что статус-код ответа равен 200 и тело ответа содержит "Hello, Beego!".
Тесты в Beego, как и в любом Go-приложении, можно запустить с помощью команды go test:
go test ./...
Команда рекурсивно найдет и выполнит все тесты в вашем проекте Beego.
А вот для тестирования API можно использовать пакет httptest. Предположим, есть API для получения информации о пользователях.
Пример контроллера API:
// controllers/user.go package controllers import ( "encoding/json" "github.com/beego/beego/v2/server/web" ) type UserController struct { web.Controller } func (c *UserController) GetUser() { userId := c.Ctx.Input.Param(":id") user := User{Id: userId, Name: "Test User"} c.Data["json"] = &user c.ServeJSON() } type User struct { Id string `json:"id"` Name string `json:"name"` }
Пример теста API:
// tests/api_test.go package test import ( "bytes" "net/http" "net/http/httptest" "testing" _ "yourapp/routers" "github.com/stretchr/testify/assert" ) func TestGetUser(t *testing.T) { req, err := http.NewRequest("GET", "/user/1", nil) if err != nil { t.Fatal(err) } rr := httptest.NewRecorder() web.BeeApp.Handlers.ServeHTTP(rr, req) assert.Equal(t, http.StatusOK, rr.Code) expected := `{"id":"1","name":"Test User"}` assert.Equal(t, expected, strings.TrimSpace(rr.Body.String())) }
Создаем HTTP GET запрос к нашему API /user/1 и проверяем, что статус код ответа 200 OK и тело ответа соответствует ожидаемому JSON-объекту.
При тестировании компонентов, взаимодействующих с БД, нужно изолировать тесты от реальной БД. Это можно сделать с помощью мокинга или настройки тестовой БД:
func init() { orm.RegisterDataBase("default", "sqlite3", "file::memory:?mode=memory&cache=shared", 30) }
Юзаем SQLite в памяти для создания изолированной тестовой среды.
func TestCreateUser(t *testing.T) { o := orm.NewOrm() o.Using("default") // user := &User{Name: "New User"} id, err := o.Insert(user) if err != nil { t.Errorf("Не удалось создать пользователя: %v", err) } assert.NotEqual(t, 0, id) }
Вставляем нового пользователя в тестовую БД и проверяем, что операция вставки прошла успешно, а возвращаемый идентификатор пользователя не равен нулю.
Прочие возможности
Работа с сессиями и куками
В Beego сессии активируются в файле конфигурации app.conf с использованием параметров sessionon, sessionprovider, и sessionname. Пример конфигурации для активации сессий:
sessionon = true sessionprovider = "memory" sessionname = "beegosessionID"
После активации, сессии можно использовать прямо в контроллерах:
func (c *MainController) AnyMethodName() { // установка значения сессии c.SetSession("key", "value") // получение значения сессии value := c.GetSession("key") // удаление значения сессии c.DelSession("key") // уничтожение сессии c.DestroySession() }
Работа с куками также предельно упрощена:
func (c *MainController) AnyMethodName() { // установка куки c.Ctx.SetCookie("name", "value", expire) // чтение куки value := c.Ctx.GetCookie("name") }
Логирование
Настройка:
logs.SetLogger(logs.AdapterFile, `{"filename":"log/myapp.log"}`) logs.SetLevel(logs.LevelInfo)
Настраивает логирование в файл с определенным уровнем логирования.
Beego позволяет определять спец. методы обработки ошибок в контроллерах:
func (c *MainController) Error404() { c.Data["content"] = "Page not found" c.TplName = "404.tpl" }
Автоматическая генерация документации API
Beego интегрирован с Swagger, позволяя автоматически генерировать документацию для API. Для этого необходимо использовать комментарии к коду и инструмент bee для генерации документации:
// @Title Get User // @Description get user by uid // @Param uid path int true "The key for staticblock" // @Success 200 {object} User // @Failure 403 :uid is empty // @router /:uid [get] func (u *UserController) Get() { // реализация метода }
Используя команду bee run -gendoc=true -downdoc=true, можно сгенерировать документацию Swagger, которая будет доступна по адресу /swagger приложения.
Beego упрощает стандартные задачи в создание веб-приложений.
Если вы изучаете Go или только планируете начать, рекомендуем обратить внимание на два курса — Golang Developer. Basic и Golang Developer. Professional.
Курс Golang Developer. Basic подойдёт тем, кто делает первые шаги в разработке на Go. Вы освоите синтаксис, работу с модулями, тестирование, базовые подходы к работе с HTTP и структурированием кода.
Если вы уже имеете опыт и хотите углубиться в особенности языка, его внутреннее устройство, освоить конкурентное программирование, микросервисы, RPC и работу с сетями, — будет актуален курс Golang Developer. Professional.

