Ответы пользователя по тегу Go
  • Как распарсить json в массив структур golang (gin)?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Можно через map[string]Params
    Например вот так
    package main
    
    import (
        "encoding/json"
        "fmt"
        "log"
    )
    
    type Params struct {
        NativeId string `json:"nativeId"`
        Source   string `json:"source"`
        Url      string `json:"url"`
        Title    string `json:"title"`
        Price    string `json:"price"`
    }
    
    func main() {
        jsonDataBytes := []byte(`{
            "test_2407811386": {"nativeId":"2407811386","source":"test","url":"https://test.ru/123","title":"TEST»","price":"123"},
            "test_2415474304": {"nativeId":"2415474304","source":"test","url":"https://test/234","title":"TEST2»","price":"234"}
        }`)
        objects := map[string]Params{}
        err := json.Unmarshal(jsonDataBytes, &objects)
        if err != nil {
            log.Fatalf("unmarshal error: %s\n", err.Error())
        }
        fmt.Printf("%v\n", objects)
    }
    Ответ написан
    Комментировать
  • Зачем нужны интерфейсы в go?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    В вашем примере не хватает самого важного, чтобы стало понятно - вызова функции с параметром типа интерфейс.
    func someFunc(numbers NumberInterface) {
       numbers.Sum()
    }


    Если не будет интерфейсов - вы можете передать только конкретный тип Numbers.
    А если вы создадите тип Numbers2 - то вы не сможете передать его в туже функцию, у которой тип аргумента Numbers.
    Эта проблема отлично решается при помощи интерфейсов.
    Ответ написан
    4 комментария
  • Как сделать автоматическое заполнение форм на сайте через скрипт Golang?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Можно использовать встроенную функцию, так будет надёжнее:
    formData := url.Values{}
    formData.Set("user_forms[0][name]", authDate.fName)
    ...
    resp, err := http.PostForm(postUrl, formData)


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

    Для решения такой задачи будет лучше использовать какой нибудь web scraping framework.

    Например:
    https://github.com/gocolly/colly
    https://github.com/anaskhan96/soup
    Ответ написан
    2 комментария
  • Выполнение/вызов функции из Go файла при нажатии на кнопку html?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Алгоритм у вас будет примерно таким...
    При нажатии на кнопку у вас будет вызвана функция (обработчик события onClick у кнопки).
    В этой функции вы сделаете AJAX запрос по протоколу HTTP на ваш сервер, где запущено ваше ПО на Go.
    В main.go вы запускаете HTTP сервер, который примет запрос от вашей функции JavaScript, обработает его и ответит.

    Разберём самый простой вариант с методом GET, чтобы вам было проще тестировать.

    На сервере (своём компьютере) запускаете main.go примерно такого содержания
    package main
    
    import (
        "fmt"
        "net/http"
    )
    
    func helloHandler(w http.ResponseWriter, req *http.Request) {
        fmt.Fprintf(w, "hello\n")
    }
    
    func main() {
        http.HandleFunc("/hello", helloHandler)
    
        http.ListenAndServe(":8081", nil)
    }

    Этот сервис будет слушать порт 8081 любого IP адреса на сервере, где будет запущен.
    Предположим, что вы тестируете на своём компьютере, в этом случае для проверки вам нужно в браузере откройте url `127.0.0.1:8081/hello`
    После этого вы должны увидеть в ответе просто текст: "hello".
    Когда этот этап будет завершен - вы сможете перейти к вызову этого URL уже из JavaScript.
    Для этого можете использовать то, что вам привычнее, если такого нет - можете воспользоваться этим примером
    const req = new XMLHttpRequest();
      const url='http://127.0.0.1:8081/hello';
      req.open("GET", url);
      req.send();
      req.onreadystatechange=(e)=>{
        console.log(req.responseText)
      }

    PS: Если не понятно или что-то не получится - пишите, помогу разобраться.
    Ответ написан
    21 комментарий
  • Для чего нужен указатель структуры в слайсе?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Тут сильно зависит от того, как вы будете использовать данные.

    Например, если вы во все функции передаёте players
    someFunc1(players)
    someFunc2(players)
    someFunc3(players)

    тогда указатели на конкретного player'a использовать особо смысла нет, от этого будет даже хуже, т.к. при создании нового player'a (при взятии адреса) будет задействован сборщик мусора, что вызовет бесполезную доп. нагрузку.

    Если же player структура не маленькая или их очень много, и обработка будет происходить каждого player'a в отдельности + при этом будет использоваться много разных функций, например вот так
    func main() {
        var players[] *Player
        players = append(players, &Player{Id: 1, Name: "Bob"})
        ...
        players = append(players, &Player{Id: 100, Name: "Bob"})
        for i := range players {
            someFunc1(players[i])
            someFunc2(players[i])
            someFunc3(players[i])
            someFunc4(players[i])
            // или одна из someFunc вызывает еще какие то функции и передаёт players туда 
        }
    }

    в этом случае вы получите выигрыш в производительности за счёт того, что при вызове каждой функции будет копироваться лишь указатель (8байт на 64битных процессорах) вместо всей структуры
    Если точнее - вы получите доп. нагрузку за счёт того, что будет задействован сборщик мусора, но за счёт того, что вместо большого объёма данных при каждом вызове функции будет копироваться лишь указатель - вы получите выигрыш, который нивелирует проигрыш от сборщика мусора.

    На всякий случай о том, когда есть смысл использовать указатель, а когда нет
    https://qna.habr.com/q/1046708#answer_2019152
    Ответ написан
    3 комментария
  • Go run автоматическая пересборка при изменении кода?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Я использую github.com/cespare/reflex для автоматической сборки при изменении кода, очень удобно
    Ответ написан
    Комментировать
  • Как обычно логируют краши от паник?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Если говорить про http сервер - я перенял практику из Django.

    У меня есть http middleware, который:
    - вызывает recover
    - нормально отвечает на запрос, а не падает, т.е. 500 + что мы уже знаем об ошибке и работаем над её устранением
    - собирает всю информацию по запросу, http заголовки, cookies, настройки сервера, переменные окружения и т.д. и оповещает меня по почте/телеграм

    Это даёт мне возможность узнавать о проблеме до того, как кто-то напишет в поддержку.

    Люди, в хорошем смысле, удивляются, когда у них что-то не получается, сервер отвечает 500, они не поймут что делать. А я уже узнал об этом, исправил ошибку и написал им письмо о том, что была ошибка, но уже всё исправлено :)
    Ответ написан
    Комментировать
  • Как создать переменую областью видимости пакета,чтобы не видели другие горутины и без их блокировки?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Можно сделать что-то тапа такого.
    Реализация не очень красивая, но, думаю, это даст вам понимание в каком направлении можно двигаться.
    package pkg2
    import "fmt"
    
    type Pkg2 struct {
         str2 []string
    }
    
    func New() *Pkg2 {
        return &Pkg2{
            str2: []string{},
        }
    }
    
    func (p *Pkg2) Test(str string){
        p.str2 = append(p.str2, str)
        fmt.Printf("str:%v %v \n", str, p.str2)
    }


    package main
    
    import (
      "sync"
      "pkg2"
    )
    var wg sync.WaitGroup
    
    func main() {
      text:=[]string{"a1", "a2", "a3", "a4", "a5"}
      for i,str:=range text{
        wg.Add(1)
        p := pkg2.New()
        go start(str, p)
      }
      wg.Wait()
    }
    
    func start(str string, p *pkg2.Pkg2){
        p.Test(str)
        wg.Done()
    }
    Ответ написан
    3 комментария
  • Как вытянуть id и текс сообщения в телеграме на go?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Вот тут расписаны все поля структуры Message https://pkg.go.dev/github.com/go-telegram-bot-api/...

    Пример того, как можно получить нужные вам данные
    package main
    
    import (
    	"log"
    
    	tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
    )
    
    func main() {
    	bot, err := tgbotapi.NewBotAPI("MyAwesomeBotToken")
    	if err != nil {
    		log.Panic(err)
    	}
    
    	u := tgbotapi.NewUpdate(0)
    	u.Timeout = 60
    
    	updates := bot.GetUpdatesChan(u)
    
    	for update := range updates {
                if update.Message == nil { // не обрабатываем если нет сообщения
                    continue
                }
    
                // ID сообщения, int
                log.Printf("message id: %s\n", update.Message.MessageID)
    
                // так вы можете получить текст сообщения полный (тип string)
                log.Printf("message: %s\n", update.Message.Text)
    
                if update.Message.IsCommand() {
                    // так вы получаете команду
                    log.Printf("command: %s\n", update.Message.Command())
    
                    // так вы получаете аргументы (параметры) команды, string
                    log.Printf("command: %s\n", update.Message.CommandArguments())
                }
            }
        }
    }
    Ответ написан
    Комментировать
  • Не могу загрузить в базу данных mysql значения golang. как это решить?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    У вас не запущен сервис MySQL, либо MySQL не слушает localhost:3306
    Ответ написан
  • Почему Go с горутинами работает на одном ядре?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Горутины выполняются на разных процессорах как и должны, у вас четвёртое число слишком большое, все горутины завершают выполнение, а одна продолжает работать. По этому и создаётся впечатление, что они все работают на одном ядре.

    Чтобы в этом убедиться добавим два fmt.Printf, чтобы получилось вот так
    package main
    
    import (
        "fmt"
        "runtime"
        "sync"
    )
    
    var wg sync.WaitGroup
    
    func main() {
        runtime.GOMAXPROCS(8)
        arr := []int{1343434, 1343434300, 234343400, 334343434000, 400434340, 203434340, 4232, 23545, 15535, 353535, 33434434, 5334345, 3533434345, 3535}
        for idx, el := range arr {
            wg.Add(1)
            go test(el, idx)
        }
        wg.Wait()
    }
    
    func test(el int, idx int) {
        fmt.Printf("%d started: %d\n", idx, el)
        for i := 0; i < el; i++ {
            el = el - 1
        }
        fmt.Printf("%d completed: %d\n", idx, el)
        defer wg.Done()
    }


    Вывод будет примерно таким
    4 started: 400434340
    8 started: 15535
    8 completed: 7767
    13 started: 3535
    13 completed: 1767
    6 started: 4232
    6 completed: 2116
    10 started: 33434434
    3 started: 334343434000
    7 started: 23545
    7 completed: 11772
    5 started: 203434340
    12 started: 3533434345
    11 started: 5334345
    2 started: 234343400
    1 started: 1343434300
    11 completed: 2667172
    9 started: 353535
    9 completed: 176767
    0 started: 1343434
    0 completed: 671717
    10 completed: 16717217
    5 completed: 101717170
    4 completed: 200217170
    2 completed: 117171700
    1 completed: 671717150
    12 completed: 1766717172

    При внимательном просмотре станет видно, что нет записи 3 completed.

    меняю элементы с индексами 4 и 5 на такие же числа 334343434000, 334343434000, т.е. делаем чтобы все горутины отработали, но чтобы 3 осталось, т.е. вот так

    arr := []int{1343434, 1343434300, 234343400, 334343434000, 334343434000, 334343434000, 4232, 23545, 15535, 353535, 33434434, 5334345, 3533434345, 3535}


    Запускаем заново и видим, что теперь 3 ядра отлично загружены
    %Cpu0  :  0.3 us,  1.0 sy,  0.0 ni, 98.7 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
    %Cpu1  :100.0 us,  0.0 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
    %Cpu2  :  1.7 us,  0.7 sy,  0.0 ni, 97.7 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
    %Cpu3  :  1.6 us,  2.9 sy,  0.0 ni, 95.5 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
    %Cpu4  :100.0 us,  0.0 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
    %Cpu5  :  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
    %Cpu6  :100.0 us,  0.0 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
    %Cpu7  :  5.6 us,  1.0 sy,  0.0 ni, 93.4 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
    Ответ написан
    Комментировать
  • Как можно измерить производительность http сервера?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Для подобного тестового кода можно использовать ab (Apache HTTP server benchmarking tool)

    Например так
    ab -n 10000 -c 1000 http://localhost:8080/

    Где:
    -n это количество запросов, которое нужно сделать
    -с это количество одновременных запросов

    Запустил у себя на одном из серверов, результат вот такой
    Server Software:
    Server Hostname:        localhost
    Server Port:            8080
    
    Document Path:          /
    Document Length:        5 bytes
    
    Concurrency Level:      1000
    Time taken for tests:   0.509 seconds
    Complete requests:      10000
    Failed requests:        0
    Write errors:           0
    Total transferred:      1210000 bytes
    HTML transferred:       50000 bytes
    Requests per second:    19627.39 [#/sec] (mean)
    Time per request:       50.949 [ms] (mean)
    Time per request:       0.051 [ms] (mean, across all concurrent requests)
    Transfer rate:          2319.25 [Kbytes/sec] received

    Т.е. при 1000 одновременных запросов сервер сможет обрабатывать в среднем 19627 запросов в секунду

    Но важно понимать, что в реальном проекте у вас будет не один endpoint, и что каждый endpoint, в зависимости от того, что он будет делать - будет показывать разный RPS.

    Например если сейчас вы добавите еще один endpoint и будете делать запросы в базу для формирования ответа - RPS будет значительно меньше.
    Ответ написан
  • Является ли передача переменной по указателю затратной операцией?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Я видел эти же или похожие бенчмарки, которые приведены в этой статье, год или два назад.
    Тут важно отметить, что методика тестирования в статье не учитывает очень важный момент - там передача значения происходит всего один раз, т.е. функцию вызвали один раз и на том всё.

    Но часто бывает так, что данные приходится передавать многократно.

    Например есть функция, которая извлекает из базы данные, вторая функция обрабатывает эти данные, во время работы второй функции - вызываются еще несколько разных функций и все эти данные в каждую из них передаются, а потом еще все эти данные передаются в JSON сериализатор и т.д.
    Т.е. одни и те же данные передаются много раз из функции в функцию.

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

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

    Но если эти данные нужно будет передавать во много разных функций - то в большинстве случаев передача через указатель будет быстрее, даже с учётом того, что garbage collector это ресурсоёмко.
    Ответ написан
    Комментировать
  • Как сделать редирект всех запросов к домену на другой домен, без конкретной uri?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Для этого лучше сделать middleware и уже там делать всё, что вам нужно
    Т.е. либо дальше передаёте управление, либо редирект и завершение обработки запроса.
    https://github.com/gin-gonic/gin#custom-middleware
    Что то типа такого
    func DomainRedirect() gin.HandlerFunc {
        return func(c *gin.Context) {
            if ваше_условие {
                c.Redirect(302, "https://wifer-test.ru/" + c.Request.URL.String())
                return
            }
            c.Next()
        }
    }
    ....
    r := gin.New()
    r.Use(DomainRedirect())
    Ответ написан
  • В чем сложность поддержки проектов на Go?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Более четырёх лет пишу на Go, лично для меня поддерживать проекты на других языках сложнее ))
    До Go много лет писал на Perl/PHP/Python/Java/C/Ruby on rails/JavaScript и "крутил" еще не мало чего.
    Сейчас пишу только на Go и Python.

    Основная сложность в Go - это то, что если вы начинаете писать "криво" - то писать так очень трудно :))
    Подход к реализации в Go достаточно непривычный для тех, кто приходит из скриптовых языков.
    Например запрет циклических импортов - это самое первое, что "взрывает" мозг :)
    Нужно время чтобы адаптироваться после скриптовых языков.

    Про обработку ошибок - это да, это, пожалуй, единственное что мне не нравится в Go.
    Для себя я решил эту проблему за 1-2 часа :) и больше меня это не беспокоит.

    По ООП - в Go оно есть, лично мне оно нравится намного больше, чем ООП в других языках ))
    Всё, что реально нужно - всё есть, но при этом ничего лишнего нет.
    Интерфейсы - просто прекрасны :)) Каналы, горутины - это просто "счастье" :))

    А если объективно - то я бы не стал говорить о сложности поддержки проекта применимо к какому то конкретному языку.

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

    А когда в таком проекте нужно что-то доработать или исправить ошибку, то почти всегда очень не просто найти то место, где нужно править, и после правок в одном месте, как правило, что-то ломается в другом :)

    Как минимум, нужно соблюдать принципы SOLID, иначе код получается, мягко говоря, не поддерживаемым.
    Но понимание этих принципов так же приходит с опытом, их нельзя научиться соблюдать просто прочитавши книгу или статью. Нужно сделать несколько проектов используя эти принципы, написать криво, понять как было бы правильно, всё переделать и так несколько раз.
    Тогда, со временем, начнёт получаться понятный и легко поддерживаемый код.

    Так мы приходим к тому, что если опыта нет - тогда проект поддерживать будет достаточно тяжело не взирая на язык и т.д.

    Хочется добавить, что как бы не писали про Go, что он простой и т.д. - он явно сложнее Python/PHP и т.д.
    Это еще один повод сказать, что поддерживать код на Go сложно ))

    Также стоит учитывать, что проекты которые пишут на Go зачастую сложнее, чем проекты для которых выберут скриптовый язык. Go выбирают когда планируются большие нагрузки, параллельная обработка чего-либо и т.д., а это, в свою очередь, дополнительно усложняет проект.
    Ответ написан
    7 комментариев
  • Что выбрать для телеграм бота? Вебхуки или лонгполлинг?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    "Long polling" периодически опрашивает телеграм сервер, за счёт этого пользователю будет казаться, что бот "торзмозит", т.к. между отправкой сообщения от пользователя и до получения ответа будут паузы.
    По крайней мере у меня было именно так.
    Еще стоит сказать о том, что "Long polling" не подойдёт если говорить о нагрузках.

    После перехода на WebHook'и - сразу заработало всё очень резво.
    Также с использованием WebHook'ов у вас будет возможность горизонтально масштабировать бота в случае роста нагрузок.
    Если бы у меня стояла такая задача - я бы использовал WebHook'и.
    Ответ написан
    3 комментария
  • Как в gorilla mux сделать серверную мидлвару?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Есть вариант использовать httptest.ResponseRecorder, я его использовал ранее для написания тестов.

    https://pkg.go.dev/net/http/httptest#ResponseRecorder

    Пример будет примерно таким
    func (m *LoggingMiddleware) LogBody(next http.Handler) http.Handler {
        fn := func(w http.ResponseWriter, r *http.Request) {
            wRecorder := httptest.NewRecorder()
            next.ServeHTTP(wRecorder, r)
            resp := wRecorder.Result()
            body, _ := io.ReadAll(resp.Body)
            // не забыть записать ответ в w
        }
    
        return http.HandlerFunc(fn)
    }


    Пример его использования из официальной документации https://pkg.go.dev/net/http/httptest#example-Respo...

    Или, можно сделать свой responseWriter, в handler передавать свой writer, а потом, после обработки, записать полученный ответ сервера в оригинальный responseWriter
    Что-то типа такого
    func (m *LoggingMiddleware) LogBody(next http.Handler) http.Handler {
        fn := func(w http.ResponseWriter, r *http.Request) {
            var buf bytes.Buffer
            logWriter := NewLogToConsoleResponseWriter(&b)
            next.ServeHTTP(logWriter, r)
            // тут у вас в buf будет ответ сервера, с которым можно работать
            // главное потом не забыть записать его в `w`
        }
    
        return http.HandlerFunc(fn)
    }
    Ответ написан
    5 комментариев
  • Как распарсить массивы из yaml в golang?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    У вас поля в структуре не экспортируемые, по этому и не работает.
    Вот рабочий пример
    package main
    
    import (
        "fmt"
        "io/ioutil"
    
        "gopkg.in/yaml.v3"
    )
    
    func main() {
    
        fmt.Println("start")
    
        c, err := readYaml("./tets.yml")
    
        if err != nil {
            panic(err.Error())
        }
    
        fmt.Println(c.Master, c.Kibana, c.Data, c.Pass, c.User)
    }
    
    type ClusterEnv struct {
        Master []string `yaml:"master,flow"`
        Data   []string `yaml:"data,flow"`
        Kibana []string `yaml:"kibana,flow"`
        User   string   `yaml:"user"`
        Pass   string   `yaml:"pass"`
    }
    
    func readYaml(filename string) (*ClusterEnv, error) {
    
        buf, err := ioutil.ReadFile(filename)
        if err != nil {
            return nil, err
        }
    
        c := &ClusterEnv{}
        err = yaml.Unmarshal(buf, c)
        if err != nil {
            return nil, fmt.Errorf("in file %q: %v", filename, err)
        }
    
        return c, nil
    }
    Ответ написан
    Комментировать
  • Нужен ли Nginx для веб приложения на Golang?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Не нужен, в Go есть полноценный веб сервер.

    Nginx есть смысл использовать в случае если есть необходимость обслуживать больше одного домена на одном и том же IP:Port, ну и для раздачи статики (изображений, CSS, Javascript и т.д.)

    Еще есть смысл использовать Nginx когда у вас большая нагрузка и ваш сервис на Go работает на нескольких серверах - Nginx'ом можно балансировать нагрузку между этими серверами.
    Ответ написан
    9 комментариев
  • Как правильно передать множество параметров в функцию в Golang?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    В таких случаях лучше передавать структуру как параметр.
    Пример
    type User struct {
        Name string
        Email string
        // все нужные поля далее
    }
    
    func CreateUser(r.Context(), user)
    Ответ написан
    Комментировать