Skip to content

.NET Server Proxy

For C# idioms see ../integrations/csharp.md.

ASP.NET Core minimal API

csharp
using System.Security.Cryptography;
using System.Text;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpClient("mfd", c =>
    c.BaseAddress = new Uri("https://tiles.mapsfordevs.com"));
builder.Services.AddOutputCache(opts =>
{
    opts.AddPolicy("tiles", b =>
        b.Cache().Expire(TimeSpan.FromDays(1)));
});

var app = builder.Build();
app.UseOutputCache();

app.MapGet("/tiles/{z:int}/{x:int}/{y:int}.pbf",
    async (int z, int x, int y, IHttpClientFactory factory, IConfiguration cfg, CancellationToken ct) =>
{
    var key    = cfg["MFD_SRV_KEY"]!;
    var secret = cfg["MFD_SRV_SECRET"]!;
    var path   = $"/tiles/{z}/{x}/{y}.pbf";
    var ts     = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString();

    using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
    var sig = Convert.ToHexString(
        hmac.ComputeHash(Encoding.UTF8.GetBytes($"GET\n{path}\n{ts}"))
    ).ToLowerInvariant();

    var http = factory.CreateClient("mfd");
    var req  = new HttpRequestMessage(HttpMethod.Get, path);
    req.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", key);
    req.Headers.Add("X-MFD-Timestamp", ts);
    req.Headers.Add("X-MFD-Signature", sig);

    var upstream = await http.SendAsync(req, ct);
    var bytes    = await upstream.Content.ReadAsByteArrayAsync(ct);

    return Results.File(bytes, "application/x-protobuf");
}).CacheOutput("tiles");

app.Run();

Configuration

json
// appsettings.json (or use User Secrets / env vars)
{
  "MFD_SRV_KEY":    "mfd_srv_xxx",
  "MFD_SRV_SECRET": "hex_secret_here"
}

For production use environment variables — never commit secrets to appsettings.json.

Controller-style

csharp
[ApiController]
[Route("tiles")]
public class TilesController : ControllerBase
{
    private readonly IHttpClientFactory _factory;
    private readonly IConfiguration     _cfg;

    public TilesController(IHttpClientFactory factory, IConfiguration cfg)
    {
        _factory = factory;
        _cfg     = cfg;
    }

    [HttpGet("{z:int}/{x:int}/{y:int}.pbf")]
    [ResponseCache(Duration = 86400, Location = ResponseCacheLocation.Any)]
    public async Task<IActionResult> Get(int z, int x, int y, CancellationToken ct)
    {
        // … same signing logic
    }
}

In-memory cache

csharp
builder.Services.AddMemoryCache();

app.MapGet("/tiles/{z:int}/{x:int}/{y:int}.pbf",
    async (int z, int x, int y, IMemoryCache cache, /* deps */, CancellationToken ct) =>
{
    var cacheKey = $"tile:{z}:{x}:{y}";
    if (!cache.TryGetValue(cacheKey, out byte[] bytes))
    {
        // fetch upstream into bytes
        cache.Set(cacheKey, bytes, TimeSpan.FromDays(1));
    }
    return Results.File(bytes, "application/x-protobuf");
});

Production checklist

  • Run behind Kestrel + nginx / IIS reverse proxy for TLS.
  • IHttpClientFactory reuses sockets; never new HttpClient() per request.
  • Bind mfd_srv_* source IP to your egress NAT in our dashboard.
  • Set up Application Insights for upstream latency monitoring.