Skip to content

Architecture

PdfAPI is hosted in IIS as a single ASP.NET Web API 2 application with no OWIN self-hosting and no WCF surface. Startup wires Web API configuration and an Autofac container before any request is served; every controller resolves a per-request PDF service and, where tracking applies, a per-request NHibernate unit of work against the Oracle IMG schema.

Hosting model

The service is hosted in IIS. Global.asax instantiates WebApiApplication : System.Web.HttpApplication, whose Application_Start runs the App_Start configuration classes, including WebApiConfig.Register(HttpConfiguration).

Aspect Finding
Host IIS (System.Web.HttpApplication) — no OWIN, no WCF, no self-host
Application class WebApiApplication (PdfAPI\Global.asax.cs)
Startup call chain Application_Start → LicenseHelper.LicenseSyncfusion() → AreaRegistration.RegisterAllAreas() → GlobalConfiguration.Configure(WebApiConfig.Register) → FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters) → RouteConfig.RegisterRoutes(RouteTable.Routes) → BundleConfig.RegisterBundles(BundleTable.Bundles) → build and configure the Autofac container (ContainerBuilder, RegisterControllers, RegisterApiControllers, IoCConfig.RegisterDependencies, builder.Build()) → assign DependencyResolver and GlobalConfiguration.Configuration.DependencyResolver
Virtual directory / app pool Not determined from source**
Target framework .NET Framework 4.6.2

Request pipeline

The following diagram shows how a request reaches a controller action and how the NHibernate unit of work is scoped for tracked operations.

flowchart TD
    CLIENT["Client request"] --> IIS["IIS + HttpApplication"]
    IIS --> HANDLER["ApiResponseHandler<br>DelegatingHandler"]
    HANDLER --> ROUTE["Attribute routing<br>MapHttpAttributeRoutes"]
    ROUTE --> CTRL["Controller : ApiController"]
    CTRL --> FACTORY["PdfServiceFactory.Create()<br>reads pdfapi.pdfservicetype"]
    FACTORY --> SVC["SyncfusionPdfService<br>or ActivePdfService"]
    SVC --> TRACK["PdfTrackingService"]
    TRACK --> UOW["NHibernate IUnitOfWork<br>IUnitOfWorkFactory(&quot;oracle&quot;)"]
    UOW --> DB["Oracle IMG schema"]
    SVC --> CORE["PdfAPI.Core / PdfApi.SyncFusionCore<br>format operations"]

Layer responsibilities

Layer Type Responsibility
WebApiApplication System.Web.HttpApplication Application start-up and configuration wiring
PdfAPI\App_Start\WebApiConfig.cs Configuration Message handler, formatter, route, and error-detail-policy configuration
ConvertController, ExtractController, MergeController, LegacyController, InfoController, ApiInfoController System.Web.Http.ApiController Request binding and delegation to the service layer; four of six controllers add per-action try/catch error handling
PdfServiceFactory / PdfService (abstract) / SyncfusionPdfService / ActivePdfService Service layer Selects and executes the PDF processor, coordinates tracking and incident email
PdfTrackingService NHibernate Persistence through IUnitOfWork against the Oracle IMG schema — the sole data-access component in the repository

Routing configuration

Aspect Finding
MapHttpAttributeRoutes() called Yes
Convention route templates api/{controller}/{id} (DefaultApi, id optional) — registered but matched by no live action, because every action carries [Route]*
Route prefix / versioning [RoutePrefix("v1")] (ConvertController, ExtractController, MergeController), [RoutePrefix("v1/legacy")] (LegacyController), [RoutePrefix("v1/info")] (InfoController); ApiInfoController has no [RoutePrefix]
Controller folder layout All six controllers live in PdfAPI\Controllers\v1\; the folder name contributes nothing to the URL — only [RoutePrefix]/[Route] values do

Base controller

Every controller declares System.Web.Http.ApiController directly. There is no repository-local base controller (no ApiBase-style type), so no shared response construction, logging, or identity resolution is contributed by inheritance.

Controller Base Inherited behavior
ConvertController, ExtractController, MergeController, LegacyController, InfoController, ApiInfoController ApiController —

Composition root

