Верните результат для каждой цели

GolangBeginner
Практиковаться сейчас

Введение

У мероприятия Garden Night есть несколько локальных веб-страниц: регистрация, карта, страница персонала и информация о билетах. Перед открытием организаторам нужен достоверный результат для каждого задания на проверку. Отказ или медленный ответ не должны приводить к исчезновению страницы из отчёта.

Вы дополните функцию проверки на Go и сохраните настоящий JSON-отчёт. Предполагается знание функций, структур, срезов, модулей и базового синтаксиса каналов Go, а также прохождение Building a Lost and Found API или равноценный опыт работы с HTTP. Ошибки HTTP-клиента, ограниченное ожидание и ответственность за ресурсы ответа объясняются до применения. Все цели — контролируемые локальные тестовые службы. Это разовая проверка перед мероприятием, а не интернет-сканер или система мониторинга. Самостоятельная среда этого раздела не требует файлов из предыдущей VM. Отведите около 55 минут с учётом чтения и обычных ошибок; это оценка автора.

Прочитайте результат небольшой успешной проверки

На этом шаге запустите небольшой работающий пример и разберитесь, что идентифицирует один результат. Начните в Workspace. Исходники Go и локальная служба мероприятия уже подготовлены. Все следующие команды выполняются в Workspace.

cd выбирает каталог. Go находится в /usr/local/go/bin; export добавляет этот каталог перед существующим $PATH текущей оболочки. Настройка действует только в этом сеансе терминала. Повторяйте обе строки в новой оболочке; глобальные файлы её настройки менять не нужно.

cd /home/labex/project/site-checker
export PATH=/usr/local/go/bin:$PATH

cat читает файлы, не изменяя их. Посмотрите два обычных задания и структуры данных Go:

cat normal.json types.go

У каждой цели есть id задания, запрашиваемый url и want — текст, ожидаемый в теле ответа. Результат сохраняет id и url; kind обозначает прикладной исход, а status — полученный HTTP-статус или 0, если заголовки ответа не пришли. Задание не равно URL: два человека могут независимо попросить проверить одну и ту же страницу.

go run . собирает и запускает пакет Go в этом каталоге. Готовая команда принимает -input для списка JSON, -workers для максимального числа одновременных проверок и -timeout для HTTP-бюджета времени каждой цели. 200ms означает 200 миллисекунд. Здесь один воркер последовательно проверяет две быстрые страницы. Символ > оболочки направляет стандартный вывод в файл, заменяя существующий; ошибки по-прежнему видны в терминале.

go run . -input normal.json -workers 1 -timeout 200ms > normal-report.json

Прочитайте действительно сохранённый результат:

cat normal-report.json

Оба задания должны иметь kind: "ok" и status: 200 с исходными ID и URL. 200 означает, что сервер принял этот GET, но само по себе не доказывает наличие ожидаемого содержимого. Первый вариант понимает только простой случай. Далее вы сделаете результаты полезными и для остальных страниц.

При go: command not found повторите export для текущего сеанса. Если файл не найден, повторите cd и проверьте его имя. Если позднее Go сообщит об ошибке компиляции, используйте имя файла и номер строки, чтобы найти и исправить этот небольшой участок. Следующий шаг объясняет редактирование, сохранение и проверку изменений без замены всего файла. Не меняйте main.go, types.go, все входные JSON-файлы и подготовленную локальную службу.

Отличайте полученные ответы от сбоев запросов

На этом шаге редактируйте небольшую функцию Check в Workspace, по одному решению за раз. Оставьте run.go последовательным, чтобы сосредоточиться на смысле результата одной цели. Сначала прочитайте начальный вариант:

cat check.go

Результат начинается с ID и URL этого задания. client.Get возвращает ответ и ошибку. Существующая ветвь ошибки обрабатывает сбой соединения; успешный вызов возлагает на функцию ответственность за закрытие тела ответа. Сохраните defer response.Body.Close() сразу после этой ветви: defer выполняется при возврате из функции, включая досрочный. Сохраните и присваивание полученного статуса. Вы расширите начальный вариант, а не замените его предоставленной готовой программой.

Редактируйте и исправляйте ошибки локально

Перед редактированием создайте копию исходника. cp копирует файл; окончание .bak важно, потому что другой файл .go компилировался бы как часть этого пакета и мог бы дважды определить Check.

cp check.go check.go.bak

Откройте существующий файл в редакторе терминала:

nano check.go

