Вы все правильно поняли! И подтвердили информацию которую я почерпнул в ходе изучения этого вопроса. Собственно так сейчас и делаю. Никаких panic и glog.fatal :)
Подскажите такой вопрос. Это уже для интереса. Как бы в месте появления ошибки я руками записываю текстовое представление в glog.Error, который мне сохраняет информацию вместе с данными где я вызвал этот самый glog.Error в таком виде [E1107 18:20:58.734819 06405 server.go:39]. Но это данные не о самой ошибке, а именно о самом выводе, которые косвенно указывают на то место где произршла ошибка. А можно как-то эту информацию достать из самого объекта error, кроме его текстового представления?
Спасибо за ответ. Для себя заново открыл recovery :)
Пример и идею обработки ошибок вашу понял, погуглил чтобы развить эту тему.
Вот на такую интересное обсуждение натолкнулся www.reddit.com/r/golang/comments/2a1sie/when_to_ca...
Большинство советует использовать панику в крайних случаях, request handlers к которым не относится.
Для себя выработал решение:
в месте ошибки записывать данные в лог и поднимать ошибку выше через return, а в контроллере отправлять http.Error(w, http.StatusText(500), 500) и прерывать выполнение.
Вроде работает :) ошибки пишутся в лог (с нужными данными - где и когда), а пользователь получает свою 500.
"который на выходе и дает html-код с "проблемами"" Вы меня немного не так поняли :) . К самому редактору никаких претензий нет. Выбирал его долго и мучительно. "Проблемы" могут появиться благодаря нехорошим пользователям. Я не могу гарантировать что вместо вывод ckeditor мне не подсунут post запрос напрямую с интересными закладками в html. Вот собственно и нужна библиотека которая вычистит html и оставит только разрешенное (безопасное).
Про CKEditor (раз так надо) + BBCode plugin отличный совет.
Я так понимаю вы имеете в виду вот это - BBCode Output Format ckeditor.com/addon/bbcode
Не знал что так можно.
До этого во многих проектах сохранял полученный из CKEditor исходники в виде html и при выводе пропускал через htmlpurifier.org или для Rails через встроенный Sanitizer и потом естественно в template выводил без escaping. В таких случаях получаю исходное форматирование от автора статьи и защиту, если этот автор что надумает плохое.
Тот же самый алгоритм сейчас перекладываю на Go.
На счет использовать BBCode для хранения исходного кода - попробую этот вариант, один фиг у меня комментарии работают через bbcodes. Получится собрать все в одну кучу а не обрабатывать по разному статьи и комменты.
ps. вы написали "CKEditor (раз так надо)". А почему "раз так надо". Что вы имели в виду под этим? Есть альтернативные метода создания функционала блога например. когда нужно предоставить пользователю удобное средство для написания статей?
Спасибо за ответ. Для себя выбрал вариант через map. Из ссылки что привел Sergey Lerg узнал вот такой интересный момент - что вместо map[int]bool лучше использовать map[int]struct{}, потому что пустая структура не занимает место в памяти.
@Tyranron, спасибо за пример.
Отличная идея с uidsToArticles := map[int][]Article{} Действительно код стал лучше выглядеть.
Вот эта часть тоже заинтересовала - IN (?"+strings.Repeat(",?")+")", uids... По ходу изучения сталкивался с полярными мнениями на счет этого куска кода. Одни говорят, что надо переменные из array передавать через биндинг по одной (как у вас). Другие говорят что надо страивать прямо в запрос (как у меня) и мол это хоть и не безопасно, но быстрее. Я взял за основу первый вариант, потому что у меня Postgres, а там placeholders вида $1, $2, $3 ... и получается что вот эта часть strings.Repeat(",?") растягивается на цикл.
Спасибо за советы. Вы помогли пересмотреть мое отношение к процессу разработки на Go. Думаю дальше будет легче. Есть ли у вас еще какие-нибудь ресурсы в интернете с информацией по Go (может быть статьями) или другие сайты где вы предпочитаете общаться на тему Go, чтобы можно было пообщаться персонально? Toster хоть и хорош, но общения в комментариях к ответу как-то не очень выглядит :) Может быть какой форум?
@Tyranron, не могли бы прокомментировать мои результаты. С join и без join. Как-то без join совсем не очень красивы выглядит. Неужели такая вермишель кода и циклов выгоднее одного join?
c join:
(пример из самого вопроса)
с двумя запросами:
articles := []Article{}
err := DB.Select(&articles, "SELECT * FROM articles")
uIds := []string{}
for _, a := range articles {
uIds = append(uIds, strconv.Itoa(a.UserId))
}
users := []User{}
err = DB.Select(&users, "SELECT * FROM users WHERE id IN ("+strings.Join(uIds, ",")+")")
if err != nil {
glog.Fatalln(err)
}
usersMap := make(map[int]User)
for _, u := range users {
usersMap[u.Id] = u
}
for i, a := range articles {
articles[i].User = usersMap[a.UserId]
}
@Tyranron спасибо за советы. Приму к руководству однозначно. По базам данных, по привычке из Rails использую Postgres. Почитаю что пишут про эту базу и сложные запросы.
Это отличный подход. Я так уже более 3 лет делаю в Salesforce :) просто потому что там нет JOIN. Но вот пришел в мир plain sql и как бы хочется делать "красиво". Как раз до этого пытался узнать у гугла что лучше - один join из 2 таблиц или 2 запроса отдельно. Почему-то все советуют join, а не заниматься сбором parent ids в коде для подготовки второго запроса. Мне тоже проще так делать (в смысле 2 запроса) на код ложиться вообще замечательно.
Спасибо за совет. Буду рыть в этом направлении.
Если не сложно, приведите пример когда все-таки join лучше чем 2 и более отдельных запроса.
"На мой взгляд это overingeniring." Супер. Опять вы подтвердили мои догадки :) Мне почему-то сразу показалось что это избыточно, но вот подтверждения найти не мог. Еще раз спасибо за помощь!
Спасибо uvelichitel. Вы очень помогли с этим моментом, немного хаоса улеглось в голове :) . Конечно с &u все предельно ясно :).
Еще тогда маленькое уточнение, чтобы совсем уж разобраться со всеми сомнениями.
Такая замечательная штука html/template. Тот же самый момент - видел что проинициализированные templates засовывают в контекст чтобы потом в функции рендера достать из контекста и использовать.
если я буду поступать как описал выше с DB, но только для такой ситуации?
var templates map[string]*template.Template
функция main:
templates["home"] = template.Must(template.ParseFiles(...))
templates["articleList"] = template.Must(template.ParseFiles(...))
а в функции рендера
templates[name].ExecuteTemplate(...)
С этим проблем не возникнет. Пока что я склоняюсь к тому что все должно отработать нормально. Но что тут может случиться плохого в случае конкурентного запроса.
Или может у вас есть свой рецепт или пример как правильно варить templates, поделитесь, буду признателен.
uvelichitel, спасибо за ответ. Но все как-то слишком сложно звучит. Извини, но я пришел с другой стороны в Go (из php, RoR, не из С++) поэтому приходится вникать в суть конкурентного выполнения запросов на пальцах. Подскажи на простом примере.
есть у меня такая замечательная переменная
var DB *sqlx.DB
я в main делаю так:
DB, err = sqlx.Connect("postgres", config.Database)
Теперь во всех запросах я обращаюсь вот так:
err := DB.Select(&u, "SELECT * FROM users")
т.е. просто обращаюсь к переменной DB напрямую.
Это нормально?
Просто видел в одном примере, что перед тем как обратиться к переменной DB ее в middleware сначала кладут в контекст
c.Env["DbMap"] = application.DbMap
а потом из контекста в контроллере достают
c.Env["DbMap"].(*gorp.DbMap)
Если я правильно понимаю, то middleware выполняется в том же контексте (gourutine) что и сам handler.
Как бы вы поступили в таком простом случае?
(я пытаюсь разобраться в этом примере https://github.com/haruyama/golang-goji-sample , может это просто пример плохой для изучения? избыточный)
Просто пока у меня получается делать приложение без всех этих бубнов с middleware и context и в дев режиме работает отлично. Хочу понять - какие будут косяки, если я не буду складывать все в контекст, чтобы потом использовать это в обработчике запроса.
Подробно объяснить проблему пока не могу :) Сложно это все описать правильными терминами. Если по простому - то проблема в том чтобы обеспечить каждому запросу свой набор (копию) данных, а также обесчпечить безопасность при одновременном обращении к этим данным, чтобы не вызвать конфликта. Я пока это так вижу.
Все! Я наверное начал понимать (вчитался в статью, которую привел в предыдущем примере) :) у меня "болезнь фреймворков". Я никогда не задумывался откуда все эти данные поступаю в запрос, потому что все данные для меня подготавливали фреймфворки, я только использовал. С Go это не пройдет и надо разбираться во всем на более низком уровне :).
ВОТ ИМЕННО! это я и имею в виду :))))) Да, так работает, НО почему никто так не делает а примерах и делают все всё через контекст? Вот что меня смущает. Если все так просто зачем нужны все эти танцы с бубном?
Boniface спасибо за ответ. Но я имел в виду немного другое. Как создавать функцию это понятно. Не совсем понятно (вернее раньше было) как мне в этой функции обращаться к глобальным данным, например настройкам приложения, коннектору базы данных, тем переменным, которые я инициализирую на этапе запуска сервера в функции main. Просто в go все запросы выполняются параллельно в goroutines и везде пишут про то что при использовании глобальных переменных надо обеспечить "какую-то" безопасность от конфликтов во время одновременного выполнения.
Вот простой пример - сделаю я структуру
var a string
в main ее заполню
func main() {
a := "hello"
}
Почему никто в примерах не обращается к переменно a в http hendler
к этой переменной напрямую?
например ,
func HelloServer(w http.ResponseWriter, req *http.Request) {
io.WriteString(w, a)
}
а создают какой-то context, ложат в этот context переменную и передают в функциою через параметры.
Подскажите такой вопрос. Это уже для интереса. Как бы в месте появления ошибки я руками записываю текстовое представление в glog.Error, который мне сохраняет информацию вместе с данными где я вызвал этот самый glog.Error в таком виде [E1107 18:20:58.734819 06405 server.go:39]. Но это данные не о самой ошибке, а именно о самом выводе, которые косвенно указывают на то место где произршла ошибка. А можно как-то эту информацию достать из самого объекта error, кроме его текстового представления?