Привет, Хабр!
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 упрощает стандартные задачи в создание веб-приложений.
Больше про языки программирования эксперты OTUS рассказывают в рамках практических онлайн-курсов. С полным каталогом курсов можно ознакомиться по ссылке.
n0ne
Хотелось бы сравнения с gin, Fiber, etc., в чем лучше, в чем хуже, быстродействие и т.п.