.NET
Resolve the caller automatically
Middleware determines the caller's IP, resolves it once per request, and hands you the result anywhere via HttpContext.GetGeo() - with no IP handling in your code.
Register the middleware#
Add it after UseRouting. Endpoint metadata such as [SkipGeo] is only visible to the middleware once routing has run.
var app = builder.Build();
app.UseRouting();
app.UseIpGeoTrace();
app.MapControllers();Read the result#
Call HttpContext.GetGeo()from any endpoint. It is always safe to call - it never throws for a missing lookup. Show prices in the visitor's currency:
[HttpGet("checkout")]
public IActionResult Checkout()
{
var geo = HttpContext.GetGeo();
var currency = geo.IsResolved ? geo.Value.Country?.Currency ?? "USD" : "USD";
return Ok(_catalog.PricedIn(currency));
}The same works from a minimal API - route the visitor to the nearest warehouse:
app.MapGet("/shipping-estimate", (HttpContext http) =>
{
return http.GetGeo().TryGetValue(out var visitor)
? Results.Ok(Warehouses.NearestTo(visitor.Location?.Latitude, visitor.Location?.Longitude))
: Results.Ok(Warehouses.Default);
});Lookup status#
GeoLookup.Status tells you exactly what happened:
Resolved-Valuecarries the data;FromCachesays whether an API call was made.Skipped- the address was missing, invalid, private, link-local or loopback, so no API call was made.Failed-Errorcarries the reason, such asrate_limitedorquota_exceeded.NotAttempted- the middleware did not run, the endpoint opted out, orShouldResolvereturned false.
Skipping endpoints#
Health checks, metrics and internal endpoints should not spend lookups. Opt them out with the attribute:
[HttpGet("health")]
[SkipGeo]
public IActionResult Health() => Ok();Or centrally with a predicate:
app.UseIpGeoTrace(options =>
{
options.ShouldResolve = context => !context.Request.Path.StartsWithSegments("/internal");
});Choosing the caller's IP#
By default the middleware uses Connection.RemoteIpAddress. Behind a proxy or load balancer, configure the standard forwarded-headers middleware before UseIpGeoTrace rather than parsing headers yourself.
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor
});Private, link-local and loopback addresses (including IPv4-mapped IPv6 forms) are detected locally and skipped without an API call or quota usage.
Configuration#
All settings live on the AddIpGeoTrace options callback, and each one is optional.
builder.Services.AddIpGeoTrace(apiKey, options =>
{
options.CacheEnabled = true;
options.CacheTtl = TimeSpan.FromHours(6);
options.Timeout = TimeSpan.FromSeconds(3);
options.MaxRetries = 0;
});Caching
Off by default. Turn it on and repeat lookups of the same IP are served from memory - no API call, no latency, and nothing counted against your quota. Only successful lookups are cached, never errors. Supply your own IGeoCache (Redis, distributed cache, anything) and caching turns on automatically.
Timeout & retries
The per-request timeout defaults to 10 seconds. Rate limits (429) and outages (503) are retried automatically, honoring the API's Retry-After header - the default is 2 retries. Set MaxRetries to 0 if you chain your own resilience handler (for example Polly) so requests are not retried twice.
You're set
That is the whole surface. Start with the defaults, then turn on caching once you see repeat traffic. Questions? support@ipgeotrace.com.