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

Master list (in progress) of how to get parts of fields for use in Twig templates. I’m always having to look these up, so I thought I’d hash them out and write them down.

Litespeed Cache là plugin WordPress dùng để kết hợp với Web Server LiteSpeed nhằm tăng tốc website WordPress của bạn gấp nhiều lần

In this article, we are going to see how some tools & libraries will make people's lives easier during the development & code review process.

In this tutorial, you will learn how to improve the custom code, theme and module, and general code development by using the pre-commit hook on git

Trước khi tìm hiểu xem PHP Code Sniffer là gì thì các bạn cần phải nắm được coding convention là gì đã.