--Use Spring Boot + Spring Mobile to determine whether the device you are accessing is a smartphone, tablet, or computer.
Only two, DeviceInfoController.java and pom.xml.
├── pom.xml
└── src
└── main
└── java
└── com
└── example
└── deviceinfo
└── DeviceInfoController.java
-Introduced Spring Mobile in dependencies --Added repository with milestone version of Spring Mobile to repositories (because the version of Spring Mobile used this time is 2.0.0.M3)
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>deviceinfo</artifactId>
<version>0.0.1</version>
<name>deviceinfo</name>
<description>Deviceinfo project for Spring Boot</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--Introduced Spring Mobile-->
<dependency>
<groupId>org.springframework.mobile</groupId>
<artifactId>spring-mobile-device</artifactId>
<version>2.0.0.M3</version>
</dependency>
</dependencies>
<repositories>
<!--Added repository with milestone version of Spring Mobile-->
<repository>
<id>spring-milestone</id>
<name>Spring Milestone Repository</name>
<url>http://repo.spring.io/milestone</url>
</repository>
</repositories>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
DeviceInfoController.java
package com.example.deviceinfo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.mobile.device.Device;
import org.springframework.mobile.device.DevicePlatform;
import org.springframework.mobile.device.DeviceResolverHandlerInterceptor;
import org.springframework.mobile.device.DeviceUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.HashMap;
import java.util.Map;
@SpringBootApplication
@Configuration
@RestController
public class DeviceInfoController implements WebMvcConfigurer {
public static void main(String[] args) {
SpringApplication.run(DeviceInfoController.class, args);
}
//Required to use Spring Mobile
@Bean
public DeviceResolverHandlerInterceptor deviceResolverHandlerInterceptor() {
return new DeviceResolverHandlerInterceptor();
}
//Required to use Spring Mobile
// WebMvcConfigurer#addInterceptors
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(deviceResolverHandlerInterceptor());
}
@RequestMapping("/")
public Map index(WebRequest req) {
Map res = new HashMap<String, String>();
String userAgent = req.getHeader(HttpHeaders.USER_AGENT);
res.put("useragent", userAgent);
Device device = DeviceUtils.getCurrentDevice(req);
if (device == null) {
res.put("device", "null");
} else if (device.isMobile()) {
res.put("device", "mobile");
} else if (device.isTablet()) {
res.put("device", "tablet");
} else if (device.isNormal()) {
res.put("device", "normal");
}
if (device != null) {
DevicePlatform dp = device.getDevicePlatform();
switch (dp) {
case ANDROID:
res.put("platform", "android");
break;
case IOS:
res.put("platform", "ios");
break;
case UNKNOWN:
res.put("platform", "unknown");
break;
}
}
return res;
}
}
Generate a JAR file with mvn package.
$ mvn package
Start Spring Boot with java command.
$ java -jar target/deviceinfo-0.0.1.jar
Try to access by specifying various user agents with Ruby + curl.
require 'json'
ualist = []
## Smartphone
# iPhone + iOS + Safari
ualist << 'Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Mobile/15E148 Safari/604.1'
# iPhone + iOS + Chrome
ualist << 'Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/69.0.3497.91 Mobile/15E148 Safari/605.1'
# Galaxy A30 SCV43 + Android + Browser
ualist << 'Mozilla/5.0 (Linux; Android 9; SCV43 Build/PPR1.180610.011) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/9.0 Chrome/67.0.3396.87 Mobile Safari/537.36'
# Xperia XZ3 SOV39 + Chrome
ualist << 'Mozilla/5.0 (Linux; Android 9; SOV39 Build/52.0.C.1.119) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.91 Mobile Safari/537.36'
## Tablet
# iPad mini + iOS + Safari
ualist << 'Mozilla/5.0 (iPad; CPU OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Mobile/15E148 Safari/604.1'
# Qua tab QZ10 + Android + Chrome
ualist << 'Mozilla/5.0 (Linux; Android 8.1.0; KYT33 Build/3.020VE.0072.a) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.126 Safari/537.36'
## Desktop
# macOS + Safari
ualist << 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1.2 Safari/605.1.15'
# Windows 10 + Microsoft Edge
ualist << 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.18362'
ualist.each do |ua|
json = `#{"curl --silent --user-agent '#{ua}' http://localhost:8080/"}`
puts JSON.pretty_generate(JSON.parse(json))
puts
end
Execution result. You can see that the user agent has been analyzed and can be clearly identified.
{
"useragent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Mobile/15E148 Safari/604.1",
"device": "mobile",
"platform": "ios"
}
{
"useragent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/69.0.3497.91 Mobile/15E148 Safari/605.1",
"device": "mobile",
"platform": "ios"
}
{
"useragent": "Mozilla/5.0 (Linux; Android 9; SCV43 Build/PPR1.180610.011) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/9.0 Chrome/67.0.3396.87 Mobile Safari/537.36",
"device": "mobile",
"platform": "android"
}
{
"useragent": "Mozilla/5.0 (Linux; Android 9; SOV39 Build/52.0.C.1.119) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.91 Mobile Safari/537.36",
"device": "mobile",
"platform": "android"
}
{
"useragent": "Mozilla/5.0 (iPad; CPU OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Mobile/15E148 Safari/604.1",
"device": "tablet",
"platform": "ios"
}
{
"useragent": "Mozilla/5.0 (Linux; Android 8.1.0; KYT33 Build/3.020VE.0072.a) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.126 Safari/537.36",
"device": "tablet",
"platform": "android"
}
{
"useragent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1.2 Safari/605.1.15",
"device": "normal",
"platform": "unknown"
}
{
"useragent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.18362",
"device": "normal",
"platform": "unknown"
}
Modify DeviceInfoController.java so that Device can be specified as an argument of the handler method (method with @RequestMapping annotation).
package com.example.deviceinfo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.mobile.device.Device;
import org.springframework.mobile.device.DeviceHandlerMethodArgumentResolver;
import org.springframework.mobile.device.DevicePlatform;
import org.springframework.mobile.device.DeviceResolverHandlerInterceptor;
import org.springframework.mobile.device.DeviceUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@SpringBootApplication
@Configuration
@RestController
public class DeviceInfoController implements WebMvcConfigurer {
public static void main(String[] args) {
SpringApplication.run(DeviceInfoController.class, args);
}
//Required to use Spring Mobile
@Bean
public DeviceResolverHandlerInterceptor deviceResolverHandlerInterceptor() {
return new DeviceResolverHandlerInterceptor();
}
//Required to use Spring Mobile
// WebMvcConfigurer#addInterceptors
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(deviceResolverHandlerInterceptor());
}
// @Method with RequestMapping(Handler method)To the argument of
//Required to be able to specify the Device of Spring Mobile
@Bean
public DeviceHandlerMethodArgumentResolver deviceHandlerMethodArgumentResolver() {
return new DeviceHandlerMethodArgumentResolver();
}
// @Method with RequestMapping(Handler method)To the argument of
//Required to be able to specify the Device of Spring Mobile
// WebMvcConfigurer#addArgumentResolvers
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(deviceHandlerMethodArgumentResolver());
}
@RequestMapping("/")
public Map index(WebRequest req, Device device) {
Map res = new HashMap<String, String>();
String userAgent = req.getHeader(HttpHeaders.USER_AGENT);
res.put("useragent", userAgent);
//This process is unnecessary because Device can be specified as an argument of the handler method.
//Device device = DeviceUtils.getCurrentDevice(req);
if (device == null) {
res.put("device", "null");
} else if (device.isMobile()) {
res.put("device", "mobile");
} else if (device.isTablet()) {
res.put("device", "tablet");
} else if (device.isNormal()) {
res.put("device", "normal");
}
if (device != null) {
DevicePlatform dp = device.getDevicePlatform();
switch (dp) {
case ANDROID:
res.put("platform", "android");
break;
case IOS:
res.put("platform", "ios");
break;
case UNKNOWN:
res.put("platform", "unknown");
break;
}
}
return res;
}
}
The next article is about an older version, but I suspect it hasn't changed much.
LiteDeviceResolver is used by default for device resolution. It is based on the WordPress Mobile Pack detection algorithm. Another DeviceResolver implementation can be plugged in by injecting the constructor argument of DeviceResolverHandlerInterceptor. Resolvers for more sophisticated devices like WURFL can identify specific device features such as screen size, manufacturer, model, or priority markup.
If you look at the source code of LiteDeviceResolver.java, you can see how the device is identified from the user agent string.
spring-mobile/LiteDeviceResolver.java at v2.0.0.M3 · spring-projects/spring-mobile · GitHub
// Android special case
if (userAgent.contains("android")) {
return resolveWithPlatform(DeviceType.MOBILE, DevicePlatform.ANDROID);
}
// Apple special case
if (userAgent.contains("iphone") || userAgent.contains("ipod") || userAgent.contains("ipad")) {
return resolveWithPlatform(DeviceType.MOBILE, DevicePlatform.IOS);
}
In order to use Spring Mobile, you need to implement WebMvcConfigurer # addInterceptors and add the DeviceResolverHandlerInterceptor object to the InterceptorRegistry object in its method.
This is because the DeviceResolverHandlerInterceptor object creates a Device object and adds it as an attribute in HttpServletRequest # setAttribute before processing in the handler method.
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Device device = deviceResolver.resolveDevice(request);
request.setAttribute(DeviceUtils.CURRENT_DEVICE_ATTRIBUTE, device);
return true;
}
spring-mobile/DeviceUtils.java at v2.0.0.M3 · spring-projects/spring-mobile · GitHub
DeviceUtils.getCurrentDevice only returns the set attributes.
public static Device getCurrentDevice(HttpServletRequest request) {
return (Device) request.getAttribute(CURRENT_DEVICE_ATTRIBUTE);
}
Recommended Posts