Try our conversational search powered by Generative AI!

Sanjay Kumar
May 4, 2021
  3479
(3 votes)

Update GeoIP2 database automatically

The purpose of the blog to update the latest GeoIP2 database automatically. Because the GeoLite2 Country, City, and ASN databases are updated weekly, every Tuesday of the week. So we required to update the database up to date, which we can accomplish through the schedule job.

About GeoIP2:

MaxMind GeoIP2 offerings provide IP geolocation and proxy detection for a wide range of applications including content customization, advertising, digital rights management, compliance, fraud detection, and security.

The GeoIP2 Database MaxMind provides both binary and CSV databases for GeoIP2. Both formats provide additional data not available in our legacy databases including localized names for cities, subdivisions, and countries.

 

Requirement:

In one of my current projects, we required to read the zip code/postal code on basis of the client IP Address and populate into the address area. Similarly, you can retrieve the Country, City Name, Latitude & Longitude, Metro Code etc.

Solution:

  1. Create the schedule job and download GeoLiteCity.dat.gz.
  2. Uncompressed the file and copy the GeoLite2-City.mmdb database file on physical location which you have define in basePath.
  3. Read the client IP Address.
  4. Read the downloaded GeoLite2-City.mmdb database through DatabaseReader and search the IP address into city database and retrieve zip code/postal code.

 

Okay, so let's do some coding for achieving the above approaches:

Step1: Create the schedule job.

public class GeoIp2DataBaseUpdateJob : ScheduledJobBase
{
        private bool _stopSignaled;
        private const string GeoLiteCityFileDownloadUrl = "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City&suffix=tar.gz";

        public GeoIp2DataBaseUpdateJob()
        {
            this.IsStoppable = true;
        }

        /// <summary>
        /// Called when a user clicks on Stop for a manually started job, or when ASP.NET shuts down.
        /// </summary>
        public override void Stop()
        {
            base.Stop();
            _stopSignaled = true;
        }

        /// <summary>
        /// Called when a scheduled job executes
        /// </summary>
        /// <returns>A status message to be stored in the database log and visible from admin mode</returns>
        public override string Execute()
        {
                   // 1. Download file
            var result = DownloadFile()
                // 2. Unzip file
                .Bind(UnzipFile)
                // 3. Replace at physical location
                .Bind(ReplaceCurrentFile)
                .Match(
                    right => $"👍 GeoIP2 database updated successfully.",
                    left => $"👎 GeoIP2 database update failed.");

            if (_stopSignaled)
            {
                return "Stop of job was called";
            }

            return result;
        }

        private static Either<Exception, string> DownloadFile()
        {
            var licenseKey = ConfigurationManager.AppSettings["Geolocation.LicenseKey"];
            var uri = new Uri(GeoLiteCityFileDownloadUrl + $"&license_key={licenseKey}");

            return Prelude.Try(
                    () =>
                    {
                        using (var client = new WebClient())
                        {
                            var tempDir = Path.GetDirectoryName(Path.GetTempPath());
                            var localFile = Path.Combine(tempDir, "MaxMind/GeoLite2-City.tar.gz");
                            var maxMindFolderPath = Path.GetDirectoryName(localFile);
                            if (!Directory.Exists(maxMindFolderPath) && !string.IsNullOrWhiteSpace(maxMindFolderPath))
                            {
                                Directory.CreateDirectory(maxMindFolderPath);
                            }

                            client.DownloadFile(uri, localFile);
                            return localFile;
                        }
                    })
                .Succ(localFile => Prelude.Right<Exception, string>(localFile))
                .Fail(ex => Prelude.Left<Exception, string>(ex));
        }

        private static Either<Exception, string> ReplaceCurrentFile(string unzippedFileName)
        {
            return Prelude.Try(
                    () =>
                    {
                        var maxMindDbFilePath = Path.Combine(EPiServerFrameworkSection.Instance.AppData.BasePath, "MaxMind/GeoLite2-City.mmdb");
                        File.Copy(unzippedFileName, maxMindDbFilePath, true);

                        //Delete extracted folder
                        var dir = Path.GetDirectoryName(unzippedFileName);
                        if (Directory.Exists(dir))
                        {
                            Directory.Delete(dir, true);
                        }
                        return unzippedFileName;

                    })
                .Succ(fileName => Prelude.Right<Exception, string>(fileName))
                .Fail(ex => Prelude.Left<Exception, string>(ex));
        }

        private static Either<Exception, string> UnzipFile(string downloadedFileName)
        {
            return Prelude.Try(
                    () =>
                    {
                        var dir = Path.GetDirectoryName(downloadedFileName);
                        FileInfo tarFileInfo = new FileInfo(downloadedFileName);

                        DirectoryInfo targetDirectory = new DirectoryInfo(dir ?? string.Empty);
                        if (!targetDirectory.Exists)
                        {
                            targetDirectory.Create();
                        }
                        using (Stream sourceStream = new GZipInputStream(tarFileInfo.OpenRead()))
                        {
                            using (TarArchive tarArchive = TarArchive.CreateInputTarArchive(sourceStream, TarBuffer.DefaultBlockFactor))
                            {
                                tarArchive.ExtractContents(targetDirectory.FullName);
                            }
                        }

                        var filePath = Directory.GetFiles(dir ?? string.Empty, "*.mmdb", SearchOption.AllDirectories)?.LastOrDefault();

                        //Delete .tar.gz file
                        if (File.Exists(downloadedFileName))
                        {
                            File.Delete(downloadedFileName);
                        }
                        return filePath;
                    })
                .Succ(fileName => Prelude.Right<Exception, string>(fileName))
                .Fail(ex => Prelude.Left<Exception, string>(ex));
        }

    }

