- Add DISCOVERY_LOG_LEVEL=debug - Add DISCOVERY_SHOW_PROGRESS=true - Temporary changes for debugging InitializerProcessor fixes on production
21 KiB
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:
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:
$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:
$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:
$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:
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:
// 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:
// 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:
// 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:
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:
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:
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
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:
// 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:
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:
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:
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:
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:
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:
// 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:
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:
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:
$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.
/usersstatt/user) - Verwenden Sie Kebab-Case für URLs (z.B.
/user-profilesstatt/userProfiles) - Verwenden Sie Camel-Case für JSON-Eigenschaften (z.B.
firstNamestattfirst_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:
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: Erfahren Sie mehr über das Routing-System.
- Controller-Anleitung: Erfahren Sie mehr über Controller.
- Validierungs-Anleitung: Erfahren Sie mehr über die Validierung von Anfragen.
- Sicherheits-Anleitung: Erfahren Sie mehr über die Sicherheitsfunktionen des Frameworks.
- Architekturübersicht: Überblick über die Architektur des Frameworks.