Tập tành Redirection based on users IP Address
30th Jul 2022![Tập tành Redirection based on users IP Address](/sites/default/files/2022-07/IP-drupal.jpg)
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 :)
- 61 views
Add new comment