我是如何一步步解决问题 让Spring MVC返回HTML类型的视图
前言
这两天在折腾SSM,在捣鼓Spring MVC
的时候,我想让Spring MVC的前端控制器(DispatcherServlet
)给用户返回的是HTML类型的视图而不是JSP类型的视图,于是我按照常规的思路,把Spring MVC
配置文件里面的视图解析器配置修改成HTML后缀的,然后就遇上了各种问题了......当然这些问题也都是我对Spring MVC不够了解才导致的,接下来详细说一下我遇到的问题以及解决过程。
遇上问题
为了将返回给用户的视图从JSP
改成HTML
嘛,我就寻思着不就是把Spring MVC
配置文件的视图配置改一下,把.jsp
改成.html
嘛。
原来返回JSP的配置 Spring-MVC.xml:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="prefix" value=""></property><property name="suffix" value=".jsp"></property>
</bean>
复制代码
因为我的JSP文件就是放在web
根目录下,所以这里prefix
就留空了。
修改成返回HTML的配置 Spring-MVC.xml:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="prefix" value=""></property><property name="suffix" value=".html"></property>
</bean>
复制代码
然后写Controller
将视图返回给前端控制器DispatchServlet
看看能不能将HTML类型的视图返回给用户
package com.nChat.controller;import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;@Controller
public class UserController {@RequestMapping(value = "/login")public String login(){return "/register"; //返回web根目录下的register.html}
}复制代码
emmmm配置文件修改好了,按照我的想法,运行肯定没“问题”吧,可现实总是打我脸...可能是我太年轻了吧,运行之后报错,页面显示404
24-Dec-2018 21:57:35.769 警告 [http-nio-8080-exec-3] org.springframework.web.servlet.DispatcherServlet.noHandlerFound No mapping for GET /
24-Dec-2018 21:57:35.778 警告 [http-nio-8080-exec-2] org.springframework.web.servlet.DispatcherServlet.noHandlerFound No mapping for GET /
24-Dec-2018 21:57:35.854 警告 [http-nio-8080-exec-1] org.springframework.web.servlet.DispatcherServlet.noHandlerFound No mapping for GET /
24-Dec-2018 21:57:38.542 警告 [http-nio-8080-exec-4] org.springframework.web.servlet.DispatcherServlet.noHandlerFound No mapping for GET /register.html
复制代码
错误的意思大概是前端控制器DispatchServlet
找不到请求相对应的mapping
,所以抛出noHandlerFound
的异常
问题分析
我们来分析看看为啥出现这个问题,首先贴出我们前端控制器DispatchServlet
的工作流程先
流程用文字说明大概如下:
- 用户发送请求,被 SpringMVC 的前端控制器
DispatcherServlet
拦截。 - DispatcherServlet 收到请求后自己不进行处理,而是将请求转发给处理器映射器
HandlerMapping
。 - 处理器映射器根据请求的URL确定映射关系找出相应的处理器适配器,并且返回HandlerExecutionChain对象给前端控制器。 处理器映射器找到具体的处理器适配器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给 DispatcherServlet。
- DispatcherServlet根据3返回的
HandlerExecutionChain
调用相应的处理器适配器HandlerAdapter
。 - 经过处理器适配器
HandlerAdapter
调用具体的处理器(Controller
,也叫后端控制器)。 - Controller将结果封装到ModelAndView返回给HandlerAdapter。
- HandlerAdapter将Controller执行结果ModelAndView返回给DispatcherServlet。
- DispatcherServlet将ModelAndView传给视图解析器
ViewReslover
,查询到相应的视图View。 - ViewReslover解析后返回具体的View。
- DispatcherServlet把Model交给View进行渲染(即将模型数据填充至视图中)。
- DispatcherServlet响应用户。
看完整个流程是不是知道问题出在哪了??org.springframework.web.servlet.DispatcherServlet.noHandlerFound No mapping for GET /
这个报错是出在了流程的第3
步中,也就是DispatchServlet
将请求转发给HandlerMapping
后,HandlerMapping
根据用户的请求找不到相应处理器映射器,所以就报了这个错误。那造成这个问题的原因会不会是我们没定义相应的Controller
,导致也没有相应的处理器适配器,但是我们的Controller
确实已经写好了,而且用返回JSP类型视图的代码测试数是正常的,没任何问题。
那我们一步步排错,我们在Controller
中打印输出个字符,判断看看请求有没有进Controller
先
@RequestMapping(value = "/login")
public String login(){System.out.println("coming");return "/register"; //返回web根目录下的register.html
}
复制代码
简单粗暴,添加个System.out.println("coming");
如果请求进来了我们就可以看到打印coming
的内容,我们再重新运行项目测试看看
coming
24-Dec-2018 23:07:38.974 警告 [http-nio-8080-exec-4] org.springframework.web.servlet.DispatcherServlet.noHandlerFound No mapping for GET /register.html
复制代码
我们可以看到确实进来了,既然进来了,也就是说用户的请求至少已经执行到第5
步了,那和前面说错误出现在第3
步不是矛盾了吗??莫急,继续往下分析,既然肯定用户的请求前5
步都没问题的,那打印完coming
后为什么又出现了本该出现在第2
的错误呢?org.springframework.web.servlet.DispatcherServlet.noHandlerFound No mapping for GET /register.html
,莫不是从第5
步又跳到第2
步了?
为了继续排查下去,我们继续添加个Controller
对应register.html
,看看他还报错不
@Controller
public class UserController {@RequestMapping(value = "/login")public String login(){System.out.println("coming");return "/register"; //返回web根目录下的register.html}@RequestMapping(value = "/register.html")public String aa(){System.out.println("coming aa");return "/aa";}
}
复制代码
重新启动项目运行看看,输出如下
coming
coming aa
24-Dec-2018 23:29:00.759 警告 [http-nio-8080-exec-4] org.springframework.web.servlet.DispatcherServlet.noHandlerFound No mapping for GET /aa.html
复制代码
输出表明既进到了/login
又进到了/register.html
,然而还是继续报错org.springframework.web.servlet.DispatcherServlet.noHandlerFound No mapping for GET /aa.html
,一样的错误但是造成的错误原因不一样,上面的错误是因为找不到/register.html
相应的mapping,然后我们把/register.html
的Controller加上并且返回/aa.html
,所以导致了这次错误找不到/aa.html
相应的mapping。 看到这里是不是有点头绪了?我们在Controller
返回ModelAndView
给HandlerAdapter
,HandlerAdapter
再把ModelAndView
返回给DispatchServlet
,然后DispatchServlet
再把ModelAndView
传给视图解析器ViewReslover
解析,也就是图中对应的第6
到第8
步,到这里之前都是没问题的,问题就出现在了第9
步身上了,ViewReslover
返回的视图名给DispatchServlet
,重点来了!!!! DispatchServlet
又把这个视图名当做一个新的请求,去交给HandlerMapping
处理!!也就是图中的第2
步,然后无限死循环下去......
遇上新问题
那问题又来了,为什么DispatchServlet
会把它当成一个新的请求去处理呢?是不是我们Servlet配置的匹配规则写的不对,把返回的视图也拦截上了?我们来看看我们的Servlet配置 项目的web.xml
<servlet><servlet-name>Spring-MVC</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:*.xml</param-value></init-param><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>Spring-MVC</servlet-name><url-pattern>/</url-pattern></servlet-mapping>
复制代码
匹配规则我写的是/
,网上的很多文章也都说匹配规则/
是不会拦截.jsp、html
等格式的URL的,只会拦截/login
这样的,而/*
的匹配规则才会拦截所有请求包括/login、.jsp、html
等,实践证明网上的这些观点都是错误的! 在这里我要更正一下,匹配规则/
和/*
都是会拦截所有请求(包括/login、.jsp、.html、.css等)也就是说他们俩的作用是一样的,既然说.jsp、.html
都会拦截那为什么配置/
规则的时候.jsp的能正常而.html的却被再次拦截导致死循环和配置/*
不管是.jsp还是.html都再次被拦截从而进入死循环?
拨开云雾见月明
既然配置/
和/*
的作用一样,为啥结果不一样呢?玄机就在Tomcat
上,在Tomcat
的conf/
目录下,有个web.xml
的文件。
这个web.xml
在Tomcat启动的时候就被加载进来,对所有webapp都有效,至于Tomcat下的web.xml和我们自己项目下的web.xml的区别和联系请自行到参考文章查看。这里我们详细分析一下Tomcat下的web.xml里面有啥,打开conf/web.xml
看到他里面定义了个拦截.jsp
和.jspx
的Servlet Tomcat的conf/web.xml
<!-- The mappings for the JSP servlet -->
<servlet-mapping><servlet-name>jsp</servlet-name><url-pattern>*.jsp</url-pattern><url-pattern>*.jspx</url-pattern>
</servlet-mapping>
复制代码
这个拦截规则也就是把所有的.jsp
、.jspx
URL请求都拦截在servlet-name
为jsp的servlet中,我们搜索<servlet-name>jsp</servlet-name>
查找一下servlet使用的类
Tomcat的conf/web.xml
<servlet><servlet-name>jsp</servlet-name><servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class><init-param><param-name>fork</param-name><param-value>false</param-value></init-param><init-param><param-name>xpoweredBy</param-name><param-value>false</param-value></init-param><load-on-startup>3</load-on-startup>
</servlet>
复制代码
可以看到这个拦截.jsp
、.jspx
的servlet使用的类是org.apache.jasper.servlet.JspServlet
,也就是使用的类和我们项目下web.xml的servlet使用的org.springframework.web.servlet.DispatcherServlet
类不一样,使用的类不一样也导致.jsp
、.jspx
的URL请求都不会走上图中DispatchServlet的流程,而是走它使用的类的具体流程,想了解的可以查询这个类的相关资料。到这里我们可以明确一点的就是我们项目下web.xml的servlet规则定义成/
或者/*
的时候.jsp
正常来说应该是会被我们的项目下web.xml的servlet拦截的,但是根据servlet-mapping的匹配规则,.jsp
、.jspx
的URL都会先被Tomcat下web.xml里面的servlet拦截,而导致.jsp
、.jspx
不会被我们项目的web.xml里面的servlet拦截。**这也就是为什么匹配规则写成/
的时候.jsp
会被忽略不进行拦截的原因,那问题又来了匹配规则写成/*
的时候.jsp
没有被忽略仍然进行拦截的呢?原因是规则/*
会覆盖所有默认的servlet,从而将所有请求都拦截了下来,接下来我们可以修改Tomcat的web.xml下的servlet配置,让项目的web.xml下的servlet配置规则为/
时候也支持返回html
类型的视图 Tomcat的conf/web.xml
<!-- The mappings for the JSP servlet -->
<servlet-mapping><servlet-name>jsp</servlet-name><url-pattern>*.jsp</url-pattern><url-pattern>*.jspx</url-pattern><url-pattern>*.html</url-pattern>
</servlet-mapping>
复制代码
添加个规则<url-pattern>*.html</url-pattern>
,即URL是.html类型的话就走Tomcat的<servlet-name>jsp</servlet-name>
这个servlet而不走我们项目的servlet,然后重新运行项目测试一下
(请忽略乱码问题o(╥﹏╥)o) 看到了吧,确实可以通过Controller
返回html类型的视图了吧,也就是在执行完第9
步后返回的视图不再是被org.springframework.web.servlet.DispatcherServlet
拦截,而是被org.apache.jasper.servlet.JspServlet
拦截,从而跳出了DispatchServlet
的魔抓不会再死循环了。 那以后想要返回.html类型的视图是不是都要去修改Tomcat的web.xml?也不用这样,我们只要在我们的项目web.xml中配置一个相同名的servlet即可,它会自动覆盖Tomcat的web.xml的,如
<servlet-mapping><servlet-name>jsp</servlet-name><url-pattern>.html</url-pattern>
</servlet-mapping>
复制代码
这样配,但是会报错Cannot resolve Servlet 'jsp'
,我也不懂为啥,知道的朋友可以补充下。Tomcat下的web.xml除了有名为jsp
的servlet,还有一个大家应该都很熟悉的,就是名为default
的servlet,它的作用和jsp
的大概一样,大家知道它是拿来配置静态资源的,却很少了解它怎么来的 Tomcat的conf/web.xml
<servlet><servlet-name>default</servlet-name><servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class><init-param><param-name>debug</param-name><param-value>0</param-value></init-param><init-param><param-name>listings</param-name><param-value>false</param-value></init-param><load-on-startup>1</load-on-startup>
</servlet><!-- The mapping for the default servlet -->
<servlet-mapping><servlet-name>default</servlet-name><url-pattern>/</url-pattern>
</servlet-mapping>
复制代码
我们也可以用它来代替jsp
的servlet来达到不拦截.html类型的视图,同样我们在项目的web.xml下配置名为default
的servlet
<servlet-mapping><servlet-name>default</servlet-name><url-pattern>*.html</url-pattern>
</servlet-mapping>
复制代码
这样即可让.html的请求都不会被前端控制器DispatchServlet拦截到。
总结
当然要返回.html类型的视图也不是只有这种方法,也可以通过更换视图解析器,但是我觉得这样没必要,因为InternalResourceViewResolver
视图解析器本身就是支持.html的,只是我们没处理正确而已。通过这次出现的问题,引发我了对Spring MVC的进一步了解,也把分析、解决问题的过程记录下,希望能让自己印象更深刻点,也希望能帮助到大家。
参考文章
servlet-mapping url-pattern / 和 /*区别
Tomcat文件夹下的context.xml和web.xml
web.xml中出现default是什么意思?
转自:ddnd.cn/2018/12/24/…
转载于:https://juejin.im/post/5c2220cdf265da610e801e27
我是如何一步步解决问题 让Spring MVC返回HTML类型的视图相关推荐
- java去除json 转移,Spring MVC返回的json去除根节点名称的方法
这篇文章主要介绍了Spring MVC返回的json去除根节点名称的方法,非常不错,具有参考借鉴价值,需要的朋友可以参考下 spring xml中配置视图如果是如下 那么返回结果会是: {" ...
- Spring MVC 返回NULL时客户端用$.getJSON的问题
如果Spring MVC返回是NULL,那么客户端的$.getJSON就不会触发: ===============20170419补充======================= 后台的输出为: D ...
- 一步步完成jsRender + Spring MVC + Nginx前后端分离示例
2019独角兽企业重金招聘Python工程师标准>>> 本篇博文的目标是使用前端页面渲染插件jsRender做前后端分离,后端采用Spring MVC给出REST API,并结合Ng ...
- spring MVC 返回json
spring MVC如何返回json呢? 有两种方式: 方式一:使用ModelAndView Java代码 @ResponseBody @RequestMapping("/save&qu ...
- Java Web(11) Spring MVC 返回Json
2019独角兽企业重金招聘Python工程师标准>>> 1. 首先是对Spring mvc 进行xml配置 <?xml version="1.0" enco ...
- Spring MVC 解决日期类型动态绑定问题
出处:http://www.cnblogs.com/crazy-fox/archive/2012/02/18/2357699.html ean 名为User,则在相同的包中存在UserEditor类可 ...
- Spring Mvc返回html页面404错误解决记录--转载
原文地址:http://53873039oycg.iteye.com/blog/2061992 以前使用Spring Mvc时候都是返回jsp页面或者ftl页面,昨天想返回html页面,spring- ...
- spring mvc DispatcherServlet详解之四---视图渲染过程
整个spring mvc的架构如下图所示: 现在来讲解DispatcherServletDispatcherServlet的最后一步:视图渲染.视图渲染的过程是在获取到ModelAndView后的过程 ...
- spring mvc返回页面显示空白_Spring 框架基础(06):Mvc架构模式简介,执行流程详解...
一.SpringMvc框架简介 1.Mvc设计理念 MVC是一种软件设计典范,用一种业务逻辑.数据.界面显示分离的方法组织代码,将业务逻辑聚集到一个组件里面,在改进和个性化定制界面及用户交互的同时,不 ...
- Spring学习手册 1:Spring MVC 返回JSON数据
目录 完整代码在这 Spring MVC对JSON数据格式的支持非常好,配置完成后什么都不用管靠注解就可以轻松返回JSON格式的数据. Spring 对JSON的支持有三种方式,下面会一一介绍,在此之 ...
最新文章
- R 语言绘制环状热图
- Quartz.Net—MisFire
- 删除DataTable中列重复的行
- python断点调试从哪里看数据_Python Pdb 断点调试 - 简明教程
- zabbix—自动发现端口并监控
- php yield mysql_PHP 5.5 新特性关键字 yield
- C++编程调试秘笈(第1次阅读)
- RFID打印机有什么用
- 计算机组成原理05章在线测试,《计算机组成原理》第05章在线测试.docx
- JAVA获得当前时间的几种方法
- CUDA memory
- MATLAB车牌识别GUI设计实现
- 比特大陆发布终端 AI 芯片 端云联手聚焦安防
- 现场总线CAN和工业以太网EtherCAT详解
- 《智慧工地单点解析系列(一)—— 劳务实名制》
- Proxmox集群网络配置
- “Stream has already been operated upon or closed” Exception in Java
- windows7到底是多用户多任务操作系统还是单用户多任务操作系统
- ceisum添加风场插件
- linux 内核printk 打印信息查询方法