Tập tành Redirection based on users IP Address

30th Jul 2022
Tập tành Redirection based on users IP Address
Table of contents

The first thing came to my mind when I had to do this task is to use Event Listener to redirect user to domain, but I was wrong as that didn't worked for me as the response was getting cached and the users were redirected to the same domain as the the first user got redirected to

we had 5 domain on our website and we need to redirect a user to US domain if user is browsing from US
so user hit exapmle.com and got redirected to example.com/us

To overcome the response caching, we needed to run our custom redirection code before the cache module runs and bring page from cache to do so we used StackMiddleware 

Step 1 : Create Services.yml file

create custom_module.services.yml and paste below code

services:

  http_middleware.custom_module:

    class: Drupal\custom_module\StackMiddleware\CustomModule

    tags:

      - { name: http_middleware, priority: 201, responder: true }

Step 2: Create File in below structure

Custom_module/src/StackMiddleware/CustomModule.php

I have used multiple API's to get user location from IP Address and some of them are free and some are paid, all API's used has a limit to the requests that we can made

Using Multiple API's has it's own downside of getting results late and impact page load by 1-2 seconds depending on the number of API's used and their response time

so I recommend to use one paid API for better Performance and accurate results

<?php

namespace Drupal\custom_module\StackMiddleware;

use Symfony\Component\HttpFoundation\Request;

use Symfony\Component\HttpFoundation\Response;

use Symfony\Component\HttpKernel\HttpKernelInterface;

use Symfony\Component\HttpFoundation\RedirectResponse;


/**

 * Performs a custom task.

 */

class CustomModule implements HttpKernelInterface

{

  /**

   * The wrapped HTTP kernel.

   *

   * @var \Symfony\Component\HttpKernel\HttpKernelInterface

   */

  protected $httpKernel;

  /**

   * Creates a HTTP middleware handler.

   *

   * @param \Symfony\Component\HttpKernel\HttpKernelInterface $kernel

   *   The HTTP kernel.

   */

  public function __construct(HttpKernelInterface $kernel)

  {

    $this->httpKernel = $kernel;

  }

  /**

   * {@inheritdoc}

   */

