动态 返回 CSS 实现

本文介绍如何通过识别浏览器的 User-Agent,判断设备是移动端还是桌面端,并返回对应的 CSS 样式。

解析 User-Agent

常见的 User-Agent 示例

  • Google Chrome:
    Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36

  • Microsoft Edge:
    Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36 Edg/117.0.2045.43

  • Mozilla Firefox:
    Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:127.0) Gecko/20100101 Firefox/127.0

  • iPhone Safari:
    Mozilla/5.0 (iPhone; CPU iPhone OS 15_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.3 Mobile/15E148 Safari/604.1

  • Android Chrome:
    Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Mobile Safari/537.36

  • 微信浏览器:
    Mozilla/5.0 (Linux; Android 13; IN2017 Build/RKQ1.211119.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/122.0.6261.120 Mobile Safari/537.36 XWEB/1220133 MMWEBSDK/20240501 MMWEBID/9296 MicroMessenger/8.0.49.2685(0x28003145) WeChat/arm64 Weixin GPVersion/1 NetType/WIFI Language/en ABI/arm64

  • QQ 浏览器:
    Mozilla/5.0 (Linux; Android 13; IN2017 Build/RKQ1.211119.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/109.0.5414.86 MQQBrowser/6.2 TBS/047101 Mobile Safari/537.36 V1_AND_SQ_9.0.50_6258_YYB_D QQ/9.0.50.16545 NetType/WIFI WebP/0.3.0 AppId/537217916 Pixel/1080 StatusBarHeight/107 SimpleUISwitch/0 QQTheme/1103 StudyMode/1 CurrentMode/2 CurrentFontScale/1.0 GlobalDensityScale/0.90000004 AllowLandscape/false InMagicWin/0

解析 User-Agent

Tio-Utils 提供了 UserAgentUtil 工具类,可以方便地解析 User-Agent 并获取相关信息。

示例代码如下:

import com.litongjava.tio.http.common.HttpRequest;
import com.litongjava.tio.http.server.annotation.RequestPath;
import com.litongjava.tio.utils.http.useragent.UserAgent;
import com.litongjava.tio.utils.http.useragent.UserAgentUtil;

@RequestPath("/")
public class IndexController {
  @RequestPath()
  public UserAgent index(HttpRequest request) {
    return UserAgentUtil.parse(request.getUserAgent());
  }
}

解析后的 JSON 内容示例如下:

{
  "version": "127.0.0.0",
  "mobile": false,
  "engineVersion": "537.36",
  "engine": {
    "name": "Webkit",
    "pattern": "webkit",
    "unknown": false
  },
  "platform": {
    "mobile": false,
    "name": "Windows",
    "pattern": "windows",
    "unknown": false
  },
  "browser": {
    "mobile": false,
    "name": "Chrome",
    "pattern": "chrome",
    "unknown": false
  },
  "os": {
    "name": "Windows 10 or Windows Server 2016",
    "pattern": "windows nt 10\\.0",
    "unknown": false
  }
}

动态 CSS 实现

实现思路

  1. 解析浏览器的 User-Agent
    使用 UserAgentUtil 解析浏览器的 User-Agent,判断请求来自移动端还是桌面端。

  2. 返回对应的 CSS 样式

    • 如果是移动端,则返回移动端的 CSS 样式,样式文件位于运行时目录的 mobile/{uri}.css
    • 如果是桌面端,则返回桌面端的 CSS 样式,样式文件位于运行时目录的 pc/{uri}.css
  3. 自动创建 CSS 文件
    如果请求的 CSS 文件不存在,系统会自动创建该文件,并写入默认内容。

实现代码

以下是实现动态 CSS 返回的代码示例:

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import com.litongjava.tio.http.common.HttpRequest;
import com.litongjava.tio.http.common.HttpResponse;
import com.litongjava.tio.http.server.annotation.RequestPath;
import com.litongjava.tio.http.server.util.Resps;
import com.litongjava.tio.utils.environment.EnvUtils;
import com.litongjava.tio.utils.http.useragent.UserAgent;
import com.litongjava.tio.utils.http.useragent.UserAgentUtil;

@RequestPath("/css")
public class CSSController {

  private Map<String, String> cssMap = new ConcurrentHashMap<>();

  @RequestPath("/{filename}")
  public HttpResponse index(HttpRequest request, String filename) {
    // 解析 User-Agent,判断请求来自移动端还是桌面端
    UserAgent userAgent = UserAgentUtil.parse(request.getUserAgent());

    // 根据设备类型定义对应的目录
    String directory = userAgent.isMobile() ? "css/mobile/" : "css/pc/";

    String pathname = directory + filename;

    if (EnvUtils.isDev()) {
      // 创建 CSS 文件对象
      File file = new File(pathname);

      // 检查文件是否存在
      if (!file.exists()) {
        // 如果文件不存在,则创建必要的父目录
        File parentFile = file.getParentFile();
        if (!parentFile.exists()) {
          parentFile.mkdirs(); // 创建目录
        }

        try {
          // 创建新 CSS 文件并写入默认内容(可选)
          Files.write(file.toPath(), "/* Default CSS content */".getBytes(StandardCharsets.UTF_8));
        } catch (IOException e) {
          e.printStackTrace(); // 处理异常
        }
      }
    }

    String cssContent = cssMap.get(pathname);
    if (cssContent == null) {
      // 创建 CSS 文件对象
      File file = new File(pathname);
      try {
        // 读取并返回 CSS 文件的内容
        cssContent = new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8);
        cssMap.put(pathname, cssContent);
      } catch (IOException e) {
        e.printStackTrace(); // 处理异常
        return Resps.css(request, "/* Error loading CSS */");
      }
    }
    return Resps.css(request, cssContent);
  }
}

请求地址示例:http://localhost:8000/css/index.css
返回的数据示例:

/* Default CSS content */