Пишемо backend на go з мінімальними зусиллями за допомогою GoJs

Я бачив купу статей на цю тему (я думаю ви теж бачили і зараз думаєте, що це черговий шлак) і все зводилося до опису роута на кожен виклик api, і в підсумку ми отримували купку коду з яким перейти на той самий jsonp вимагало кілька днів або тижнів). Також я часто зустрічав на тостері відповіді, в яких писали, що нода відразу працює на всьому (ajax ws, jsonp, rpc-json). Чи це Правда, я не знаю, але все ж мені прийшло в голову виправити це. Я для свого проекту зробив апі відразу за трьома протоколами, а саме: ajax ws, jsonp (по мережі ходить щось похожое на json-rpc).

Ось що ми маємо на сервері:

package main

import (
api "github.com/v-grabko/GoJsBackend"
"log"
)

func Testing(ps map[string]string) map[string]string {
ps["test"] = "Testing"
return ps
}
func main() {
api.AddMethod("TestMethod", func(ps map[string]string) map[string]string {
ps["test"] = "TestMethod"
return ps
})
api.AddMethod("Testing", Testing)
log.Fatal(api.RunServer(":8080"))
}

Тепер опишу що тут відбувається. Метод api.AddMethod додає новий метод в пулл (метод приймає map і зобов'язаний повернути map). Тут тест. функції просто до отриманими даними додають ще дані і повертають їх. Потім api.RunServer запускає апі сервер порт 8080.

Дані по мережі ходять у форматі json. Формат ajax і ws однаковий.

type MyJsonNameB struct {
 
Map Data[string]string `json:"data"`
 
Method string `json:"method"`
 
}
 

А для jsonp він трохи інший:

type MyJsonName struct {
 
C string `json:"с"`
 
Map Data[string]string `json:"data"`
 
Method string `json:"method"`
 
}
 

До всього цього добра я звертався з js. З часом я зробив вибір авто протоколу і трохи згодом я зробив абстракцію над 3 протоколами, а в абстракцію пхав драйвер протоколу, який реалізував метод driver.send(data call);. Я тоді розігнався з написанням булочок до цього всього і зліпив щось на зразок мікро фреймворку (подоба MVC, тільки замість моделей ми робимо запити до API прямо в контролері).

Потім я подумав, що непогано б поділитися своєю роботою з спільнотою. Так я створив GoJs. Він досить малий, але має майже все, що потрібно (а то, чого немає зроблю; пропонуйте в коментарі на пошту v.grabko@box.ua, а в ідеалі Pull Requests).

Отже, почнемо занурення в цей міні фреймворк. Спочатку необхідно створити скелет.

<html>
<head>
<!-- Підключимо фреймворк-->
<script src="/js/GoJs/main.js"></script>
<!-- І запустимо нашого звіра-->
<script>
main({
//конфігурація
controllers_dir: "/app/controllers",
views_dir: "/app/views",
gojs_dir: "/js/GoJs",
ApiServer: "localhost:8080"
}, 
//кажемо, що при старті виконати з контролера метод start index
"start@index", 
//а в цій функції ми можемо і зобов'язані кастомизировать що завгодно. 
function () {

});
</script>
</head>
<body>
<!--В цей див шаблонизатор буде вантажити результат своєї роботи -->
<div id="page"></div>
</body>
</html>

Тепер в корені проекту створюємо три директорії після створення клонируем в /js/GoJs фреймворк. Тепер запустимо тестовий GoJsBackend сервер:

/app/controllers
/app/views
/js/GoJs
Під час запуску ми написали, що хочемо викликати з контролера метод start index. Настав час його створити. В директорії /app/controllers створимо start.js.

Скелет контролера (у всіх він однаковий):

start = {
index: function () {}
};

Що ж тут відбувається? Ми створюємо об'єкт контролера (фреймворк помистит його в об'єкт RegistryLoad.controllers).

Важливо! Після першого завантаження контролер кешується і потім повторно використовується. Щоб його заново завантажити, необхідно перезавантажити сторінку.

Тепер, коли скелет створений, давайте зробимо запит до сервера і отримані дані віддамо під змію:

start = {
index: function () {
window.backend.Get({
method: "TestMethod",
data: {
подія: "PageSkeleton",
data: "0"
}
}, function (t) {
View("index", t);
});
}
};

Тепер створюємо в директорії /app/views змію. Всі в'юшки мають розширення .tpl з цього ім'я файлу буде index.tpl з наступним змістом:

{{var.event}}<br>
 
{{var.data}}<br>
 
{{var.test}}<br>
 

А що, якщо нам необхідно виконати кілька запитів до api? Ви напевно подумали, що необхідно зробити якось так:

var modelData = {};
window.backend.Get({
method: "TestMethod",
data: {
подія: "PageSkeleton",
data: "0"
}
}, function (t) {
modelData.TestMethod = t.test
window.backend.Get({
method: "Testing",
data: {
подія: "PageSkeleton",
data: "0"
}
}, function (t) {
modelData.Testing = t.test
View("index", modelData);
});
});

{{var.TestMethod}}<br>
 
{{var.Testing}}<br>
 

Так, це працює. Але це, якщо чесно, пекельний говнокод. Для цього створена абстракція, яка виконує асинхронні запити «синхронно». Ви напевно запитаєте: навіщо городити велосипед, якщо можна використовувати синхронні запити? Все дуже просто. Вони підвісять браузер.

window.backend.GetSync([
{
method: "TestMethod",
data: {
подія: "PageSkeleton",
data: "0"
}
},{
method: "Testing",
data: {
подія: "PageSkeleton",
data: "0"
}
}
],function(ret){
View("index", {
TestMethod : ret.TestMethod.test,
Testing : ret.Testing.test
});
});

А що, якщо ми захочемо дані з одного контролера передати в інший? Про це буде написано трохи нижче.

Роутинг

Роутер працює одночасно з html5.history та location.hash (ви абстраговані). Всі маршрути прописані прямо у в'юшках. Їх немає як таких. Вони автоматично парсятся після генерації посилання. Наприклад, ми захочемо викликати з контролера errors метод NotFound

{{href.RedHref|/errors/NotFound|Викликати метод}}
 

Отже, що тут відбувається:

RedHref ->все що міститься тут буде помещенно в атрибут class="" посилання
/errors/NotFound ->тут ми першим параметром передали ім'я контролера, а другим його метод.
Викликати метод -> ім'я посилання
А що, якщо ми захотіли змінну передати в інший контролеер? Запросто:

{{href.RedHref|/errors/NotFound/vars/{{var.test}}|Викликати метод}}
 

А в викликаному контролері пишемо:

errors = {
NotFound:function(){
alert(router.GetData("vars"));
},
};

Але все не так солодко, як здається. Приміром, зараз чомусь в Опері міні абстракція window.backend.GetSync не працює взагалі. Там чомусь не працює рекурсія і колбек не відпрацює.

github.com/v-grabko/GoJs
github.com/v-grabko/GoJsBackend
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.