  public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = TRUE)

  {

    //Get User IP from Server request

    $host_protocol = $request->server->get('HTTP_X_FORWARDED_PROTO', null);

    $baseUrl = $host_protocol . '://' . $request->server->get('HTTP_HOST', null);

 
    $requestUrl = $request->server->get('REQUEST_URI', null);

 
    if ($requestUrl == '/node' || $requestUrl == "/") {

      $requestUrl = '';

    }

    $pageUrl = $baseUrl . $requestUrl;

    $urlarr = [];

    $domain_array = ['in', 'sg', 'uk', 'uae', 'us'];

    $country_array = ['IN', 'SG', 'GB', 'AE', 'US'];

    $ip = $request->server->get('HTTP_X_FORWARDED_FOR', null);


    // Check if user has both IPV6 and IPV4 and use IPV4 to get ip location

    if(strpos($ip, ',') !== FALSE){

      $ip = str_replace(' ', '', $ip);

      $ip_arr = [];

      $ip_arr = explode(',',$ip);

      $ip = $ip_arr[0];

    }

   

    // List of apis to use for location

    $api = 'https://ipapi.co/' . $ip . '/json';

    $api1 = 'https://freegeoip.app/json/' . $ip;

    $api2 = 'https://ipinfo.io/' . $ip . '/json';


    $api_arr = [ $api , $api1 , $api2 ];


    $countryCode = '';

    $redirectUrl = '';

    $urlarr = explode("/", $pageUrl);

    if (!isset($urlarr[3])) {

      $urlarr[3] = '';

    }

    // URL Check for specific strings to prevent redirection and minimize API hit

    if (

      strpos($pageUrl, '?redirect=false') === false &&

      strpos($pageUrl, '/blog') === false &&

      strpos($pageUrl, '/server-status?auto') === false &&

      strpos($pageUrl, '.html') === false &&

      strpos($pageUrl, '.pdf') === false &&

      strpos($pageUrl, '.aspx') === false &&

      strpos($pageUrl, '.global') === false &&

      strpos($pageUrl, '.json') === false &&

      strpos($pageUrl, '.php') === false

    ) {

      if (!in_array($urlarr[3], $domain_array)) {

        // Fetching user current location

        foreach($api_arr as $apiv){

          $countryCode = $this->get_country($apiv);

          if ($countryCode != null && $countryCode != "" && $countryCode != "error") {

            break;

          }

        }

        if ($countryCode != null && $countryCode != "" && $countryCode != "error") {

          //Redirecting site different country code

          if (in_array($countryCode, $country_array)) {

            $ival = array_search($countryCode, $country_array);

            $redirectUrl = $baseUrl . '/' . $domain_array[$ival] . $requestUrl;

 
            return $response =  new RedirectResponse($redirectUrl);

          }

        } else {

          //Logging IP of user if all 3 api failed to get Country code from User IP

          \Drupal::logger('custom_module')->notice('Country Unknown for IP = ' . print_r($ip, 1));

        }

      }

    }


    $response = $this->httpKernel->handle($request, $type, $catch);

    $response = $this->changeResponse($response);

    return $response;

  }

  /**

   * Remove configured HTTP headers.

   *

   * @param \Symfony\Component\HttpFoundation\Response $response

   *   The response object.

   *

   * @return \Symfony\Component\HttpFoundation\Response

   *   The given response object

   *   without the HTTP response headers that should be removed.

   */

  protected function changeResponse(Response $response): Response

  {

    $response->headers->remove('X-Drupal-Cache-Tags');

    return $response;

  }


  /**

   * {@inheritdoc}

   * Function to get Country Code from API

   */

  protected function get_country($url = null)

  {

    if ($url != null) {


      $curl = curl_init();

      curl_setopt_array($curl, [

        CURLOPT_RETURNTRANSFER => 1,

        CURLOPT_URL => $url,

        CURLOPT_USERAGENT => 'location api in crul',

      ]);

      $apidata = curl_exec($curl);

      $httpheader = curl_getinfo($curl, CURLINFO_HTTP_CODE);

      curl_close($curl);


      if ($httpheader == 200) {

        if (!empty($apidata)) {

          $adata = json_decode($apidata, true);

          if (array_key_exists('error', $adata)) {

            return 'error';

          } elseif (array_key_exists('country_code', $adata)) {

            $countryCode = $adata['country_code'];

            return $countryCode;

          } elseif (array_key_exists('country', $adata)) {

            $countryCode = $adata['country'];

            return $countryCode;

          } else {

            return 'error';

          }

        } else {

          return 'error';

        }

      } else {

        return 'error';

      }

    } else {

      return 'error';

    }

  }

}

Note: I know some things can be improved and nothing is perfect. This worked for me and I hope It work for everyone :) 

Bạn thấy bài viết này như thế nào?
0 reactions

Add new comment

Image CAPTCHA
Enter the characters shown in the image.

Related Articles

Under Form Validation Define Counter - Character, Countet Maimum - 1000, Counter Maximum Message - %\%d Characters remaining

Short snippet that shows how to get url parameters in drupal 9. to get query parameter form the url, you can us the following.

Recently I found myself needing to manage cookie data in Drupal 9 for the first time. As oft, my first step in the process was to search through Drupal core and the Drupal API for examples of “the right way” to handle cookies. After I didn’t find any reasonable existing solution within Drupal, it was time to solve this on my own.

Regardless of your position on either side of the web, there is always risk of being a victim of a cyber attack. This has put businesses in a tizz to make personal data of the visitors secure.

Migration from one version of Drupal to the other version of Drupal has always been important as it helps to move with time and stay updated with the latest technology.