Зарегистрируйтесь, чтобы продолжить обучение

Веб-разработка на Go Веб-разработка на Go

Все веб-сайты работают по одному и тому же принципу. Всегда есть клиент и сервер, которые взаимодействуют друг с другом:

  • Клиент отправляет запрос
  • Сервер обрабатывает запрос
  • Сервер отправляет ответ клиенту

Такая последовательность называется циклом «запрос-обработка-ответ».

Веб-приложения постоянно обрабатывают запросы и формируют ответы на них. Чтобы упростить эту задачу, можно воспользоваться библиотеками, которые абстрагируют в себе основную работу с запросом и ответом.

Библиотеки упрощают работу над циклом «запрос-обработка-ответ» и освобождают время разработчика для более важных задач. Например, он может сосредоточиться на архитектуре или бизнес-логике своего приложения.

Как во многих других языках, в Golang есть готовые библиотеки для обработки веб-запросов. В этом уроке мы изучим, как реализовать веб-приложение с помощью библиотек.

Реализуем веб-приложение на Go

В этом уроке мы будем использовать пакет стандартной библиотеки net/http. С его помощью можно написать веб-приложение буквально в несколько строк кода:

package main

import (
    "net/http"
)

func main() {
    // обозначаем, что на запрос по пути "/" возвращается строка "Hello World"
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        // тело ответа — это массив байт
        w.Write([]byte("Hello world!"))
    })

    // запускаем веб-приложение для обработки запросов
    http.ListenAndServe(":80", nil)
}

Пробуем запустить веб-приложение:

go run main.go

Открываем любой браузер и переходим по адресу http://localhost — мы должны получить в ответ строку Hello world!. А теперь разберем этот пример по циклу «запрос-обработка-ответ»:

  • Веб-браузер выступает в роли клиента, а Go-приложение — в роли сервера
  • Клиент отправляет HTTP-запрос по пути /
  • Сервер читает запрос, обрабатывает его и возвращает строку Hello world!

Вернемся к примеру и рассмотрим подробнее, что происходит в каждой строчке приложения.

Сначала мы вызывали функцию http.HandleFunc:

// Обозначаем, что на запрос по пути "/" возвращается строка "Hello World!"
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    // Тело ответа — это массив byte
    w.Write([]byte("Hello world!"))
})

Эта функция настраивает обработчик по заданному пути. В нашем примере при любом запросе по пути / всегда будет возвращаться строка Hello world!.

Дальше мы использовали функцию http.ListenAndServe:

// Запускаем веб-приложение для обработки запросов
http.ListenAndServe(":80", nil)

В этой части кода запускается веб-приложение на 80 порту и слушает входящие запросы. Все входящие запросы будут обработаны согласно логике, описанной в обработчиках http.HandleFunc.

Вся бизнес-логика приложения находится внутри обработчиков. Попробуем разобраться в их работе и изучим, как они обрабатывают запросы с различными путями и параметрами.

Как сервер сопоставляет пути и обработчики

Когда сервер получает запрос, он смотрит, по какому пути пришел запрос — таким образом он определяет обработчика. Этот процесс сопоставления обработчика по пути называется маршрутизацией или роутингом.

Чтобы использовать роутинг различных запросов на уровне кода Go-приложения, нужно описать обработчиков http.HandleFunc под каждый путь запроса. Рассмотрим, как будет выглядеть код веб-приложения с двумя обработчиками на примере платформы Хекслет:

package main

import (
    "net/http"
)

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("Welcome to Hexlet"))
    })

    http.HandleFunc("/about", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("Hexlet is the leading educational platform for Software Engineers"))
    })

    http.ListenAndServe(":80", nil)
}

Запускаем веб-приложение, открываем браузер и проверяем:

  • Переходим на страницу http://localhost/ — получаем в ответ строку Welcome to Hexlet
  • Переходим на http://localhost/about — получаем строку Hexlet is the leading educational platform for Software Engineers

Так веб-приложение понимает, по какому пути пришел запрос, и выбирает под него соответствующий обработчик. Мы фокусируемся только на том, что находится внутри обработчиков. При этом реализация роутинга абстрагирована внутри пакета net/http.

Эта реализация обработчиков хорошо работает, когда мы работаем со статичными страницами. Но как быть, если нужно вывести много информации, которая не вмещается на одну страницу?

Как работать с параметрами запросов

Усложним пример с платформой Хекслета. Предположим, что нам надо вывести список курсов на странице/courses, но не более 5 курсов за раз. В этом случае нужно просто разбить список курсов постранично. Такое разбиение данных называется пагинацией (от английского pagination — «разбиение по страницам»).

Самый популярный подход к пагинации — использование параметров запроса. Например, на страницу приходит запрос по пути /courses?page=1 — по параметру page видим, что запрашивается первая страница — возвращаем курсы с 1 по 5.

При получении запроса веб-приложение на net/http считывает всю информацию и заполняет объект r *http.Request.

Теперь попробуем реализовать пагинацию в коде, считав параметры запроса:

package main

import (
    "net/http"
)

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("Welcome to Hexlet"))
    })

    http.HandleFunc("/courses", func(w http.ResponseWriter, r *http.Request) {
        // считываем параметр page из запроса
        page := r.URL.Query().Get("page")

        // рассчитываем, какую страницу нужно вернуть
        var pageCourses string
        switch page {
        case "":
            pageCourses="Введите номер курса!"
        case "1":
            pageCourses="Как написать свой первый \"Hello world\" на go..."
        case "2":
            pageCourses="Как работает сборщик мусора в go..."
        default:
            pageCourses="Курс в разработке..."
        }

        // возвращаем страницу курса
        w.Write([]byte(pageCourses))
    })

    http.ListenAndServe(":80", nil)
}

Выводы

  • Язык Go позволяет "из коробки" реализовать веб-приложение, используя стандартный пакет net/http
  • Настройка маршрутизации веб-приложения в Go осуществляется с помощью функции http.HandleFunc, которая задает функцию-обработчика на каждый путь HTTP-запроса
  • Вся информация о HTTP-запросе лежит в объекте r *http.Request
  • Ответ клиенту формируется с помощью функций объекта w http.ResponseWriter

Дополнительные материалы

  1. Стандартный пакет net/http
  2. HTTP Requests
  3. HTTP Responses

Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff

Используйте Хекслет по-максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»