5 мин чтенияИнженерия

Как сократить URL в PowerShell с помощью Invoke-RestMethod

Сократите URL в PowerShell одной командой, обработайте ответы 429, не храните API-ключ в скрипте и сократите целый столбец CSV с помощью ForEach-Object -Parallel.

Marius Voß
DevRel · edge infra
Как сократить URL в PowerShell: Invoke-RestMethod отправляет URL назначения в API сервиса сокращения и возвращает короткую ссылку как объект

Сокращение URL в PowerShell выполняется одной командой. Invoke-RestMethod отправляет URL назначения в API сервиса сокращения, передаёт API-ключ в заголовке Bearer и возвращает разобранный объект, а не строку JSON. Ничего устанавливать не нужно на любой машине, где уже есть PowerShell.

Это раздел серии для системных администраторов, в которой также рассматриваются Python, JavaScript, C# и Go. Формат эндпоинта и модель авторизации описаны в обзоре бесплатного API сервиса сокращения URL, а маршрут панели управления - в общем руководстве по сокращению URL.

Самый быстрый способ: один вызов Invoke-RestMethod

$headers = @{
    Authorization  = "Bearer $env:ELIDO_API_KEY"
    'Content-Type' = 'application/json'
}

$body = @{
    destination_url = 'https://example.com/spring-sale?utm_source=newsletter'
} | ConvertTo-Json

$link = Invoke-RestMethod -Method Post `
    -Uri 'https://api.elido.app/v1/links' `
    -Headers $headers `
    -Body $body `
    -TimeoutSec 10

$link.short_url   # https://s.elido.me/ab12cd

Invoke-RestMethod десериализует ответ, и в этом его отличие от Invoke-WebRequest. Шаг ConvertFrom-Json не нужен, как и распаковка .Content: $link уже является объектом с полями id и short_url.

ConvertTo-Json обязателен. Если передать хеш-таблицу напрямую в -Body, PowerShell закодирует её как форму, API увидит application/x-www-form-urlencoded, и вы получите ответ 400, который будет выглядеть как проблема сервера.

Есть одна неожиданность, специфичная для PowerShell: здесь 401 или 404 - это завершающая ошибка, а не возвращаемое значение. Все остальные клиенты в этой серии заставляют проверять код состояния, а этот - перехватывать исключение.

Обработка 429 и повторный запуск скрипта

Для любой запланированной задачи нужно добавить три вещи. try/catch из-за описанного выше поведения с ошибками. Ожидание с учётом Retry-After. И Idempotency-Key, чтобы повторный запрос возвращал исходную ссылку, а не создавал вторую для того же URL назначения.

