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("oracle")"]
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. PdfTrackingServiceis the sole data-access component; every controller and service reaches the database only through it.IncludeErrorDetailPolicy.Alwaysproduces framework exception detail beforeApiResponseHandlerruns, 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 |
Related documentation¶
- API index
- Overview
- Endpoints
- Authentication and authorization
- Dependencies
- Configuration
- Operations and processes
- Business rules
- Data model
- Operational runbook
- Tech debt
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