经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » 编程经验 » 查看文章
《优化接口设计的思路》系列:第十篇—网站的静态资源怎么获取?
来源:cnblogs  作者:sum墨  时间:2024/4/19 8:56:38  对本文有异议

一、前言

大家好!我是sum墨,一个一线的底层码农,平时喜欢研究和思考一些技术相关的问题并整理成文,限于本人水平,如果文章和代码有表述不当之处,还请不吝赐教。

作为一名从业已达六年的老码农,我的工作主要是开发后端Java业务系统,包括各种管理后台和小程序等。在这些项目中,我设计过单/多租户体系系统,对接过许多开放平台,也搞过消息中心这类较为复杂的应用,但幸运的是,我至今还没有遇到过线上系统由于代码崩溃导致资损的情况。这其中的原因有三点:一是业务系统本身并不复杂;二是我一直遵循某大厂代码规约,在开发过程中尽可能按规约编写代码;三是经过多年的开发经验积累,我成为了一名熟练工,掌握了一些实用的技巧。

前面的文章都是先说概念,再说怎么设计和实现,今天我打算换一种写法,从一个功能需求的实现来讲一下静态资源是如何访问的?

功能需求如下:

  1. 现有一个后端应用,默认访问方式如下:http://47.120.49.119:8080/#/
  2. 用电脑、平板、手机等设备都可以访问,且不同的设备样式要适配,前端做了两套,但是访问接口都是同一个;
  3. 由于没有使用cdn,界面渲染的时候需要用到的字体库、图标库和一些图片也放在了后端应用;

光文字讲需求可能不太直观,我放一些图片就好理解了,如下:

使用电脑访问http://47.120.49.119:8080/#/出现的界面

使用手机或平板访问http://47.120.49.119:8080/#/出现的界面

可以看到这两个的样式不一样,组件也不一样,但是接口都是同一个。

前端的资源如下

文件有html、js、css,还有一些特殊的文件如字体,文件类型还是比较丰富的,且这样的资源有两份,一份是电脑端,一份是移动端。由于没有使用cdn,我们需要通过后端服务来访问这些资源。
那么说到这里不知道大家有没有理解这个需求呢?其实简单理解就是SpringBoot的接口访问静态资源,下面就开始讲一下我是如何实现这个功能的。

二、功能实现

1. 从访问一个index.html开始

(1)创建一个SpringBoot项目

这个我就不啰嗦了,使用ide或者https://start.spring.io/网站创建一个即可。

(2)引入SpringBoot模板引擎— Thymeleaf

pom.xml引入

  1. <!-- thymeleaf -->
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-thymeleaf</artifactId>
  5. </dependency>

(3)在resources目录下创建一个static文件夹,在static文件夹创建一个index.html

(4)application.properties添加如下配置

  1. spring.thymeleaf.prefix=classpath:/static/
  2. spring.thymeleaf.suffix=.html
  3. spring.thymeleaf.mode=HTML

(5)写一个IndexController.java

  1. package com.summo.file.controller;
  2. import org.springframework.stereotype.Controller;
  3. import org.springframework.web.bind.annotation.GetMapping;
  4. @Controller
  5. public class IndexController {
  6. @GetMapping("/")
  7. public String index() {
  8. return "index";
  9. }
  10. }

(6)访问一下index.html

这个还是比较简单的,我已经成功了,大家成功了吗?如果发现返回的不是HTML而是字符串,检查一下IndexController的注解,是@Controller而不是@RestController,方法上也不要加 @ResponseBody。

2. 判断当前的请求来自于电脑还是手机

(1)判断逻辑分析

