官方文档:https://varnish-cache.org/docs/3.0/tutorial/devicedetection.html
方法转自:https://www.scommerce-mage.com/blog/magento-2-varnish-device-detection.html
今天我们将讨论当使用 Varnish 开启全页缓存时,如何根据不同的设备在同一个 URL 上提供不同的内容。我们对我们的一个客户网站有类似的要求,他们希望根据设备在其主页、类别和产品页面上提供不同的内容。他们面临的问题是 varnish 不区分设备,因此它将提供与第一个访问者缓存的相同内容。
例如,如果首先在移动设备上查看该网站,那么它将向其他用户提供相同的内容,而不管他们的设备如何。如果您的移动设备、平板电脑和台式机具有不同的内容和不同尺寸的图像,那么您的网站将给用户带来糟糕的体验,尤其是当移动缓存页面提供给桌面访问者时,反之亦然。
Magento 2 开箱即用,使用上下文变量根据以下参数在同一 URL 上提供不同的内容 - :
客户群
选择的语言
选定的商店
所选货币
客户是否登录
可以使用以下链接在此处找到有关上下文变量以及如何使用它们的更多信息。
最初我们认为上下文变量应该是我们的答案,因为我们应该能够使用 PHP 中的以下函数检测设备,它将根据设备提供不同的内容
/** * @return bool */ private function isMobile(){ //return true; $regex_match = "/(nokia|iphone|ipad|motorola|^mot\-|softbank|foma|docomo|kddi|up\.browser|up\.link|" . "htc|dopod|blazer|netfront|helio|hosin|huawei|novarra|CoolPad|webos|techfaith|palmsource|" . "blackberry|alcatel|amoi|ktouch|nexian|samsung|^sam\-|s[cg]h|^lge|ericsson|philips|sagem|wellcom|bunjalloo|maui|" . "symbian|smartphone|mmp|midp|wap|phone|windows ce|iemobile|^spice|^bird|^zte\-|longcos|pantech|gionee|^sie\-|portalmmm|" . "jig\s browser|hiptop|^ucweb|^benq|haier|^lct|opera\s*mobi|opera\*mini|320x320|240x320|176x220" . ")/i"; //DISPLAY DESKTOP THEME ON HAUWEI TAB if(preg_match("/(huaweimediapad)/i", strtolower($_SERVER['HTTP_USER_AGENT']))){ return false; } if (preg_match($regex_match, strtolower($_SERVER['HTTP_USER_AGENT']))) { return true; } if ((strpos(strtolower($_SERVER['HTTP_ACCEPT']),'application/vnd.wap.xhtml+xml') > 0) or ((isset($_SERVER['HTTP_X_WAP_PROFILE']) or isset($_SERVER['HTTP_PROFILE'])))) { return true; } if(stripos($_SERVER['HTTP_USER_AGENT'],"Android") && stripos($_SERVER['HTTP_USER_AGENT'],"mobile")){ return true; } if(stripos($_SERVER['HTTP_USER_AGENT'],"Android")){ return false; } $mobile_ua = strtolower(substr($_SERVER['HTTP_USER_AGENT'], 0, 4)); $mobile_agents = array( 'w3c ','acs-','alav','alca','amoi','audi','avan','benq','bird','blac', 'blaz','brew','cell','cldc','cmd-','dang','doco','eric','hipt','inno', 'ipaq','java','jigs','kddi','keji','leno','lg-c','lg-d','lg-g','lge-', 'maui','maxo','midp','mits','mmef','mobi','mot-','moto','mwbp','nec-', 'newt','noki','oper','palm','pana','pant','phil','play','port','prox', 'qwap','sage','sams','sany','sch-','sec-','send','seri','sgh-','shar', 'sie-','siem','smal','smar','sony','sph-','symb','t-mo','teli','tim-', 'tosh','tsm-','upg1','upsi','vk-v','voda','wap-','wapa','wapi','wapp', 'wapr','webc','winw','winw','xda ','xda-'); if (in_array($mobile_ua,$mobile_agents)) { return true; } if (isset($_SERVER['ALL_HTTP']) && strpos(strtolower($_SERVER['ALL_HTTP']),'OperaMini') > 0) { return true; } return false; }
我们使用 Magento 2 文档中指定的以下代码创建了我们的插件,但很快我们意识到它不起作用。因为用于为同一 URL 生成不同内容的X-Magento-Vary cookie 不存在,因为该 cookie 仅在 PUT / POST 请求而不是 GET 请求上创建。
namespace Scommerce\ContextVariable\Plugin; use Magento\Framework\App\Http\Context as HttpContext; /** * Plugin on \Magento\Framework\App\Http\Context */ class ContextVariablePlugin { const USER_AGENT_CONTEXT_VARIABLE = 'USER_AGENT_CONTEXT_VARIABLE'; const DEFAULT_VALUE = 'desktop'; /** * @param HttpContext $subject * @return array */ public function beforeGetVaryString(HttpContext $subject) { //Identifying if user is on mobile browser or not if($this->isMobile()) { $browserStatus = 'mobile'; } else{ $browserStatus = self::DEFAULT_VALUE; } $subject->setValue( self::USER_AGENT_CONTEXT_VARIABLE, $browserStatus, self::DEFAULT_VALUE ); return []; } }
清漆和移动设备检测的终极优化解决方案
最终对我们有用的解决方案包括以下步骤:
创建devicedetect.vcl 以确定用于给定页面的设备
使用devicedetect.vcl检测客户端并调用它
Device Detect VCL 文件将帮助您设置 req.http.X-UA-Device以确定它是移动设备还是平板电脑或桌面设备
检查req.http.X-UA-Device 并更改 VARY 标头以生成不同的内容
以下 VCL 文件将为您提供正确的标头信息,这些信息可以在varnish.vcl文件中进一步设置,也可以在您的代码中用于确定设备类型 -:
sub devicedetect { unset req.http.X-UA-Device; set req.http.X-UA-Device = "pc"; # Handle that a cookie may override the detection alltogether. if (req.http.Cookie ~ "(?i)X-UA-Device-force") { /* ;?? means zero or one ;, non-greedy to match the first. */ set req.http.X-UA-Device = regsub(req.http.Cookie, "(?i).*X-UA-Device-force=([^;]+);??.*", "\1"); /* Clean up our mess in the cookie header */ set req.http.Cookie = regsuball(req.http.Cookie, "(^|; ) *X-UA-Device-force=[^;]+;? *", "\1"); /* If the cookie header is now empty, or just whitespace, unset it. */ if (req.http.Cookie ~ "^ *$") { unset req.http.Cookie; } } else { if (req.http.User-Agent ~ "\(compatible; Googlebot-Mobile/2.1; \+http://www.google.com/bot.html\)" || (req.http.User-Agent ~ "(Android|iPhone)" && req.http.User-Agent ~ "\(compatible.?; Googlebot/2.1.?; \+http://www.google.com/bot.html") || (req.http.User-Agent ~ "(iPhone|Windows Phone)" && req.http.User-Agent ~ "\(compatible; bingbot/2.0; \+http://www.bing.com/bingbot.htm")) { set req.http.X-UA-Device = "mobile-bot"; } elsif (req.http.User-Agent ~ "(?i)(ads|google|bing|msn|yandex|baidu|ro|career|seznam|)bot" || req.http.User-Agent ~ "(?i)(baidu|jike|symantec)spider" || req.http.User-Agent ~ "(?i)pingdom" || req.http.User-Agent ~ "(?i)facebookexternalhit" || req.http.User-Agent ~ "(?i)scanner" || req.http.User-Agent ~ "(?i)slurp" || req.http.User-Agent ~ "(?i)(web)crawler") { set req.http.X-UA-Device = "bot"; } elsif (req.http.User-Agent ~ "(?i)ipad") { set req.http.X-UA-Device = "tablet-ipad"; } elsif (req.http.User-Agent ~ "(?i)ip(hone|od)") { set req.http.X-UA-Device = "mobile-iphone"; } /* how do we differ between an android phone and an android tablet? http://stackoverflow.com/questions/5341637/how-do-detect-android-tablets-in-general-useragent */ elsif (req.http.User-Agent ~ "(?i)android.*(mobile|mini)") { set req.http.X-UA-Device = "mobile-android"; } // android 3/honeycomb was just about tablet-only, and any phones will probably handle a bigger page layout. elsif (req.http.User-Agent ~ "(?i)android 3") { set req.http.X-UA-Device = "tablet-android"; } /* Opera Mobile */ elsif (req.http.User-Agent ~ "Opera Mobi") { set req.http.X-UA-Device = "mobile-smartphone"; } // May very well give false positives towards android tablets. Suggestions welcome. elsif (req.http.User-Agent ~ "(?i)android") { set req.http.X-UA-Device = "tablet-android"; } elsif (req.http.User-Agent ~ "PlayBook; U; RIM Tablet") { set req.http.X-UA-Device = "tablet-rim"; } elsif (req.http.User-Agent ~ "hp-tablet.*TouchPad") { set req.http.X-UA-Device = "tablet-hp"; } elsif (req.http.User-Agent ~ "Kindle/3") { set req.http.X-UA-Device = "tablet-kindle"; } elsif (req.http.User-Agent ~ "Touch.+Tablet PC" || req.http.User-Agent ~ "Windows NT [0-9.]+; ARM;" ) { set req.http.X-UA-Device = "tablet-microsoft"; } elsif (req.http.User-Agent ~ "Mobile.+Firefox") { set req.http.X-UA-Device = "mobile-firefoxos"; } elsif (req.http.User-Agent ~ "^HTC" || req.http.User-Agent ~ "Fennec" || req.http.User-Agent ~ "IEMobile" || req.http.User-Agent ~ "BlackBerry" || req.http.User-Agent ~ "BB10.*Mobile" || req.http.User-Agent ~ "GT-.*Build/GINGERBREAD" || req.http.User-Agent ~ "SymbianOS.*AppleWebKit") { set req.http.X-UA-Device = "mobile-smartphone"; } elsif (req.http.User-Agent ~ "(?i)symbian" || req.http.User-Agent ~ "(?i)^sonyericsson" || req.http.User-Agent ~ "(?i)^nokia" || req.http.User-Agent ~ "(?i)^samsung" || req.http.User-Agent ~ "(?i)^lg" || req.http.User-Agent ~ "(?i)bada" || req.http.User-Agent ~ "(?i)blazer" || req.http.User-Agent ~ "(?i)cellphone" || req.http.User-Agent ~ "(?i)iemobile" || req.http.User-Agent ~ "(?i)midp-2.0" || req.http.User-Agent ~ "(?i)u990" || req.http.User-Agent ~ "(?i)netfront" || req.http.User-Agent ~ "(?i)opera mini" || req.http.User-Agent ~ "(?i)palm" || req.http.User-Agent ~ "(?i)nintendo wii" || req.http.User-Agent ~ "(?i)playstation portable" || req.http.User-Agent ~ "(?i)portalmmm" || req.http.User-Agent ~ "(?i)proxinet" || req.http.User-Agent ~ "(?i)windows\ ?ce" || req.http.User-Agent ~ "(?i)winwap" || req.http.User-Agent ~ "(?i)eudoraweb" || req.http.User-Agent ~ "(?i)htc" || req.http.User-Agent ~ "(?i)240x320" || req.http.User-Agent ~ "(?i)avantgo") { set req.http.X-UA-Device = "mobile-generic"; } } }
清漆 VCL 文件
您需要在 Varnish VCL 文件中进行以下更改,以根据检测到的设备或在 X-UA-Device 变量中设置的值获取不同的内容 -
include "devicedetect.vcl"; sub vcl_recv { call devicedetect; } # req.http.X-UA-Device is copied by Varnish into bereq.http.X-UA-Device # so, this is a bit conterintuitive. The backend creates content based on the normalized User-Agent, # but we use Vary on X-UA-Device so Varnish will use the same cached object for all U-As that map to # the same X-UA-Device. # If the backend does not mention in Vary that it has crafted special # content based on the User-Agent (==X-UA-Device), add it. # If your backend does set Vary: User-Agent, you may have to remove that here. sub vcl_backend_response { if (bereq.http.X-UA-Device) { if (!beresp.http.Vary) { # no Vary at all set beresp.http.Vary = "X-UA-Device"; } elsif (beresp.http.Vary !~ "X-UA-Device") { # add to existing Vary set beresp.http.Vary = beresp.http.Vary + ", X-UA-Device"; } } # comment this out if you don't want the client to know your classification set beresp.http.X-UA-Device = bereq.http.X-UA-Device; } # to keep any caches in the wild from serving wrong content to client #2 behind them, we need to # transform the Vary on the way out. sub vcl_deliver { if ((req.http.X-UA-Device) && (resp.http.Vary)) { set resp.http.Vary = regsub(resp.http.Vary, "X-UA-Device", "User-Agent"); } }
原创文章,转载请标明出处!