Всем привет! Хочу поделиться первым опытом программирования на языке Go.
Cвою дорогу в программирование я начал как frontend разработчик. Далее перешёл на backend на Python и, немного для себя, на C# Asp.Net MVC. Но вот недавно, пару месяцев назад, я познакомился с этим прекрасным языком Go.
1 — компилируемый язык.
2 — очень необычный язык.
Структуры, интерфейсы… пришлось ломать свой мозг, переучиваться. И первые проблемы, кончено, это именно переучиваться. Где классы? Где ООП? Где любимые паттерны?
Но немного поломав голову, язык Go мне раскрылся и я его полюбил. Ну и, конечно, первым делом решил написать свой первый «велосипед», а именно web framework под названием MGGO.
Конечно сразу хотелось сделать MVC framework наподобие Asp.Net. Но это оказалось не так просто, потому что язык Go своеобразный. Но вот что получилось:
Контроллер это структура, в котором методы могут быть как внешние(api) так и только внутренние, а также, которые отвечают за представление(view).
Сразу на примере рассмотрим контроллер новостей.
После этого новости доступны по ссылке /news/
Где news — это имя контроллера, а основная страница это index, за которую отвечает метод IndexView.
Теперь про api. У контроллера News есть метод Read. По умолчанию он только сервисный. Для того, чтобы разрешать api запрос к данному методу, нужно в функции контроллера «init» добавить права на метод.
После этого метод Read доступен для api вызова любому пользователю.
Результат
Немного о параметрах. Все параметры внутри объекта params будут переданы в качестве полей структуры.
Для примера немного изменим функцию Read:
И api вызов должен быть с параметром ID:1 — params{ID:1})
PostgreSQL. В framework подключена библиотека go-pg. Давайте сразу на примере. Наш контроллер новостей имеет статичные данные. Нужно это изменить. Для этого первым делом нужно создать таблицу News. Добавляем пару строчек в функцию init.
Теперь после инициализации мы получим таблицу news, с полями id, title и text.
Меняем метод Read:
Если мы вызовем метод News.Read с параметром ID:1, то в результате получим данные из таблицы news с ключом 1.
Про возможности go-pg можно ознакомиться в документации.
Но это ещё не все. Все описать тут не получится. Надеюсь, напишу и выложу подробную документацию вскоре. Вот вкратце что ещё умеет Mggo.
? Веб сокеты
? События серверные и клиентские
? Кэширование результатов метода
? Поддержка JsonRPC вызовов
? Либа для быстрого создание контроллера и view
На этом пока все. Надеюсь, кому-то статья будет полезной. Конечно, есть куча других готовых и крутых фреймворков. Я лишь хотел попробовать сделать что-то свое для своего личного опыта.
Ссылка на проект
Ссылка на пример и быстрый старт
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)
tuxi
18.06.2019 00:58Веб-фреймфорк — это наверное все же шаблонизатор удобный и интересный какой-никакой должен быть. Я вот лелею надежду xslt прикрутить без прибивания гвоздями xsltproc. Чтобы можно было отдавать контент и указывать во что его превращать, выбирая нужный шаблон. ХТМЛ или в ПДФ например.
А уж как роутинг сделать (есть valyala/fasthttprouter например) и ORM с кешем проложить прослойкой между базой и остальным — это я думаю не самая главная проблема.
Но в любом случае, тема затронута интересная. Спасибо.
ErgoZru
Архитектура ну совсем так себе. Вы своим пакетом заставляете людей использовать ini файлы для конфигурации… один вопрос — зачем??? (хотел написать иначе, но не хочу показаться грубым)
Зачем делать так:
Когда можно сделать:
Где params какая-то публичная структура из пакета mggo.
Как задумка — интересная, но реализация — жуть… все прибито гвоздями, и хттп хендлеры, и sql (pg), и кэш безразмерный с очень специфической реализацией…
Webgev Автор
Спасибо за совет! Вы правы, через публичную структуру будет намного удобнее. Будем дорабатывать…
ErgoZru
туда же:
1) добавить go.mod
2) добавить абстракцию для обработки запросов http (может я хочу работать с fasthttp)
3) добавить абстракцию для обработки sql (может я не хочу pq, а хочу тот же sqlite3, mysql, оракл (ну вдруг я извращенец)).
4) реализация кэша очень замудренная, советую посмотреть готовые in-app пакеты, коих на гитхабе несколько популярных для go. Текущий кэш крайне опасен для памяти.
5) опять же абстракцию и для кэша не мешало бы, у вас там мелькает редис, но есть и другие, а логика плюс минус та же.
6) код явно не проверялся на race, а их там будет. Используется много мапов, но только в кэше есть mutex (код кэша явно откуда то взят, либо гайды либо либы другие).
7) GetAPIResult — там есть рефлект, довольно тяжелая вещь, если она вызывается на каждый запрос — то зря. Нагрузку данная реализация не выдержит.
8) логирование — жутко страшное (я про реализацию), плюс еще и в файлы… никакой настройки снаружи…
это то, что просится прям сразу сходу при первом взгляде. Особо не смотрел, возможно там еще есть где кривости.