这个听起来很难,其实很简单,前端在访问后端接口的时候,会携带一个USER-AGENT的请求头,通过这个USER-AGENT请求头就可以区分访问来源,判断逻辑代码如下:

  1. /**
  2. * 校验是否手机端
  3. *
  4. * @param request
  5. * @return
  6. */
  7. public static boolean isFromMobile(HttpServletRequest request) {
  8. //1. 获得请求UA
  9. String userAgent = request.getHeader("USER-AGENT").toLowerCase();
  10. //2.声明手机和平板的UA的正则表达式
  11. // \b 是单词边界(连着的两个(字母字符 与 非字母字符) 之间的逻辑上的间隔),
  12. // 字符串在编译时会被转码一次,所以是 "\\b"
  13. // \B 是单词内部逻辑间隔(连着的两个字母字符之间的逻辑上的间隔)
  14. String phoneReg = "\\b(ip(hone|od)|android|opera m(ob|in)i" + "|windows (phone|ce)|blackberry"
  15. + "|s(ymbian|eries60|amsung)|p(laybook|alm|rofile/midp" + "|laystation portable)|nokia|fennec|htc[-_]"
  16. + "|mobile|up.browser|[1-4][0-9]{2}x[1-4][0-9]{2})\\b";
  17. String tableReg = "\\b(ipad|tablet|(Nexus 7)|up.browser" + "|[1-4][0-9]{2}x[1-4][0-9]{2})\\b";
  18. // 3.移动设备正则匹配:手机端、平板
  19. Pattern phonePat = Pattern.compile(phoneReg, Pattern.CASE_INSENSITIVE);
  20. Pattern tablePat = Pattern.compile(tableReg, Pattern.CASE_INSENSITIVE);
  21. if (null == userAgent) {
  22. userAgent = "";
  23. }
  24. // 4.匹配
  25. Matcher matcherPhone = phonePat.matcher(userAgent);
  26. Matcher matcherTable = tablePat.matcher(userAgent);
  27. if (matcherPhone.find() || matcherTable.find()) {
  28. //来自手机或者平板
  29. return true;
  30. } else {
  31. //来自PC
  32. return false;
  33. }
  34. }

(2)新建两个文件夹,将手机端和电脑端的index界面区分开来

(3)IndexController代码改一下

  1. import java.util.regex.Matcher;
  2. import java.util.regex.Pattern;
  3. import javax.servlet.http.HttpServletRequest;
  4. import org.springframework.stereotype.Controller;
  5. import org.springframework.web.bind.annotation.GetMapping;
  6. @Controller
  7. public class IndexController {
  8. @GetMapping("/")
  9. public String index(HttpServletRequest request) {
  10. if (isFromMobile(request)) {
  11. return "mp/index";
  12. } else {
  13. return "web/index";
  14. }
  15. }
  16. /**
  17. * 校验是否手机端
  18. *
  19. * @param request
  20. * @return
  21. */
  22. public static boolean isFromMobile(HttpServletRequest request) {
  23. //1. 获得请求UA
  24. String userAgent = request.getHeader("USER-AGENT").toLowerCase();
  25. //2.声明手机和平板的UA的正则表达式
  26. // \b 是单词边界(连着的两个(字母字符 与 非字母字符) 之间的逻辑上的间隔),
  27. // 字符串在编译时会被转码一次,所以是 "\\b"
  28. // \B 是单词内部逻辑间隔(连着的两个字母字符之间的逻辑上的间隔)
  29. String phoneReg = "\\b(ip(hone|od)|android|opera m(ob|in)i" + "|windows (phone|ce)|blackberry"
  30. + "|s(ymbian|eries60|amsung)|p(laybook|alm|rofile/midp" + "|laystation portable)|nokia|fennec|htc[-_]"
  31. + "|mobile|up.browser|[1-4][0-9]{2}x[1-4][0-9]{2})\\b";
  32. String tableReg = "\\b(ipad|tablet|(Nexus 7)|up.browser" + "|[1-4][0-9]{2}x[1-4][0-9]{2})\\b";
  33. // 3.移动设备正则匹配:手机端、平板
  34. Pattern phonePat = Pattern.compile(phoneReg, Pattern.CASE_INSENSITIVE);
  35. Pattern tablePat = Pattern.compile(tableReg, Pattern.CASE_INSENSITIVE);
  36. if (null == userAgent) {
  37. userAgent = "";
  38. }
  39. // 4.匹配
  40. Matcher matcherPhone = phonePat.matcher(userAgent);
  41. Matcher matcherTable = tablePat.matcher(userAgent);
  42. if (matcherPhone.find() || matcherTable.find()) {
  43. //来自手机或者平板
  44. return true;
  45. } else {
  46. //来自PC
  47. return false;
  48. }
  49. }
  50. }

电脑端访问

手机端访问

到这里,一个接口区分电脑端和手机端的功能实现了,由于index.html引入了一些js、css、ttf、woff 等静态资源,这些文件如何访问呢?继续看。

3. 其他类型的静态资源如何访问

mp目录下文件结构如下

