Сайт на Golang. Часть 1. Поднимаем сервер, пишем первые маршруты и возвращаем данные в JSON

Этой статьей я хочу запустить небольшой цикл пошаговых инструкций, в которых мы рассмотрим основные моменты, необходимые для создания и запуска полноценного сайта на Golang. Не знаю как вам, но мне бы в свое время такие статьи очень упростили процесс обучения. Сам я с трудом собирал эту информацию по крупинкам из книг на английском, статей разных авторов или ответов на всеми любимом stackoferflow.

В этой статье мы создадим и запустим свой сервер, пропишем первые маршруты роутинга и узнаем как возвращать клиенту (часть сайта, с которой работают пользователи) простой текст и данные в формате JSON.

Мы могли бы написать все в одном файле «main.go», но я предлагаю сразу начать создавать структуру нашего проекта. Скажем, она может выглядеть следующим образом:

  • Папка «app» — здесь будет храниться исходный код нашего приложения на Golang. Внутри создадим еще две папки: «controller» — для хендлеров, обслуживающих маршруты; «model» — для описания структур и их методов.
  • Папка «public» — здесь будут находиться все внешние файлы: html, js, css, изображения и т.д;
  • файл «main.go» — основной файл нашего проекта.

В файле main.go размещаем следующий код:

package main

import (
	"github.com/julienschmidt/httprouter"
	"goSiteProject/app/controller"
	"log"
	"net/http"
)

func main() {
	//создаем и запускаем в работу роутер для обслуживания запросов
	r := httprouter.New()
	routes(r)

	//прикрепляемся к хосту и свободному порту для приема и обслуживания входящих запросов
	//вторым параметром передается роутер, который будет работать с запросами
	err := http.ListenAndServe("localhost:4444", r)
	if err != nil {
		log.Fatal(err)
	}
}

func routes(r *httprouter.Router) {
	//путь к папке со внешними файлами: html, js, css, изображения и т.д.
	r.ServeFiles("/public/*filepath", http.Dir("public"))
	//что следует выполнять при входящих запросах указанного типа и по указанному адресу
	r.GET("/", controller.StartPage)
	r.GET("/users", controller.GetUsers)
}

Я предпочитаю использовать роутер от Julien Schmidt. Он очень легковесный, функциональный и простой.

Для обслуживания запросов по первым двум маршрутам («/» и «/users») нам необходимо в папке (или пакете) «controller» создать функции — хендлеры, реализующие соответствующий интерфейс.

Чтобы на стартовой странице выводилось простое текстовое приветствие, создадим файл «pages.go» в нем разместим следующий код:

package controller

import (
	"fmt"
	"github.com/julienschmidt/httprouter"
	"net/http"
)

func StartPage(rw http.ResponseWriter, r *http.Request, p httprouter.Params) {
	text := "Приветствую тебя на стартовой странице этого сайта!"
	//возвращаем простой текст
	fmt.Fprint(rw, text)
}

Функция «StartPage» реализует интерфейс хендлера, который обрабатывает запрос, приходящий по маршруту «/» для запросов типа GET. В нашем случае мы пока просто выводим текстовое сообщение в окно браузера при заходе на данную страницу «localhost:4444» или, чтобы было понятнее, «localhost:4444/».

Прежде чем писать хендлер для маршрута «/users» нам потребуется создать структуру для типа данных «User» и написать функцию, которая будет возвращать нам массив с данными всех пользователей. В пакете «model» создаем файл «user.go» и размещаем там следующий код:

package model

type User struct {
	Id       int
	Name     string
	Surname  string
}

func GetAllUsers () (users []User, err error) {
	users = []User{
		{1,"Джон","До"},
		{2,"Говард","Рорк"},
		{3,"Джек","Доусон"},
		{4,"Лизель","Мемингер"},
		{5,"Джейн","Эйр"},
		{6,"Мартин","Иден"},
		{7,"Джон","Голт"},
		{8,"Сэмвелл","Тарли"},
		{9,"Гермиона","Грейнджер"},
	}
	return
}

Разбирать вопрос подключения к СУБД мы будем в следующих частях данной статьи, поэтому пока мы заполнили массив возвращаемых данных (список пользователей) вручную. Ошибок на текущий момент здесь не может быть, поэтому пока просто объявляем переменную «err» в списке возвращаемых значений и возвращаем ее в значении по умолчанию (nil) вместе с массивом пользователей «users»

Теперь создадим файл «users.go» и в нем разместим хендлер для маршрута «/users» :

package controller

import (
	"encoding/json"
	"github.com/julienschmidt/httprouter"
	"goSiteProject/app/model"
	"net/http"
)

func GetUsers(rw http.ResponseWriter, r *http.Request, p httprouter.Params) {
	//получаем список всех пользователей
	users, err := model.GetAllUsers()
	if err != nil {
		http.Error(rw, err.Error(), 400)
		return
	}
	//возвращаем список клиенту в формате JSON
	err = json.NewEncoder(rw).Encode(users)
	if err != nil {
		http.Error(rw, err.Error(), 400)
		return
	}
}

Получив массив с данными, мы преобразуем их в JSON одной простой функцией и возвращаем клиенту. В случае возникновения ошибки, мы возвращаем ее текст со статусом ответа «400» — «Bad Request».

Запускаем сервер командой в консоли «go run main.go».

Проверяем маршрут «/»:

И маршрут «/users»:

Как видим, все работает так, как мы запланировали. В следующей части мы рассмотрим как выводить статические и генерировать динамические HTML страницы.