В nano перемещайтесь клавишами со стрелками. Ввод вставляет текст; Backspace и Delete удаляют символы. Ctrl+K вырезает текущую строку, поэтому используйте его, только если нужно удалить всю строку. Ctrl означает удерживать Control при нажатии другой клавиши. Нажмите Ctrl+W, введите func Check, затем Enter для поиска; Ctrl+C отменяет поиск. Редактируйте в позиции курсора. Для сохранения нажмите Ctrl+O, проверьте, что имя всё ещё check.go, и нажмите Enter. Нажмите Ctrl+X для выхода. Повторяйте nano check.go при очередной правке. Если открыли неверное имя или внесли нежелательные изменения, нажмите Ctrl+X, затем N на вопрос о сохранении. В нежелательном запросе имени файла Ctrl+C отменяет запрос. Выполните pwd, чтобы узнать каталог, используйте cd первого шага и откройте правильный файл перед новой попыткой.

После каждой правки gofmt -w check.go форматирует файл на месте. Сообщение об отсутствующей скобке или другой синтаксической ошибке содержит имя файла и номер строки: снова откройте файл, найдите соседнюю инструкцию и исправьте этот участок. После сохранения используйте cat check.go, чтобы проверить фактическое содержимое на диске. Сохраните уже написанные работающие части; обычная ошибка исходника не требует сброса VM или перезаписи всей функции.

Определите, принял ли сервер запрос к этой странице

Ответ 403 — успешный HTTP-обмен, поэтому Get не возвращает ошибку Go автоматически. Мероприятие ожидает ровно статус 200; другие полученные статусы означают http_error. Это прикладное правило мероприятия, а не утверждение, что любой другой статус 2xx всегда является ошибкой HTTP.

В Check после сохранения response.StatusCode напишите условие сравнения с http.StatusOK, стандартной константой библиотеки для 200. При другом статусе задайте вид результата "http_error" и немедленно верните результат. Пока сохраните существующий итоговый результат "ok". Возврат здесь закроет тело через существующий defer, не ожидая чтения ненужного тела ошибки.

Сохраните, выйдите из nano и отформатируйте:

gofmt -w check.go

Перед запуском предскажите результаты обычных заданий и задания страницы персонала. Можно изучить все задания, не редактируя их:

cat targets.json
go run . -input targets.json -workers 1 -timeout 200ms

На этом этапе задание персонала должно дать http_error с 403, а полученные страницы 200 пока остаются ok, даже если содержимое неверно. Медленная проверка билетов пока называется connection_error; ниже вы выделите тайм-ауты. Эти намеренно оставленные ошибки показывают, что должны изменить следующие правки. Для каждого входного ID, включая оба ID регистрации, по-прежнему должен быть результат.

Проверьте содержимое полученной страницы

Для ответа 200 io.ReadAll(response.Body) возвращает байты тела и ошибку. Добавьте io и strings рядом с net/http в группе импортов: import (, затем по одному имени пакета в кавычках на строку, затем ). Импорты называют пакеты, используемые файлом; неиспользуемый импорт — ошибка компиляции, поэтому добавляйте его при реализации соответствующих вызовов.

Перед классификацией успешно полученного тела прочитайте его в body и err. При сбое чтения пока верните результат с "connection_error". Сохраните уже полученный статус в результате. После успешного чтения сначала задайте или сохраните result.Kind = "ok". Затем, после этого присваивания и до итогового возврата, проверьте ожидаемый маркер. string(body) преобразует байты в текст, а strings.Contains(string(body), target.Want) определяет, содержит ли текст маркер. При ложном ответе измените вид на "content_mismatch". Не присваивайте "ok" снова после этого условия: оно затёрло бы несовпадение. В Go ! отрицает логическое значение. Для успешно прочитанного тела сохраните один итоговый возврат.

Сохраните и выйдите, затем отформатируйте и повторите те же настоящие задания:

gofmt -w check.go
go run . -input targets.json -workers 1 -timeout 200ms

Теперь неправильная карта должна дать content_mismatch с 200, а оба задания регистрации — по-прежнему ok с 200; результат персонала остаётся http_error с 403. Полученный статус не доказывает совпадение маркера. Тайм-аут билетов всё ещё требует собственной категории.

Отличайте ограниченное ожидание от другого сбоя

Бюджет HTTP-клиента включает чтение тела ответа. Тайм-аут до заголовков оставляет нулевой статус; тайм-аут после заголовков 200 должен сохранить статус 200. Добавьте net в импорты. У net.Error есть метод Timeout(). Этот короткий пример Go проверяет, поддерживает ли ошибка интерфейс, до вызова его метода:

if networkError, ok := err.(net.Error); ok && networkError.Timeout() {
    // The operation exceeded its time budget.
}

Утверждение типа возвращает преобразованное значение и логический признак. && вычисляет правую часть только при истинной левой. В обеих ветвях ошибок — запроса и чтения тела — сохраните "connection_error" по умолчанию, затем замените его на "timeout", когда эта защищённая проверка успешна. Сохраните возврат каждой ветви и исходную идентичность задания. Сохраните, выйдите и отформатируйте:

gofmt -w check.go

Существующая служба также предлагает два полезных контролируемых случая: /garden/bodylate/check отправляет заголовки 200 перед конечной задержкой тела 600ms; /garden/deniedbody/check отправляет заголовки 403 перед той же ненужной задержкой тела. Создайте дополнительный вход для изучения этих случаев, сохранив все подготовленные списки JSON:

nano body-probes.json

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

[
  {"id": "body-200", "url": "http://127.0.0.1:8080/garden/bodylate/check", "want": "GARDEN OPEN"},
  {"id": "body-403", "url": "http://127.0.0.1:8080/garden/deniedbody/check", "want": "GARDEN OPEN"}
]

Сохраните через Ctrl+O, подтвердите body-probes.json и Enter, затем выйдите через Ctrl+X. Перед использованием прочитайте настоящий файл:

cat body-probes.json

Предскажите оба результата, затем запросите их с бюджетом 200ms:

go run . -input body-probes.json -workers 1 -timeout 200ms

Ожидается timeout со статусом 200 для body-200 и http_error со статусом 403 для body-403. Отказ известен до чтения тела, поэтому задержка этого тела не должна превращать его в тайм-аут. Тайм-аут клиента прекращает ожидание; сервер может позже завершить свою конечную работу. Не судите о правильности по общей длительности.

Наконец, повторно выполните исходный полный список заданий:

go run . -input targets.json -workers 1 -timeout 200ms

Сопоставляйте по ID, а не по позиции строки. Ожидаются два ok, один http_error с 403, один content_mismatch с 200, один timeout с нулём и один connection_error с нулём. Прикладные неудачи — обычные значения отчёта, поэтому команда всё равно завершается успешно. Ошибка компиляции или синтаксиса JSON означает, что команда не смогла выполнить работу: исправьте локальную правку по указанным файлу и месту, затем повторите команду. Проверка курса испытывает исходный код и закрытие тел на независимых входах; конкретный редактор или имя дополнительного файла не требуются.

Сохраните и прочитайте полный отчёт

На этом шаге сохраните полезный отчёт, не потеряв ни одного задания. Прочитайте готовый последовательный цикл:

cat run.go

http.Client{Timeout: budget} задаёт конечный бюджет каждого запроса. Цикл вызывает Check один раз для каждой цели и добавляет все результаты, включая ошибки. Нельзя возвращаться после первой неудачной страницы или собирать результаты в map с URL в качестве ключа. Инициализированный пустой срез также создаёт пустой JSON-массив, когда заданий нет.

Теперь сохраните полный отчёт. Намеренно заменяется только файл отчёта; входные задания остаются нетронутыми.

go run . -input targets.json -workers 1 -timeout 200ms > report.json

Прочитайте файл, который вы действительно создали:

cat report.json

По ID проследите связь двух регистрационных заданий с одним URL. Каждое должно присутствовать ровно один раз. Результат 403 сообщает организаторам, что сервер ответил и отказал в запросе; это не потеря соединения. Неверный маркер карты требует проверить содержимое, несмотря на статус 200. Тайм-аут означает превышение бюджета этой проверки, а не то, что служба никогда не ответит.

В завершение проверьте подготовленный пустой список. Стандартный вывод показывается напрямую, поэтому полезный отчёт не заменяется:

go run . -input empty.json -workers 1 -timeout 200ms

Ожидайте [] и нормального завершения. Сохраните report.json, обновлённый check.go и все исходные входные данные. Проверки курса читают этот отчёт и запускают неизменённый код против отдельных локальных тестовых служб. Скопированные или статические строки не заменяют настоящие запросы.

Резюме

Вы получили достоверный результат для каждого локального задания, отличили HTTP-отказы от транспортных сбоев, распознали неверное содержимое, ограничили время HTTP-запросов и закрыли все полученные тела ответов. Вы сохранили и прочитали полный JSON-отчёт, оставив повторяющиеся URL отдельными заданиями с разными ID. Следующий раздел сохраняет эти решения и меняет распределение работы между небольшим числом воркеров.