Veraltete Routen pflegen

SEO kann es erforderlich machen, URLs zu verändern. Natürlich sollten bestehende Routen trotzdem noch funktionieren. Denn immerhin könnte jemand eine URL gespeichert oder gar verlinkt haben.

Veraltete Routen

Ein Beispiel könnten Landing-Pages für Städte sein.
Die URLs zu den Städte-Seiten:

# config/routes.rb
Rails.application.routes.draw do
  resource :city, only: :show
end

sehen anhand einer eindeutigen Slug beispielsweise für Berlin so aus: reisen.de/city/berlin.

Veraltete Routen umleiten

Nachdem diese Landing-Pages live gegangen sind, sollen nun folgende Routen die Städte anzeigen: reisen.de/nach/berlin. Natürlich sollen die alten Routen immer noch funktionieren.
Im Routes-Generator von Rails steht dafür der Helper ActionDispatch::Routing::Redirection#redirect zur Verfügung. Grundsätzlich leitet er die alten Routen auf die neuen Routen um. Außerdem sendet er automatisch einen Status-Code (standardmäßig 301 - Moved permanently) an den Browser. Die meisten Browser entfernen darauf hin die veraltete Route aus ihrem Cache und cachen stattdessen die neue Route. Das gleiche gilt für Suchmaschienen.
Der Routes-Generator sieht für das Beispiel nun so aus:

# config/routes.rb
Rails.application.routes.draw do
  # neue Route
  get 'nach/:slug', to: 'cities#show', as: :city
  # veraltete Route wird auf neue Route umgeleitet
  get 'city/:slug', to: redirect('nach/%{slug}')
end

Redirects mit Abhängigkeiten

Sobald der redirect von Logik abhängt, bietet es sich an, diese Logik in eine Rack Applikation auszulagern, allein schon um die routes.rb übersichtlich zu behalten. Aber auch, um mehrere gleichartige Umleitungen zu behandeln.
Ein klassisches Beispiel wäre, nur die Städte zu der neuen Route umzuleiten, die überhaupt bekannt sein können. Also alle Städte, die bis zu einem Zeitpunkt im System erstellt wurden.
Für alle neu angelegten Städte soll die 404-Page gerendert werden. Der erweiterte Routes-Generator:

# config/routes.rb
Rails.application.routes.draw do
  resource :city, as: 'nach', only: :show
  # Der CityRedirector soll nur die Routen
  # für die relevanten Städte umleiten
  get 'city/:slug', to: redirect('CityRedirector')
end

Der CityRedirector lädt einmalig alle Städte-Slugs, die bis zum 01.07.2017 erstellt wurden und prüft, ob der Slug-Parameter der angeforderten Stadt enthalten ist. Falls nicht, wird kein Redirect ausgeführt:

class CityRedirector
  DUE_DATE = Date.new(2017, 7, 1)

  def self.call(params, request)
    "to/#{params[:slug]}" if legacy_city_slugs.include?(params[:slug])
  end

  private

  def self.legacy_city_slugs
    @@legacy_city_slugs ||= City.where('created_at < ?', DUE_DATE)
                                .pluck('slug')
  end
end

Nach einer gewissen Zeit lohnt es sich, Analysen auf die veralteten Routen zu prüfen, in welchem Umfang diese eigentlich noch angefragt werden. Gegebenfalls können diese dann ganz entfernt werden, und die Redirects natürlich auch.