上一篇:03-SpringMVC视图解析器的使用https://blog.csdn.net/fsjwin/article/details/109562410
1.@RequestMapping请求
1.1 指定模块名字user
在实际开发中一个controller就是一个模块,为了区分模块,我们会使用模块名称区分
- 控制器UserController.java
package com.yuhl.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
/**
* @author yuhl
* @Date 2020/11/8 16:06
* @Classname MyController
* @Description TODO
*/
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping(value = "/login.do",method = RequestMethod.GET)
public ModelAndView first(){
ModelAndView mv = new ModelAndView();
mv.addObject("msg", "我回来了");
mv.addObject("msg2", "又回来哦了2");
//底层就是一个请求转发,记住低就是servlet哦
mv.setViewName("first");
return mv;
}
@RequestMapping(value = "/login2.do",method = RequestMethod.POST)
public ModelAndView other(){
ModelAndView mv = new ModelAndView();
mv.addObject("msg", "我回来了");
mv.addObject("msg2", "又回来哦了2");
//底层就是一个请求转发,记住低就是servlet哦
mv.setViewName("other");
return mv;
}
}
2. index.jsp页面发起请求
get方式<br/>
<a href="user/login.do">第other次点击</a><br/>
post方式
<form action="user/login2.do" method="post">
<input type="submit" value="登录">
</form>
- 获得结果正常
1.2 定义请求提交方式
如果访问的请求方式和controller中定义的不一致则无法正常访问
- 错误的访问index.jsp
get方式<br/>
<a href="user/login2.do">第other次点击</a><br/>
post方式
<form action="user/login.do" method="post">
<input type="submit" value="登录">
</form>
- 错误提示
HTTP Status 405 – Method Not Allowed
Type Status Report
Message Request method 'GET' not supported
Description The method received in the request-line is known by the origin server but not supported by the target resource.
Apache Tomcat/8.5.40
2.处理器controller方法的请求参数
2.1 HttpServletRequest 获得参数收
- 后端后的参数
@RequestMapping(value = "/login.do",method = RequestMethod.GET)
public ModelAndView first(HttpServletRequest request, HttpServletResponse response, HttpSession session){
ModelAndView mv = new ModelAndView();
mv.addObject("msg", "我回来了 name = " +request.getParameter("name"));
mv.addObject("msg2", "又回来哦了2");
//底层就是一个请求转发,记住低就是servlet哦
mv.setViewName("first");
return mv;
}
- 返回获得的参数结果
http://localhost:8080/springmvc/user/login.do?name=于红亮
2.2 逐个参数接
使用request.getParament太麻烦了。有没有更简单的方式呢?肯定有的,spring对帮我们写好了。
- 在方法中直接写传过来的name值就可以获得。
package com.yuhl.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
/**
* @author yuhl
* @Date 2020/11/8 16:06
* @Classname MyController
* @Description TODO
*/
@Controller
public class MyController1 {
@RequestMapping(value = "/para.do")
public ModelAndView para(String name, Integer age){
ModelAndView mv = new ModelAndView();
mv.addObject("msg", "我回来了"+"name="+name+";age="+age);
mv.addObject("msg2", "又回来哦了2");
//底层就是一个请求转发,记住低就是servlet哦
mv.setViewName("first");
return mv;
}
}
- 前端页面index.jsp
***********************************************************<br/>
逐个参数接<br/>
<form action="para.do" method="get">
姓名: <input type="text" name="name"/><br/>年龄:<input type="text" name="age"/>
<input type="submit" value="登录">
</form>
- get方式正常接收
4. post方式中文乱码
请求:
乱码:
如何结果这个中文的乱码呢?往下看:
2.3 请求参数中文乱码问题
get无乱码,post有乱码,使用过滤器处理乱码问题,框架中有个现成的过滤器,当然你也可以自定义啦!
- web.xml配置过滤器
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<filter>
<!--过滤器,解决post中文乱码问题的filter-name没有要求 ,spring真贴心,都准备好了-->
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<!--强制请求编码-->
<init-param>
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
<!--强制相应编码-->
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
- 页面请求post
***********************************************************<br/>
逐个参数接<br/>
<form action="para.do" method="post">
姓名: <input type="text" name="name"/><br/>年龄:<input type="text" name="age"/>
<input type="submit" value="登录">
</form>
- 结果
- 为什配置了这个过滤器就可以,源码
public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException {
if (request instanceof HttpServletRequest && response instanceof HttpServletResponse) {
HttpServletRequest httpRequest = (HttpServletRequest)request;
HttpServletResponse httpResponse = (HttpServletResponse)response;
String alreadyFilteredAttributeName = this.getAlreadyFilteredAttributeName();
boolean hasAlreadyFilteredAttribute = request.getAttribute(alreadyFilteredAttributeName) != null;
if (!this.skipDispatch(httpRequest) && !this.shouldNotFilter(httpRequest)) {
if (hasAlreadyFilteredAttribute) {
if (DispatcherType.ERROR.equals(request.getDispatcherType())) {
this.doFilterNestedErrorDispatch(httpRequest, httpResponse, filterChain);
return;
}
filterChain.doFilter(request, response);
} else {
request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);
try {
this.doFilterInternal(httpRequest, httpResponse, filterChain);
} finally {
request.removeAttribute(alreadyFilteredAttributeName);
}
}
} else {
filterChain.doFilter(request, response);
}
} else {
throw new ServletException("OncePerRequestFilter just supports HTTP requests");
}
}
这里源码看的很清楚,会读取web.xml中的配置参数,这里设置请求和相应的字符编码:
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String encoding = this.getEncoding();
if (encoding != null) {
if (this.isForceRequestEncoding() || request.getCharacterEncoding() == null) {
request.setCharacterEncoding(encoding);
}
if (this.isForceResponseEncoding()) {
response.setCharacterEncoding(encoding);
}
}
filterChain.doFilter(request, response);
}
2.4 校正请求参数名@RequestParar
参数名和uri连接中的名字不一样怎么办?
可以使用@RequestParar做修正。
- 页面传参index.jsp
***********************************************************<br/>
逐个参数接<br/>
<form action="para.do" method="post">
姓名: <input type="text" name="rname"/><br/>
年龄:<input type="text" name="rage"/>
<input type="submit" value="登录">
</form>
2. controller层的代码:
@Controller
public class MyController1 {
@RequestMapping(value = "/para.do")
public ModelAndView para(@RequestParam(value = "rname",required = false) String name, @RequestParam(value = "rage",required = false
) Integer age){
ModelAndView mv = new ModelAndView();
mv.addObject("msg", "我回来了"+"name="+name+";age="+age);
mv.addObject("msg2", "又回来哦了2");
//底层就是一个请求转发,记住低就是servlet哦
mv.setViewName("first");
return mv;
}
}
- 特别注意required = false
4. 页面测试
- 结果正常
2.5 接收对象参数
如果参数特别多,你的形参就惨了。使用对象完美解决你的问题。哈哈!
1.实体类封装需要传递的参数
package com.yuhl.domain;
import com.sun.xml.internal.ws.api.policy.SourceModel;
/**
* @author yuhl
* @Date 2020/11/9 11:25
* @Classname User
* @Description User实体类
*/
public class User {
private String name;
private Integer age;
public User() {
System.out.println("User.User");
}
public User(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
System.out.println("User.setName");
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
System.out.println("User.setAge");
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
- controller处理请求
@RequestMapping(value = "/para.do")
public ModelAndView para(User user){
ModelAndView mv = new ModelAndView();
mv.addObject("msg", "我回来了"+"name="+user.getName()+";age="+user.getAge());
mv.addObject("msg2", "又回来哦了2");
mv.addObject("user", user);
//底层就是一个请求转发,记住低就是servlet哦
mv.setViewName("first");
return mv;
}
- 页面测试
-
测试结果
-
后台日志打印说明问题
User.User
User.setAge
User.setName
3.处理器controller方法的返回值
3.1 返回ModelAndView
之前我们的例子都是返回ModelAndView
3.2 返回String
如果返回的是String,也即返回的就是视图的名字,这里有两个问题
1. 返回的是视图的名字那么我们怎么样把数据带回到页面呢?可以使用request.setAttribute("age","11")返回,
2. 另外一个问题就是使用了视图解析器,一定不能再写/WEB-INF/view/first.jsp的结果要不然结果就是/WEB-INF/view/WEB-INF/view/first.jsp.jsp
- controller代码
package com.yuhl.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
/**
* @author yuhl
* @Date 2020/11/8 16:06
* @Classname MyController
* @Description TODO
*/
@Controller
public class MyControllerReturn {
@RequestMapping(value = "/return.do")
public String para(HttpServletRequest request, String name, Integer age){
request.setAttribute("name",name);
request.setAttribute("age",age);
return "first";
}
}
2. 如果想在此处写上全资源名字,则需要去掉视图解析器,鱼和熊掌不可兼得
3.3 返回void
返回void怎么传参数呢?
writer = response.getWriter();
- 引入jackson依赖
<!--引入jackson的两个引用-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.11.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.2</version>
</dependency>
- 前端页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<script type="text/javascript" src="js/jquery-3.4.1.min.js"></script>
<script type="text/javascript">
$(function () {
$("button").click(function () {
alert("button click");
$.ajax({
url: "returnvoid_ajax.do",
data:{
name: "zhangsan",
age: 11
},
type:"post",
dataType:"json",
success:function (resp) {
alert(resp.name + " -- "+resp.age);
}
});
});
});
</script>
<script type="text/javascript">
function showValue() {
alert(1);
$.ajax({
url: "returnvoid_ajax.do",
data:{
name: "zhangsan",
age: 11
},
type:"post",
dataType:"json",
success:function (resp) {
alert(resp.name + " -- "+resp.age);
}
})
}
</script>
</head>
<body>
<a href="first.do">第一次点击</a> <br/>
<a href="other.do">第other次点击</a><br/>
get方式<br/>
<a href="user/login.do">第other次点击</a><br/>
post方式
<form action="user/login2.do" method="post">
<input type="submit" value="登录">
</form>
get方式,演示错误405的情况<br/>
<a href="user/login2.do">第other次点击</a><br/>
post方式,演示错误405的情况
<form action="user/login.do" method="post">
<input type="submit" value="登录">
</form>
***********************************************************<br/>
逐个参数接<br/>
<form action="para.do" method="post">
姓名: <input type="text" name="name"/><br/>
年龄:<input type="text" name="age"/>
<input type="submit" value="登录">
</form>
******************************return.do*****************************<br/>
<form action="return.do" method="post">
姓名: <input type="text" name="name"/><br/>
年龄:<input type="text" name="age"/>
<input type="submit" value="登录">
</form>
******************************return.do*****************************<br/>
<button id="btn" onClick="showValue()">ajax请求</button>
<%-- <input type="button" value="OK" onClick="showValue()" />--%>
<div id="myDiv"><h2>Let AJAX change this text</h2></div><br/>
<button id="b01" type="button">ajax请求</button>
</body>
</html>
- controller
@RequestMapping(value = "/returnvoid_ajax.do")
public void returnvoid_ajax(HttpServletResponse response, String name, Integer age){
//模拟已经调用了service
User user = new User(name,age);
String json = "";//用户返回的json字符串
if (user != null) {
ObjectMapper om = new ObjectMapper();
try {
json = om.writeValueAsString(user);
System.out.println(json);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
PrintWriter writer = null;
try {
//告诉浏览器使用json解析哦!
response.setContentType("application/json;charset=utf-8");
writer = response.getWriter();
} catch (IOException e) {
e.printStackTrace();
}
writer.println(json);
writer.flush();
writer.close();
}
}
- 测试
3.4 返回Object
在ajax请求中使用的list、map、User等,相应ajax请求。
先给出结论,怎么实现json的返回呢?
1. 添加jackson的依赖
spring默认使用jackson
2. 添加注解驱动注解<mvc:annotation-driver/>
功能类似于:
json = om.writeValueAsString(user);
3. 添加注解@ResponseMapping
功能类似于:
response.setContentType("application/json;charset=utf-8");
writer = response.getWriter();
writer.println(json);
- 添加jackson注解
前面已经添加过了
<!--引入jackson的两个引用-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.11.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.2</version>
</dependency>
-
添加mvc:annotation-driver/注解
为什么要添加呢?我们看一下源码。想要把对象对转化为其他的xml、二进制、json等格式水平mvc好心的帮我们提前写好了转换器,我们的任务是加载并使用即可,顶级接口类:HttpMessageConverter 他下面有很多类型实现类分别处理各种类型的转换:
而我们最关注的是有没有把jackson的这个转换器加载进来。但没有添加次标签的时候我们跟一下源码看一下。
从图片可以看出添加次行注解的重要性,没有添加注解前就没有jasckson的convert,添加之后就有了。所以别忘记加哦!
- 添加注解@ResponseMapping
添加这个注解的主要作用就是使用MappingJackson2HttpMessageConverter的转换功能。
由来上面的这三步就不需要写的那么麻烦了,看下面的例子:
结果完全正确:
4. 原理解释
1. 当返回User对象的时候会先调用HttpMessageConverter.canRead()方法看那个convert能够处理,当遍历到MappingJackson2HttpMessageConverter的时候返回为turr.
2. 然后又HttpMessageConverter.read()方法处理去做实际的转化
3. 之后调用的 @ResponseBody把结果写回到页面。
- 返回List对象(Object中的一种)
controlloer:
/**
* 返回的是List对象
* @param response
* @param name
* @param age
* @return
*/
@RequestMapping(value = "/returnvoid_ajax.do")
@ResponseBody
public List<User> returnvoid_ajax(HttpServletResponse response, String name, Integer age){
List<User> userList = new ArrayList<User>();
userList.add(new User(name,age));
userList.add(new User(name,age));
//模拟已经调用了service
return userList;
}
5. 返回String(Object中的一种) 表示的是String数据,不是视图哦!区分就看有没有@ResponseBody:有的话就是数据data,没有就是视图。
底层使用的是String2HttpMessageConverter
/**
* 返回的是List对象
* @param response
* @param name
* @param age
* @return
*/
@RequestMapping(value = "/returnvoid_ajax.do")
@ResponseBody
public String returnvoid_ajax(HttpServletResponse response, String name, Integer age){
List<User> userList = new ArrayList<User>();
userList.add(new User(name,age));
userList.add(new User(name,age));
//模拟已经调用了 service
return "hello springmvc+"+name+""+age;
}
4.解读url-pattern
- 问题提出
returnvoid_ajax.do有我自的controller进行处理,其实就是有DispatchServlet转过来的,但是上面的js资源(image等静态资源一个道理),是有谁处理的呢?
看tomcat/conf/web.xml
也就是有tomcat来中的org.apache.catalina.servlets.DefaultServlet来处理的。
所以我们自己的web.xml就把你能写
如果写了的话tomcat的<url-pattern>/</url-pattern>
就会失效,导致静态资源你就拿不到了。 - 如果你一定要写的话也是可以解决的。
就是要把请求重新转给tomcat的DefaultServlet就可以了。
有springmvc的:DefaultServletHttpRequestHandler类处理
public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Assert.state(this.servletContext != null, "No ServletContext set");
RequestDispatcher rd = this.servletContext.getNamedDispatcher(this.defaultServletName);
if (rd == null) {
throw new IllegalStateException("A RequestDispatcher could not be located for the default servlet '" + this.defaultServletName + "'");
} else {
rd.forward(request, response);
}
}
家里这个注解 <mvc:default-servlet-handler/>
目的就是重新把控制权还回tomcat的DefaultServlet。哈哈哈!
5.相对路径&绝对路径
- 相对地址和绝对地址
相对路径
<script type="text/javascript" src="static/js/jquery-3.5.1.min.js"></script>
绝对路径待协议的
<script type="text/javascript" src="http://localhost:8080/springmvc/static/js/jquery-3.5.1.min.js"></script>
绝对路径待协议的,通上面一个
<script type="text/javascript" src="${pageContext.request.contextPath}/static/js/jquery-3.5.1.min.js"></script>
//相对路径
url: "returnvoid_ajax.do", //页面的地址栏:http://localhost:8080/springmvc/returnvoid_ajax.do
//绝对路径
//url: "/returnvoid_ajax.do", //页面的地址栏:http://localhost:8080/returnvoid_ajax.do
- 相对路径循环访问的问题
页面index.jsp
<a href="student/haha.do">相对路径循环调用问题</a><br/>
controller:MyControllerReturn.java
@RequestMapping(value = "/student/haha.do")
//@ResponseBody
public ModelAndView returnvoid_ajax(){
ModelAndView mv = new ModelAndView();
mv.setViewName("/index.jsp");
return mv;
}
点击两次【相对路径循环调用问题】连接如图:
怎么解决这个问题:
1. 加入${pageContext.request.contextPath}
<a href="${pageContext.request.contextPath}/student/haha.do">相对路径循环调用问题</a><br/>
2. 加base标签 <%--base是html的标签,会在本页面相对路径前加上这个base--%>
<base href="${pageContext.request.contextPath}/" />和<a href="student/haha.do">相对路径循环调用问题</a><br/>
当点击一次后看连接:
此时在次点击是我们使用绝对路劲就很安全,要不然用相对路径的话就会在student/后面再加资源路径。就会错误。
base的简写形式
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
String basePath = request.getScheme() + "://" +
request.getServerName() + ":" + request.getServerPort() +
request.getContextPath() + "/";
%>
<html>
<head>
<title>Title</title>
<%--base是html的标签,会在本页面相对路径前加上这个base--%>
<%--<base href="${pageContext.request.contextPath}/" />--%>
<base href="<%=basePath%>" />
6.总结
- DispatchServlet调用简图
2. 总结
下一篇:05-SSM整合https://blog.csdn.net/fsjwin/article/details/109611485