Skrócenie URL-a w Ruby to jedno żądanie POST. Wyślij długi link do API skracacza za pomocą Net::HTTP, przekaż klucz API jako token Bearer i odczytaj short_url z otrzymanego JSON-a. Gem nie jest potrzebny, co ma znaczenie, gdy skrypt musi działać na maszynie, nad którą nie masz kontroli.
Opis endpointu i modelu uwierzytelniania znajduje się w przeglądzie bezpłatnego API skracacza URL; ten artykuł jest wersją dotyczącą Ruby ogólnego przewodnika jak skrócić URL, który omawia ścieżkę przez dashboard.
Ta sama seria dla innych środowisk uruchomieniowych: Python, JavaScript, PHP i Go. Nazwy pól pochodzą z Elido; ten kształt można przenieść do większości nowoczesnych skracaczy.
Najszybszy sposób: Net::HTTP.post
Dwa requires, jedno wywołanie:
require "json"
require "net/http"
response = Net::HTTP.post(
URI("https://api.elido.app/v1/links"),
{ destination_url: "https://example.com/spring-sale?utm_source=newsletter" }.to_json,
"Authorization" => "Bearer #{ENV.fetch('ELIDO_API_KEY')}",
"Content-Type" => "application/json",
)
raise "shorten failed: #{response.code}" unless response.is_a?(Net::HTTPSuccess)
puts JSON.parse(response.body)["short_url"] # => https://s.elido.me/ab12cd
ENV.fetch zamiast ENV[] jest użyte celowo: brakujący klucz powoduje natychmiastowe przerwanie działania, zamiast wysłania Bearer i powrotu z mylącym 401.
Wiersz unless response.is_a?(Net::HTTPSuccess) jest tym, co wiele osób pomija. Net::HTTP zgłasza wyjątek przy niedziałającym gnieździe lub przekroczeniu limitu czasu i przy niczym więcej, więc 401 z treścią błędu dociera i wygląda dokładnie jak sukces, dopóki JSON.parse nie zwróci hasha bez short_url.
Dodaj limity czasu, ponowienia i klucz idempotencji
Net::HTTP.post ma wadę, której nie da się obejść: nie przyjmuje argumentów określających limit czasu. W przypadku czegokolwiek działającego bez nadzoru przejdź na Net::HTTP.start, które je obsługuje.
Przy okazji zajmij się błędem, który naiwne ponowienie pogarsza. Jeśli żądanie POST dotrze do API, ale odpowiedź zaginie po drodze, kod przekroczy limit czasu, ponowi żądanie i utworzy drugi link dla tego samego miejsca docelowego. Stały Idempotency-Key zamyka tę lukę: zahaszuj miejsce docelowe, a API zwróci oryginalny link zamiast tworzyć nowy.
require "digest"
def shorten(destination, attempts: 3)
uri = URI("https://api.elido.app/v1/links")
key = Digest::SHA256.hexdigest(destination) # stable across retries and re-runs
attempts.times do |attempt|
response = Net::HTTP.start(uri.host, uri.port, use_ssl: true,
open_timeout: 5, read_timeout: 10) do |http|
request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer #{ENV.fetch('ELIDO_API_KEY')}"
request["Content-Type"] = "application/json"
request["Idempotency-Key"] = key
request.body = { destination_url: destination }.to_json
http.request(request)
end
case response
when Net::HTTPSuccess then return JSON.parse(response.body)["short_url"]
when Net::HTTPTooManyRequests then sleep(response["Retry-After"].to_i.clamp(1, 60))
when Net::HTTPServerError then sleep(2**attempt) # 1s, 2s, 4s
else raise "shorten failed: #{response.code} #{response.body}"
end
end
raise "shorten failed after #{attempts} attempts"
end
Instrukcja case dotycząca klasy odpowiedzi jest czytelniejsza niż stos porównań kodów statusu i jasno pokazuje zasady: 429 czeka tyle, ile żąda serwer, 5xx stosuje wykładnicze narastanie opóźnienia, a wszystko inne, na przykład 401 lub 422, powoduje wyjątek przy pierwszej próbie, ponieważ ponowienie tego nie naprawi. Szczegółowe omówienie limitów szybkości i idempotencji wyjaśnia znaczenie nagłówków.
Chcesz uruchomić ten kod bez zmian? Utwórz klucz w bezpłatnym planie, wyeksportuj go jako ELIDO_API_KEY, a każdy fragment kodu na tej stronie zadziała bez modyfikacji.
Wersja z Faraday dla istniejącej aplikacji
W aplikacji Rails ręcznie napisana pętla ma niewłaściwy kształt. Faraday udostępnia jeden obiekt połączenia z dołączonym nagłówkiem uwierzytelniania, JSON-em w obu kierunkach i zadeklarowaną polityką ponowień zamiast kodowania jej ręcznie:
# Gemfile: gem "faraday" and gem "faraday-retry"
ELIDO = Faraday.new(url: "https://api.elido.app") do |f|
f.request :json
f.request :retry, max: 3, interval: 0.5, backoff_factor: 2,
retry_statuses: [429, 500, 502, 503, 504]
f.response :json
f.response :raise_error
f.headers["Authorization"] = "Bearer #{ENV.fetch('ELIDO_API_KEY')}"
f.options.timeout = 10
end
def shorten(destination)
ELIDO.post("/v1/links",
{ destination_url: destination },
{ "Idempotency-Key" => Digest::SHA256.hexdigest(destination) })
.body["short_url"]
end
Przed wklejeniem tego kodu warto wiedzieć dwie rzeczy. Middleware ponowień przeniesiono w Faraday 2 do osobnego gema faraday-retry, więc potrzebny jest oddzielny wiersz w Gemfile. A raise_error odwraca zachowanie Net::HTTP: teraz 4xx i 5xx zgłaszają Faraday::ClientError i Faraday::ServerError, czego potrzebujesz w zadaniu, które powinno ponowić działanie zamiast zapisać nil.
Skróć listę bez przekraczania limitu szybkości
Ruby zwalnia globalną blokadę, gdy wątek czeka na operację wejścia-wyjścia, więc wątki naprawdę się tutaj opłacają nawet w CRuby. Nie wolno jednak uruchamiać jednego wątku na URL: CSV z 2000 wierszy zamienia się w 2000 gniazd i ścianę odpowiedzi 429. Queue oraz stała pula utrzymują stałą współbieżność niezależnie od długości listy.
def shorten_all(urls, concurrency: 8)
queue = Queue.new
results = {}
mutex = Mutex.new
urls.each { |u| queue << u }
concurrency.times { queue << :done }
workers = concurrency.times.map do
Thread.new do
while (url = queue.pop) != :done
value = begin
shorten(url)
rescue => e
"ERROR: #{e.message}" # one bad row must not sink the batch
end
mutex.synchronize { results[url] = value }
end
end
end
workers.each(&:join)
results
end
Indeksowanie wyników za pomocą oryginalnego URL-a sprawia, że częściowa awaria pozostaje widoczna, a partię można uruchomić ponownie. Mutex nie jest opcjonalny: zwykły Hash zapisywany z ośmiu wątków tworzy wyścig danych i da o sobie znać podczas tego uruchomienia, na którym najbardziej Ci zależy.
W Rails opakuj shorten w Active Job i pozwól adapterowi kolejki zarządzać narastaniem opóźnienia. Ponowienie wewnątrz akcji kontrolera zajmuje wątek Puma przez każde sleep, a import ograniczany limitem szybkości może po cichu zagłodzić proces webowy. Jeśli podłączasz to do procesu publikacji, artykuł o webhookach dla zdarzeń linków opisuje pobieranie danych o kliknięciach bez odpytywania.
Którą opcję wybrać
Zadanie rake lub jednorazowy skrypt: Net::HTTP.post, cztery wiersze i gotowe. Cokolwiek zaplanowanego: wersja Net::HTTP.start z limitami czasu i kluczem idempotencji. Aplikacja Rails, która już używa Faraday: obiekt połączenia z ponowieniami zadeklarowanymi raz i używanymi wszędzie.
Wybór ma niewielkie znaczenie w porównaniu z trzema podstawowymi nawykami. Pobieraj klucz ze środowiska, ustaw jawny limit czasu i sprawdzaj klasę odpowiedzi przed sparsowaniem treści. Strona API i SDK zawiera listę wygenerowanych klientów, jeśli wolisz nie zarządzać tym samodzielnie, a rozwiązania dla deweloperów opisują pozostałe możliwości API po rozpoczęciu tworzenia linków z kodu.
Przeczytaj serię artykułów głównych
Ten artykuł należy do klastra engineering. Zacznij od przewodnika po bezpłatnym API skracacza URL, aby poznać kształt endpointu i uwierzytelnianie, a następnie przeczytaj limity szybkości i idempotencję, aby poprawnie zachowywać się pod obciążeniem. Aktualnym źródłem referencyjnym jest dokumentacja API, a skracacze URL dla deweloperów opisują, co sprawdzić w API przed rozpoczęciem pracy z nim.
Powiązane na blogu
Najczęściej zadawane pytania
Jak skrócić URL w Ruby?
Wyślij długi URL metodą POST do API skracacza za pomocą Net::HTTP, przekazując klucz API jako nagłówek Bearer i miejsce docelowe w treści JSON, a następnie odczytaj short_url z sparsowanej odpowiedzi. Net::HTTP.post to jednolinijkowe wywołanie w bibliotece standardowej, więc w pierwszej wersji niczego nie trzeba instalować.
Czy do skracania URL-i w Ruby potrzebuję gema?
Nie. Net::HTTP i biblioteka json są dostarczane z Ruby i obsługują całe wywołanie. Faraday ma sens, gdy potrzebujesz współużywalnego połączenia, middleware do ponowień i automatycznego kodowania JSON, co zwykle ma miejsce w aplikacji Rails, a nie w samodzielnym skrypcie.
Jak ustawić limit czasu w Net::HTTP?
Użyj Net::HTTP.start z open_timeout i read_timeout zamiast skrótu Net::HTTP.post, który nie pozwala ustawić żadnego z nich. Bez tych ustawień zawieszone połączenie może blokować workera przez pełne domyślne 60 sekund, a w zadaniu w tle oznacza to utratę jednego miejsca na zadanie.
Dlaczego Net::HTTP nie zgłasza wyjątku dla 401?
Ponieważ 401 to prawidłowa odpowiedź HTTP, a nie błąd transportu. Net::HTTP zgłasza wyjątki tylko dla błędów gniazda i limitów czasu, więc klasę odpowiedzi trzeba sprawdzić samodzielnie za pomocą is_a?(Net::HTTPSuccess) albo odczytać response.code. Middleware raise_error Faradaya robi to za Ciebie.
Jak skrócić wiele URL-i naraz w Ruby?
Umieść URL-e w Queue i uruchom przeciwko niej niewielką pulę wątków, zwykle około ośmiu. Ruby zwalnia globalną blokadę podczas operacji wejścia-wyjścia, więc wątki naprawdę nakładają oczekiwanie na sieć, a stała pula utrzymuje liczbę żądań poniżej limitu szybkości API, który nieograniczona pętla z jednym wątkiem na URL przekroczyłaby natychmiast.
Gdzie powinna znajdować się pętla ponowień w aplikacji Rails?
W zadaniu w tle, a nie w akcji kontrolera. Ponowienie, które śpi przez kilka sekund, przez cały ten czas zajmuje wątek Puma, więc partia ograniczana limitem szybkości może zagłodzić proces webowy. Active Job z adapterem kolejki obsługuje narastanie opóźnienia i bez dodatkowej pracy udostępnia historię ponowień.
Wypróbuj Elido
Wklej URL, otrzymaj krótki link
Bez rejestracji. Link działa 30 dni. Zarejestruj się, aby zachować go na zawsze.
Za darmo, bez rejestracji · 2 dziennie