index.html想要引入js和css目录下的文件,路径应该这样写

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset=utf-8>
  5. <meta name=viewport content="width=device-width,initial-scale=1">
  6. <title>summo-sbmy-front-mp</title>
  7. <link href=/mp/css/app.css rel=stylesheet>
  8. </head>
  9. <body>
  10. <div id=app></div>
  11. <script type=text/javascript src=/mp/js/manifest.js></script>
  12. <script type=text/javascript src=/mp/js/vendor.js></script>
  13. <script type=text/javascript src=/mp/js/app.js></script>
  14. </body>
  15. </html>

这样就可以访问到指定目录的资源了

整体逻辑其实很简单,我觉得最容易出问题的地方在路径的设置,有时候文件和代码都弄好了,但就是加载不出来,基本上就是路径设置错了。所以大家要是自己实现的话,尽量先按照我的文件路径来,先搞成功再说。

三、扩展知识

1. 给界面传递动态值

在Spring MVC中,将变量传递到视图通常通过Model对象或使用ModelAndView对象进行,举个例子:
IndexController.java代码如下

  1. package com.summo.file.controller;
  2. // 确保引入相关的包
  3. import org.springframework.ui.Model;
  4. @Controller
  5. public class IndexController {
  6. @GetMapping("/")
  7. public String index(HttpServletRequest request, Model model) {
  8. // 添加属性到model中
  9. model.addAttribute("message", "欢迎访问我们的网站!");
  10. // 设置一个版本号
  11. model.addAttribute("version", "1.0.0");
  12. // 根据客户端类型选择视图
  13. if (isFromMobile(request)) {
  14. return "mp/index"; // 返回移动端页面
  15. } else {
  16. return "web/index"; // 返回桌面端页面
  17. }
  18. }
  19. }

index.html代码如下

  1. <!DOCTYPE html>
  2. <html xmlns:th="http://www.thymeleaf.org">
  3. <head>
  4. <title>首页</title>
  5. <script type="text/javascript" th:src="'/mp/js/manifest-' + ${version} + '.js'"></script>
  6. </head>
  7. <body>
  8. <h1>Hello World,我是电脑端</h1>
  9. <!-- Thymeleaf中使用的表达式 -->
  10. <p th:text="${message}"></p>
  11. </body>
  12. </html>

访问之后可以看到参数已经替换掉了

动态传值在实际开发中经常使用到,比如我们一般在配置文件中维护好js、css 的版本号,然后将版本号传给index.html达到动态控制前端版本。

2. 项目打包静态资源没有打进去

正常情况下,打完包后静态资源文件会在static文件夹下,但是上次我打包就发现静态资源文件没有打包进去,后来才知道需要在pom.xml文件里面配置一下,具体配置如下:

  1. <build>
  2. <finalName>summo-sbmy</finalName>
  3. <resources>
  4. <resource>
  5. <!-- 指定配置文件所在的resource目录 -->
  6. <directory>src/main/resources</directory>
  7. <includes>
  8. <include>**/*.html</include>
  9. <include>**/*.js</include>
  10. <include>**/*.css</include>
  11. </includes>
  12. <filtering>true</filtering>
  13. </resource>
  14. <resource>
  15. <!-- 指定配置文件所在的resource目录 -->
  16. <directory>src/main/resources</directory>
  17. <includes>
  18. <include>**/*.woff</include>
  19. <include>**/*.ttf</include>
  20. </includes>
  21. <filtering>false</filtering>
  22. </resource>
  23. </resources>
  24. </build>

这里需要注意的是,.woff和.ttf这类文件比较特殊,需要单独开一块并且设置filtering为false。这个配置说明对这类文件不执行过滤,因为过滤可能破坏文件内容,字体文件应该以其原始形式包含在构建产物中。

四、总结一下

访问静态资源的接口大家接触的不多,主要是因为现在前后端分离了,前端自己使用CDN放资源,后端只用维护一个index.html文件,其他的资源都通过CDN访问,已经变得很简单了。但是有时候想要用却不知道从哪里开始,希望这篇文章可以给大家一个大概的思路,还有就是处理静态资源的框架很多,最常见的就是Thymeleaf、Velocity,这两个都可以实现上面的效果,但建议不要混用。

文末小彩蛋,自己花一个星期做的小网站,放出来给大家看看,网址如下:http://47.120.49.119:8080

原文链接:https://www.cnblogs.com/wlovet/p/18143730

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号