Composition root location, as actually found in this repository: PdfAPI\App_Start\IocConfig.cs — the file name carries a lowercase c (IocConfig.cs), while the class it declares is IoCConfig with public static void RegisterDependencies(ContainerBuilder builder). This path was discovered behaviorally, not by filename convention: the Autofac API surface (new Autofac.ContainerBuilder(), builder.Build(), AutofacDependencyResolver, AutofacWebApiDependencyResolver, RegisterCommonDependencies() was searched across the whole solution closure and traced back from PdfAPI\Global.asax.cs → Application_Start. No Autofac.Module subclass, no RegisterAssemblyTypes, and no separate *.Dependencies or SAIF.DependencyMaps.* project exists in this repository.

Field Value
Project PdfAPI
File PdfAPI\App_Start\IocConfig.cs
Entry method IoCConfig.RegisterDependencies(ContainerBuilder)
Invoked from PdfAPI\Global.asax.cs → Application_Start (after RegisterControllers/RegisterApiControllers, before builder.Build())
Resolver Both AutofacDependencyResolver (MVC) and AutofacWebApiDependencyResolver (Web API) are assigned in Global.asax.cs
Autofac version Not read from packages.config in this analysis pass — see Dependencies for package versions
Additional registration files None

The container is built across PdfAPI\Global.asax.cs and PdfAPI\App_Start\IocConfig.cs together: Global.asax.cs creates the ContainerBuilder, registers MVC and Web API controllers by assembly scan, calls IoCConfig.RegisterDependencies(builder), builds the container, and assigns both dependency resolvers. IocConfig.cs calls builder.RegisterCommonDependencies(typeof(HttpApplication)) (from SAIF.Web.Mvc.Extensions) to supply ILogService, IEnvironmentService, and the named IUnitOfWorkFactory("oracle"), then layers on the two application-specific registrations below.

Registration Lifetime Resolved by Purpose
builder.RegisterCommonDependencies(typeof(HttpApplication)) Determined inside the extension method — not visible in this repository** All controllers and services Supplies ILogService, IEnvironmentService, and IUnitOfWorkFactory("oracle")
IUnitOfWork (lambda over IUnitOfWorkFactory("oracle").GetUnitOfWork(DbConnectionManager.GetOracleConnectionString(IEnvironmentService, EnvironmentConfigKeys.OracleDatabase, "IMG"))) InstancePerRequest PdfServiceFactory, PdfService, PdfTrackingService NHibernate unit of work for the Oracle IMG schema
PdfServiceFactory (concrete type, no interface) InstancePerRequest ConvertController, ExtractController, MergeController, LegacyController, InfoController Selects and constructs the PDF processor per request

IPdfService is not registered with Autofac; controllers receive PdfServiceFactory and call .Create() themselves, so the concrete processor is selected per request at runtime rather than by container configuration (BR-PDFAPI-003).

Error handling

Three mechanisms co-exist in this repository; they are not interchangeable, and one does not apply to API endpoints at all.

Mechanism Registered in Scope Behavior
ApiResponseHandler (DelegatingHandler, package SAIF.Web.API) config.MessageHandlers.Add(new ApiResponseHandler()); in PdfAPI\App_Start\WebApiConfig.cs Global — every Web API request/response, including framework-produced failures Wraps/post-processes the HTTP response for the whole service; the exact envelope shape is implemented inside the SAIF.Web.API package and is not visible in this repository**
Per-action try/catch + ILogService.LogException ConvertController (both actions), ExtractController, MergeController, LegacyController Per-action (5 of 13 endpoints) The action swallows the exception, logs it, and returns Ok(new PdfResult("<message>", request)) — HTTP 200 with Status = Failed
MVC HandleErrorAttribute filters.Add(new HandleErrorAttribute()); in PdfAPI\App_Start\FilterConfig.cs MVC controllers only (GlobalFilters.Filters) — has no effect on any ApiController action Not part of the API error contract

config.Filters is never populated — there is no Web API ExceptionFilterAttribute, no IExceptionHandler, and no IExceptionLogger. config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always; applies in every environment, so an unhandled exception produces full framework exception detail (message, type, stack trace) before the response reaches ApiResponseHandler. That handler's final wrapping/post-processing and the actual client-visible envelope are not visible in this repository** (see Endpoints: Error response shape). InfoController (7 endpoints) and ApiInfoController (1 endpoint) have no try/catch, so their validation failures surface as framework errors rather than as a PdfResult.

API documentation surface

Aspect Finding
Swagger / Swashbuckle Active — via [assembly: PreApplicationStartMethod(typeof(SwaggerConfig), "Register")] in PdfAPI\App_Start\SwaggerConfig.cs; there is no explicit SwaggerConfig.Register() call in Application_Start
Exposed route Not determined from source** — SwaggerConfig.Register() calls EnableSwagger(c => c.SingleApiVersion("v1", "PdfAPI")).EnableSwaggerUi(), with no custom RootUrl configured
Package and version Swashbuckle / Swashbuckle.Core 5.3.2, WebActivatorEx 2.0

Telemetry

Aspect Finding
Instrumentation None found — a search for ConfigureOpenTelemetry( across the solution closure returned zero matches
Registered in —

The only observability in force is logging through ILogService (LogException, TraceError, TraceWarn, TraceDebug) plus log4net 2.0.5, and a stray Console.WriteLine(result.ToString()) at the end of SyncfusionPdfService.FinalizeResult (see Tech debt).

Design constraints

  • The NHibernate unit of work is scoped InstancePerRequest, so the transaction boundary for tracking writes is the HTTP request, not the business operation.
  • PdfTrackingService is the sole data-access component; every controller and service reaches the database only through it.
  • IncludeErrorDetailPolicy.Always produces framework exception detail before ApiResponseHandler runs, in every environment; the final client-visible envelope is unknown** (see Endpoints: Error response shape) — this is a design constraint with security implications regardless (see Tech debt).

Integration points

System Type Purpose
Oracle IMG schema Database Tracking record persistence for convert/merge requests
ActivePDF server (UNC working directory) Downstream service Alternate PDF processor, selected by pdfapi.pdfservicetype == "activepdf"
SMTP (mail.saif.com) Downstream service Failure incident email sent from PdfService.SendIncidentEmail

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

  • PdfAPI\Global.asax.cs
  • PdfAPI\App_Start\WebApiConfig.cs
  • PdfAPI\App_Start\IocConfig.cs
  • PdfAPI\App_Start\SwaggerConfig.cs
  • PdfAPI\App_Start\FilterConfig.cs
  • PdfAPI\Services\PdfServiceFactory.cs
  • PdfAPI\Services\PdfService.cs
  • PdfAPI\Services\PdfTrackingService.cs
  • PdfAPI\Services\Syncfusion\SyncfusionPdfService.cs
  • PdfAPI\Services\ActivePdf\ActivePdfService.cs