Skip to content

Feat/unified error envelope, unified response migration, and validation hardening#77

Merged
Rashed99Azm merged 2 commits into
develop-backendfrom
feat/unified-error-envelope,-unified--Response--migration,-and-validation-hardening
Jun 25, 2026
Merged

Feat/unified error envelope, unified response migration, and validation hardening#77
Rashed99Azm merged 2 commits into
develop-backendfrom
feat/unified-error-envelope,-unified--Response--migration,-and-validation-hardening

Conversation

@Rashed99Azm

Copy link
Copy Markdown
Collaborator

refactor: unify error messages and responses across all API layers

Summary

Enforces Response<T> as the single response contract across the entire API surface, ensuring consistent response payloads regardless of where a request is handled.

This change completes the response standardization effort by covering handlers, middleware, authentication, rate limiting, validation, and short-circuit endpoints.

Changes

Handler Layer

  • Standardized all handler responses through Response<T>.Ok() / Response<T>.Fail()

  • All endpoints return via .ToHttpResult()

Middleware Layer

  • Updated ExceptionHandlingMiddleware to emit the same response envelope structure:

{
  "success": true,
  "code": "...",
  "message": "...",
  "data": {},
  "errors": [],
  "traceId": "...",
  "correlationId": "...",
  "timestamp": "..."
}

Authentication & Authorization

  • JWT OnChallenge responses use the shared envelope

  • JWT OnForbidden responses use the shared envelope

  • Rate limiting OnRejected responses use EnvelopeWriter

Short-Circuit Endpoints

  • Replaced inline Results.StatusCode(...)

  • Replaced inline Results.Ok(...)

  • Standardized responses through:

    • EnvelopeResults.*

    • Response<T>.Ok().ToHttpResult()

Validation

  • FluentValidation failures return the standard envelope

  • Domain validation failures return the standard envelope

  • Unified field-level validation errors through a shared FieldError structure

Content Endpoints

The following remain raw responses by design:

  • File downloads

  • iCal responses

  • HTTP 204 No Content responses

All error paths for these endpoints still return the standard envelope.

Development Endpoints

  • /dev/* endpoints are intentionally excluded from envelope enforcement

Remaining Flat-Response Violations

This PR closes the final three known flat-response violations:

Before | After -- | -- Results.StatusCode(415) | EnvelopeResults.UnsupportedMediaType() Results.StatusCode(415) | EnvelopeResults.UnsupportedMediaType() Results.Ok(summary) | Response.Ok(summary).ToHttpResult()

Result

  • ✅ Single response contract across all API layers

  • ✅ Consistent success and error payloads

  • ✅ Unified validation error structure

  • ✅ Authentication, middleware, and rate-limiting responses aligned

  • ✅ All known flat-response violations eliminated

…ardening

Phase 1 — Envelope correctness (no contract change)
- ExceptionHandlingMiddleware: locale resolved from Accept-Language header
- UnauthorizedAccessException maps to 401; correlationId added to envelope body
- BAD_REQUEST message made distinct from VALIDATION_ERROR
- General.NOT_FOUND renamed to RESOURCE_NOT_FOUND_GENERIC
- GetShareLinkQueryHandler consumer fixed
- 6/6 middleware tests pass

Phase 2 — Contract changes + handler migration
- DomainException → 422 BUSINESS_RULE_VIOLATION (ERR910)
- Rate-limit 429 envelope via EnvelopeWriter (replaced External plain-text,
  added Internal OnRejected)
- JWT 401/403 envelope via OnChallenge/OnForbidden
- EnvelopeWriter, EnvelopeResult, EnvelopeResults created as single
  serialization/status-mapping source of truth
- ~40 handlers migrated to Response<T>; all endpoint files processed

Phase 3 — Dead Result<T> deletion
- Deleted: Result.cs, Error.cs, OkApiResult.cs, CreatedApiResult.cs,
  NoContentApiResult.cs, ResultExtensions.cs, ResultValidationBehavior.cs,
  ValidationBehavior.cs
- All source projects build

Phase 4 — Cleanup and hardening
- ApplicationErrors → DomainKeys; moved Errors/ → Messages/; namespace
  CCE.Application.Errors → CCE.Application.Messages
- Bulk-replaced 193 ApplicationErrors. references and 51 using directives
- Hellang ProblemDetails dependency removed
- LoggingBehavior + ValidationBehavior registrations removed
- CSP hardened: X-Frame-Options, base-uri, form-action, object-src added
- Fixed middleware order comment in External/Program.cs

Phase 5 — Build integrity + DomainKeys completeness
- Added DomainKeysIntegrityTests (3 tests) in CCE.ArchitectureTests:
  bidirectional consistency between DomainKeys and SystemCodeMap — all pass
- Fixed missing using Microsoft.Extensions.Configuration in EnvelopeWriter.cs
- Filled 33 orphaned SystemCodeMap entries as new DomainKeys constants
  (General, Identity, Content, Verification, Validation, new InterestTopic class)
- Added 5 missing SystemCodeMap entries + SystemCode constants
  (ERR032, CON073–CON076)

Phase 6 — Validator hardening + SystemCodeMap completion (code review fixes)
- Add .WithErrorCode(MessageKeys.Validation.*) to Login, ForgotPassword,
  RefreshToken, Logout, UpdateMyProfile, CreateUser, SubmitExpertRequest
  validators — all were emitting ERR900 on validation failure
- Replace all remaining .WithMessage() with .WithErrorCode()
- RegisterUserCommandValidator: add RuleLevelCascadeMode = CascadeMode.Stop
  to prevent double REQUIRED_FIELD + INVALID_FORMAT on empty fields; add
  .NotEmpty() guards before Password Must() and ConfirmPassword Equal()
- Rename MatchStoryPasswordPolicy → MatchStrongPasswordPolicy (typo)
- ResetPasswordCommandValidator: add .NotEmpty() guards on NewPassword
  and ConfirmPassword; update method reference to renamed policy check
- Add ERR410 ROLE_NOT_FOUND, ERR411 INVALID_RESET_TOKEN,
  ERR412 EMAIL_CHANGE_FAILED, ERR127 OTP_UNAUTHORIZED to SystemCode +
  SystemCodeMap + Resources.yaml
- Add CON077–CON082 claims/permissions grant/revoke/update; CON083
  AD_LOGIN_SUCCESS; CON084 NEWSLETTER_SUBSCRIBED; CON085 TOPICS_LISTED;
  CON086 SECTION_REORDERED — all branch feature keys that were falling
  back to ERR900
- ExceptionHandlingMiddleware: remove ex.Message fallback from
  UnauthorizedAccessException to prevent internal message leakage
- ListRedisKeysQuery: replace raw "ITEMS_LISTED" with
  MessageKeys.General.ITEMS_LISTED

All source projects build with 0 errors, 0 warnings.
…ll pattern

Deleted all 52 convenience shortcut methods from MessageFactory, leaving
exactly 9 core methods (Ok, NotFound, Conflict, Unauthorized, Forbidden,
BusinessRule, ValidationError, Field). Updated ~130 handlers across
CCE.Application to call the generic API directly with explicit MessageKeys
constants.

Previously 83% of handlers used generic calls while 17% used shortcuts,
forcing every reader to check whether a shortcut exists before writing a
handler. Three handlers already mixed both styles. The factory now has one
pattern with no exceptions.

Rule going forward: new outcomes add a MessageKeys constant, a SystemCodeMap
entry, and a Resources.yaml string — nothing else touches MessageFactory.
@Rashed99Azm Rashed99Azm merged commit 153497e into develop-backend Jun 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants