Beego in Golang for Beginners

RESTful And MVC.

Let's install and create the first project

Install Beego easier than steamed turnips:

go get -u github.com/beego/beego/v2

The command will download Beego and add it to working directory GOPATH.

You will also need to install Bee from the creators of Beego, which is a tool command line:

go get -u github.com/beego/bee/v2

After installing Beego and Bee You can now start working.

In the terminal, go to the directory where you want to place the new project, and run the command:

bee new <имя_проекта>

After creating the project, in the project directory you can see standard structure Beego catalogs:

controllers – for controller files

models – for data models

routers — to configure routes

views – for view templates

main.go — entry point to the application

Launch looks simple enough:

bee run

So let's start the web server on localhost with default port 8080 and you can see the results of the application by going to the address http://localhost:8080 in the browser.

Working with a router

Routing in Beego it is defined in the file routers/router.go. In this file you can specify which controllers should handle different URL paths. Beego supports static And parameterized routing and route groupingV. For example, example of static routing:

package routers

import (
    "myapp/controllers"
    "github.com/beego/beego/v2/server/web"
)

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

To process requests to the site root / used MainController.

Example of parameterized routing:

web.Router("/user/:id", &controllers.UserController{})

To process requests to /user/123Where 123 is a dynamic parameter idused UserController.

Controllers in Beego are inherited from beego.Controller and contain methods corresponding to HTTP methods Get, Post, Delete etc., which are called when accessing the routes associated with them. Controller example:

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 processes GET requests. Data passed to the view index.tplare installed via c.Data.

Beego allows you to customize routes more flexibly using functionsweb.NSRouter, web.IncludeAnd web.NSNamespace for grouping routes and generally more convenient organization of applications.

Example of grouping routes:

ns := web.NewNamespace("/v1",
    web.NSRouter("/user", &controllers.UserController{}, "get:ListUsers"),
    web.NSRouter("/user/:id", &controllers.UserController{}, "get:GetUser"),
)

web.AddNamespace(ns)

Created a namespace /v1which groups routes related to users.

Also there are interceptorswhich allow code to be executed before or after certain controllers:

web.InsertFilter("/user/*", web.BeforeExec, InterceptorFunc)

