Eine URL in Ruby zu kürzen ist ein einziger POST. Senden Sie den langen Link mit Net::HTTP an die API eines Shorteners, übergeben Sie Ihren API-Schlüssel als Bearer-Token und parsen Sie short_url aus der zurückgegebenen JSON-Antwort. Dafür ist kein Gem erforderlich. Das ist wichtig, wenn das Skript auf einem Rechner laufen muss, den Sie nicht kontrollieren.
Das Format des Endpunkts und das Authentifizierungsmodell weiter unten sind in der Übersicht zur kostenlosen URL-Shortener-API dokumentiert. Dieser Beitrag ist die Ruby-spezifische Version des allgemeinen Leitfadens zum Kürzen einer URL, der stattdessen den Weg über das Dashboard behandelt.
Dieselbe Serie, andere Laufzeitumgebungen: Python, JavaScript, PHP und Go. Die Feldnamen stammen von Elido; das Format lässt sich auf die meisten modernen Shortener übertragen.
Der schnellste Weg: Net::HTTP.post
Zwei Requires, ein Aufruf:
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 statt ENV[] zu verwenden, ist Absicht: Ein fehlender Schlüssel führt sofort zu einem Fehler, statt Bearer zu senden und mit einem verwirrenden 401 zurückzukommen.
Die Zeile unless response.is_a?(Net::HTTPSuccess) überspringen viele. Net::HTTP löst bei einem nicht erreichbaren Socket oder einem Timeout eine Exception aus, sonst aber bei nichts. Daher sieht eine 401-Antwort mit Fehler-Body genauso wie ein Erfolg aus, bis JSON.parse Ihnen einen Hash ohne short_url liefert.
Timeouts, Retries und einen Idempotency-Key hinzufügen
Net::HTTP.post hat einen Fehler, den Sie nicht beheben können: Es akzeptiert keine Timeout-Argumente. Für alles, was unbeaufsichtigt läuft, wechseln Sie zu Net::HTTP.start, das diese Argumente unterstützt.
Kümmern Sie sich dabei um den Fehler, den ein naiver Retry noch verschlimmert. Wenn der POST die API erreicht, aber die Antwort auf dem Rückweg verloren geht, läuft Ihr Code in einen Timeout, wiederholt den Aufruf und erstellt einen zweiten Link für dasselbe Ziel. Ein stabiler Idempotency-Key schließt diese Lücke: Hashen Sie das Ziel, und die API gibt den ursprünglichen Link zurück, statt einen neuen zu erzeugen.
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
Der case-Ausdruck nach der Antwortklasse ist übersichtlicher als eine Ansammlung von Statuscode-Vergleichen und macht die Richtlinie deutlich: Bei einem 429 wartet der Code so lange, wie der Server vorgibt, bei einem 5xx wird das Backoff exponentiell verlängert, und alles andere, etwa ein 401 oder 422, löst beim ersten Versuch eine Exception aus, weil ein Retry den Fehler nicht beheben wird. Der Deep-Dive zu Ratenlimits und Idempotenz erläutert die Semantik der Header vollständig.
Möchten Sie den Code genau so ausführen? Erstellen Sie einen Schlüssel im kostenlosen Tarif, exportieren Sie ihn als ELIDO_API_KEY, und jedes Snippet auf dieser Seite funktioniert unverändert.
Die Faraday-Version für eine vorhandene App
In einer Rails-App hat die von Hand geschriebene Schleife die falsche Form. Faraday liefert Ihnen ein Verbindungsobjekt mit angehängtem Authentifizierungs-Header, JSON in beide Richtungen und eine deklarierte statt ausprogrammierte Retry-Richtlinie:
# 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
Zwei Dinge sollten Sie wissen, bevor Sie den Code einfügen. Die Retry-Middleware ist in Faraday 2 in ein eigenes faraday-retry-Gem verschoben worden, daher benötigt sie eine separate Zeile im Gemfile. Und raise_error kehrt das Verhalten von Net::HTTP um: 4xx und 5xx lösen jetzt Faraday::ClientError und Faraday::ServerError aus. Das ist für einen Job sinnvoll, der einen Retry ausführen soll, statt einen nil-Wert zu speichern.
Eine Liste kürzen, ohne das Ratenlimit zu überschreiten
Ruby gibt die globale Sperre frei, während ein Thread auf I/O wartet. Daher sind Threads hier selbst unter CRuby ein echter Vorteil. Sie dürfen nur nicht einen Thread pro URL starten: Aus einer CSV-Datei mit 2.000 Zeilen werden 2.000 Sockets und eine Wand aus 429-Antworten. Eine Queue mit einem festen Pool hält die Parallelität unabhängig von der Listenlänge konstant.
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
Wenn Sie die Ergebnisse nach der ursprünglichen URL indizieren, bleiben Teilausfälle sichtbar und der Batch kann erneut ausgeführt werden. Der Mutex ist nicht optional: Ein einfacher Hash, in den acht Threads schreiben, erzeugt eine Race Condition, die Sie genau bei dem Lauf treffen wird, auf den es ankommt.
Verpacken Sie shorten in Rails in einen Active Job und überlassen Sie das Backoff dem Queue-Adapter. Ein Retry innerhalb einer Controller-Aktion hält bei jedem sleep einen Puma-Thread fest, und ein gedrosselter Import kann den Webprozess unbemerkt aushungern. Wenn Sie das in einen Veröffentlichungsablauf integrieren, erklärt der Beitrag zu Webhooks für Link-Ereignisse, wie Sie Klickdaten ohne Polling zurückerhalten.
Welche Variante Sie wählen sollten
Für eine Rake-Aufgabe oder ein einmaliges Skript: Net::HTTP.post, vier Zeilen, fertig. Für alles Geplante: die Net::HTTP.start-Version mit Timeouts und Idempotency-Key. Für eine Rails-App, die bereits Faraday verwendet: das Verbindungsobjekt mit einmal deklarierten und überall wiederverwendeten Retries.
Die Wahl ist im Vergleich zu den drei grundlegenden Gewohnheiten kaum entscheidend: den Schlüssel aus der Umgebung beziehen, ein explizites Timeout setzen und die Antwortklasse prüfen, bevor der Body geparst wird. Die Seite APIs und SDKs listet die generierten Clients auf, falls Sie all das lieber nicht selbst verwalten möchten. Lösungen für Entwickler zeigt, was die API noch anbietet, sobald Links aus Code heraus erstellt werden.
Die Cornerstone-Serie lesen
Dieser Beitrag gehört zum Engineering-Cluster. Beginnen Sie mit dem Leitfaden zur kostenlosen URL-Shortener-API, um das Endpunktformat und die Authentifizierung kennenzulernen, und lesen Sie dann Ratenlimits und Idempotenz, um sich unter Last korrekt zu verhalten. Die aktuelle Referenz sind die API-Dokumente, und URL-Shortener für Entwickler erklärt, was Sie bei einer API prüfen sollten, bevor Sie darauf aufbauen.
Verwandte Beiträge im Blog
Häufig gestellte Fragen
Wie kürze ich eine URL in Ruby?
Senden Sie die lange URL mit Net::HTTP per POST an die API eines Shorteners, übergeben Sie Ihren API-Schlüssel als Bearer-Header und das Ziel in einem JSON-Body, und lesen Sie anschließend short_url aus der geparsten Antwort. Net::HTTP.post ist eine einzige Zeile in der Standardbibliothek, daher muss für die erste Version nichts installiert werden.
Brauche ich ein Gem, um URLs in Ruby zu kürzen?
Nein. Net::HTTP und die json-Bibliothek werden mit Ruby ausgeliefert und decken den gesamten Aufruf ab. Faraday lohnt sich, wenn Sie eine wiederverwendbare Verbindung, Retry-Middleware und automatische JSON-Kodierung möchten. Das ist in der Regel in einer Rails-App der Fall, nicht in einem eigenständigen Skript.
Wie setze ich bei Net::HTTP ein Timeout?
Verwenden Sie Net::HTTP.start mit open_timeout und read_timeout statt der Abkürzung Net::HTTP.post, bei der Sie keines von beiden setzen können. Ohne diese Werte kann eine hängende Verbindung einen Worker die vollen 60 Sekunden des Standardwerts blockieren, und in einem Hintergrundjob bedeutet das einen verlorenen Job-Slot.
Warum löst Net::HTTP bei einem 401 keine Exception aus?
Weil ein 401 eine gültige HTTP-Antwort und kein Transportfehler ist. Net::HTTP löst nur bei Socket- und Timeout-Fehlern eine Exception aus. Sie müssen die Klasse der Antwort daher selbst mit is_a?(Net::HTTPSuccess) prüfen oder response.code auslesen. Faradays raise_error-Middleware erledigt das für Sie.
Wie kürze ich viele URLs auf einmal in Ruby?
Legen Sie die URLs in eine Queue und lassen Sie einen kleinen Thread-Pool, normalerweise mit etwa acht Threads, darauf arbeiten. Ruby gibt die globale Sperre während I/O frei, sodass Threads Wartezeiten im Netzwerk tatsächlich überlappen. Ein fester Pool hält Sie unter dem API-Ratenlimit, das eine unbegrenzte Schleife mit einem Thread pro URL sofort sprengen würde.
Wo sollte die Retry-Schleife in einer Rails-App laufen?
In einem Hintergrundjob, nicht in einer Controller-Aktion. Ein Retry, der einige Sekunden schläft, hält währenddessen einen Puma-Thread fest, sodass ein gedrosselter Batch den Webprozess aushungern kann. Active Job mit einem Queue-Adapter übernimmt das Backoff und liefert Ihnen kostenlos eine Retry-Historie.
Elido testen
URL einfügen, kurzer Link in Sekunden
Kein Konto nötig. Link bleibt 30 Tage aktiv. Konto erstellen, um ihn dauerhaft zu behalten.
Kostenlos, keine Anmeldung erforderlich · 2 pro Tag