PLATFORM GUIDE · UPDATED 11 AUG 2026

ASP.NET Core: APIs that stay up

Nine years of ASP.NET Core in production — the patterns that keep charger-facing APIs answering in milliseconds while OEM integrations, roaming callbacks, and IoT telemetry hammer them around the clock.

Where it sits in my stack

Every service I architect — OCPI endpoints for roaming partners, Beckn BAP/BPP callbacks, vending telemetry ingestion, ERP sync jobs — is an ASP.NET Core application. It is the layer where protocol compliance, authentication, and business logic meet, so its design decides whether the platform is debuggable at 2 a.m. or not.

The patterns that matter

  • Middleware for cross-cutting concerns: request logging, correlation IDs, OCPI token validation, and rate limiting live in the pipeline — never scattered across controllers.
  • Typed HttpClients with Polly: every outbound call to a CPO or eMSP gets retry, circuit-breaker, and timeout policies; roaming partners fail more often than your own code.
  • Options pattern + validation on startup: a service that boots with bad config should crash loudly, not limp.
  • Background services (IHostedService): session-expiry sweeps, CDR dispatch, and heartbeat monitors run inside the same deployable unit.

Performance, practically

  • Async endpoints end to end — thread starvation is the silent killer under charger load spikes.
  • Response caching only where protocol allows; OCPI location data caches well, session state never.
  • Pagination and streaming for large result sets — a 50,000-row CDR export must never materialize in memory.
  • Measure with Application Insights before tuning; the slow query is rarely where you think.

Versioning APIs partners depend on

When 30+ CPO and eMSP networks integrate against your endpoints, breaking changes are outages you cause for other companies. I version at the URL level (mirroring OCPI’s own version discovery), keep old versions alive through negotiated sunset windows, and treat every response field as a contract — additive changes only.

Worked example: a partner-grade outbound client

Every call to a CPO or eMSP goes through a typed HttpClient with retry, circuit-breaker, and timeout policies — partners fail more often than your own code:

builder.Services.AddHttpClient<IOcpiClient, OcpiClient>(c =>
    {
        c.BaseAddress = new Uri(cfg["Partner:BaseUrl"]);
        c.DefaultRequestHeaders.Authorization =
            new AuthenticationHeaderValue("Token", cfg["Partner:Token"]);
    })
    .AddPolicyHandler(HttpPolicyExtensions.HandleTransientHttpError()
        .WaitAndRetryAsync(3, n => TimeSpan.FromSeconds(Math.Pow(2, n))))
    .AddPolicyHandler(HttpPolicyExtensions.HandleTransientHttpError()
        .CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)))
    .AddPolicyHandler(Policy.TimeoutAsync<HttpResponseMessage>(10));

Three lines of policy configuration replace a class of 2 a.m. incidents: a flapping partner trips the breaker instead of exhausting your thread pool.

FAQ

Minimal APIs or controllers?

Controllers for protocol surfaces with many endpoints and shared filters (OCPI modules), minimal APIs for small internal services. Consistency within a platform matters more than the choice itself.

How do you secure partner-facing APIs?

Token-based auth per OCPI credentials exchange, TLS everywhere, per-partner rate limits, and full request auditing — partner traffic is untrusted input even when contracted.

What does 99% uptime actually require?

Health checks wired to orchestration, graceful shutdown handling in-flight charging sessions, zero-downtime deploys, and alerting on error-rate deltas rather than absolute thresholds.