Skip to content

SAIF.Web.API architecture

SAIF.Web.API is a cross-cutting-concerns library for ASP.NET Web API 2 (System.Web.Http) host applications. It supplies a response/error post-processing message handler, a delegated role-based authorization attribute, an HTTPS-enforcement action filter, a static exception-to-HTTP-status mapper, and a request-header extension helper. It contains no controllers, no entry point, and no database access; every actual authorization and configuration decision is delegated to services assumed to be supplied by the hosting application — no such registration or supply exists inside this library.*

Responsibilities

  • Post-process every outbound Web API response to add a correlation MessageID/MessageIDUrl, redact sensitive request content from debug traces, and rewrite non-success responses into a structured ErrorResponse body (ApiResponseHandler).
  • Enforce role/resource-based authorization on Web API controllers/actions by delegating the authorization decision to an injected ISecurityApiClient (SaifAuthorizeAttribute).
  • Enforce HTTPS on Web API actions, with a configurable relaxation for local/developer requests (RequireSSLAttribute).
  • Provide a static, reusable mapping from typed SAIF.API.Exceptions.* exceptions to HttpStatusCode values for consuming controllers/exception filters (ExceptionHandler).
  • Consume a response-envelope/typed-exception contract (ErrorResponse, SAIF.API.Exceptions.*) by fully-qualified type name; the compile-time-resolved assembly that actually supplies these types could not be confirmed — see Component boundaries.**

Component boundaries

  • ApiResponseHandler (SAIF.Web.API, derives System.Net.Http.DelegatingHandler): registered by the host in HttpConfiguration.MessageHandlers; no such registration exists in this repository.* Once registered, it wraps every request/response pair, times it, resolves ILogService/IEnvironmentService per request via IDependencyScope, and rewrites error responses.
  • SaifAuthorizeAttribute (SAIF.Web.API, derives System.Web.Http.AuthorizeAttribute, sealed): applied by the host to its own controllers/actions (no controllers exist inside this library, and no such application exists in this repository).* Resolves ISecurityApiClient/IEnvironmentService per request and never calls base.IsAuthorized, so the base attribute's built-in identity check is bypassed entirely.
  • RequireSSLAttribute (SAIF.Web.API, derives System.Web.Http.Filters.ActionFilterAttribute): applied by the host as an action filter; no such application exists in this repository.* Reads the ambient application's RequireSSLForLocal app setting directly via ConfigurationManager.AppSettings, bypassing IEnvironmentService; no such application or setting exists in this repository.*
  • ExceptionHandler (SAIF.Web.API, static class): a stateless mapping utility with no call site inside this library; intended for host controllers/exception filters to call explicitly.
  • HttpRequestExtensions (SAIF.Web.API, static extension class): a generic request-header reader (TryGetFirstHeader) with no security-specific role in this library.
  • ErrorResponse / SAIF.API.Exceptions.* (consumed types, not defined in this library's boundary): ApiResponseHandler and ExceptionHandler reference these fully-qualified type names at compile time, but SAIF.Web.API.csproj declares no <ProjectReference>/<PackageReference> to any project or package that names them explicitly. A sibling project in this repository, SAIF.Web.API.Core (out of scope for this library's documentation), declares types with these exact namespace-qualified names, and the SAIF.Framework.Core NuGet package is also a candidate supplier. Which assembly the compiler and runtime actually resolve these types from is unresolved — see Dependencies.**
  • Externally supplied contracts consumed via the host's dependency scope (not defined in this library): IEnvironmentService, ILogService/ILogServiceForApi, ISecurityApiClient — all originate from the SAIF.Framework.Core NuGet package.

Interaction model

  • The hosting Web API application registers ApiResponseHandler in HttpConfiguration.MessageHandlers, so it wraps every inbound request/outbound response pair in the pipeline; no registration call site exists in this repository, so this describes the intended host-side usage pattern rather than an observed registration.*
  • The hosting application decorates its own controllers/actions with [SaifAuthorizeAttribute(roles)] and/or [RequireSSLAttribute]; this library provides the attribute implementations only — it applies neither to any code of its own.*
  • ApiResponseHandler and SaifAuthorizeAttribute both resolve ILogService, IEnvironmentService, and ISecurityApiClient per request via ASP.NET Web API's IDependencyScope (request.GetDependencyScope().GetService(typeof(T))) — a service-locator pattern, not constructor injection. This means the library assumes the hosting application has registered these services with its own IDependencyResolver; if ISecurityApiClient or IEnvironmentService is unregistered, SaifAuthorizeAttribute.IsAuthorized throws MissingDependencyException rather than denying access.*
  • SaifAuthorizeAttribute delegates the actual per-role authorization decision to ISecurityApiClient.IsAuthorized(user, role); the concrete implementation (WCF SecurityService facade, HTTP API, or other mechanism) is defined in SAIF.Framework.Core and is not determinable from this repository.**
  • SAIF.Web.API references ErrorResponse and SAIF.API.Exceptions.* by fully-qualified type name at compile time, but SAIF.Web.API.csproj declares no <ProjectReference> or <PackageReference> that names either the sibling SAIF.Web.API.Core project or an equivalent package containing these types explicitly. Both SAIF.Web.API.Core (same repository) and the SAIF.Framework.Core NuGet package are candidate suppliers of these identically-named types; which one the build actually resolves is an open question that would require inspecting the restored SAIF.Framework.Core 2.0.25119.2 assembly to settle — see Dependencies.**
  • ExceptionHandler.ToHttpError has no call site inside this library; it is exposed as a static utility for host controllers/exception filters to call explicitly.*

Architecture diagram

