SkillGridИзучай computer science на практике

Как вызывать API из своего кода

Твоя программа может пользоваться чужими программами по HTTP. Делать это правильно — значит обрабатывать то, что может пойти не так, потому что запрос из кода ведёт себя совсем не как запрос из браузера.

14 мин · 45 XP

Тебе нужна завтрашняя погода в программе. Ты открываешь документацию API во вкладке браузера, вставляешь адрес и видишь прекрасную страницу с JSON. Работает. Ты копируешь этот адрес в код, запускаешь — и программа падает с простынёй текста. С API между этими двумя моментами ничего не изменилось. Изменилось то, кто спрашивал.

GET /v1/weather?city=Berlin HTTP/1.1
Host: api.example.org
Accept: application/json


HTTP/1.1 200 OK
Content-Type: application/json

{"city": "Berlin", "tempC": 14}

req:
  GET /v1/weather?city=Berlin

res:
  status = 200
  body.tempC = 14

if (status != 200) -> error
body.tempC

Твой браузер — это программа, которая молча делает за тебя много лишней работы. Он хранит cookie, чтобы ты оставался в аккаунте. Он идёт по перенаправлениям и показывает тебе конечную страницу, не упоминая три перехода по пути. Он повторяет запрос, когда соединение обрывается. Он выполняет JavaScript, без которого современный сайт вообще ничего не покажет. А получив 404, он бодро рисует красивую страницу ошибки, и запрос выглядит успешным. Твоя программа не делает ничего из этого, пока ты сам это не напишешь. Код 404 для твоего кода — не страница ошибки, а число, которое надо проверить; и если ты его не проверишь, ты с радостью попробуешь прочитать данные о погоде из текста ошибки.

Есть ещё одно семейство, которое ты будешь встречать в реальном коде постоянно: 3xx. Перенаправление — это сервер, говорящий «не здесь, смотри вон там». Браузер идёт по нему за тебя, и ты этого не замечаешь. Твой HTTP-клиент может идти, а может и нет, в зависимости от настроек, и от этого зависит, какие данные ты в итоге получишь.

Your programWeather APIGET /v1/weather?city=Berlinno answer after 2 seconds, stop waitingGET /v1/weather?city=Berlin (try 2)200 OK, tempC 14the second try worked, the user saw only a small delay

Тайм-аут говорит: я буду ждать ответ вот столько, а потом перестану и буду считать это неудачей. Без него твоя программа ждёт вечно сервер, который никогда не ответит: кнопка крутится, запрос не заканчивается, и пользователь не понимает, что происходит. Две секунды — обычный выбор для запроса, ответа на который ждёт человек. Важное следствие: тайм-аут не доказывает, что запроса не было. Та сторона могла его получить и сделать работу, а потерялся только ответ на обратном пути.

Поэтому разумный платёжный поток выглядит так: каждой попытке дай уникальный ключ, отправь его вместе с запросом и задай тайм-аут. Если запрос упал по тайм-ауту, отправь его снова с тем же ключом. Если деньги уже ушли, та сторона узнает ключ и скажет тебе об этом вместо повторного списания. И никогда не повторяй бесконечно: пара попыток с растущей паузой между ними, потом сдаться и честно сказать пользователю.

Практика

  1. Сопоставь каждый код состояния с тем, что должен сделать твой код.

    Подсказки
    • Читай первую цифру как семейство: 2xx сработало, 4xx твой запрос неверный, 5xx сломалась та сторона.
    • Один из этих кодов значит, что сервер принял твой запрос, но ещё не закончил, поэтому ждать — правильный ход.

    15 XP

  2. Ты вставил адрес API в браузер и получил красивую страницу с JSON. Тот же адрес в программе выдаёт ошибку. Какое объяснение самое вероятное?

    Подсказки
    • Твой браузер — это программа, которая без спроса делает за тебя лишнее: cookie, вход в аккаунт, перенаправления, повторы.
    • Спроси, что твоя программа делает с ответом, когда по адресу приходит 404 вместо JSON.

    14 XP

  3. Твоя программа вызывает платёжный API. Тайм-аута нет, и сеть зависла. Что произойдёт на самом деле?

    Подсказки
    • Спроси, что происходит, когда ответ не приходит: код, который ждёт, находится внутри работающего процесса, которому надо что-то делать дальше.
    • Повторять безопасно только тогда, когда тот же запрос дважды не приносит лишнего вреда.

    15 XP