关键词:[SpringBoot] [RequestMapping] [文件路径]
一、问题出现
@Controller
@RequestMapping("/goods")
public class GoodsController {
@Autowired
UserServiceImpl userService;
@Autowired
IGoodsService goodsService;
/**
* 跳转到商品列表页
*
* @return
*/
@RequestMapping("/toList")
public String toList(User user, Model model) {
if (user == null) {
return "login";
}
model.addAttribute("user", user);
model.addAttribute("goodsList", goodsService.findGoodVo());
return "goodsList";
}
}
二、初步思考
1)如代码:控制层加了@RequestMapping("/goods")
后,访问 goodsImg
的路径奇怪的变成了 http://localhost:8080/goods/img/iphone12.png
,且加载不出,而我直接在地址栏输入 http://localhost:8080/img/iphone12.png
却能够访问;
2)再加上,检查->网络 这里面所有的当前页面静态文件都是 http://localhost:8080/static目录下的子目录/xxx
的形式,没有一个像 ihpone12.png
一样在路径中出现 /goods
;
3)我就觉得是不是路径中这个 /goods
导致了这个错误;
三、逐个细化
3.1 target 文件
- target 文件夹是用来存放项目构建后的文件和目录、jar包、war包、编译的class文件,都是maven构建时生成的;
- 在idea的spring boot项目中,如果target目录中没有同步更新目录文件和资源就会报错;
然而我的项目中,多次运行包括删除target文件再运行,都是含有 img 文件的
所以肯定不是 target 文件的问题,即我的 图片 已经完成了资源构建,但是我的访问地址有问题,导致访问不到它;
3.2 @RequestMapping
本质上 RequestMapping 是起到请求映射的作用,将对应的 url (也就是 RequestMapping 的 value 值)放行,经过一顿处理,映射到某一页面(return 值);
那么:
- 在方法上加 RequestMapping 注解便是直接拦截这个 url;
- 在类上加 RequestMapping 注解便是在这个类的所有方法之前加上这层路径;
比如这个代码:
@Controller
@RequestMapping("/goods")
public class GoodsController {
@Autowired
UserServiceImpl userService;
@Autowired
IGoodsService goodsService;
/**
* 跳转到商品列表页
*
* @return
*/
@RequestMapping("/toList")
public String toList(User user, Model model) {
if (user == null) {
return "login";
}
model.addAttribute("user", user);
model.addAttribute("goodsList", goodsService.findGoodVo());
return "goodsList";
}
}
- 如果我访问
http://localhost:8080/goods/toList
时就会被放行,进入到toList
方法中,一顿处理,映射到goodsList
页面; - 如果没加
@RequestMapping("/goods")
在类上,我访问http://localhost:8080/toList
就进入到toList
方法中,然后映射到goodsList
页面;
这么一来我能理解为什么 img/iphone12.png
前面会加一个 /goods
,但为什么其他所有的静态文件确没加/goods
?这更加让我确信是 /goods
让我无法访问 已有的 img 中的图片;
3.3 Spring 与 Themeleaf 相对路径绝对路径
再来看一下 我 WebMvcConfigurer:
/**
* 保证静态资源能够被访问
* @param registry
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
}
我已经将 所有对静态资源访问的路径(/**
)都映射为 /static/
了,那无论是 http://localhost:8080/goods/img/iphone12.png
还是 http://localhost:8080/img/iphone12.png
应该都是去访问 static 文件夹下的东西;
https://blog.csdn.net/CatAndBeer/article/details/108460781 这篇文章指出是 页面中 相对路径的原因导致 RequestMapping 在资源上面多加了一层目录,页面中使用绝对路径即可;
但实际上我页面中都用的绝对路径
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>商品列表</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<!-- jquery -->
<script type="text/javascript" th:src="@{/js/jquery.min.js}"></script>
<!-- bootstrap -->
<link rel="stylesheet" type="text/css" th:href="@{/bootstrap/css/bootstrap.min.css}"/>
<script type="text/javascript" th:src="@{/bootstrap/js/bootstrap.min.js}"></script>
<!-- jquery-validator -->
<script type="text/javascript" th:src="@{/jquery-validation/jquery.validate.min.js}"></script>
<script type="text/javascript" th:src="@{/jquery-validation/localization/messages_zh.min.js}"></script>
<!-- layer -->
<script type="text/javascript" th:src="@{/layer/layer.js}"></script>
<!-- md5.js -->
<script type="text/javascript" th:src="@{/js/md5.min.js}"></script>
<!-- common.js -->
<script type="text/javascript" th:src="@{/js/common.js}"></script>
</head>
<body>
<div class="panel panel-default" style="height:100%;background-color:rgba(222,222,222,0.8)">
<div class="panel-heading">秒杀商品列表</div>
<table class="table" id="goodslist">
<tr>
<td>商品名称</td>
<td>商品图片</td>
<td>商品原价</td>
<td>秒杀价</td>
<td>库存数量</td>
<td>详情</td>
</tr>
<tr th:each="goods,goodsStat : ${goodsList}">
<td th:text="${goods.goodsName}"></td>
<td><img th:src="@{${goods.goodsImg}}" width="100" height="100"/></td>
<td th:text="${goods.goodsPrice}"></td>
<td th:text="${goods.seckillPrice}"></td>
<td th:text="${goods.stockCount}"></td>
<td><a th:href="'/goods/toDetail/'+${goods.id}">详情</a></td>
</tr>
</table>
</div>
</body>
</html>
th:src="@{xxx路径}"
表示在 xxx路径 前加上上下文路径,那不就是绝对路径么;
3.4 关于 配置类 和 约定的配置
@EnableWebMvc
默认会将这里配置类 覆盖 MVC事先约定的配置;- SpringBoot 中 MVC 约定的配置就是
/**
,但这里我们有了配置类,覆盖约定的配置@EnableWebMvc
就是启动你这个配置类;
所以,这里要么不加 @EnableWebMvc
,让 SpringBoot 用自己的 MVC 配置,要么就是加上 @EnableWebMvc
,然后在类里面把这个静态资源映射也给写上;
但这两个不是我问题所在,因为我 target 文件中已有 img文件夹和两张图片,所以必然不是这里的问题
四、一个尝试
其实,通过第三部分,我确定是 路径的 /goods
导致的问题,而其他静态资源不受影响,就 img 受影响,那我就把注意力放在 页面上的路径,其实是可以发现:
这两个红框地方,唯一的区别就是在 上下文路径后有没有一个 /
,所以我进行尝试:
<tr th:each="goods,goodsStat : ${goodsList}">
<td th:text="${goods.goodsName}"></td>
<td><img th:src="@{${'/'+goods.goodsImg}}" width="100" height="100"/></td>
<td th:text="${goods.goodsPrice}"></td>
<td th:text="${goods.seckillPrice}"></td>
<td th:text="${goods.stockCount}"></td>
<td><a th:href="'/goods/toDetail/'+${goods.id}">详情</a></td>
</tr>
加上了斜杠 /
,emmm
然后就恢复正常了????
这说明https://blog.csdn.net/CatAndBeer/article/details/108460781 该文章说的没错,的确是绝对路径的原因,我一开始疏忽大意,但是 加上 /
和 不加 /
为什么会让两个路径完全不同:
- 加上
/
,我的图片路径:http://localhost:8080/img/iphone12.png
; - 不加
/
,我的图片路径:http://localhost:8080/goods/img/iphone12.png
我很想知道其中的原因😭😭😭
五、深入思考—html页面中相对路径
html页面中相对路径有两种:
-
/test/page1
,这是相对于服务器根路径而言的,以之前的例子为例,使用结果就是直接从8080以后开始替换,如http://localhost:8080/test/page1
-
test/page2
,这是相对于当前路径而言的,比如当前路径为http://localhost:8080/test/page1
,那么替换以后就是
绝对路径:
绝对路径就是直接http://localhost:8080/test/page1
,十分简单,但是也相当于写死了
http://localhost:8080/test/test/page
,在这种情况下也有对应的语法,../
表示上级目录,./
表示当前目录,如test/page2
就相当于./test/page2
,如果写成../test/page2
,那么替换后的路径就是http://localhost:8080/test/page
了;
这么一想就清楚了:
- 我不加
/
,表示相对当前路径,即当前 url +/img/iphone12.png
=http://localhost:8080/goods/img/iphone12.png
; - 我加上
/
,表示相对服务器根路径,http://localhost:8080/img/iphone12.png
,这样的话,经过 MVC 配置的静态资源映射就直接变成了http://localhost:8080/static/img/iphone12.png
;
所以,这实际上跟 Themeleaf 并没有多大关系,因为去掉 @{}
,只要加上 /
,我依旧可以访问静态资源:
<tr th:each="goods,goodsStat : ${goodsList}">
<td th:text="${goods.goodsName}"></td>
<td><img th:src="${'/'+goods.goodsImg}" width="100" height="100"/></td>
<td th:text="${goods.goodsPrice}"></td>
<td th:text="${goods.seckillPrice}"></td>
<td th:text="${goods.stockCount}"></td>
<td><a th:href="'/goods/toDetail/'+${goods.id}">详情</a></td>
</tr>
但为什么
http://localhost:8080/goods/img/iphone12.png
就不能匹配/**
映射到/static/
呢?难道是认为他不是获取静态资源?
貌似是的,因为成功时 如下显示:
失败时:
失败时,路径为 http://localhost:8080/goods/img/iphone12.png
,我认为 这意味着它发出一个非获取静态资源的请求给 SpringBoot,SpringBoot 去找 Controller,没找到,所以这个响应不了;
所以,我标题是起错了嘛 😂,是HTML相对路径的问题,就当是顺便普及知识;
六、注意点
- 问题的解决在于 HTML 页面的相对路径,细节没把握好;
- 项目Debug时,最好关闭 浏览器的 缓存,否则可能导致非正确方法解决问题的情况;