⬅️ Back to Controller Index

Request/Response Lifecycle

The complete flow of an HTTP request through the Controller Layer.

Complete Request Flow

Here’s how a typical request (creating a supplier) flows through all layers:

1. HTTP Request arrives at Spring DispatcherServlet
   POST /api/suppliers
   Content-Type: application/json
   Authorization: Bearer <token>
   { "name": "ACME Corp", ... }
   ↓
2. Request routing matches URL pattern to controller method
   DispatcherServlet identifies SupplierController.create()
   ↓
3. Spring Security checks @PreAuthorize annotation
   @PreAuthorize("hasRole('ADMIN')")
   β”œβ”€ If not authorized β†’ AccessDeniedException β†’ 403 Forbidden
   └─ If authorized β†’ Continue
   ↓
4. @Valid triggers DTO field validation
   Validates CreateSupplierDTO fields
   β”œβ”€ @NotBlank on name field
   β”œβ”€ @Email on contactEmail field
   └─ All constraints checked
   ↓
   If validation fails:
   β”œβ”€ MethodArgumentNotValidException thrown
   β”œβ”€ GlobalExceptionHandler catches
   └─ Returns 400 Bad Request
   ↓
5. Jackson deserializes request body JSON β†’ DTO
   { "name": "ACME Corp", ... }
      ↓
   CreateSupplierDTO object (fully populated)
   ↓
6. Controller method executes
   supplierService.create(dto)
   ↓
7. Service layer executes
   - Service validator checks business rules
   - Service calls repository
   - Repository persists to database
   - Service returns DTO
   ↓
   If exception in service:
   β”œβ”€ Service throws exception
   β”œβ”€ GlobalExceptionHandler catches
   └─ Returns 4xx or 5xx error
   ↓
8. Controller receives SupplierDTO from service
   Has: id, name, contactName, etc.
   ↓
9. Jackson serializes DTO β†’ JSON response body
   SupplierDTO object
      ↓
   { "id": "abc123", "name": "ACME Corp", ... }
   ↓
10. Controller builds ResponseEntity with status code
    ResponseEntity.created(location).body(created)
    β”œβ”€ Status: 201 CREATED
    β”œβ”€ Location header: /api/suppliers/abc123
    └─ Body: Serialized DTO
    ↓
11. HTTP Response returned to client
    HTTP/1.1 201 Created
    Location: /api/suppliers/abc123
    Content-Type: application/json
    { "id": "abc123", "name": "ACME Corp", ... }

Request Validation Checkpoint

Request JSON received
    ↓
DispatcherServlet routes to controller
    ↓
Spring Security @PreAuthorize check
    β”œβ”€ Not authenticated? β†’ 401 Unauthorized
    β”œβ”€ Not authorized? β†’ 403 Forbidden
    └─ Authorized? β†’ Continue
    ↓
Jackson deserializes JSON β†’ DTO
    ↓
@Valid annotation triggers field validation
    β”œβ”€ Any constraint violations? β†’ 400 Bad Request
    └─ All valid? β†’ Continue
    ↓
Controller method invoked

Response Path

Service returns domain DTO
    ↓
Controller wraps in ResponseEntity
    β”œβ”€ Set status code (200, 201, 204, etc.)
    β”œβ”€ Add headers if needed (Location, etc.)
    └─ Add body if applicable
    ↓
Jackson serializes DTO β†’ JSON
    ↓
HTTP Response to client
    β”œβ”€ Status line
    β”œβ”€ Headers
    └─ JSON body

Error Handling Path

Any layer throws exception
    ↓
Exception propagates up to GlobalExceptionHandler
    ↓
Handler identifies exception type
    β”œβ”€ IllegalArgumentException β†’ 400 Bad Request
    β”œβ”€ NoSuchElementException β†’ 404 Not Found
    β”œβ”€ IllegalStateException β†’ 409 Conflict
    β”œβ”€ AccessDeniedException β†’ 403 Forbidden
    └─ Generic Exception β†’ 500 Internal Server Error
    ↓
Handler builds ErrorResponse object
    β”œβ”€ Code: error type (BAD_REQUEST, NOT_FOUND, etc.)
    β”œβ”€ Message: human-readable explanation
    β”œβ”€ Path: request URL
    └─ Timestamp: when error occurred
    ↓
Jackson serializes ErrorResponse β†’ JSON
    ↓
HTTP Error Response to client
    β”œβ”€ Appropriate status code (400, 404, 409, etc.)
    └─ JSON body with error details

⬅️ Back to Controller Index