If you want to get the physical location of a user on your site there’s a couple of different methods that you can use, you can either use their IP address to get an approximate location or you can request a more accurate location from them using the HTML geolocation API. An example of both these mothods in action can be seen here.
Location From IP Address
When someone requests a page from your site the request object provides the IP address of the user and this IP address can be used to look up their approximate location using a free service such as FreeGeoIP (now ipstack). I’ve created a NuGet package called FreeGeoIPCore to make calling the API easier, though it looks like I’ll need to create a new one as it’s being depreciated soon.
The request object can be retrieved in your controller from IHttpContextAccessor which is available through Dependency Injection (DI).
WeatherController
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using FreeGeoIPCore;
using MetOfficeDataPoint;
using MetOfficeDataPoint.Models;
using MetOfficeDataPoint.Models.GeoCoordinate;
namespace bitScry.Controllers.Projects
{
[Route("Projects/[controller]")]
public class WeatherController : Controller
{
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IConfiguration _config;
public WeatherController(IConfiguration config, IHttpContextAccessor httpContextAccessor)
{
_config = config;
_httpContextAccessor = httpContextAccessor;
}
public IActionResult Index(double longitude, double latitude)
{
FreeGeoIPClient ipClient = new FreeGeoIPClient();
string ipAddress = AppCode.Projects.Weather.GetRequestIP(_httpContextAccessor);
FreeGeoIPCore.Models.Location location = ipClient.GetLocation(ipAddress).Result;
GeoCoordinate coordinate = new GeoCoordinate();
// If location is provided then use over IP address
if (longitude == 0 && latitude == 0)
{
coordinate.Longitude = location.Longitude;
coordinate.Latitude = location.Latitude;
}
else
{
coordinate.Longitude = longitude;
coordinate.Latitude = latitude;
}
return View();
}
}
}
AppCode.Projects.Weather
using bitScry.Models.Projects.Weather;
using FreeGeoIPCore.AppCode;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Primitives;
using System;
using System.Linq;
namespace bitScry.AppCode.Projects
{
public static class Weather
{
public static string GetRequestIP(IHttpContextAccessor httpContextAccessor, bool tryUseXForwardHeader = true)
{
string ip = null;
if (tryUseXForwardHeader)
ip = GetHeaderValueAs<string>(httpContextAccessor, "X-Forwarded-For").SplitCsv().FirstOrDefault();
// RemoteIpAddress is always null in DNX RC1 Update1 (bug).
if (ip.IsNullOrWhitespace() && httpContextAccessor.HttpContext?.Connection?.RemoteIpAddress != null)
ip = httpContextAccessor.HttpContext.Connection.RemoteIpAddress.ToString();
if (ip.IsNullOrWhitespace())
ip = GetHeaderValueAs<string>(httpContextAccessor, "REMOTE_ADDR");
// _httpContextAccessor.HttpContext?.Request?.Host this is the local host.
if (ip.IsNullOrWhitespace())
throw new Exception("Unable to determine caller's IP.");
// Remove port if on IP address
ip = ip.Substring(0, ip.IndexOf(":"));
return ip;
}
public static T GetHeaderValueAs<T>(IHttpContextAccessor httpContextAccessor, string headerName)
{
StringValues values;
if (httpContextAccessor.HttpContext?.Request?.Headers?.TryGetValue(headerName, out values) ?? false)
{
string rawValues = values.ToString(); // writes out as Csv when there are multiple.
if (!rawValues.IsNullOrWhitespace())
return (T)Convert.ChangeType(values.ToString(), typeof(T));
}
return default(T);
}
}
}
Location From HTML Geolocation API
You can request a users location on page load if you’d like but in the below example I have a button that when clicked by a user calls a javascript function to get the location and then redirects to the the above controller with the returned latitude and longitude as URL parameters.
Index
<div class="row">
<button id="locationButton" type="button" class="btn btn-default" onclick="getLocation()">Use Actual Location</button>
</div>
<script type="text/javascript">
function getLocation() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(useLocation);
}
};
function useLocation(position) {
window.location.replace('?longitude=' + position.coords.longitude + '&latitude=' + position.coords.latitude);
};
</script>
4 Comments
hudis · 28 September 2022 at 1:51 pm
is there anything more recent? I would like to use this with .net core 3.1
is this compatible with it?
Shinigami · 28 September 2022 at 6:45 pm
Hi, yep this should work on .Net Core 3.1.
I haven’t tried it but there’s nothing too complicated here that I can see would prevent it.
Fabienne · 29 September 2022 at 2:25 pm
Hello,
Thanks for your nuget. I’m trying to use it in a net 6 project.
It’s compatible with ?
For now, I have a 301 error (Moved permanently). Is it a compatibility problem or something else ?
Thanks for your answer !
Shinigami · 29 September 2022 at 6:01 pm
Ah, I think the problem may be that the API has been depreciated.
I created a package for IpStack which still works, there’s an example linked to in the readme.
https://github.com/mattosaurus/IpStack
I’ll update this post when I get a chance.