Grundlegende Routingkonventionen
Werfen wir einen Blick auf unsere Routen, wie sie jetzt sind, mit rake routes
:
$ rake routes Prefix Verb URI Pattern Controller#Action list_posts GET /list_posts(.:format) application#list_posts GET /show_post/:id(.:format) application#show_post new_post GET /new_post(.:format) application#new_post create_post POST /create_post(.:format) application#create_post GET /edit_post/:id(.:format) application#edit_post POST /update_post/:id(.:format) application#update_post POST /delete_post/:id(.:format) application#delete_post POST /create_comment_for_post/:post_id(.:format) application#create_comment POST /list_posts/:post_id/delete_comment/:comment_id(.:format) application#delete_comment list_comments GET /list_comments(.:format) application#list_comments
Eine der wichtigsten Erkenntnisse für Rails-Konventionen ist, dass datenzentrierte Apps (oder grobe Apps) in der Regel sehr ähnliche Möglichkeiten haben, Benutzer mit der Anwendung interagieren zu lassen:
- eine Seite, um eine Liste von etwas anzuzeigen
- etwas anzeigen
- ein Formular anzeigen, um eine neue Instanz von etwas zu erstellen
- tatsächlich etwas erstellen
- ein Formular anzeigen, um eine Instanz von etwas zu bearbeiten
- tatsächlich etwas aktualisieren
- etwas löschen
Diese sieben „Interaktionsmuster“ sind so häufig, dass Rails eine Reihe von Konventionen vom Routing zu Controllern, Ansichten und Formularen bereitstellt, um diese Workflows zu vereinfachen. Wir werden uns jetzt auf den Routing-Teil konzentrieren.
Für das Routing gilt die Konvention, dass Rails empfiehlt, die URLs und die entsprechenden Controller-Aktionen folgendermaßen zu strukturieren, anstatt die URL-Muster zu erstellen:
Workflow | HTTP-VERB | PFAD | Controller-Aktion |
---|---|---|---|
Zeige eine Liste der Beiträge | GET | /posts | posts#index |
Zeige einen Beitrag | GET | /posts/:id | beiträge#anzeigen |
Zeige die zu erstellende Seite post | GET | /posts/new | posts#new |
Erstelle einen Beitrag | BEITRAG | /Beiträge | Beiträge#erstellen |
Zeige die Seite, um einen Beitrag zu bearbeiten | GET | /posts/:id/edit | posts#edit |
Aktualisieren Sie einen Beitrag | PUT/PATCH | /posts/:id | posts#update |
Einen Beitrag löschen | LÖSCHEN | /Beiträge/:id | Beiträge#destroy |
Wir können den post
Teil unseres aktuellen Routings ändern, um dieser Konvention zu folgen:
# posts GET /list_posts -> GET /posts posts#index GET /show_post/:id -> GET /posts/:id posts#show GET /new_post -> GET /posts/new posts#new POST /create_post -> POST /posts posts#create GET /edit_post/:id -> GET /posts/:id/edit posts#edit POST /update_post/:id -> PUT/PATCH /posts/:id posts#update POST /delete_post/:id -> DELETE /posts/:id posts#destroy
Und in der routes.rb
-Datei,
Rails.application.routes.draw do ### posts ### get '/posts' => 'posts#index' get '/posts/new' => 'posts#new' get '/posts/:id' => 'posts#show' post '/posts' => 'posts#create' get '/posts/:id/edit' => 'posts#edit' patch '/posts/:id' => 'posts#update' put '/posts/:id' => 'posts#update' delete '/posts/:id' => 'posts#destroy' # comments later.. ... end
Beachten Sie, dass mit diesem neuen Muster die URL selbst viel weniger Verben enthält, wir uns jedoch auf die HTTP-Verben verlassen (GET
/POST
/PUT
/PATCH
/DELETE
) um die Semantik für ansonsten gleiche URL-Muster bereitzustellen, z. B. /posts/:id
.
Beachten Sie, dass PUT
bis vor kurzem zum Aktualisieren von Ressourcen verwendet wurde, als PATCH
als bessere semantische Übereinstimmung für diese Aktion ermittelt wurde. Seitdem hat Rails PATCH
bevorzugt, aber beide Verben für Updates zugelassen.
Eine weitere Änderung, die wir später sehen werden, ist, dass wir alle URLs, die sich auf Beiträge beziehen, an PostsController
anstatt alles in ApplicationController
. Die Aktionsnamen von PostController
- new
- index
- show
- create
- edit
- update
- destroy
sind ebenfalls Konventionen, die der 7-Interaktion entsprechen muster.
Dies ist die stärkste Reihe von Konventionen, die Rails Ihnen als Anwendungsentwickler auferlegt. Wir werden Sie bitten, sich zu bemühen, es auswendig zu lernen. Wir werden dies im Laufe des Buches viele Male wiederholen, um Ihnen dabei zu helfen.
Routing für mehrere Ressourcen
Oft müssen mehrere Ressourcen in der URL vorhanden sein. Wenn wir beispielsweise für unsere Blog-App eine Route zum Erstellen eines Kommentars benötigen, ist es sehr wichtig, dass wir wissen, auf welchem Beitrag dieser Kommentar erstellt wird.
Wir machen das gerade:
post 'create_comment_for_post/:post_id' => 'application#create_comment'
Die Rails-Konvention einer URL mit mehreren zugeordneten Ressourcen besteht darin, mit der „enthaltenden“ Ressource bis zur „innersten“ Ressource zu beginnen. Zum Beispiel würden sich unsere Routen für Kommentare ändern in:
post '/posts/:id/comments' => 'comments#create' delete '/posts/:post_id/comments/:id' => 'comments#destroy' get '/comments' => 'comments#index'
Die Semantik dieser URLs lautet: „Kommentar unter einem bestimmten Beitrag erstellen“ und „Kommentar unter einem bestimmten Beitrag löschen“. Die letzte Route, „Alle Kommentare im System anzeigen“, muss nicht unter einem Post „scoped“ sein, so dass die URL nicht mit „/ posts“ führen muss.
Wenn wir mehrere Ressourcen in der URL mit ihren IDs haben müssen, ist die Konvention so, dass die letzte Ressource den Platzhalter :id
und der ganze Rest :resource_id
wie :post_id
.
Restful
REST steht für „Representational State Transfer“. Es ist ein Softwarearchitekturstil und eine Richtlinie zum Erstellen skalierbarer Webdienste. REST, als eine Möglichkeit, Webanwendungen zu strukturieren, hat viele Facetten, aber schauen wir uns nur an, wie es auf die Schnittstelle zutrifft, die wir für die Interaktion mit Webanwendungen verwenden, die URLs.
Um zu sehen, wie REST die Semantik von URLs vereinfacht, gehen wir durch ein Beispiel. Nehmen wir an, wir bauen einen Online-Pizza-Bestellservice auf. Eine naïve Implementierung der Schnittstellen des Dienstes (URLs) wäre:
/check_order?order_id=2 /place_new_order /pizza_details?type=1 /pay_for_my_order?order_id=12 /cancel_order?order_id=3 /expedite_order?order_id=13
Ein RESTful Interface Design würde so aussehen:
GET /orders/2 POST /orders GET /pizzas/1 POST /orders/12/payments DELETE /orders/3 POST /orders/13/expeditions
Sie können sehen, RESTful-Schnittstellen konzentrieren sich auf „Substantive“ oder „Ressourcen“, Entfernen von „Verben“ aus den Schnittstellen, verlassen sich jedoch stattdessen auf die standardisierten HTTP-Verben (GET
/POST
/PUT
/DELETE
) CRUD-Semantik für die Aktionen bereitzustellen. Verben wie check
, place
, cancel
werden direkt den HTTP-Verben GET
zum Abrufen, POST
zum Erstellen und DELETE
zum Zerstören der Ressource order
zugeordnet. pay
und expedite
sind als Erstellung (HTTP POST) von Unterressourcen von payments
und expeditions
unter Ressource orders
strukturiert.
RESTful Interfaces standardisierte Interaktionsmuster für Webanwendungen, um sie einfacher zu verstehen und zu programmieren. Da sich die Schnittstellen auf die Manipulation von Ressourcen konzentrieren, sind die Antworten viel vorhersehbarer. Eine Analogie dazu, wie das RESTful-Architekturmuster die Interaktion mit Webdiensten vereinfacht, wäre, wie die relationale Datenbank und die SQL-Sprache die Datenspeicherung vereinfachen. Vor relationalen Datenbanken und SQL wurden Daten typischerweise in proprietären Systemen mit spezifischer Logik gespeichert, um mit ihnen zu interagieren, was bedeutete, dass Sie bei jedem Projekt einen anderen Satz von Anweisungen lernen mussten, um mit Daten zu interagieren. Relationale Datenbanken und SQL gaben eine gemeinsame Datenstruktur (Datensätze als Zeilen und Spalten in Tabellen gespeichert) und eine Reihe von gemeinsamen Schnittstellen – vier einfache Verben von SELECT
, INSERT
, UPDATE
und DELETE
, die alle Arten von Anwendungen unterstützen können.
Durch die Verwendung einer RESTful-Schnittstelle für Ihre Webanwendung wird Ihre Anwendung auch an die Art und Weise angepasst, wie die Backend-Daten in Datenbanken gespeichert werden, und die Entwicklung wird vereinfacht. Das werden wir in den folgenden Kapiteln sehen.
Ressourcennotation
Wir haben über die Routingkonventionen von Rails und RESTful-URL-Muster gesprochen. Jetzt würde unsere routes.rb
-Datei wie folgt aussehen:
### config/routes.rb ### Rails.application.routes.draw do ### posts ### get '/posts' => 'posts#index' get '/posts/new' => 'posts#new' get '/posts/:id' => 'posts#show' post '/posts' => 'posts#create' get '/posts/:id/edit' => 'posts#edit' patch '/posts/:id' => 'posts#update' put '/posts/:id' => 'posts#update' delete '/posts/:id' => 'posts#destroy' ### comments ### get '/comments' => 'comments#index' post '/posts/:id/comments' => 'comments#create' delete '/posts/:post_id/comments/:id' => 'comments#destroy' end
Tatsächlich sind die Routen wie im Abschnitt posts
sehr häufig, daher bietet Rails eine spezielle resources
-Notation, mit der Sie diese für Sie generieren können.
Wir können einfach:
resources :posts
Diese Zeile generiert automatisch die 8 Zeilen, die wir zuvor hatten. Beachten Sie, dass Rails für die Aktion update
aus Kompatibilitätsgründen sowohl PUT
als auch PATCH
zulässt.
Sie können bundle exec rake routes
in Ihrer Befehlszeilenumgebung ausführen, um zu überprüfen, ob genau derselbe Routensatz generiert wird.
Beachten Sie, dass diese einzelne Zeile 8 Routen generiert. In unserem Fall benötigen wir hier zufällig alle Routen, aber wenn Sie nur eine Teilmenge der Routen benötigen, können Sie sie expliziter definieren als:
resources :posts, only:
Diese Zeile oben würde nur 3 Routen für Sie generieren – geben Sie bundle exec rake routes
ein, um es zu überprüfen.
Verschachtelte Ressourcen
Rails ermöglicht es uns auch, Ressourcen zu verschachteln, um URL-Muster mit mehreren Ressourcen zu erstellen, siehe unten:
### config/routes.rb ### Rails.application.routes.draw do resources :posts do resources :comments, only: end resources :comments, only: :index end
Führen Sie bundle exec rake routes
aus und sehen Sie die Ausgabe – sie sollte dieselbe sein wie zuvor.
Pfadhelfer
Wenn Sie bundle exec rake routes
ausführen, beachten Sie die Spalte Prefix
für einige dieser Routen. Diese sollen uns helfen, die von Rails bereitgestellten Pfadhelfer für unsere Routen zu verwenden. Wir können diese Präfixe gefolgt von _path
in unseren Controllern und Ansichten verwenden, um Pfade einfach zu erstellen.
Zum Beispiel:
posts_path # => '/posts' post_id = 123 post_comments_path(post_id) # => '/posts/123/comments'
Was ist der Vorteil der Verwendung von URL-Helfern, anstatt die Pfade wie /posts/123/comments
direkt in Ihrem Code fest zu codieren? (zum Beispiel in Ihrer Controller-Aktion?) Der Grund, warum Sie Pfadhelfer verwenden möchten, ist, dass es viel einfacher ist, die URL-Helfer zu verwenden, um einen anderen Pfad zurückzugeben, wenn Sie die URL-Muster in Ihrer App ändern möchten:
get '/register', to: 'users#new', as: 'register'
Wenn wir dieser Route die „as“ -Klausel hinzufügen und bundle exec rake routes
ausführen, wird die Spalte Prefix
mit register
angezeigt, und Sie können register_path
verwenden, um /register
den Pfad abzurufen. Wenn wir nun den Pfad in /login
ändern möchten, müssen wir nur Folgendes tun:
get '/login', to: 'users#new', as: 'register'
Jetzt gibt uns unser register_path
/login
. Wir müssen unseren Code in der Rails-App überhaupt nicht ändern.
Weitere Routing-Konventionen
Bevor wir fortfahren, werfen wir einen kurzen Blick auf einige Variationen dessen, was wir bereits gesehen haben, sowie auf einige andere Funktionen, die uns in Rails Routing zur Verfügung stehen:
### config/routes.rb ### Rails.application.routes.draw do # pointing our homepage (root path) to posts#index root to: 'posts#index' # `match` & `via:` allow us to define one route and use several HTTP verbs # `as:` lets us define the name of the route prefix match '/authors/:id' => 'authors#update', via: , as: :update_author # update_author PUT|PATCH /authors/:id(.:format) authors#update resources :posts do # define extra params to pass for requests to a route get 'popular', on: :collection, action: :index, popular: true # popular_posts GET /posts/popular(.:format) posts#index {:popular=>true} get 'preview', on: :member # ... end # we can even use routes to redirect get '/home', to: redirect('/') end
Lassen Sie uns alle oben aufgeführten Funktionen einzeln aufschlüsseln.
-
root:
root
wird verwendet, um anzugeben, welche Aktion „/“ zugeordnet wird (die URL der obersten Ebene unserer Site, die keinen Pfad enthält).Die Root-URL Ihrer Website / App wird am häufigsten verwendet, daher sollteroot
oben in der Routendatei angegeben werden. -
spiel + über:
match
wird verwendet, um URL-Muster einer oder mehreren Routen zuzuordnen. Wir können auch angeben, welches HTTP-Verb für den Abgleich mit einer URL mit diesem Muster verwendet werden darf. Wir tun dies, indem wir einen Hash an diematch
-Methode übergeben. Der Schlüssel für diesen Hash istvia
, und der Wert ist ein Array von HTTP-Verben.match
ist eine allgemeinere Form einiger häufiger verwendeter HTTP-Routing-Methoden, z. B.get
,post
unddelete
. Es können alle die gleichen Optionen wie diese Methoden verwendet werden, aber im Vergleich zu diesen Methoden bietet es etwas mehr Flexibilität.
Zum Beispiel können wir mitmatch
eine URL angeben, die für zwei verschiedene Routen übereinstimmt, die jeweils zwei verschiedenen HTTP-Verben entsprechen. Normalerweise würde dies zwei Befehle anstelle von einem erfordern. Wir können dies in unserem obigen Beispiel sehen, in dem wir das URL-Muster'/authors/:id'
mit der Aktion'authors#update'
abgleichen. Wir geben dann an, welche HTTP-Verben verwendet werden können, um die Anforderung für diese URL mitvia:
. Geben Sie bei Verwendung vonmatch
immer eine HTTP-Methode an, da dies negative Auswirkungen auf die Sicherheit Ihrer Anwendung haben kann.match
stellt eine weitere Option für das Routing zu bestimmten Aktionen in Rails dar. Im Allgemeinen ist es am besten, bei den häufiger verwendetenHttpHelpers
-Methoden wieget
undpost
zu bleiben. -
as: Wir haben bereits erwähnt, dass die Option
as:
mit unserer Routendeklaration verwendet werden kann, um das Präfix für unsere URL-Helfer zu ändern. Dies kann auf verschiedene Arten verwendet werden. Wir könnenas:
, damit unsere URL-Helfer besser mit benutzerdefinierten URLs übereinstimmen. Eine andere Verwendung besteht darin, den aktuellen URL-Pfad-Helfer in etwas Intuitiveres oder etwas zu ändern, das besser mit den Ressourcen innerhalb einer Anwendung übereinstimmt. -
sammelroute: Siehe oben, wie wir unsere
/popular
-Route unter derposts
-Ressource verschachteln. Wir verwenden dannon: :collection
, um anzugeben, unter welchem Teil einer Ressource wir diese Route verschachteln. Wir haben viele Beiträge, so dass eine Sammlung betrachtet wird. Durch Angabe vonon: :collection
sagen wir: „Passen Sie eine URL an den Pfad/posts/popular
an.“ Wenn wiron: :collection
nicht hinzugefügt haben, geht Rails davon aus, dass diese Route einer anderen Ressource entspricht, die einem einzelnen Mitglied unserer Posts-Sammlung zugeordnet ist. In diesem Fall würde unsere Route/posts/:id/popular
. Wir können auch mehrere Sammelrouten angeben, indem wir ein Blockformat verwenden.
collection do get 'popular' end
- übergeben eines zusätzlichen Parameters: Wir können einen Standardparameter angeben, der immer mit unserem
params
-Hash übergeben wird, wenn wir mit bestimmten URLs abgeglichen werden. Es gibt zwei Möglichkeiten, dies anzugeben; Ein Weg ist der gleiche wie oben:
get 'popular', on: :collection, action: :index, popular: true
popular: true
wird an unsere Aktion übergeben, mit der die beliebte Route über den Hash params
übereinstimmt. Es hat einen Schlüssel von :popular
und einen Wert von true
. Wir können auch eine explizitere Syntax verwenden, indem wir einen Hash an unsere Routenmethode get
übergeben, wobei der Schlüssel defaults:
und der Wert ein weiterer Hash ist, der den Namen des Parameters enthält, den wir an unsere Aktion übergeben möchten.
get 'popular', on: :collection, action: :index, defaults: { popular: true}
-
member route: Mit
on: member
können wir angeben, dass unsere Route mit einem Member einer Sammlung übereinstimmt. Wenn wir dies verwenden, wird das dynamische Segment als:id
angezeigt. Ein URL-Helfer wird ebenfalls alspreview_post
erstellt. Indem wir diese Route miton: member
angeben, teilen wir Rails mit, dass dies ein Mitglied dieser bestimmten Ressource ist. Dass es sich nicht nur um eine weitere Ressource handelt, die unter Posts verschachtelt ist, sondern enger mit unseren Posts in dieser Anwendung verknüpft oder verwandt ist. Dieses Format verwendet die gleiche Syntax wie das Blockformat zum Definieren mehrerer Sammlungsrouten, ersetzen Sie einfachcollection
durchmember
. -
umleitung: Es gibt noch ein anderes Konzept, über das in unserem obigen Beispiel gesprochen werden kann, und zwar die Umleitung. Rails gibt uns die Möglichkeit, von einem Pfad zu einem anderen umzuleiten, indem wir einen Umleitungshelfer in Verbindung mit einer Route verwenden.
get '/home', to: redirect('/')
. Wenn jemand versucht, auf den Pfad/home
zuzugreifen, wird er sofort zum Stammpfad/
weitergeleitet. Dies ist natürlich nicht nur auf einen URL-Pfad beschränkt; Wir könnten auch so etwas haben:get '/home', to: redirect('/travel')
.
Review
Schienenrouten stehen vor einer Anwendung. Eine Route interpretiert eine eingehende HTTP-Anforderung und:
- passt eine Anforderung an eine Controller-Aktion an, die auf der Kombination eines HTTP-Verbs und des Anforderungs-URL-Musters basiert
- erfasst Daten in der URL, die in
params
in Controller-Aktionen verfügbar sein sollen - Rails ermutigt Entwickler, beim Einrichten von Routen RESTful-URL-Muster zu verwenden, wobei Ressourcen mit HTTP-Verben manipuliert werden.
- Mit dem Makro
resources
können Sie sehr schnell RESTful-Routen generieren.