The following diagram shows the inbound request path through this library's components, from the client request to the response (or the unresolved contracts dependency).

flowchart TD
    Client[Client request] --> Handler["ApiResponseHandler<br>(DelegatingHandler)"]
    Handler --> Pipeline["Host Web API pipeline<br>(controllers/actions)"]
    Pipeline --> AuthAttr["SaifAuthorizeAttribute<br>(AuthorizeAttribute)"]
    Pipeline --> SSLAttr["RequireSSLAttribute<br>(ActionFilterAttribute)"]
    Pipeline --> ExHandler["ExceptionHandler<br>(static utility,<br>host-invoked)"]
    Handler -.->|"resolved assembly<br>unconfirmed"| Contracts["ErrorResponse /<br>SAIF.API.Exceptions.*<br>(source unresolved:<br>SAIF.Web.API.Core?<br>SAIF.Framework.Core?)"]
    ExHandler -.->|"resolved assembly<br>unconfirmed"| Contracts
    Handler --> ClientResp[Response to client]

The following diagram shows how ApiResponseHandler, SaifAuthorizeAttribute, and RequireSSLAttribute — the same nodes shown above — each depend on services the hosting application is expected to supply.

flowchart TD
    AuthAttr["SaifAuthorizeAttribute<br>(AuthorizeAttribute)"] --> SecClient["ISecurityApiClient<br>(SAIF.Framework.Core)"]
    Handler["ApiResponseHandler<br>(DelegatingHandler)"] --> EnvSvc["IEnvironmentService<br>(SAIF.Framework.Core)"]
    AuthAttr --> EnvSvc
    Handler --> LogSvc["ILogService /<br>ILogServiceForApi<br>(SAIF.Framework.Core)"]
    AuthAttr --> LogSvc
    SSLAttr["RequireSSLAttribute<br>(ActionFilterAttribute)"] --> AppConfig["Host<br>ConfigurationManager.<br>AppSettings"]

Sequence diagram

The following diagram shows the primary request sequence when a decorated action fails authorization.

sequenceDiagram
    participant Client
    participant Handler as ApiResponseHandler
    participant Auth as SaifAuthorizeAttribute
    participant SecClient as ISecurityApiClient
    participant LogSvc as ILogService
    Client->>Handler: HTTP request
    Handler->>Auth: base.SendAsync -> pipeline dispatch
    Auth->>Auth: Resolve ISecurityApiClient / IEnvironmentService via IDependencyScope
    Auth->>SecClient: IsAuthorized(user, role) for each configured role
    SecClient-->>Auth: false (no matching role)
    Auth->>LogSvc: LogException(SecurityException) if user known
    Auth-->>Handler: 401 Unauthorized response
    Handler->>Handler: BuildApiResponse (MessageID, ErrorResponse body)
    Handler-->>Client: 401 response with structured ErrorResponse

Design constraints

  • Targets .NET Framework 4.6.2 and ASP.NET Web API 2 (System.Web.Http, IIS/OWIN hosting) — not ASP.NET Core; RequireSSLAttribute's redirect further couples it to System.Web/HttpContext.Current.
  • All cross-cutting service dependencies (IEnvironmentService, ILogService, ISecurityApiClient) are resolved via the per-request IDependencyScope service locator, not constructor injection — the hosting application's DI container must register concrete implementations, or dependent operations fail (MissingDependencyException for authorization; graceful degradation for logging/Splunk-URL resolution).*
  • The library ships no Web.config/app.config <appSettings> of its own; the one directly-read app setting (RequireSSLForLocal) is read from the ambient application's configuration — no such application or supplying configuration exists in this repository.*
  • No database access of any kind exists in this library — it is purely a Web API cross-cutting-concerns layer.
  • SAIF.Web.API references ErrorResponse/SAIF.API.Exceptions.* by fully-qualified type name, but SAIF.Web.API.csproj expresses no <ProjectReference> or <PackageReference> that names their supplying project/package explicitly; whether the compile-time assembly is the sibling SAIF.Web.API.Core project or the SAIF.Framework.Core package is unresolved — see Dependencies.**
  • SaifAuthorizeAttribute never calls base.IsAuthorized, so the base AuthorizeAttribute's built-in authenticated-identity check is bypassed; an attribute applied with no roles authorizes every request, including unauthenticated ones.
  • RequireSSLAttribute enforces HTTPS unconditionally for non-local requests; for local requests it is controlled by the ambient application's RequireSSLForLocal app setting — no such application or setting exists in this repository.* If the key is absent, or present but unparsable by bool.TryParse, requireSSL is set to false and local requests skip HTTPS enforcement — see Configuration.

Note: Statements marked with * were inferred from code structure, naming conventions, or contextual evidence where direct confirmation was not available in source artifacts. Statements marked with ** could not be confirmed; treat as unknown until verified.

Sources

  • src/SAIF.Web.API/ApiResponseHandler.cs
  • src/SAIF.Web.API/ExceptionHandler.cs
  • src/SAIF.Web.API/HttpRequestExtensions.cs
  • src/SAIF.Web.API/RequireSSLAttribute.cs
  • src/SAIF.Web.API/SaifAuthorizeAttribute.cs
  • src/SAIF.Web.API/app.config
  • src/SAIF.Web.API/SAIF.Web.API.csproj
  • src/SAIF.Web.API.Core/ApiResponse.cs
  • src/SAIF.Web.API.Core/BuildInfo.cs
  • src/SAIF.Web.API.Core/ErrorResponse.cs
  • src/SAIF.Web.API.Core/SAIFException.cs
  • src/SAIF.Web.API.Core/SAIF.Web.API.Core.csproj
  • src/SAIF.Web.API.sln