需求
日常开发中,一个网页往往需要同时适配PC端和移动端。简单的页面我们可以使用CSS来完成自适应,但比较复杂的页面可能需要给PC端和移动端分别开发不同的模板。那么Springboot如何能根据访问的终端特点(User-Agent)来自动使用对应的模板呢?
原理
主要原理是通过springboot的拦截器,拦截需要多模板的请求。通过判断请求的user-agent判断用户所使用的的终端是PC还是移动设备。最后通过重设ModelAndView
的ViewName
来实现调用不同的模板。
演示
创建多套模板
本案例使用thymeleaf作为模板引擎。首先我们在templates
目录下新建pc
和wap
两个目录,然后在这两个目录下都新建一个index.html
的网页模板。代码如下:
-
pc/index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>PC端</title> </head> <body> <h1>PC端网页</h1> </body> </html>
-
wap/index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>移动端</title> </head> <body> <h1>移动端网页</h1> </body> </html>
创建拦截器
然后新建一个名为BaseInterceptor
的拦截器,继承HandlerInterceptor
。代码如下:
package com.coderbbb.book1.config;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class BaseInterceptor implements HandlerInterceptor {
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
boolean mobile = mobile(request);
System.out.println(mobile);
if (mobile) {
modelAndView.setViewName("wap/" + modelAndView.getViewName());
} else {
modelAndView.setViewName("pc/" + modelAndView.getViewName());
}
}
private boolean mobile(HttpServletRequest request) {
boolean isMoblie = false;
String[] mobileAgents = {"iphone", "android", "phone", "mobile", "wap", "netfront", "java", "operamobi", "operamini", "ucweb", "windowsce", "symbian", "series", "webos", "sony", "blackberry", "dopod", "nokia", "samsung", "palmsource", "xda", "pieplus", "meizu", "midp", "cldc", "motorola", "foma", "docomo", "up.browser", "up.link", "blazer", "helio", "hosin", "huawei", "novarra", "coolpad", "webos", "techfaith", "palmsource", "alcatel", "amoi", "ktouch", "nexian", "ericsson", "philips", "sagem", "wellcom", "bunjalloo", "maui", "smartphone", "iemobile", "spice", "bird", "zte-", "longcos", "pantech", "gionee", "portalmmm", "jigbrowser", "hiptop", "benq", "haier", "^lct", "320x320", "240x320", "176x220", "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-", "tsm-", "upg1", "upsi", "vk-v", "voda", "wap-", "wapa", "wapi", "wapp", "wapr", "webc", "winw", "winw", "xda", "xda-", "Googlebot-Mobile"};
if (request.getHeader("User-Agent") != null) {
for (String mobileAgent : mobileAgents) {
if (request.getHeader("User-Agent").toLowerCase().contains(mobileAgent)) {
isMoblie = true;
break;
}
}
}
return isMoblie;
}
}
核心代码就是上面这段。通过拦截器的postHandle
方法拦截所有请求,通过mobile
函数来判断终端类型。如果是移动端设备,则给ModelAndView
的ViewName
增加"wap/"的路径。如果是PC设备,则增加"pc/"的路径。
拦截器注入springboot
springboot的拦截器必须注册才能正常工作。我们新建一个名为WebConfig
的类,继承WebMvcConfigurer
,来完成拦截器的注册工作。代码如下:
package com.coderbbb.book1.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private BaseInterceptor baseInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(baseInterceptor);
}
}
编写Controller
创建一个简单的Controller来返回数据。然后使用不同的终端访问这个网页,应该会调用不同的模板。代码如下:
package com.coderbbb.book1;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class HelloController {
@RequestMapping(value = "/hello", method = RequestMethod.GET)
public String hello(Model model){
model.addAttribute("msg","这是我开发的第一个HTML网页");
return "index";
}
}
效果演示
-
使用chrome模拟移动端访问上面的controller:
-
使用chrome正常访问上面的controller:
引申
同样的思路,还可以实现很多类似的功能。比如实现多皮肤换肤的功能,只需要把皮肤的名称保存到浏览器的localstore或者cookie中,发起请求的时候在header或者url参数中携带皮肤名称。springboot获取到皮肤名称后,重置ModelView
的ViewName
来实现皮肤切换。