Files
michaelschiemer/docs/api/index.md
Michael Schiemer 55a330b223 Enable Discovery debug logging for production troubleshooting
- Add DISCOVERY_LOG_LEVEL=debug
- Add DISCOVERY_SHOW_PROGRESS=true
- Temporary changes for debugging InitializerProcessor fixes on production
2025-08-11 20:13:26 +02:00

839 lines
21 KiB
Markdown

# API-Dokumentation
Diese Dokumentation bietet einen Überblick über die API des Frameworks und wie Sie sie verwenden können, um RESTful APIs zu erstellen.
## Einführung
Das Framework bietet umfassende Unterstützung für die Erstellung von RESTful APIs mit Funktionen wie:
- Einfache Routendefinition für API-Endpunkte
- Automatische Konvertierung von Daten in JSON
- Validierung von Anfragen
- Authentifizierung und Autorisierung
- Versionierung
- Rate Limiting
- API-Dokumentation
## API-Routen definieren
### Grundlegende API-Routen
Sie können API-Routen in der Datei `config/routes.php` definieren:
```php
use App\Framework\Routing\Router;
use App\Application\Controllers\Api\UserController;
return function (Router $router) {
$router->group('/api', function (Router $router) {
$router->get('/users', [UserController::class, 'index']);
$router->get('/users/{id}', [UserController::class, 'show']);
$router->post('/users', [UserController::class, 'store']);
$router->put('/users/{id}', [UserController::class, 'update']);
$router->delete('/users/{id}', [UserController::class, 'destroy']);
});
};
```
### API-Ressourcen-Routen
Für RESTful APIs können Sie API-Ressourcen-Routen verwenden:
```php
$router->apiResource('users', UserController::class);
```
Dies erstellt die folgenden Routen:
| HTTP-Methode | URI | Aktion | Routenname |
|--------------|------------------|----------|----------------|
| GET | /users | index | users.index |
| POST | /users | store | users.store |
| GET | /users/{user} | show | users.show |
| PUT/PATCH | /users/{user} | update | users.update |
| DELETE | /users/{user} | destroy | users.destroy |
### Verschachtelte API-Ressourcen
Sie können auch verschachtelte API-Ressourcen definieren:
```php
$router->apiResource('users.posts', UserPostController::class);
```
Dies erstellt Routen wie:
- `/users/{user}/posts`
- `/users/{user}/posts/{post}`
### API-Versionierung
Sie können verschiedene Versionen Ihrer API unterstützen:
```php
$router->group('/api/v1', function (Router $router) {
$router->apiResource('users', Api\V1\UserController::class);
});
$router->group('/api/v2', function (Router $router) {
$router->apiResource('users', Api\V2\UserController::class);
});
```
## API-Controller
API-Controller sind spezielle Controller, die JSON-Antworten zurückgeben:
```php
namespace App\Application\Controllers\Api;
use App\Framework\Http\Controller;
use App\Framework\Http\Request;
use App\Framework\Http\Response;
use App\Application\Models\User;
class UserController extends Controller
{
public function index(Request $request): Response
{
$users = User::all();
return $this->json([
'data' => $users
]);
}
public function show(Request $request, int $id): Response
{
$user = User::find($id);
if (!$user) {
return $this->json([
'error' => 'Benutzer nicht gefunden'
], 404);
}
return $this->json([
'data' => $user
]);
}
public function store(Request $request): Response
{
$this->validate($request, [
'name' => 'required|string|max:255',
'email' => 'required|email|unique:users,email',
'password' => 'required|string|min:8',
]);
$user = User::create($request->only(['name', 'email', 'password']));
return $this->json([
'data' => $user,
'message' => 'Benutzer erstellt'
], 201);
}
public function update(Request $request, int $id): Response
{
$user = User::find($id);
if (!$user) {
return $this->json([
'error' => 'Benutzer nicht gefunden'
], 404);
}
$this->validate($request, [
'name' => 'string|max:255',
'email' => 'email|unique:users,email,' . $id,
]);
$user->fill($request->only(['name', 'email']));
$user->save();
return $this->json([
'data' => $user,
'message' => 'Benutzer aktualisiert'
]);
}
public function destroy(Request $request, int $id): Response
{
$user = User::find($id);
if (!$user) {
return $this->json([
'error' => 'Benutzer nicht gefunden'
], 404);
}
$user->delete();
return $this->json([
'message' => 'Benutzer gelöscht'
]);
}
}
```
## API-Antworten
### Erfolgsantworten
Für Erfolgsantworten können Sie die `json`-Methode verwenden:
```php
// 200 OK
return $this->json([
'data' => $users
]);
// 201 Created
return $this->json([
'data' => $user,
'message' => 'Benutzer erstellt'
], 201);
// 204 No Content
return $this->noContent();
```
### Fehlerantworten
Für Fehlerantworten können Sie ebenfalls die `json`-Methode verwenden:
```php
// 400 Bad Request
return $this->json([
'error' => 'Ungültige Anfrage'
], 400);
// 401 Unauthorized
return $this->json([
'error' => 'Nicht autorisiert'
], 401);
// 403 Forbidden
return $this->json([
'error' => 'Zugriff verweigert'
], 403);
// 404 Not Found
return $this->json([
'error' => 'Benutzer nicht gefunden'
], 404);
// 422 Unprocessable Entity (Validierungsfehler)
return $this->json([
'error' => 'Validierungsfehler',
'errors' => $validator->errors()
], 422);
// 500 Internal Server Error
return $this->json([
'error' => 'Serverfehler'
], 500);
```
### Konsistente Antwortstruktur
Es ist wichtig, eine konsistente Antwortstruktur für Ihre API zu haben:
```php
// Erfolgsantwort
return $this->json([
'success' => true,
'data' => $data,
'message' => $message,
'meta' => [
'pagination' => [
'total' => $total,
'per_page' => $perPage,
'current_page' => $currentPage,
'last_page' => $lastPage,
]
]
]);
// Fehlerantwort
return $this->json([
'success' => false,
'error' => $errorMessage,
'errors' => $validationErrors,
'code' => $errorCode
], $statusCode);
```
## API-Ressourcen
API-Ressourcen ermöglichen es Ihnen, Modelle in JSON-Antworten zu transformieren:
```php
namespace App\Application\Resources;
use App\Framework\Http\Resources\JsonResource;
use App\Application\Models\User;
class UserResource extends JsonResource
{
public function toArray(): array
{
/** @var User $this->resource */
return [
'id' => $this->resource->id,
'name' => $this->resource->name,
'email' => $this->resource->email,
'created_at' => $this->resource->created_at->format('Y-m-d H:i:s'),
'updated_at' => $this->resource->updated_at->format('Y-m-d H:i:s'),
];
}
}
```
Verwenden Sie die Ressource in Ihrem Controller:
```php
use App\Application\Resources\UserResource;
public function show(Request $request, int $id): Response
{
$user = User::find($id);
if (!$user) {
return $this->json([
'error' => 'Benutzer nicht gefunden'
], 404);
}
return $this->json([
'data' => new UserResource($user)
]);
}
public function index(Request $request): Response
{
$users = User::all();
return $this->json([
'data' => UserResource::collection($users)
]);
}
```
## API-Authentifizierung
### Token-basierte Authentifizierung
Das Framework unterstützt Token-basierte Authentifizierung für APIs:
```php
namespace App\Application\Controllers\Api;
use App\Framework\Http\Controller;
use App\Framework\Http\Request;
use App\Framework\Http\Response;
use App\Framework\Auth\Auth;
use App\Framework\Auth\Token;
class AuthController extends Controller
{
public function login(Request $request): Response
{
$this->validate($request, [
'email' => 'required|email',
'password' => 'required|string',
]);
$credentials = $request->only(['email', 'password']);
if (!Auth::attempt($credentials)) {
return $this->json([
'error' => 'Ungültige Anmeldeinformationen'
], 401);
}
$user = Auth::user();
$token = Token::create($user);
return $this->json([
'data' => [
'user' => $user,
'token' => $token,
'token_type' => 'Bearer',
]
]);
}
public function logout(Request $request): Response
{
$token = $request->bearerToken();
if ($token) {
Token::revoke($token);
}
return $this->json([
'message' => 'Erfolgreich abgemeldet'
]);
}
}
```
### Authentifizierung mit dem Token
```php
namespace App\Application\Middleware;
use App\Framework\Http\Middleware;
use App\Framework\Http\Request;
use App\Framework\Http\Response;
use App\Framework\Auth\Token;
class ApiAuthMiddleware implements Middleware
{
public function handle(Request $request, callable $next): Response
{
$token = $request->bearerToken();
if (!$token || !Token::validate($token)) {
return response()->json([
'error' => 'Nicht autorisiert'
], 401);
}
$user = Token::getUser($token);
Auth::setUser($user);
return $next($request);
}
}
```
Registrieren Sie die Middleware:
```php
// In der Middleware-Konfiguration
$middleware->group('api', [
ApiAuthMiddleware::class
]);
// In den Routen
$router->group('/api', function (Router $router) {
// Öffentliche Routen
$router->post('/login', [AuthController::class, 'login']);
// Geschützte Routen
$router->group('', function (Router $router) {
$router->apiResource('users', UserController::class);
$router->post('/logout', [AuthController::class, 'logout']);
})->middleware('api');
});
```
## API-Validierung
Die Validierung für APIs funktioniert ähnlich wie für reguläre Anfragen, gibt jedoch JSON-Antworten zurück:
```php
public function store(Request $request): Response
{
$validator = new Validator($request->all(), [
'name' => 'required|string|max:255',
'email' => 'required|email|unique:users,email',
'password' => 'required|string|min:8',
]);
if ($validator->fails()) {
return $this->json([
'error' => 'Validierungsfehler',
'errors' => $validator->errors()
], 422);
}
$user = User::create($validator->validated());
return $this->json([
'data' => $user,
'message' => 'Benutzer erstellt'
], 201);
}
```
Oder mit der `validate`-Methode:
```php
public function store(Request $request): Response
{
try {
$this->validate($request, [
'name' => 'required|string|max:255',
'email' => 'required|email|unique:users,email',
'password' => 'required|string|min:8',
]);
} catch (ValidationException $e) {
return $this->json([
'error' => 'Validierungsfehler',
'errors' => $e->errors()
], 422);
}
$user = User::create($request->only(['name', 'email', 'password']));
return $this->json([
'data' => $user,
'message' => 'Benutzer erstellt'
], 201);
}
```
## API-Paginierung
Das Framework unterstützt Paginierung für API-Antworten:
```php
public function index(Request $request): Response
{
$page = $request->query('page', 1);
$perPage = $request->query('per_page', 15);
$users = User::paginate($perPage, $page);
return $this->json([
'data' => UserResource::collection($users->items()),
'meta' => [
'pagination' => [
'total' => $users->total(),
'per_page' => $users->perPage(),
'current_page' => $users->currentPage(),
'last_page' => $users->lastPage(),
]
]
]);
}
```
## API-Filterung und Sortierung
Sie können Filterung und Sortierung für Ihre API implementieren:
```php
public function index(Request $request): Response
{
$query = User::query();
// Filterung
if ($request->has('name')) {
$query->where('name', 'like', '%' . $request->query('name') . '%');
}
if ($request->has('email')) {
$query->where('email', 'like', '%' . $request->query('email') . '%');
}
if ($request->has('role')) {
$query->whereHas('roles', function ($q) use ($request) {
$q->where('name', $request->query('role'));
});
}
// Sortierung
$sortBy = $request->query('sort_by', 'created_at');
$sortDirection = $request->query('sort_direction', 'desc');
$allowedSortFields = ['id', 'name', 'email', 'created_at'];
if (in_array($sortBy, $allowedSortFields)) {
$query->orderBy($sortBy, $sortDirection === 'asc' ? 'asc' : 'desc');
}
// Paginierung
$page = $request->query('page', 1);
$perPage = $request->query('per_page', 15);
$users = $query->paginate($perPage, $page);
return $this->json([
'data' => UserResource::collection($users->items()),
'meta' => [
'pagination' => [
'total' => $users->total(),
'per_page' => $users->perPage(),
'current_page' => $users->currentPage(),
'last_page' => $users->lastPage(),
]
]
]);
}
```
## API-Rate Limiting
Das Framework bietet Rate Limiting für APIs, um Missbrauch zu verhindern:
```php
namespace App\Application\Middleware;
use App\Framework\Http\Middleware;
use App\Framework\Http\Request;
use App\Framework\Http\Response;
use App\Framework\Cache\Cache;
class RateLimitMiddleware implements Middleware
{
private int $maxRequests;
private int $timeWindow;
public function __construct(int $maxRequests = 60, int $timeWindow = 60)
{
$this->maxRequests = $maxRequests;
$this->timeWindow = $timeWindow;
}
public function handle(Request $request, callable $next): Response
{
$key = 'rate_limit:' . $this->getIdentifier($request);
$requests = Cache::get($key, 0);
if ($requests >= $this->maxRequests) {
return response()->json([
'error' => 'Zu viele Anfragen',
'message' => 'Bitte versuchen Sie es später erneut'
], 429);
}
Cache::put($key, $requests + 1, $this->timeWindow);
$response = $next($request);
$response->headers->set('X-RateLimit-Limit', $this->maxRequests);
$response->headers->set('X-RateLimit-Remaining', $this->maxRequests - $requests - 1);
return $response;
}
private function getIdentifier(Request $request): string
{
if (Auth::check()) {
return 'user:' . Auth::id();
}
return 'ip:' . $request->ip();
}
}
```
Registrieren Sie die Middleware:
```php
// In der Middleware-Konfiguration
$middleware->group('api', [
RateLimitMiddleware::class
]);
// Oder mit Parametern
$middleware->group('api', [
new RateLimitMiddleware(100, 60) // 100 Anfragen pro Minute
]);
```
## API-Dokumentation
Das Framework unterstützt die automatische Generierung von API-Dokumentation mit OpenAPI/Swagger:
```php
namespace App\Application\Controllers\Api;
use App\Framework\Http\Controller;
use App\Framework\Http\Request;
use App\Framework\Http\Response;
use App\Application\Models\User;
/**
* @OA\Tag(
* name="Users",
* description="API-Endpunkte für die Benutzerverwaltung"
* )
*/
class UserController extends Controller
{
/**
* @OA\Get(
* path="/api/users",
* summary="Liste aller Benutzer abrufen",
* tags={"Users"},
* @OA\Parameter(
* name="page",
* in="query",
* description="Seitennummer",
* @OA\Schema(type="integer")
* ),
* @OA\Parameter(
* name="per_page",
* in="query",
* description="Anzahl der Elemente pro Seite",
* @OA\Schema(type="integer")
* ),
* @OA\Response(
* response=200,
* description="Erfolgreiche Operation",
* @OA\JsonContent(
* @OA\Property(property="data", type="array", @OA\Items(ref="#/components/schemas/User")),
* @OA\Property(property="meta", type="object")
* )
* )
* )
*/
public function index(Request $request): Response
{
// ...
}
/**
* @OA\Get(
* path="/api/users/{id}",
* summary="Einen bestimmten Benutzer abrufen",
* tags={"Users"},
* @OA\Parameter(
* name="id",
* in="path",
* required=true,
* description="Benutzer-ID",
* @OA\Schema(type="integer")
* ),
* @OA\Response(
* response=200,
* description="Erfolgreiche Operation",
* @OA\JsonContent(
* @OA\Property(property="data", ref="#/components/schemas/User")
* )
* ),
* @OA\Response(
* response=404,
* description="Benutzer nicht gefunden",
* @OA\JsonContent(
* @OA\Property(property="error", type="string")
* )
* )
* )
*/
public function show(Request $request, int $id): Response
{
// ...
}
// Weitere Methoden mit Dokumentation...
}
/**
* @OA\Schema(
* schema="User",
* required={"id", "name", "email"},
* @OA\Property(property="id", type="integer", format="int64"),
* @OA\Property(property="name", type="string"),
* @OA\Property(property="email", type="string", format="email"),
* @OA\Property(property="created_at", type="string", format="date-time"),
* @OA\Property(property="updated_at", type="string", format="date-time")
* )
*/
```
Generieren Sie die API-Dokumentation:
```bash
php console.php api:docs
```
Dies erstellt eine OpenAPI/Swagger-Dokumentation, die Sie in einem Browser anzeigen können.
## Beste Praktiken für APIs
### Versionierung
Versionieren Sie Ihre API, um Änderungen zu ermöglichen, ohne bestehende Clients zu beeinträchtigen:
```php
$router->group('/api/v1', function (Router $router) {
// API v1 Routen
});
$router->group('/api/v2', function (Router $router) {
// API v2 Routen
});
```
### Konsistente Benennung
Verwenden Sie konsistente Benennungskonventionen für Ihre API-Endpunkte:
- Verwenden Sie Substantive im Plural für Ressourcen (z.B. `/users` statt `/user`)
- Verwenden Sie Kebab-Case für URLs (z.B. `/user-profiles` statt `/userProfiles`)
- Verwenden Sie Camel-Case für JSON-Eigenschaften (z.B. `firstName` statt `first_name`)
### HTTP-Statuscodes
Verwenden Sie die richtigen HTTP-Statuscodes:
- 200 OK: Erfolgreiche Anfrage
- 201 Created: Ressource erfolgreich erstellt
- 204 No Content: Erfolgreiche Anfrage ohne Inhalt
- 400 Bad Request: Ungültige Anfrage
- 401 Unauthorized: Authentifizierung erforderlich
- 403 Forbidden: Keine Berechtigung
- 404 Not Found: Ressource nicht gefunden
- 422 Unprocessable Entity: Validierungsfehler
- 429 Too Many Requests: Rate Limit überschritten
- 500 Internal Server Error: Serverfehler
### Fehlerbehandlung
Implementieren Sie eine konsistente Fehlerbehandlung:
```php
try {
// Code, der eine Exception werfen könnte
} catch (ValidationException $e) {
return $this->json([
'error' => 'Validierungsfehler',
'errors' => $e->errors()
], 422);
} catch (AuthorizationException $e) {
return $this->json([
'error' => 'Nicht autorisiert',
'message' => $e->getMessage()
], 403);
} catch (NotFoundException $e) {
return $this->json([
'error' => 'Nicht gefunden',
'message' => $e->getMessage()
], 404);
} catch (\Exception $e) {
// Protokollieren Sie den Fehler
$this->logger->error('API-Fehler', [
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
return $this->json([
'error' => 'Serverfehler',
'message' => 'Ein unerwarteter Fehler ist aufgetreten'
], 500);
}
```
### Dokumentation
Dokumentieren Sie Ihre API gründlich:
- Beschreiben Sie jeden Endpunkt
- Dokumentieren Sie alle Parameter
- Geben Sie Beispiele für Anfragen und Antworten
- Erklären Sie Fehlerszenarien
## Weitere Informationen
- [Routing-Anleitung](../guides/routing.md): Erfahren Sie mehr über das Routing-System.
- [Controller-Anleitung](../guides/controllers.md): Erfahren Sie mehr über Controller.
- [Validierungs-Anleitung](../guides/validation.md): Erfahren Sie mehr über die Validierung von Anfragen.
- [Sicherheits-Anleitung](../guides/security.md): Erfahren Sie mehr über die Sicherheitsfunktionen des Frameworks.
- [Architekturübersicht](../architecture/overview.md): Überblick über die Architektur des Frameworks.