Step 2: Update the web.config settings

  1. Set the database file path for geolocation provider if you are using for personalization.
  2. Add the basePath for updating the file on a given physical location.
  3. Set MaxMind license key
<episerver.framework>
  ...
  <geolocation defaultprovider="maxmind">
    <providers>
      <add databasefilename="C:\Program Files (x86)\MaxMind\GeoLite2-City.mmdb" name="maxmind" type="EPiServer.Personalization.Providers.MaxMind.GeolocationProvider, EPiServer.ApplicationModules">
    </add></providers>
  </geolocation>
  <appData basePath="C:\Program Files (x86)\MaxMind" />
</episerver.framework>

<appSettings>
 ...
 <add key="Geolocation.LicenseKey" value="{YourLicenseKey}" 
</appSettings>

Step 3: Run the job and make sure the file is updating, if the code cause the error then update accordingly.

Step4: Create the GeoLocationUtility class and read the database file for client IP Address.

 public class GeoLocationUtility
 {
        private static string _maxMindDatabaseFileName = "GeoLite2-City.mmdb";

        public static GeoLocationViewModel GetGeoLocation(IPAddress address, NameValueCollection config)
        {
            string text = config["databaseFileName"];

            if (!string.IsNullOrEmpty(text))
            {
                _maxMindDatabaseFileName = VirtualPathUtilityEx.RebasePhysicalPath(text);
                config.Remove("databaseFileName");
            }

            if (string.IsNullOrWhiteSpace(_maxMindDatabaseFileName)
                || !File.Exists(_maxMindDatabaseFileName)
                || address.AddressFamily != AddressFamily.InterNetwork && address.AddressFamily != AddressFamily.InterNetworkV6)
            {
                return null;
            }

            var reader = new DatabaseReader(_maxMindDatabaseFileName);
            try
            {
                var dbResult = reader.City(address);
                var result = GeoLocationViewModel.Make(dbResult);
                return result;
            }
            catch
            { 
                //ignore exception
            }
            finally
            {
                reader.Dispose();
            }

            return null;
        }

Step 5: Finally, call the utility method where you want to get the zip code/postal code value such as:

  var maxMindDbFilePath = Path.Combine(EPiServerFrameworkSection.Instance.AppData.BasePath, "MaxMind/GeoLite2-City.mmdb");
   var config = new NameValueCollection
   {
    {"databaseFileName", maxMindDbFilePath}
   };

   var  postalCode= GeoLocationUtility.GetGeoLocation(ipAddress, config)?.PostalCode;

I hope you found this informative and helpful, Please leave your valuable comments in the comment box.

Thanks for your visit!

May 04, 2021

Comments

Praful Jangid
Praful Jangid May 4, 2021 06:58 AM

I will spend some of my time to implement and try your approach. Thanks for sharing.

Sunil
Sunil May 4, 2021 07:17 AM

Nice, Thanks for sharing.

Sujit Senapati
Sujit Senapati Sep 23, 2021 07:09 PM

Great article @Sanjay!

Please login to comment.
Latest blogs
Optimizely and the never-ending story of the missing globe!

I've worked with Optimizely CMS for 14 years, and there are two things I'm obsessed with: Link validation and the globe that keeps disappearing on...

Tomas Hensrud Gulla | Apr 18, 2024 | Syndicated blog

Visitor Groups Usage Report For Optimizely CMS 12

This add-on offers detailed information on how visitor groups are used and how effective they are within Optimizely CMS. Editors can monitor and...

Adnan Zameer | Apr 18, 2024 | Syndicated blog

Azure AI Language – Abstractive Summarisation in Optimizely CMS

In this article, I show how the abstraction summarisation feature provided by the Azure AI Language platform, can be used within Optimizely CMS to...

Anil Patel | Apr 18, 2024 | Syndicated blog

Fix your Search & Navigation (Find) indexing job, please

Once upon a time, a colleague asked me to look into a customer database with weird spikes in database log usage. (You might start to wonder why I a...

Quan Mai | Apr 17, 2024 | Syndicated blog

The A/A Test: What You Need to Know

Sure, we all know what an A/B test can do. But what is an A/A test? How is it different? With an A/B test, we know that we can take a webpage (our...

Lindsey Rogers | Apr 15, 2024

.Net Core Timezone ID's Windows vs Linux

Hey all, First post here and I would like to talk about Timezone ID's and How Windows and Linux systems use different IDs. We currently run a .NET...

sheider | Apr 15, 2024