您好,我是码农飞哥,感谢您阅读本文!如果此文对您有所帮助,请毫不犹豫的一键三连吧。小伙伴们,有啥想看的,想问的,欢迎积极留言告诉我喔。 。
开心编码
事情是这样的,我在用SpringBoot框架搭建的项目上写了一个新增用户的接口,接口地址是/v1/user/test
,代码如下所示(demo版,非生产代码),简单到不能再简单的一个接口,像这样的接口我都记不清自己已经写了多少个了。
@Controller public class TestRespController { //新增用户的接口 @RequestMapping("/v1/user/test") public User addUser(String userName, String password) { User user = new User(); user.setUserName(userName); user.setPassword(password); return user; } }
接口写好之后,我没有多想,就启动项目开始调试代码,我熟练的在chrome浏览器上输入了http://localhost:8080/v1/user/test 这个地址,然后点击回车,没想到的是页面上提示了一个404的错误。
这是怎么回事呢?我这接口地址没有错呀,为啥找不到接口呢?奇了怪了。我火急火燎的跑到控制台,看看有没有错误日志。让我很失望啥错误都没有输出,过滤器也准确的把我的输入的请求地址打印出来了。
苦苦寻找
为啥页面报了404的错误,而控制台没有任何错误输出呢?实在是有点诡异。问同事嘛,又不好意思,自己好歹是个高级开发工程师,404的问题都搞不定实在是说不过去。三十六计走为上,看看其他的正常的接口是怎么写的。然后,我找到了这样一个接口,这个接口是可以正常访问的,
@ResponseBody @RequestMapping("/v1/user/test/update") public User updateUser(User user) { boolean result = testNgService.updateUser(user); return result ? user : null; }
哦哦哦,我突然恍然大悟,原来是没有在接口上添加@ResponseBody 注解,我印象中这个注解可以将返回的对象序列化成json字符串,并放在响应体中,但是跟接口地址又有啥关系呢?。抱着试试看的态度,我在接口上加上了 @ResponseBody注解,代码就不列举了,太简单了不好意思列举。再次访问http://localhost:8080/v1/user/test 这个地址,接口可以正常返回数据了。
当然,如果该类下所有的接口都是返回application/json格式数据的接口,则可以直接将类上的**@Controller注解替换成 @RestController 注解。@RestController注解是一个组合注解是由@Controller注解和@ResponseBody注解组成**。
思考升华
至此,问题是解决了。但是,心中的小问号还萦绕在心头。
1.为啥页面报了404的错误,但是控制台没有错误日志呢?
2.为啥不加@ResponseBody注解接口会报404的错误呢?
知道的小伙伴,可以在下方评论留言哦,并且直接略过下面的内容,因为你们太牛逼了。
有问题,咱们就解决问题,(而不是解决提出问题的人喔,哈哈哈哈)
1. 为啥页面报了404的错误,但是控制台没有错误日志呢?
为啥出现了404的异常,但是控制台没有错误的日志输出呢?这个就很奇怪了。这个问题是我们首先需要解决的,不然不知道具体的错误是啥,就很难定位到问题。根据已经掌握SpringMVC的知识:我们在页面输入一个url之后,请求首先会到DispatcherServlet。然后,DispatcherServlet会根据url去HandelMapping中找到能处理该请求的Handler。如果找不到的话则会报错404的错误,所以,猜测SpringBoot抛出的是NoHandlerFoundException这个异常,现在的问题就是为啥这个NoHandlerFoundException没输出呢?一番百度之后,我发现只需要在配置文件application.yml中添加如下配置即可:
spring: mvc: # 允许抛出NoHandlerFoundException throw-exception-if-no-handler-found: true # 禁用资源映射 resources: add-mappings: false
加上这两个配置之后,在访问接口的话,控制台就会打印出如下的错误日志:我们看到在查找mapping时url就变成了 /v1/user/v1/user/test,不是我们期待的 /v1/user/test。
说明:加上spring.resources.add-mappings=false 禁用资源映射之后,可以输出
o.s.web.servlet.PageNotFound : No mapping for GET /v1/user/v1/user/test
springboot中的WebMvcAutoConfiguration类配置默认资源映射地址是 "/"
private static final String SERVLET_LOCATION = "/";
即使你的地址错误也会匹配到 /** 这个静态资源映射地址,就不会进入noHandlerFound方法,自然不会抛出NoHandlerFoundException了。
异常终于暴露出来了。接下来就是处理异常了。
2. 为啥不加@ResponseBody
注解接口会报404的错误呢?
要想回答这个问题,还是要回到 @ResponseBody 注解的作用上面,一番苦苦的搜索之后,我终于发现了答案。@ResponseBody 注解的作用就是将Controller返回的对象通过适当的HttpMessageConverter转换为指定的格式,写入到Response对象的body数据区中返回给前端页面。如果没有加这个注解,只有@RequestMapping这个注解的话,则返回值通常解析为跳转路径,控制器返回的时候路径会变为(在当前路径的上一级路径上追加路径),然后返回的过程找不到对应的视图,导致404的错误。这只是理论,实际上是不是这样子的呢?还需要我们验证一下。
1.不加@ResponseBody则默认会返回一个视图,如下图所示,返回一个名为
/static/helloWorld.html的视图。 @RequestMapping("/v1/user/test1") public String queryUserHtml2() { return "/static/helloWorld.html"; }
即会访问static目录下的helloWorld.html页面。
访问结果是:
页面可以正常的跳转,说明,不加上@ResponseBody注解确实会默认返回一个视图,这是正常的能找到页面的视图,如果返回一个不存在的视图会怎样呢?
@RequestMapping("/v1/user/test2") public String queryUserHtml3() { return "{\"code\":200}"; }
打印出来的路径是 /v1/user/{“code”:200},即/v1/user/test2的上级路径 /v1/user+{“code”:200},说明上面的理论解释是没有问题的。
总结
本文从实际开发中一个很小的问题入手,层层分析问题的根源,知其然更要知其所以然。总之,一句话总结@ResponseBody注解的作用就是将返回的结果通过HttpMessageConverter 转换为指定格式并放在响应体中,没有它的话,SpringBoot会默认返回一个视图。
参考
解决springboot配置@ControllerAdvice不能捕获NoHandlerFoundException问题