Dokumentation durchblättern
Module entwickeln
Routen & APIs
Wie ein Modul eigene Web-Routen, Portal- und Fahrer-Seiten sowie token-authentifizierte API-Endpunkte registriert — mit Namenskonventionen, Middleware und Beispielen.
Ein Modul, das eigene Oberflächen oder Schnittstellen anbietet, braucht eigene Routen.
Schneespur baut hier auf dem Standard-Routing von Laravel auf — es gibt keine
modul-eigene Routing-Sprache, die Sie zusätzlich lernen müssten. Diese Seite zeigt, wie
Sie Web-Routen für Admin-, Portal- und Fahrer-Bereiche registrieren und wie Sie über den
ModuleApiRegistrar token-geschützte API-Endpunkte bereitstellen.
Web-Routen registrieren
Web-Routen meldet ein Modul in der boot()-Methode seines ServiceProviders an. Das ist
bewusst dieselbe Stelle, an der auch Navigation, Widgets und Einstellungen registriert
werden — alle Erweiterungspunkte eines Moduls liegen an einem Ort.
protected function registerRoutes(): void
{
Route::middleware(['web', 'auth'])
->prefix('admin/my-module')
->name('admin.my-module.')
->group(function () {
Route::get('settings', [MySettingsController::class, 'index'])
->name('settings');
Route::post('settings', [MySettingsController::class, 'update'])
->name('settings.update');
});
}
Namenskonvention für Routen
Routennamen folgen einem festen Muster. Der Grund: Andere Teile der Anwendung und andere
Module verweisen über route('admin.my-module.settings') auf Ihre Seiten. Halten Sie sich
an das Muster, bleiben diese Verweise vorhersehbar und kollidieren nicht.
| Muster | Beispiel |
|---|---|
| Admin-Seiten | admin.{module-slug}.{action} |
| Admin-Ressource | admin.{module-slug}.{resource}.{action} |
Der {slug} im Routennamen ist immer der Modul-Slug — derselbe Bezeichner, unter dem auch
Einstellungen und API-Routen laufen. Das macht Routen einem Modul eindeutig zuordenbar.
Middleware
Middleware entscheidet, wer eine Route überhaupt erreicht. Wählen Sie sie nach der
Zielgruppe der Route aus, nicht nach Gewohnheit — eine Fahrer-Route mit reiner
auth-Middleware wäre auch für Admins und Kunden offen.
| Middleware | Zweck |
|---|---|
web | Session, CSRF, Cookies |
auth | nur angemeldete Nutzer |
auth:customer | Guard für das Kundenportal |
module.api:{slug} | Token-Authentifizierung für Modul-APIs |
Admin-Routen
Die meisten Modul-Routen richten sich an das Admin-Backend. Ein vollständiges Ressourcen-Set sieht typischerweise so aus:
Route::middleware(['web', 'auth'])
->prefix('admin/documents')
->name('admin.documents.')
->group(function () {
Route::get('/', [DocumentController::class, 'index'])->name('index');
Route::get('/create', [DocumentController::class, 'create'])->name('create');
Route::post('/', [DocumentController::class, 'store'])->name('store');
Route::get('/{document}', [DocumentController::class, 'show'])->name('show');
Route::get('/{document}/download', [DocumentController::class, 'download'])->name('download');
Route::delete('/{document}', [DocumentController::class, 'destroy'])->name('destroy');
});
Portal-Routen (für Kunden)
Erweitert Ihr Modul das Kundenportal, schützen Sie die Routen mit dem
auth:customer-Guard und der EnsureCustomer-Middleware. So sehen Kunden ausschließlich
die für sie freigegebenen Bereiche — und nur, wenn das Portal überhaupt aktiviert ist.
Route::middleware(['web', 'auth:customer', EnsureCustomer::class])
->prefix('portal/documents')
->name('portal.documents.')
->group(function () {
Route::get('/', [PortalDocumentController::class, 'index'])->name('index');
Route::get('/{document}/download', [PortalDocumentController::class, 'download'])->name('download');
});
Fahrer-Routen
Routen für die Fahrer-Oberfläche kombinieren EnsureDriver mit EnsureDsgvoInformed. Der
zweite Wächter stellt sicher, dass der Fahrer die Datenschutz-Hinweise bestätigt hat,
bevor er eine Seite erreicht, auf der personenbezogene Daten verarbeitet werden — ein
bewusst eingebauter Schritt, kein optionales Extra.
Route::middleware(['web', 'auth', EnsureDriver::class, EnsureDsgvoInformed::class])
->prefix('driver/my-feature')
->name('driver.my-feature.')
->group(function () {
Route::get('/', [DriverFeatureController::class, 'index'])->name('index');
});
Modul-API-Routen
Neben Web-Routen kann ein Modul auch maschinenlesbare Endpunkte anbieten — etwa für einen
Webhook oder einen Statusabruf durch ein externes System. Dafür gibt es den
ModuleApiRegistrar. Er erzeugt authentifizierte API-Endpunkte mit einer einheitlichen
URL- und Namensstruktur, sodass Sie sich nicht selbst um Versionierung und
Token-Prüfung kümmern müssen.
Hinweis zur Abgrenzung: Gemeint ist hier die modul-eigene API-Mechanik, die heute vorhanden und nutzbar ist. Eine übergreifende, öffentliche REST-API der Anwendung ist davon getrennt und weiterhin geplant — sie ist nicht Gegenstand dieser Seite.
Registrierung
protected function registerApiRoutes(): void
{
$registrar = app(ModuleApiRegistrar::class);
$registrar->routes('my-module', 1, function () {
Route::get('status', [MyApiController::class, 'status'])->name('status');
Route::post('webhook', [MyApiController::class, 'webhook'])->name('webhook');
});
}
Der zweite Parameter (1) ist die API-Version. Sie ist Teil der erzeugten URL — dadurch
können Sie eine v2 später hinzufügen, ohne bestehende Integrationen zu brechen, die noch
auf v1 zeigen.
Erzeugte URL-Struktur
Aus der Registrierung baut der Registrar Pfade nach diesem Schema:
/api/mod/{slug}/v{version}/{endpoint}
Aus dem Beispiel oben wird etwa /api/mod/my-module/v1/status. Der Slug und die Version
stecken fest in der URL — das macht von außen sofort erkennbar, welches Modul in welcher
Version antwortet.
Routennamen
Die Namen der API-Routen folgen dem Muster module.{slug}.api.v{version}.{name}, also
zum Beispiel module.my-module.api.v1.status. Auch hier sorgt das feste Schema dafür,
dass sich API-Routen verschiedener Module nie in die Quere kommen.
API-Authentifizierung
Modul-API-Routen sind über die Middleware module.api:{slug} (AuthenticateModuleApi)
geschützt. Sie sind also nicht offen erreichbar, sondern verlangen ein gültiges Token. Der
Ablauf bei jeder Anfrage:
- erwartet ein Bearer-Token im
Authorization-Header - prüft den Token-Hash gegen die Tabelle
module_api_tokens - kontrolliert die Berechtigungen (
abilities) und das Ablaufdatum des Tokens - antwortet bei Ungültigkeit mit
401als JSON
Authorization: Bearer <token>
Das Token-Modell
Gespeichert wird nie das Klartext-Token, sondern nur dessen Hash. Das ist der Grund, warum ein Token nach dem Erzeugen einmalig angezeigt und danach nicht mehr rekonstruiert werden kann — aus dem Hash lässt sich das Original nicht zurückrechnen.
// ModuleApiToken fields:
module_slug // which module this token belongs to
name // human-readable token name
token_hash // SHA-256 hash of the actual token
abilities // JSON array of allowed abilities
expires_at // optional expiration datetime
last_used_at // updated on each use
Verwaltet werden die Token über die Admin-Oberfläche
(AdminModuleApiTokenController) — Sie müssen sie also nicht von Hand in der Datenbank
anlegen.
Beispiel-Controller
Ein API-Controller gibt typischerweise JSON zurück. Achten Sie darauf, eingehende Daten zu
validieren, bevor Sie sie verarbeiten — validate() lehnt unpassende Anfragen mit einer
klaren Fehlerantwort ab, statt sie ungeprüft durchzulassen.
namespace Schneespur\Module\MyModule\Http\Controllers;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class MyApiController
{
public function status(): JsonResponse
{
return response()->json([
'status' => 'ok',
'module' => 'my-module',
'version' => '1.0.0',
]);
}
public function webhook(Request $request): JsonResponse
{
$payload = $request->validate([
'event' => 'required|string',
'data' => 'required|array',
]);
// Process webhook...
return response()->json(['received' => true]);
}
}
Vorhandene Core-Middleware im Überblick
Bevor Sie eine eigene Middleware schreiben, lohnt der Blick auf die bereits vorhandenen Wächter. Viele Zugriffsregeln sind damit schon abgedeckt:
| Middleware | Klasse | Zweck |
|---|---|---|
EnsureAdmin | App\Http\Middleware\EnsureAdmin | Admin-Rolle verlangen |
EnsureDriver | App\Http\Middleware\EnsureDriver | Fahrer-Rolle verlangen |
EnsureCustomer | App\Http\Middleware\EnsureCustomer | Kunden-Guard + aktiviertes Portal verlangen |
EnsureDsgvoInformed | App\Http\Middleware\EnsureDsgvoInformed | bestätigten Datenschutz-Hinweis verlangen |
AuthenticateOwntracks | App\Http\Middleware\AuthenticateOwntracks | HTTP-Basic für OwnTracks-GPS |
AuthenticateModuleApi | App\Http\Middleware\AuthenticateModuleApi | Bearer-Token für Modul-APIs |
InstallerGuard | App\Http\Middleware\InstallerGuard | Installer-Routen schützen |
RedirectToInstaller | App\Http\Middleware\RedirectToInstaller | umleiten, solange nicht installiert (robots.txt ist ausgenommen) |
SetInstallerLocale | App\Http\Middleware\SetInstallerLocale | Sprache des Installers setzen |
SecurityHeaders | App\Http\Middleware\SecurityHeaders | Härtungs-Header plus pro-Anfrage gesetztes X-Robots-Tag (siehe unten) |
Die SecurityHeaders-Middleware hängt an der web-Gruppe und läuft damit für jede
Web-Antwort — auch für Modul-Routen. Hinzugekommen in 1.1.6. Neben den üblichen
Härtungs-Headern (Clickjacking, MIME, Referrer, HSTS) setzt sie auf jeder Antwort
X-Robots-Tag: noindex, nofollow — außer für die öffentlichen Pfade, die ein
Frontpage-Modul über die PublicHomepageRegistry freigibt. Reichen Sie öffentliche
Modul-Routen deshalb durch die web-Gruppe, damit dieser Header korrekt greift.
Öffentliche Startseite & öffentliche Routen
Hinzugekommen in 1.1.6. Standardmäßig ist eine Installation privat: Die Wurzel-URL
/ leitet auf den Login um, und jede Antwort trägt einen noindex-Header. Ein
Frontpage-Modul kann diese Wurzel-URL übernehmen und einzelne Seiten öffentlich und
indexierbar machen, ohne den Rest der Anwendung zu öffnen. Hier geht es nur um die
Routing-Mechanik dahinter — die vollständige Erklärung der Indexierung steht auf einer
eigenen Seite (siehe unten).
Die Routing-Mechanik existiert bereits. Das Frontpage-Modul, das sie nutzt, ist hingegen noch nicht verfügbar und weiterhin geplant.
Wie die /-Route entscheidet
Die Core-Route für / liegt in routes/web.php. Sie fragt bei jeder Anfrage die
PublicHomepageRegistry ab, statt einmal beim Booten festgelegt zu werden. Das ist der
Grund, warum die Übernahme der Startseite auch mit route:cache funktioniert — die
Entscheidung fällt zur Request-Zeit, nicht beim Cachen der Routen.
Welche Antwort / liefert, folgt einer festen Vorrangordnung:
- Installer — solange die Installation nicht abgeschlossen ist, gewinnt der Installer.
- registrierte Startseite — danach gewinnt eine über die Registry registrierte Homepage.
- Redirect auf Login — ist keine Startseite registriert, bleibt das bisherige
Standardverhalten:
/leitet auf den Login um.
Öffentliche Zusatzrouten
Weitere öffentliche Seiten registrieren Sie als ganz normale Web-Routen (siehe oben).
Wichtig ist nur: Sie müssen durch die web-Middleware-Gruppe laufen. Nur dann greift die
SecurityHeaders-Middleware, die den X-Robots-Tag-Header pro Anfrage setzt — und für
freigegebene Pfade eben weglässt.
// Im ServiceProvider::boot() eines (geplanten) Frontpage-Moduls
Route::middleware('web')->group(function () {
Route::get('/leistungen', [SiteController::class, 'services'])->name('frontpage.services');
});
Welche dieser Routen tatsächlich öffentlich sein sollen, melden Sie anschließend der
PublicHomepageRegistry an. Alles, was Sie nicht freigeben, bleibt privat (Default-Deny).
Die Registry-API (register(), allowCrawling() usw.) ist in der
Registries-Referenz beschrieben. Das große Bild — die
dynamische robots.txt-Route, der X-Robots-Tag-Header und das <meta name="robots">-Opt-in
als drei Indexierungs-Ebenen — steht unter
Öffentliche Startseite & SEO.
Eigene Middleware schreiben
Reicht keiner der vorhandenen Wächter, registrieren Sie eine eigene Middleware mit einem Alias in Ihrem ServiceProvider. Über diesen Alias sprechen Ihre Routen die Middleware dann genauso an wie die Core-Wächter:
public function boot(): void
{
$router = app('router');
$router->aliasMiddleware('my-module.check', MyModuleCheckMiddleware::class);
}
Mehr zum ServiceProvider als zentralem Einstiegspunkt steht unter ServiceProvider; welche Rollen und Berechtigungen die Wächter prüfen, behandelt Berechtigungen & Rollen.