Всем привет! Хочу поделиться первым опытом программирования на языке Go.

Cвою дорогу в программирование я начал как frontend разработчик. Далее перешёл на backend на Python и, немного для себя, на C# Asp.Net MVC. Но вот недавно, пару месяцев назад, я познакомился с этим прекрасным языком Go.

Почему Go?


1 — компилируемый язык.
2 — очень необычный язык.

Структуры, интерфейсы… пришлось ломать свой мозг, переучиваться. И первые проблемы, кончено, это именно переучиваться. Где классы? Где ООП? Где любимые паттерны?

Но немного поломав голову, язык Go мне раскрылся и я его полюбил. Ну и, конечно, первым делом решил написать свой первый «велосипед», а именно web framework под названием MGGO.

Что я хотел и к чему стремился


Конечно сразу хотелось сделать MVC framework наподобие Asp.Net. Но это оказалось не так просто, потому что язык Go своеобразный. Но вот что получилось:

Контроллеры (controller)


Контроллер это структура, в котором методы могут быть как внешние(api) так и только внутренние, а также, которые отвечают за представление(view).

Сразу на примере рассмотрим контроллер новостей.

import mggo

type News struct{
   ID int
   Title string
   Text string 
}
func NewNews() *NewNews{
   return &NewNews{}
}
func init() {
    // регистрируем контроллер
   mggo.RegisterController("news", NewNews)
}

func(n *News) Read(ctx *mggo.BaseContext) News{
   return News{1, "First News", "Text first News"}
}

func(n *News) IndexView(ctx *mggo.BaseContext, data *mggo.ViewData, path []string){
   data.View = "news/news.html"
   data.Data["News"]
   data.Data["News"] = n.Read()
}


После этого новости доступны по ссылке /news/

Где news — это имя контроллера, а основная страница это index, за которую отвечает метод IndexView.

Теперь про api. У контроллера News есть метод Read. По умолчанию он только сервисный. Для того, чтобы разрешать api запрос к данному методу, нужно в функции контроллера «init» добавить права на метод.

func init(){
   ...
   mggo.AppendRight("News.Read", mggo.RRightGuest)
}

После этого метод Read доступен для api вызова любому пользователю.

fetch("/api/", {
        "method": "POST",
        "headers": {
          'Accept': 'application/json',
          'Content-Type': 'application/json'
        },
        "body": JSON.stringify({
            "method": "News.Read",
            "params": {}
        })
}).then(res => res.Json()).then(console.log) 

Результат

{result: {ID:1, Title:"First News", Text:"Text first News"}}

Немного о параметрах. Все параметры внутри объекта params будут переданы в качестве полей структуры.

Для примера немного изменим функцию Read:

func(n *News) Read(ctx *mggo.BaseContext) News{
   if n.ID ==1 {
      return News{1, "First News", "Text first News"}
   } else {
      return News{}
   }
}

И api вызов должен быть с параметром ID:1 — params{ID:1})

Что еще?


PostgreSQL. В framework подключена библиотека go-pg. Давайте сразу на примере. Наш контроллер новостей имеет статичные данные. Нужно это изменить. Для этого первым делом нужно создать таблицу News. Добавляем пару строчек в функцию init.

func init(){
   ...
   mggo.InitCallback(func(){
      mggo.CreateTable([]interface{}{(*News)(nil)})
   })
}

Теперь после инициализации мы получим таблицу news, с полями id, title и text.

Меняем метод Read:

func(n *News) Read(ctx *mggo.BaseContext) News{
   mggo.SQL().Select(n)
   return *n
}

Если мы вызовем метод News.Read с параметром ID:1, то в результате получим данные из таблицы news с ключом 1.

Про возможности go-pg можно ознакомиться в документации.

Но это ещё не все. Все описать тут не получится. Надеюсь, напишу и выложу подробную документацию вскоре. Вот вкратце что ещё умеет Mggo.

? Веб сокеты
? События серверные и клиентские
? Кэширование результатов метода
? Поддержка JsonRPC вызовов
? Либа для быстрого создание контроллера и view

На этом пока все. Надеюсь, кому-то статья будет полезной. Конечно, есть куча других готовых и крутых фреймворков. Я лишь хотел попробовать сделать что-то свое для своего личного опыта.

Ссылка на проект

Ссылка на пример и быстрый старт

Комментарии (5)


  1. ErgoZru
    17.06.2019 23:45

    Архитектура ну совсем так себе. Вы своим пакетом заставляете людей использовать ini файлы для конфигурации… один вопрос — зачем??? (хотел написать иначе, но не хочу показаться грубым)
    Зачем делать так:

    mggo.Run(rout, "./config.ini")


    Когда можно сделать:

    mggo.Run(rout, params)


    Где params какая-то публичная структура из пакета mggo.

    Как задумка — интересная, но реализация — жуть… все прибито гвоздями, и хттп хендлеры, и sql (pg), и кэш безразмерный с очень специфической реализацией…


    1. Webgev Автор
      17.06.2019 23:52

      Спасибо за совет! Вы правы, через публичную структуру будет намного удобнее. Будем дорабатывать…


      1. ErgoZru
        18.06.2019 00:04

        туда же:
        1) добавить go.mod
        2) добавить абстракцию для обработки запросов http (может я хочу работать с fasthttp)
        3) добавить абстракцию для обработки sql (может я не хочу pq, а хочу тот же sqlite3, mysql, оракл (ну вдруг я извращенец)).
        4) реализация кэша очень замудренная, советую посмотреть готовые in-app пакеты, коих на гитхабе несколько популярных для go. Текущий кэш крайне опасен для памяти.
        5) опять же абстракцию и для кэша не мешало бы, у вас там мелькает редис, но есть и другие, а логика плюс минус та же.
        6) код явно не проверялся на race, а их там будет. Используется много мапов, но только в кэше есть mutex (код кэша явно откуда то взят, либо гайды либо либы другие).
        7) GetAPIResult — там есть рефлект, довольно тяжелая вещь, если она вызывается на каждый запрос — то зря. Нагрузку данная реализация не выдержит.
        8) логирование — жутко страшное (я про реализацию), плюс еще и в файлы… никакой настройки снаружи…

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


  1. tuxi
    18.06.2019 00:58

    Веб-фреймфорк — это наверное все же шаблонизатор удобный и интересный какой-никакой должен быть. Я вот лелею надежду xslt прикрутить без прибивания гвоздями xsltproc. Чтобы можно было отдавать контент и указывать во что его превращать, выбирая нужный шаблон. ХТМЛ или в ПДФ например.
    А уж как роутинг сделать (есть valyala/fasthttprouter например) и ORM с кешем проложить прослойкой между базой и остальным — это я думаю не самая главная проблема.
    Но в любом случае, тема затронута интересная. Спасибо.


  1. domix32
    18.06.2019 12:51

    очень необычный язык.

    чем он необычный?