1. 背景

  前一段时间遇到一件奇怪的事,前端请求路径正确,接口也进来了,断点看见返回值也是对的,but 返回给前端就显示404了,俗话说事出反常必有妖,没办法只能看看是啥问题。

2. 工具软件说明

   idea、 spring5.3.19、 springboot 2.6.7

3. 现象重现

  下述三张图为问题重现截图:

   controller截图:

controller截图

   请求 debug 截图:

查询截图

   结果返回值截图:

结果截图

3. 问题分析

3.1 分析

  其实眼神比较好的已经发现问题所在了,奈何当时眼神不好没发现。正常情况404 表示路径不对,既然请求都进来了,那基本上就不是前端路径问题了,结果查询出来了,那么说明是在返回给前端之前出问题了,也就是下图的视图解析出问题了。

3.2 springmvc原理回顾

  让我们先回顾下 springmvc的大致原理实现,有不懂的可以去补一下:

  1. 请求到达DispatcherServlet.doDispatch 方法
  2. 获取HandlerMapper
  3. 获取处理器适配器
  4. 执行请求
  5. 得到ModelAndView
  6. 获取视图解析器
  7. 解析视图并响应
    附图:springmvc原理图

3.3 断点调试

  上述可知是在解析视图的时候没有找到对应的视图处理器,所以调试的时候看对应视图解析即可,下面是我在调试时走的弯路,如果比较着急可以直接跳到 最终问题发现 4. 问题发现

3.3.1 请求返回ModelAndView

  处理流程:由下面截图可以看见,返回值需要视图,但是呢,具体的视图为空没找到,其实问题就在这,由于将 json 也理解为某一种视图了,所以没注意到此处问题,认为此返回值很合理。
  
注意: 正常情况返回json的时候,此处的 modelAndView为空,即不需要视图(或者说特殊视图),直接返回json数据,再此之前一直以为json和jsp、html等一样都叫视图,但今天看来严格意义上来说返回json时不叫视图也是可以的,或者叫特殊视图。

请求返回的ModelAndView

3.3.2 应用默认视图

  没有指定modelAndView中的 view,所以应用默认视图,即本次请求

解析后的视图

3.3.3 解析视图视图解析

视图解析
视图值

  等等,走到这发现这视图解析的好像有点不对,看到这个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); 结果放入返回值中,因为视图为空,所以后续关于视图的处理都跳过了

json返回值处理
  由以上结果看,正常情况应该使用 RequestResponseBodyMethodProcessor 解析结果返回modelAndView为空,所以应该是没有指定返回 值是json格式导致的404,去controller类一看,用的是@Controller 而不是@RestController,至此问题原因找到了,换成@RestController 再试,成功

5. 总结

  1. 此次404 原因主要是 粗心将 @RestController 注解 写成了 @Controller 注解
  2. @Controller 用在返回值可以 是其它视图的时候使用,包含json(方法上要加@ResponseBody),@RestController相当于指定返回json快捷方式,可以让方法省去@ResponseBody注解
  3. 报错 404 原因很多,不过一般最终原因都是请求不对,此文不一定适合所有404情况
  4. 对于springmvc来说,返回json 和 返回 页面等视图不一样,json 不能叫视图或只能叫特殊视图
  5. 写程序还是要细心,不要着急,不然排错也会耗费一定的时间
Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