Dernière activité 1768571050

mike's Avatar mike a révisé ce gist 1768571049. Aller à la révision

1 file changed, 496 insertions

AlveusApiStandard.md(fichier créé)

@@ -0,0 +1,496 @@
1 + # Alveus API Standard
2 +
3 + This document describes the standardized API response format used across all Alveus API projects built with `Alveus.Api.Common`.
4 +
5 + ## Overview
6 +
7 + All API endpoints return responses wrapped in a standardized `ApiResponse` object. This ensures consistency across all APIs and makes it easier for clients to handle responses, errors, and pagination.
8 +
9 + ## Response Models
10 +
11 + ### Base Response: `ApiResponse`
12 +
13 + The base response model used for all API responses without data payloads.
14 +
15 + **Properties:**
16 + - `success` (boolean): Indicates whether the request was successful
17 + - `message` (string): A human-readable message describing the result
18 + - `timestamp` (long): When the response was generated (Unix UTC milliseconds since epoch)
19 + - `traceId` (string): Request correlation ID for debugging
20 +
21 + > **Note**: The timestamp is represented as Unix UTC milliseconds since epoch (January 1, 1970). This can be easily converted to a DateTime in most languages:
22 + > - **C#**: `DateTimeOffset.FromUnixTimeMilliseconds(timestamp)`
23 + > - **JavaScript**: `new Date(timestamp)`
24 + > - **Python**: `datetime.fromtimestamp(timestamp / 1000, tz=timezone.utc)`
25 +
26 + **Example - Success Response:**
27 + ```json
28 + {
29 + "success": true,
30 + "message": "Operation completed successfully",
31 + "timestamp": 1737033000123,
32 + "traceId": "0HN1234567890ABCDEF"
33 + }
34 + ```
35 +
36 + **Example - Simple Failure Response:**
37 + ```json
38 + {
39 + "success": false,
40 + "message": "Operation failed",
41 + "timestamp": 1737033000123,
42 + "traceId": "0HN1234567890ABCDEF"
43 + }
44 + ```
45 +
46 + ### Generic Data Response: `ApiResponse<T>`
47 +
48 + Used when returning data in the response. Inherits from `ApiResponse`.
49 +
50 + **Additional Properties:**
51 + - `data` (T, optional): The response payload
52 +
53 + **Example - User Data Response:**
54 + ```json
55 + {
56 + "success": true,
57 + "message": "User retrieved successfully",
58 + "timestamp": 1737033000123,
59 + "traceId": "0HN1234567890ABCDEF",
60 + "data": {
61 + "id": "550e8400-e29b-41d4-a716-446655440000",
62 + "username": "john.doe",
63 + "email": "[email protected]",
64 + "createdAt": "2025-01-01T00:00:00Z"
65 + }
66 + }
67 + ```
68 +
69 + **Example - List of Items:**
70 + ```json
71 + {
72 + "success": true,
73 + "message": "Products retrieved successfully",
74 + "timestamp": 1737033000123,
75 + "traceId": "0HN1234567890ABCDEF",
76 + "data": [
77 + {
78 + "id": 1,
79 + "name": "Product A",
80 + "price": 29.99
81 + },
82 + {
83 + "id": 2,
84 + "name": "Product B",
85 + "price": 49.99
86 + }
87 + ]
88 + }
89 + ```
90 +
91 + ### Error Response: `ApiErrorResponse`
92 +
93 + Used when returning validation errors. Inherits from `ApiResponse`.
94 +
95 + **Additional Properties:**
96 + - `errors` (ValidationError[], optional): Array of validation errors
97 +
98 + **ValidationError Properties:**
99 + - `field` (string): The field name that failed validation
100 + - `message` (string): The error message
101 + - `code` (string, optional): An error code for programmatic handling
102 + - `attemptedValue` (any, optional): The value that failed validation
103 +
104 + **Example - Validation Error Response:**
105 + ```json
106 + {
107 + "success": false,
108 + "message": "Validation failed",
109 + "timestamp": 1737033000123,
110 + "traceId": "0HN1234567890ABCDEF",
111 + "errors": [
112 + {
113 + "field": "email",
114 + "message": "Email address is invalid",
115 + "code": "InvalidFormat",
116 + "attemptedValue": "not-an-email"
117 + },
118 + {
119 + "field": "password",
120 + "message": "Password must be at least 8 characters",
121 + "code": "MinLength",
122 + "attemptedValue": "short"
123 + }
124 + ]
125 + }
126 + ```
127 +
128 + ### Paginated Response: `PagedApiResponse<T>`
129 +
130 + Used for paginated list endpoints. Inherits from `ApiResponse<IEnumerable<T>>`.
131 +
132 + **Additional Properties:**
133 + - `data` (T[]): Array of items for the current page
134 + - `pagination` (PaginationMetadata): Pagination information
135 +
136 + **PaginationMetadata Properties:**
137 + - `currentPage` (int): Current page number (1-based)
138 + - `pageSize` (int): Number of items per page
139 + - `totalCount` (long): Total number of items across all pages
140 + - `totalPages` (int): Total number of pages
141 + - `hasNextPage` (boolean): Whether a next page exists
142 + - `hasPreviousPage` (boolean): Whether a previous page exists
143 +
144 + **Example - Paginated Users Response:**
145 + ```json
146 + {
147 + "success": true,
148 + "message": "Users retrieved successfully",
149 + "timestamp": 1737033000123,
150 + "traceId": "0HN1234567890ABCDEF",
151 + "data": [
152 + {
153 + "id": "550e8400-e29b-41d4-a716-446655440000",
154 + "username": "john.doe",
155 + "email": "[email protected]"
156 + },
157 + {
158 + "id": "660f9511-f3ac-52e5-b827-557766551111",
159 + "username": "jane.smith",
160 + "email": "[email protected]"
161 + }
162 + ],
163 + "pagination": {
164 + "currentPage": 1,
165 + "pageSize": 2,
166 + "totalCount": 10,
167 + "totalPages": 5,
168 + "hasNextPage": true,
169 + "hasPreviousPage": false
170 + }
171 + }
172 + ```
173 +
174 + ## HTTP Status Codes
175 +
176 + The API uses standard HTTP status codes in combination with the `success` field:
177 +
178 + ### Success Codes (2xx)
179 + - **200 OK**: Standard success response
180 + - **201 Created**: Resource successfully created
181 + - **204 No Content**: Success with no data to return
182 +
183 + ### Client Error Codes (4xx)
184 + - **400 Bad Request**: Invalid request format or parameters
185 + - **401 Unauthorized**: Authentication required or failed
186 + - **403 Forbidden**: User lacks permission for the requested resource
187 + - **404 Not Found**: Resource does not exist
188 + - **409 Conflict**: Request conflicts with current state (e.g., duplicate resource)
189 + - **422 Unprocessable Entity**: Validation failed
190 +
191 + ### Server Error Codes (5xx)
192 + - **500 Internal Server Error**: Unexpected server error
193 + - **503 Service Unavailable**: Service temporarily unavailable
194 +
195 + ## Common Response Patterns
196 +
197 + ### Success with Data (200 OK)
198 + ```json
199 + {
200 + "success": true,
201 + "message": "Resource retrieved successfully",
202 + "timestamp": 1737033000123,
203 + "traceId": "0HN1234567890ABCDEF",
204 + "data": {
205 + "id": 123,
206 + "name": "Example"
207 + }
208 + }
209 + ```
210 +
211 + ### Resource Created (201 Created)
212 + ```json
213 + {
214 + "success": true,
215 + "message": "Resource created",
216 + "timestamp": 1737033000123,
217 + "traceId": "0HN1234567890ABCDEF",
218 + "data": {
219 + "id": 124,
220 + "name": "New Resource",
221 + "createdAt": "2026-01-16T14:30:00Z"
222 + }
223 + }
224 + ```
225 +
226 + ### Success Without Data (204 No Content)
227 + ```json
228 + {
229 + "success": true,
230 + "message": "No content",
231 + "timestamp": 1737033000123,
232 + "traceId": "0HN1234567890ABCDEF"
233 + }
234 + ```
235 +
236 + ### Bad Request (400)
237 + ```json
238 + {
239 + "success": false,
240 + "message": "Bad request",
241 + "timestamp": 1737033000123,
242 + "traceId": "0HN1234567890ABCDEF"
243 + }
244 + ```
245 +
246 + ### Unauthorized (401)
247 + ```json
248 + {
249 + "success": false,
250 + "message": "Unauthorized",
251 + "timestamp": 1737033000123,
252 + "traceId": "0HN1234567890ABCDEF"
253 + }
254 + ```
255 +
256 + ### Forbidden (403)
257 + ```json
258 + {
259 + "success": false,
260 + "message": "Forbidden",
261 + "timestamp": 1737033000123,
262 + "traceId": "0HN1234567890ABCDEF"
263 + }
264 + ```
265 +
266 + ### Not Found (404)
267 + ```json
268 + {
269 + "success": false,
270 + "message": "Resource not found",
271 + "timestamp": 1737033000123,
272 + "traceId": "0HN1234567890ABCDEF"
273 + }
274 + ```
275 +
276 + ### Conflict (409)
277 + ```json
278 + {
279 + "success": false,
280 + "message": "Resource already exists",
281 + "timestamp": 1737033000123,
282 + "traceId": "0HN1234567890ABCDEF"
283 + }
284 + ```
285 +
286 + ### Validation Error (422)
287 + ```json
288 + {
289 + "success": false,
290 + "message": "Validation failed",
291 + "timestamp": "2026-01-16T14:30:00.123Z",
292 + "traceId": "0HN1234567890ABCDEF",
293 + "errors": [
294 + {
295 + "field": "username",
296 + "message": "Username is required",
297 + "code": "Required"
298 + }
299 + ]
300 + }
301 + ```
302 +
303 + ### Internal Server Error (500)
304 + ```json
305 + {
306 + "success": false,
307 + "message": "An unexpected error occurred",
308 + "timestamp": 1737033000123,
309 + "traceId": "0HN1234567890ABCDEF"
310 + }
311 + ```
312 +
313 + ## Using the API Standard in Controllers
314 +
315 + Controllers should inherit from `ApiControllerBase` which provides helper methods for returning standardized responses:
316 +
317 + ### Success Methods
318 +
319 + ```csharp
320 + // Success with data
321 + return Success("User retrieved", user);
322 +
323 + // Success with data and custom status code
324 + return Success("User updated", user, 200);
325 +
326 + // Success without data
327 + return Success("Operation completed");
328 +
329 + // Created (201)
330 + return Created("User created", newUser);
331 + return Created(newUser); // Uses default message
332 +
333 + // No Content (204)
334 + return NoContent();
335 + ```
336 +
337 + ### Error Methods
338 +
339 + ```csharp
340 + // Generic failure
341 + return Failure("Something went wrong");
342 +
343 + // Bad Request (400)
344 + return BadRequest("Invalid input");
345 +
346 + // Unauthorized (401)
347 + return Unauthorized("Authentication required");
348 +
349 + // Forbidden (403)
350 + return Forbidden("Access denied");
351 +
352 + // Not Found (404)
353 + return NotFound("User not found");
354 +
355 + // Conflict (409)
356 + return Conflict("Username already exists");
357 + ```
358 +
359 + ### Pagination Methods
360 +
361 + ```csharp
362 + // Paginated response
363 + return PagedSuccess(
364 + "Users retrieved",
365 + users,
366 + currentPage: 1,
367 + pageSize: 10,
368 + totalCount: 100
369 + );
370 + ```
371 +
372 + ## Exception Handling
373 +
374 + When using the `Alveus.Api.Common.Client` for making API calls, responses are automatically parsed and exceptions are thrown for error responses:
375 +
376 + ### Built-in Exceptions
377 +
378 + All exceptions inherit from `ApiException` which contains:
379 + - `Message`: Error description
380 + - `StatusCode`: HTTP status code
381 +
382 + **Available Exception Types:**
383 + - `BadRequestException` (400)
384 + - `UnauthorizedException` (401)
385 + - `ForbiddenException` (403)
386 + - `NotFoundException` (404)
387 + - `ConflictException` (409)
388 + - `ValidationException` (422) - includes `ValidationErrors` property
389 + - `TooManyRequestsException` (429)
390 + - `InternalServerErrorException` (500)
391 + - `ServiceUnavailableException` (503)
392 +
393 + **Example Usage:**
394 + ```csharp
395 + try
396 + {
397 + var user = await apiClient.GetUserAsync(userId);
398 + }
399 + catch (NotFoundException ex)
400 + {
401 + Console.WriteLine($"User not found: {ex.Message}");
402 + }
403 + catch (ValidationException ex)
404 + {
405 + foreach (var error in ex.ValidationErrors)
406 + {
407 + Console.WriteLine($"{error.Field}: {error.Message}");
408 + }
409 + }
410 + catch (ApiException ex)
411 + {
412 + Console.WriteLine($"API Error ({ex.StatusCode}): {ex.Message}");
413 + }
414 + ```
415 +
416 + ## Best Practices
417 +
418 + 1. **Always wrap responses**: Use the helper methods from `ApiControllerBase` to ensure consistent response format
419 + 2. **Include meaningful messages**: Provide clear, actionable messages for both success and error cases
420 + 3. **Use appropriate status codes**: Match the HTTP status code to the actual result
421 + 4. **Validate input**: Return `ValidationException` with detailed field-level errors for invalid input
422 + 5. **Handle exceptions**: Use try-catch blocks and return appropriate error responses
423 + 6. **Document your endpoints**: Include response examples in API documentation
424 + 7. **Use pagination**: For list endpoints that may return many items, use `PagedSuccess`
425 + 8. **Be consistent**: Always use the same response structure across all endpoints
426 + 9. **Leverage traceId for debugging**: Use the traceId to correlate requests across services and logs
427 +
428 + ## Complete API Endpoint Example
429 +
430 + ```csharp
431 + [HttpGet("users/{id}")]
432 + [ProducesResponseType(typeof(ApiResponse<UserDto>), 200)]
433 + [ProducesResponseType(typeof(ApiResponse), 404)]
434 + public async Task<IActionResult> GetUser(string id)
435 + {
436 + var user = await _userService.GetUserByIdAsync(id);
437 +
438 + if (user == null)
439 + {
440 + return NotFound($"User with ID '{id}' not found");
441 + }
442 +
443 + return Success("User retrieved successfully", user);
444 + }
445 +
446 + [HttpPost("users")]
447 + [ProducesResponseType(typeof(ApiResponse<UserDto>), 201)]
448 + [ProducesResponseType(typeof(ApiErrorResponse), 422)]
449 + public async Task<IActionResult> CreateUser([FromBody] CreateUserRequest request)
450 + {
451 + if (!ModelState.IsValid)
452 + {
453 + var errors = ModelState
454 + .Where(x => x.Value?.Errors.Count > 0)
455 + .SelectMany(x => x.Value!.Errors.Select(e => new ValidationError
456 + {
457 + Field = x.Key,
458 + Message = e.ErrorMessage
459 + }));
460 +
461 + return Failure("Validation failed", errors, 422);
462 + }
463 +
464 + var user = await _userService.CreateUserAsync(request);
465 + return Created("User created successfully", user);
466 + }
467 +
468 + [HttpGet("users")]
469 + [ProducesResponseType(typeof(PagedApiResponse<UserDto>), 200)]
470 + public async Task<IActionResult> GetUsers(
471 + [FromQuery] int page = 1,
472 + [FromQuery] int pageSize = 10)
473 + {
474 + var (users, totalCount) = await _userService.GetUsersAsync(page, pageSize);
475 +
476 + return PagedSuccess(
477 + "Users retrieved successfully",
478 + users,
479 + page,
480 + pageSize,
481 + totalCount
482 + );
483 + }
484 + ```
485 +
486 + ## Summary
487 +
488 + This standardized response format provides:
489 + - **Consistency**: All endpoints return responses in the same structure
490 + - **Type Safety**: Strong typing with generics for data payloads
491 + - **Easy Error Handling**: Automatic exception throwing on client side
492 + - **Developer Experience**: Clear, predictable responses with helpful metadata
493 + - **Debugging**: TraceId for correlating requests across services
494 + - **Pagination Support**: Built-in pagination metadata for list endpoints
495 + - **Validation**: Structured validation error reporting
496 + - **AOT Compatibility**: Works with Native AOT compilation in .NET 8+
Plus récent Plus ancien