1. 背景
前一段时间遇到一件奇怪的事,前端请求路径正确,接口也进来了,断点看见返回值也是对的,but 返回给前端就显示404了,俗话说事出反常必有妖,没办法只能看看是啥问题。
2. 工具软件说明
idea、 spring5.3.19、 springboot 2.6.7
3. 现象重现
下述三张图为问题重现截图:
controller截图:
请求 debug 截图:
结果返回值截图:
3. 问题分析
3.1 分析
其实眼神比较好的已经发现问题所在了,奈何当时眼神不好没发现。正常情况404 表示路径不对,既然请求都进来了,那基本上就不是前端路径问题了,结果查询出来了,那么说明是在返回给前端之前出问题了,也就是下图的视图解析出问题了。
3.2 springmvc原理回顾
让我们先回顾下 springmvc的大致原理实现,有不懂的可以去补一下:
- 请求到达DispatcherServlet.doDispatch 方法
- 获取HandlerMapper
- 获取处理器适配器
- 执行请求
- 得到ModelAndView
- 获取视图解析器
- 解析视图并响应
附图:
3.3 断点调试
上述可知是在解析视图的时候没有找到对应的视图处理器,所以调试的时候看对应视图解析即可,下面是我在调试时走的弯路,如果比较着急可以直接跳到 最终问题发现 4. 问题发现
3.3.1 请求返回ModelAndView
处理流程:由下面截图可以看见,返回值需要视图,但是呢,具体的视图为空没找到,其实问题就在这,由于将 json 也理解为某一种视图了,所以没注意到此处问题,认为此返回值很合理。
注意: 正常情况返回json的时候,此处的 modelAndView为空,即不需要视图(或者说特殊视图),直接返回json数据,再此之前一直以为json和jsp、html等一样都叫视图,但今天看来严格意义上来说返回json时不叫视图也是可以的,或者叫特殊视图。
3.3.2 应用默认视图
没有指定modelAndView中的 view,所以应用默认视图,即本次请求
3.3.3 解析视图![视图解析](https://i-blog.csdnimg.cn/blog_migrate/3b85a734f1e70e7e52085ba818b27ca5.png)
等等,走到这发现这视图解析的好像有点不对,看到这个contentType 返回值,我蒙了,我要的是json格式数据,为啥要给我text/html呢,一定是哪里不对。于是找了一个正常的请求断点看下,发现在3.3.1 请求返回ModelAndView 的时候返回值ModelAndView已经是null了,好吗这感情到这都是白走的。于是回到 获取ModelAndView之前也就是 3.3.1 之前继续排查问题。
注意: 接着调试你会发现springmvc默认的是拼上请求前缀做转发,如本次请求是 /sysUser/listGet 默认视图的时候springmvc会将请求变为 /sysUser/sysUser/listGet 然后做转发(forward),如果不相信的话,可以controller新建一个对应请求断点试试,应为本系统没有此接口所以报错404,这也就解释了为什么会404了。
4.问题发现
最终发现原来是由于粗心控制层注解用错了,因为RestController里面包含了@ResponseBody 注解如下图:
上面说到问题是出在调用controller方法返回ModelAndView时的问题,重新走一遍debug看看。ha.handle -> AbstractHandlerMethodAdapter.handleInternal -> AbstractHandlerMethodAdapter.invokeHandlerMethod 此方法是获取 ModelAndView的重要方法
一路跟随就到了 HandlerMethodReturnValueHandlerComposite.selectHandler
&emps;&emps; 此方法正确返回应该是RequestResponseBodyMethodProcessor 这个,这个就是处理json的类。
上述方法结束后会执行下述方法 this.writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage); 结果放入返回值中,因为视图为空,所以后续关于视图的处理都跳过了
由以上结果看,正常情况应该使用 RequestResponseBodyMethodProcessor 解析结果返回modelAndView为空,所以应该是没有指定返回 值是json格式导致的404,去controller类一看,用的是@Controller 而不是@RestController,至此问题原因找到了,换成@RestController 再试,成功
5. 总结
- 此次404 原因主要是 粗心将 @RestController 注解 写成了 @Controller 注解
- @Controller 用在返回值可以 是其它视图的时候使用,包含json(方法上要加@ResponseBody),@RestController相当于指定返回json快捷方式,可以让方法省去@ResponseBody注解
- 报错 404 原因很多,不过一般最终原因都是请求不对,此文不一定适合所有404情况
- 对于springmvc来说,返回json 和 返回 页面等视图不一样,json 不能叫视图或只能叫特殊视图
- 写程序还是要细心,不要着急,不然排错也会耗费一定的时间