.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.
IHttpClientFactoryreuses sockets; nevernew HttpClient()per request.- Bind
mfd_srv_*source IP to your egress NAT in our dashboard. - Set up Application Insights for upstream latency monitoring.