function Get-ShortLink {
    param(
        [Parameter(Mandatory)][string] $Destination,
        [int] $Attempts = 3
    )

    # stable key: the same destination always produces the same one
    $sha  = [System.Security.Cryptography.SHA256]::Create()
    $key  = [BitConverter]::ToString(
                $sha.ComputeHash([Text.Encoding]::UTF8.GetBytes($Destination))
            ).Replace('-', '')

    $headers = @{
        Authorization     = "Bearer $env:ELIDO_API_KEY"
        'Content-Type'    = 'application/json'
        'Idempotency-Key' = $key
    }
    $body = @{ destination_url = $Destination } | ConvertTo-Json

    for ($attempt = 0; $attempt -lt $Attempts; $attempt++) {
        try {
            return (Invoke-RestMethod -Method Post -Uri 'https://api.elido.app/v1/links' `
                        -Headers $headers -Body $body -TimeoutSec 10).short_url
        }
        catch {
            $status = $_.Exception.Response.StatusCode.value__

            if ($status -eq 429) {
                $wait = $_.Exception.Response.Headers['Retry-After']
                if (-not $wait) { $wait = 2 }        # works on 5.1 too; no ?? operator
                Start-Sleep -Seconds ([int]$wait)
            }
            elseif ($status -ge 500) {
                Start-Sleep -Seconds ([math]::Pow(2, $attempt))   # 1s, 2s, 4s
            }
            else {
                throw   # 401, 403, 422: retrying will not help
            }
        }
    }

    throw "shorten failed after $Attempts attempts"
}

В PowerShell 7 можно обойти большую часть разбора исключений с помощью -SkipHttpErrorCheck: он возвращает объект ответа вместо того, чтобы выдавать ошибку, и позволяет напрямую прочитать тело ошибки API. Это особенно полезно при отладке ответа 422, когда нужно увидеть, какое поле вызвало возражение API.

PowerShell Invoke-RestMethod отправляет URL назначения с токеном Bearer и Idempotency-Key в эндпоинт ссылок сервиса сокращения и возвращает разобранный объект с short_url

Не храните ключ в скрипте

$env:ELIDO_API_KEY - простой ответ, которого достаточно для запланированной задачи с собственной сервисной учётной записью. Ключ, набранный в файле .ps1, таким ответом не является: он попадёт в систему контроля версий, в Get-History и в любые журналы транскрипции, включённые в вашей инфраструктуре.

Для всего, чем пользуются совместно, лучше подходит модуль SecretManagement:

$env:ELIDO_API_KEY = Get-Secret -Name ElidoApiKey -AsPlainText

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

Хотите запустить эти примеры без изменений? Создайте ключ на бесплатном тарифе, задайте $env:ELIDO_API_KEY, и всё на этой странице будет работать как есть.

Сокращение целого столбца CSV

На практике обычно нужно обработать не один URL, а целую таблицу. Четыре командлета и ограничение:

$rows    = Import-Csv .\campaign-urls.csv     # columns: id, destination
$funcDef = ${function:Get-ShortLink}.ToString()

$done = $rows | ForEach-Object -ThrottleLimit 8 -Parallel {
    $function:Get-ShortLink = $using:funcDef   # ship the function into each runspace
    [pscustomobject]@{
        id          = $_.id
        destination = $_.destination
        short_url   = try   { Get-ShortLink -Destination $_.destination }
                      catch { "ERROR: $($_.Exception.Message)" }
    }
}

$done | Export-Csv .\campaign-urls-short.csv -NoTypeInformation

Весь приём заключается в -ThrottleLimit 8: одновременно выполняются восемь запросов, независимо от того, содержит файл пятьдесят строк или пятьдесят тысяч. У -Parallel есть два нюанса. Каждая итерация выполняется в собственном runspace, поэтому функции и переменные вызывающего кода недоступны, если не передать их с помощью $using:. Кроме того, нужен PowerShell 7; в Windows PowerShell 5.1 обычный foreach - честный выбор для нескольких сотен строк.

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

Четырёхэтапный конвейер PowerShell импортирует CSV с URL назначения, вызывает API сервиса сокращения для каждой строки с ограничением, экспортирует короткие ссылки и выборочно проверяет результат

Перед выводом или отправкой по электронной почте выборочно проверьте результат. curl.exe -sI https://s.elido.me/ab12cd для трёх строк занимает десять секунд и выявляет случай, когда столбец сместился на одну позицию.

Когда PowerShell подходит лучше всего

Это правильный инструмент, если данные уже находятся в CSV или Active Directory, задача выполняется в планировщике Windows или её сопровождает системный администратор, а не разработчик приложения. Для всего, что поставляется внутри приложения, версию на C# с типизированным клиентом и внедрением зависимостей проще тестировать.

В любом случае используйте ключ из окружения или хранилища, явный -TimeoutSec, try/catch вокруг вызова и стабильный ключ идемпотентности для всего, что может выполняться дважды. На странице API и SDK описаны сгенерированные клиенты, а в разделе решений для разработчиков рассказано о других возможностях API.

Основная серия

Эта статья входит в кластер engineering. Начните с руководства по бесплатному API сервиса сокращения URL, затем перейдите к материалу лимиты запросов API сервиса сокращения URL и идемпотентность. Актуальная справочная информация находится в документации API, а в статье массовый импорт коротких ссылок из Google Sheets описан путь без кода для той же задачи с CSV.

Другие статьи блога

Частые вопросы

Как сократить URL в PowerShell?

Вызовите Invoke-RestMethod с параметром -Method Post для эндпоинта ссылок, передав API-ключ в заголовке Authorization, а URL назначения - в формате JSON. Командлет сам разбирает ответ, поэтому $link.short_url доступен сразу, без шага ConvertFrom-Json.

Нужен ли модуль, чтобы сокращать URL в PowerShell?

Нет. Invoke-RestMethod встроен в Windows PowerShell 3.0 и во все версии PowerShell 7, а вызов сервиса сокращения состоит из одного запроса. Модули оправданы, если вам нужны оболочки в форме командлетов для множества эндпоинтов, а не один POST.

Почему Invoke-RestMethod выдаёт ошибку для 404 или 401?

Потому что он трактует любой ответ 4xx или 5xx как завершающую ошибку, что противоположно поведению большинства HTTP-клиентов. Оберните вызов в try/catch или в PowerShell 7 передайте -SkipHttpErrorCheck, чтобы получить обратно объект ответа и прочитать тело ошибки, которое вернул API.

Как не хранить API-ключ в скрипте PowerShell?

Прочитайте его из переменной окружения с помощью $env:ELIDO_API_KEY или сохраните в модуле SecretManagement и получите через Get-Secret. Ключ, вставленный в файл .ps1, окажется в системе контроля версий, истории консоли и любых включённых журналах транскрипции.

Как сократить целый столбец URL из CSV?

Импортируйте файл через Import-Csv, передайте его в ForEach-Object -Parallel с -ThrottleLimit 8 и экспортируйте результат через Export-Csv, разместив короткую ссылку рядом с исходной. Именно ограничение удерживает выполнение в пределах лимита запросов API: без него большой файл запускает все запросы одновременно.

Работает ли ForEach-Object -Parallel в Windows PowerShell 5.1?

Нет, для него нужен PowerShell 7 или более поздней версии. В 5.1 практичные варианты - обычный цикл foreach, которого достаточно для нескольких сотен строк, или пулы runspace, если размер пакета оправдывает дополнительный код.

Попробуйте Elido

Вставьте URL - получите короткую ссылку

Без регистрации. Ссылка живёт 30 дней. Зарегистрируйтесь, чтобы оставить её навсегда.

Бесплатно, без регистрации · 2 в день

Попробуйте Elido

URL-сокращатель с хостингом в ЕС: собственные домены, глубокая аналитика, открытый API. Бесплатный тариф - без банковской карты.

Теги
how to shorten a url in powershell
powershell url shortener
invoke-restmethod post json
url shortener api powershell
powershell bulk shorten urls
powershell csv api script

Читать дальше