--- title: Прочитать исход из адреса перехода description: Объявите, куда ведёт заголовок Location, и получите исход вызова типизированным значением или исключением. sources: - eazy_sdk.response.Location - eazy_sdk.response.HeaderModel - eazy_sdk.response.FromCookie - eazy_sdk.response.Const api_fingerprint: "sha256:e0baaf10fc4f2d45fba465e6cdbbfa11454a9d652cf13a931fd612e66c470c8e" --- Форма входа часто отвечает одним и тем же статусом `302` и сообщает исход адресом перехода: почта, страница второго фактора, возврат с `fail=1` или с `errno=25`. Тело у таких ответов одинаковое, различает их только заголовок `Location`. Опишите каждый исход моделью и поставьте `Location` на её поле. Какой исход считать успехом, а какой отказом, решает операция: модель попадает в `success=` или в `errors=`. | Куда ведёт `Location` | Модель | Что получает вызывающий | |---|---|---| | `/inbox/` | `SignedIn` в `success=` | `SignedIn` с полным адресом | | `/cgi-bin/secstep?otp=1` | `CodeRequired` в `success=` | `CodeRequired` с адресом шага | | `/login?errno=25` | `Banned` в `errors=` | исключение `AccountBanned` | | `/login?fail=1` | `Rejected` в `errors=` | исключение `InvalidCredentials` | ```{literalinclude} ../../../../../../examples/docs/response_redirect.py :language: python :start-after: "# docs:example:start" :end-before: "# docs:example:end" ``` `Location(path="/inbox*")` на поле делает две вещи сразу. Случай забирает только тот ответ, чей адрес подходит под шаблон, а поле получает этот адрес целиком. Относительный адрес сначала приводится к полному по адресу запроса, поэтому `/inbox/` и `https://mail.example/inbox/` читаются одним объявлением. `Location.query("fail")` кладёт в поле значение параметра. Если параметра в адресе нет, случай уступает ответ другим. Нужное значение задаёт `Const("25")` рядом, как на любом другом поле. :::{dropdown} Файл примера целиком ```{literalinclude} ../../../../../../examples/docs/response_redirect.py :language: python ``` ::: Запуск: `uv run python examples/docs/response_redirect.py`. ```text ada@mail.example: SignedIn https://mail.example/inbox/ otp@mail.example: CodeRequired https://mail.example/cgi-bin/secstep?otp=1 banned@mail.example: banned, errno 25 ada@mail.example: rejected, fail 1 ``` ## Части шаблона Все части необязательны. Указанные части должны совпасть одновременно. | Часть | С чем сравнивается | Пример | |---|---|---| | `host` | имя хоста без порта, регистр не важен | `Location(host="*.mail.example")` | | `path` | путь как он пришёл, регистр важен | `Location(path="/inbox*")` | | `query` | значения параметров после декодирования | `Location(query={"errno": "25"})` | | `contains` | весь адрес, поиск подстроки | `Location(contains="captcha")` | В строке `host` и `path` звёздочка заменяет любую последовательность символов, включая косую черту. Остальные символы читаются буквально. Для сложного случая передайте скомпилированное регулярное выражение: оно должно совпасть с частью целиком. В `query` значение `...` означает, что параметр есть, каким бы он ни был: `Location(query={"fail": ...})`. Строка сравнивается с каждым значением параметра, если он повторяется. `Location()` без аргументов означает только одно: заголовок в ответе есть. ## Условие без модели Тот же объект работает как условие `when=`, когда значение из адреса не нужно. Он сочетается с предикатами из `eazy_sdk.response.match` через `&`, `|` и `~`. Пишите такое условие на `Bytes` или `Text`. Представление `Empty` требует пустое тело, а у перехода оно обычно занято страницей с надписью о переадресации. ## Поля с типом, допускающим `None` Поле с типом `str | None` не выбирает случай. Оно заполняется, когда адрес подходит, и остаётся пустым, когда нет. Так читают необязательные части адреса рядом с обязательным шаблоном. Поле со списком `list[str]` под `Location.query(...)` получает все значения повторяющегося параметра. Модель, у которой все поля читаются из заголовков, не трогает тело ответа. Рядом с `Location` в такой модели стоят `FromHeader("X-Request-Id")` и `FromCookie("sid")`. Пропуск обязательной куки или заголовка считается ошибкой формата: случай уже выбран, а ответ неполон. ## Что проверяется до отправки Случай на статусе из группы `3xx` обязан сказать, какой переход он описывает. Объявление без `Location` на модели и без `when=` забрало бы любой переход, поэтому SDK отклоняет его при первом вызове операции, до запроса. Если подходит любой адрес, напишите это явно: `when=Location()`. Статус, на который операция объявила случай, читает операция. Клиент не следует такому переходу, каким бы ни был лимит `max_redirects`. Переход с адресом, который не описал ни один случай, заканчивается `UnexpectedResponseError`. Операции без случаев на `3xx` ведут себя как раньше. ## Дальше - {doc}`Соберите из этих шагов вход целиком `. - {doc}`Следуйте переходам, которые не несут исхода `. - {doc}`Достаньте значение со страницы, куда привёл переход `.