InterceptorFunc will be called before each execution of controller methods that process paths matching the pattern /user/*.

There is also a mechanism auto-routingwhich automatically associates URLs with controller methods based on their names:

web.AutoRouter(&controllers.YourController{})

So Beego will automatically route requests like /yourcontroller/methodname to the appropriate methods in YourController.

To solve more specific routing problems, Beego allows configure the router using regular expressions and conditions:

web.Router("/user/:id([0-9]+)", &controllers.UserController{})

Inquiries to /user/123 will be processed UserControllerwherein :id must match regular expression [0-9]+which means one or more digits.

Beego is great for creating RESTful API thanks to its routing system. You can define routes for each of the HTTP methods (GET, POST, PUT, DELETE, etc.) and associate them with the corresponding methods in the controllers:

web.Router("/api/user", &controllers.UserController{}, "get:GetUsers;post:CreateUser")
web.Router("/api/user/:id", &controllers.UserController{}, "get:GetUser;put:UpdateUser;delete:DeleteUser")

Using interceptors, auto-routing, custom paths with regular expressions and RESTful URL support, you can perfectly manage flows.

Working with the database

ORM Beego allows you to map database tables to Go structures, turning table rows into Go objects.

Connecting to a database using MySQL as an example

Let's install:

go get -u github.com/go-sql-driver/mysql

In the Beego application configuration file conf/app.conf specify connection parameters to MySQL:

[database]
dbDriver = mysql
dbUser = ваш_пользователь
dbPass = ваш_пароль
dbName = имя_базы_данных
dbHost = 127.0.0.1
dbPort = 3306

In the application (for example, in main.go) use the information from the config to initialize the connection to the database:

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

Other databases are connected in the same way.

Defining Models

To interact with the database you will need to define models. In Beego models – these are Go structures that reflect the structure of the tables in the database. Example of a simple user model:

type User struct {
    Id       int
    Name     string
    Password string
}

To use this model in Beego ORM needs to be registered in the ORM system. This is done like this:

func init() {
    orm.RegisterModel(new(User))
}

After setting up a connection to the database and defining models, you can begin operations with data.

To create a new record in the database, use Insert ORM object:

o := orm.NewOrm()
user := User{Name: "ivan", Password: "securepassword"}

id, err := o.Insert(&user)
if err != nil {
    // обработка ошибки
}

There is a method for reading Read:

o := orm.NewOrm()
user := User{Id: 1}

err := o.Read(&user)
if err == orm.ErrNoRows {
    // зЗапись не найдена
} else if err == nil {
    // запись найдена, `user` заполнен данными
}

To update a record update:

o := orm.NewOrm()
user := User{Id: 1, Name: "ivan Updated"}

_, err := o.Update(&user, "Name")
if err != nil {
    // обработка ошибки
}

To delete an entry Delete:

o := orm.NewOrm()
user := User{Id: 1}

_, err := o.Delete(&user)
if err != nil {
    // обработка ошибки
}

There are also transactions, compound queries, lazy loading, etc. For example, to perform a transaction:

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 also supports working with queries via 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)

Creating a User Interface

Beego uses Go's templating system html/template for dynamic HTML generation. Templates allow you to separate application logic from its presentation.

Templates are usually stored in a directory views and have an extension .tpl or .html. You can use constructs in templates Go Template to insert data, execute conditions and loops:

<!-- views/index.tpl -->
<html>
<body>
    <h1>{{.Title}}</h1>
    {{range .Items}}
        <div>{{ . }}</div>
    {{else}}
        <div>No items found.</div>
    {{end}}
</body>
</html>

In Beego controllers you can pass data to a template using a map 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 automatically compiles templates and renders them when the method is called ServeHTTP. The template name is specified in the property TplName controller.

There is also possibility of localization with support i18n in the package github.com/beego/beego/v2/server/web/i18n.

Localization files are stored in INI or JSON format and contain key-value pairs for translations. By default they are placed in the directory conf/locale:

// conf/locale/en-US.ini
hello = "Hello"

// conf/locale/ru-RU.ini
hello = "Привет"

IN main.go or in the initial application function you can initialize i18n and specify the directory with localization files:

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

To use localization in templates, you can use the function i18n.Tr.

<body>
    <h1>{{i18n.Tr "en-US" "hello"}}</h1>
</body>

And in controllers, you can use the same function to get localized strings programmatically:

codefunc (this *MainController) Get() {
    locale := "ru-RU" // Обычно это значение извлекается из настроек пользователя или заголовков запроса
    greeting := web.I18n(locale, "hello")
    this.Data["Greeting"] = greeting
    this.TplName = "index.tpl"
}

The language for the user is usually determined automatically based on the HTTP request headers, but it can also be set explicitly, for example, depending on the user's choice on the site.

Testing

Tests in Beego, by default, they are placed in the folder tests project.

Let's say we have a controller MainController with method Getwhich returns a simple message. Example test for this method:

Controller Definition:

// 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!")
}

Create a test file in the folder testsFor example, 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())
}

Using the package assert libraries testify to check that the response from the server is as expected. We create an HTTP request to the main page and check that the response status code is 200 and the response body contains “Hello, Beego!”

Tests in Beego, as in any Go application, can be launched using the command go test:

go test ./...

The command will recursively find and execute all the tests in your Beego project.

But to test the API you can use the package httptest. Let's say there is an API for getting information about users.

Example API controller:

// 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 test example:

// 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()))
}

Create an HTTP GET request to our API /user/1 and check that the status is the response code 200 OK and the response body matches the expected JSON object.

When testing components that interact with the database, you need to isolate the tests from the real database. This can be done using mocking or setting up a test database:

func init() {
    orm.RegisterDataBase("default", "sqlite3", "file::memory:?mode=memory&cache=shared", 30)
}

We use SQLite in memory to create an isolated test environment.

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

We insert a new user into the test database and check that the insertion operation was successful and the returned user ID is not zero.

Other features

Working with sessions and cookies

In Beego, sessions are activated in the configuration file app.conf using parameters sessionon, sessionproviderAnd sessionname. Example configuration for activating sessions:

sessionon = true
sessionprovider = "memory"
sessionname = "beegosessionID"

Once activated, sessions can be used directly in controllers:

func (c *MainController) AnyMethodName() {
    // установка значения сессии
    c.SetSession("key", "value")
    
    // получение значения сессии
    value := c.GetSession("key")
    
    // удаление значения сессии
    c.DelSession("key")
    
    // уничтожение сессии
    c.DestroySession()
}

Working with cookies is also extremely simplified:

func (c *MainController) AnyMethodName() {
    // установка куки
    c.Ctx.SetCookie("name", "value", expire)

    // чтение куки
    value := c.Ctx.GetCookie("name")
}

Logging

Setting:

logs.SetLogger(logs.AdapterFile, `{"filename":"log/myapp.log"}`)
logs.SetLevel(logs.LevelInfo)

Configures logging to a file with a specific logging level.

Beego allows you to define specials. error handling methods in controllers:

func (c *MainController) Error404() {
    c.Data["content"] = "Page not found"
    c.TplName = "404.tpl"
}

Automatic generation of API documentation

Beego integrated with Swagger, allowing you to automatically generate documentation for the API. To do this, you need to use code comments and the tool bee to generate documentation:

// @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() {
    // реализация метода
}

Using the command bee run -gendoc=true -downdoc=trueyou can generate Swagger documentation, which will be available at /swagger applications.


Beego simplifies common tasks in creating web applications.

OTUS experts talk more about programming languages ​​in practical online courses. With a complete catalog of courses you can check out the link.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *