请求中所携带的请求参数
逐个接收
- 要求处理器方法的形参名和请求中参数名必须一致,同名的请求参数赋值给同名的形参
- 框架接收请求参数依旧是使用request对象,框架先通过request.getParameter()方法获取参数,然后在调用处理器方法时,将同名的请求参数赋值给同名的形参
- 在将同名的请求参数赋值给同名的形参的过程中,对于可以进行类型转换的参数,框架会将字符串类型转换相应的参数数据类型,如果不能进行类型转换,会报相应的错误
- 对于该种方法接收参数,get请求中文不会乱码,post请求中文会出现乱码
@Controller @RequestMapping({"/study"}) public class MyController { @RequestMapping(value = {"/test.do"}) public ModelAndView doSome(String name, int age) { ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("name", name); modelAndView.addObject("age", age); modelAndView.setViewName("show"); return modelAndView; } }
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <h1>show.jsp</h1> <h2>${name}</h2> <h2>${age}</h2> </body> </html>
过滤器解决乱码问题
- 过滤器可以自定义,也可以使用框架提供的过滤器 CharacterEncodingFilter
- 在web.xml文件中,注册声明过滤器,解决post请求乱码问题
<!-- 注册声明过滤器,解决post请求乱码问题 --> <filter> <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> <!-- 强制请求对象(HttpServletRequest)使用encoding设置的字符编码 --> <init-param> <param-name>forceRequestEncoding</param-name> <param-value>true</param-value> </init-param> <!-- 强制响应对象(HttpServletResponse)使用encoding设置的字符编码 --> <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>
RequestParam 注解
- 当请求中的参数名和处理器方法中的形参名不一致时,可以使用 RequestParam 注解
- RequestParam 注解的属性:
- value - 请求中的参数名
- required - 值为true,则请求参数不能为空;值为false,请求参数可以为空
- RequestParam 注解的使用位置:处理器方法的形参前面
@Controller @RequestMapping({"/study"}) public class MyController { @RequestMapping(value = {"/test.do"}) public ModelAndView doSome(@RequestParam("username") String name, @RequestParam("userage") int age) { ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("name", name); modelAndView.addObject("age", age); modelAndView.setViewName("show"); return modelAndView; } }
@Controller @RequestMapping({"/study"}) public class MyController { @RequestMapping(value = {"/test.do"}) public ModelAndView doSome(@RequestParam("username") String name, @RequestParam(value = "userage", required = false) Integer age) { ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("name", name); modelAndView.addObject("age", age); modelAndView.setViewName("show"); return modelAndView; } }
对象接收
- 处理器方法的形参可以使用一个对象参数,一次性接收所有的请求参数,但是需要保证请求参数名和对象的属性同名
- 框架会创建形参对应的对象,然后给该对象的属性赋值,如果请求中的参数名为name,框架会调用对象的setName()方法(set注入)
- 在该方式中不能使用 RequestParam 注解
public class User { private String name; private Integer age; public User() { } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } }
@Controller @RequestMapping({"/study"}) public class MyController { @RequestMapping(value = {"/test.do"}) public ModelAndView doSome(User user) { ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("name", user.getName()); modelAndView.addObject("age", user.getAge()); modelAndView.setViewName("show"); return modelAndView; } }
处理器方法的返回值
ModelAndView
- ModelAndView 包含数据部分和视图部分,其中 Model 是数据,最终数据会放到request作用域中,View 是视图,对于视图框架采用的是 forward 转发
- 若处理器方法处理完后,需要跳转到其它资源,且又要在跳转的资源间传递数据,此时处理器方法返回 ModelAndView 比较好。
- 若要返回 ModelAndView,则处理器方法中需要定义 ModelAndView 对象。
@Controller @RequestMapping({"/study"}) public class MyController { @RequestMapping(value = {"/test.do"}) public ModelAndView doSome(User user) { ModelAndView modelAndView = new ModelAndView(); // 向 ModelAndView 对象上放数据 modelAndView.addObject("name", user.getName()); modelAndView.addObject("age", user.getAge()); // 设置视图 modelAndView.setViewName("show"); return modelAndView; } }
String(视图)
- 如果处理器方法返回的是字符串,则这个字符串表示的是视图,这个字符串可以是视图的逻辑名称,也可以是视图的完整路径,通过配置视图解析器解析可以将其转换为物理视图地址
- 若要跳转的资源为内部资源,则视图解析器可以使用 InternalResourceViewResolver 内部资源视图解析器,此时处理器方法返回的字符串就是要跳转页面的文件名去掉文件扩展名后的部分,这个字符串与视图解析器中的 prefix、suffix 相结合,即可形成要访问的 URI
- 如果返回的字符串为视图的完整路径,则项目中不能配置视图解析器,如果配置了视图解析,则返回的视图完整路径会被作为视图的逻辑名称与视图解析器中的 prefix、suffix 相结合,形成要访问的 URI
- 对于视图,框架执行 forward 转发操作
- 在springmvc.xml文件中配置视图解析器
<!-- 声明springmvc框架中的视图解析器,帮助设置视图文件的路径 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- 前缀:视图文件所在的目录 --> <!-- InternalResourceViewResolver 的 prefix 属性为String类型 --> <!-- 最前面的 / 表示本web资源项目的根路径,即webapp --> <!-- 最后的 / 表示路径 --> <property name="prefix" value="/WEB-INF/views/"/> <!-- 后缀:视图文件的扩展名 --> <!-- InternalResourceViewResolver 的 suffix 属性为String类型 --> <property name="suffix" value=".jsp"/> </bean>
@Controller public class MyController { @RequestMapping(value = {"/test.do"}) public String doSome(HttpServletRequest request, String name, Integer age) { System.out.println("name: " + name + " " + "age: " + age); // 手动将数据添加到request作用域中 request.setAttribute("name", name); request.setAttribute("age", age); return "show"; } }
void
- 处理器方法返回 void 的应用场景,AJAX 响应
- 处理器对请求处理后,无需跳转到其它任何资源,此时可以让处理器方法返回 void
- 通过response对象向浏览器响应数据
- 处理JSON格式数据的依赖 jackson
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.9.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.0</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.js"></script> <script> $(function () { $("#btn").click(function () { $.ajax({ url: "test.do", data: { name: "李四", age: 22 }, type: "post", dataType: "json", success: function (res) { console.log(res) } }) }) }) </script> </head> <body> <h1>index</h1> <button id="btn">发起AJAX请求</button> </body> </html>
@Controller public class MyController { @RequestMapping(value = {"/test.do"}) public void doSome(HttpServletResponse response, String name, Integer age) { System.out.println("name: " + name + " " + "age: " + age); User user = new User(); user.setName(name); user.setAge(age); // 把结果对象转化为JSON格式,使用jackson ObjectMapper objectMapper = new ObjectMapper(); String res = null; try { res = objectMapper.writeValueAsString(user); } catch (JsonProcessingException e) { e.printStackTrace(); } // 设置响应的文本类型和字符集 response.setContentType("application/json;charset=utf-8"); PrintWriter out = null; try { // 获取输出对象 out = response.getWriter(); } catch (IOException e) { e.printStackTrace(); } out.println(res); out.flush(); out.close(); } }
Object
- 处理器方法也可以返回 Object 对象,这个 Object 可以是 Integer,String,自定义对象,Map,List 等。
- 返回的对象是数据。
- 返回对象,可以响应AJAX请求
- 使用步骤:
- 加入处理JSON的工具库依赖,springmvc默认使用Jackson
- 在springmvc配置文件中,加入
<mvc:annotation-driven>
注解驱动
<mvc:annotation-driven>
用于实现对象向JSON格式字符串、xml、text、二进制等数据格式的转换- java对象转换为JSON格式字符串、xml、text、二进制等数据格式的方法由HttpMessageConverter(消息转换器)接口定义,该接口的实现类实现了java对象向JSON格式字符串、xml、text、二进制等数据格式的转换
public interface HttpMessageConverter<T> { boolean canRead(Class<?> var1, @Nullable MediaType var2); // 检查处理器方法的返回值是否可以转化为var2表示的数据格式 boolean canWrite(Class<?> var1, @Nullable MediaType var2); List<MediaType> getSupportedMediaTypes(); T read(Class<? extends T> var1, HttpInputMessage var2) throws IOException, HttpMessageNotReadableException; // 将处理器方法的返回值对象,调用jackson中的ObjectMapper对象转化为JSON字符串 void write(T var1, @Nullable MediaType var2, HttpOutputMessage var3) throws IOException, HttpMessageNotWritableException; }
- 实现类 - MappingJackson2HttpMessageConverter - 负责读取和写入 json 格式的数据。 - 利用Jackson 的 ObjectMapper 将java对象转换为JSON - StringHttpMessageConverter - 负责读取字符串格式的数据和写出字符串格式的数据 - `<mvc:annotation-driven>` 该标签加入springmvc配置文件后,会自动创建HttpMessageConverter接口的七个实现类对象,包含MappingJackson2HttpMessageConverter - ![image.png](https://cdn.nlark.com/yuque/0/2023/png/28006470/1684321584048-f97e5d63-2e0a-4b8b-b98e-05a4987db989.png#averageHue=%23f1edeb&clientId=ufce7901c-eb44-4&from=paste&height=540&id=ud85d03f9&originHeight=675&originWidth=1028&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=239157&status=done&style=none&taskId=uaee66f25-5caf-4598-9d95-9569700bff7&title=&width=822.4)
- 在处理方法上使用 @ResponseBody 注解,将转换后的 JSON 数据放入到响应体中。
- 通过response对象输出数据,响应AJAX请求
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.9.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.0</version> </dependency>
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd 、 http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!-- 注解驱动 --> <mvc:annotation-driven/> </beans>
@Controller public class MyController { @RequestMapping(value = {"/test.do"}) @ResponseBody public User doSome(String name, Integer age) { System.out.println("name: " + name + " " + "age: " + age); User user = new User(); user.setName(name); user.setAge(age); // 框架会将该对象转化为JSON格式,并将转换后的 JSON 数据放入到响应体中 return user; } }
- 返回对象框架的处理流程:
- 框架会把返回User类型,调用框架的中ArrayList中每个类的canWrite()方法检查那个HttpMessageConverter接口的实现类能处理User类型的数据–MappingJackson2HttpMessageConverter
- 框架会调用实现类的write(), MappingJackson2HttpMessageConverter的write()方法把User对象转为json, 调用Jackson的ObjectMapper实现转为json
- contentType: application/json;charset=utf-8
- 框架会调用@ResponseBody把转化后的结果数据输出到浏览器, ajax请求处理完成
List
- 向浏览器客户端返回List集合类型的数据,会被转化为JSON类型的数组
@Controller public class MyController { @RequestMapping(value = {"/list"}) @ResponseBody public List<User> getList() { List<User> users = new ArrayList<>(); users.add(new User("张三", 23)); users.add(new User("李四", 33)); return users; } }
- 返回List集合框架的处理流程:
- 框架会把返回的List集合,调用框架的中ArrayList中每个类的canWrite()方法检查那个HttpMessageConverter接口的实现类能处理User类型的数据–MappingJackson2HttpMessageConverter
- 框架会调用实现类的write(), MappingJackson2HttpMessageConverter的write()方法把List集合转为json形式的数组, 调用Jackson的ObjectMapper实现转为json形式的数组
- contentType: application/json;charset=utf-8
- 框架会调用@ResponseBody把转化后的结果数据输出到浏览器, ajax请求处理完成
String(数据)
- 处理器方法的返回值是String,如果处理器方法有使用ResponseBody注解标记,则返回的String为数据,否知返回的String为视图
@RequestMapping(value = {"/string"}) @ResponseBody public String returnString() { return "向浏览器响应 String 类型的数据"; }
处理响应文本类型和编码问题
- 在RequestMapping注解中添加produces属性,来设置响应文本的类型和编码
// 响应文本类型为普通文本,文本的编码为utf-8 @RequestMapping(value = {"/string"}, produces = {"text/plain;charset=utf-8"}) @ResponseBody public String returnString() { return "向浏览器响应 String 类型的数据"; }
处理流程分析
- 返回String类型数据框架的处理流程:
- 框架会把返回的String类型数据,调用框架的中ArrayList中每个类的canWrite()方法检查那个HttpMessageConverter接口的实现类能处理User类型的数据–StringHttpMessageConverter
- 框架会调用实现类的write(), StringHttpMessageConverter的write()方法把String类型数据按照指定的编码和文本类型处理
- text/html;charset=ISO-8859-1
- 框架会调用@ResponseBody把转化后的结果数据输出到浏览器, ajax请求处理完成
public class StringHttpMessageConverter extends AbstractHttpMessageConverter<String> { public static final Charset DEFAULT_CHARSET; // 默认字符集 ISO-8859-1 public StringHttpMessageConverter(Charset defaultCharset) { // MediaType.TEXT_PLAIN "text/plain" super(defaultCharset, new MediaType[]{MediaType.TEXT_PLAIN, MediaType.ALL}); this.writeAcceptCharset = false; } ... }
中央调度器 url-pattern 为 / 的问题
Tomcat 的 DefaultServlet
- Tomcat 本身可以处理静态资源的访问,如HTML、图片、JS文件等
- 在Tomcat的配置文件web.xml中可以看到
- Tomcat的web.xml文件中配置了一个默认的servlet,在Tomcat服务器启动的时候会自动创建该servlet对象
- 所有web应用程序的默认servlet,用于处理静态资源,以及没有其他servlet处理的请求
- DefaultServlet 对应的请求地址为
/
中央调度器 url-pattern 为 / 会出现的问题
- 当我们的项目中存在servlet对应的映射请求地址为
/
,则该servlet会去替代Tomcat中的DefaultServlet,因为Tomcat收到请求后,发现/
由servlet会进行处理,就会把该请求交给该servlet进行处理,就不会调用DefaultServlet处理请求 - 所以将自己的servlet对应的请求地址设置为
/
,会导致静态资源无法进行访问,因为在默认情况下DefaultServlet没有处理静态资源的能力,没有控制器可以处理静态资源的访问 - 将自己的servlet对应的请求地址设置为
/
,动态资源是能够进行访问的,因为由相应的控制器能够处理请求
处理中央调度器 url-pattern 为 / 的问题(静态资源的访问)
方式一:使用<mvc:default-servlet-handler/>
- 在springmvc配置文件中声明了
<mvc:default-servlet-handler/>
后,springmvc框架会在容器中创建DefaultServletHttpRequestHandler处理器对象。 - DefaultServletHttpRequestHandler处理器对象会像一个检查员,对进入DispatcherServlet的 URL 进行筛查,如果发现是静态资源的请求,就将该请求转由 Web 应用服务器默认的Servlet 处理。
- 一般的服务器都有默认的 Servlet。
- 在 Tomcat 中,有一个专门用于处理静态资源访问的 Servlet 名叫 DefaultServlet。其为 default。可以处理各种静态资源访问请求。该 Servlet 注册在 Tomcat 服务器的 web.xml 中。在 Tomcat 安装目录/conf/web.xml。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!-- 组件扫描 --> <context:component-scan base-package="cw.springmvc"/> <!-- 注解驱动 --> <mvc:annotation-driven/> <!-- 默认servlet处理器 --> <mvc:default-servlet-handler/> </beans>
public class DefaultServletHttpRequestHandler implements HttpRequestHandler, ServletContextAware { ... 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/>
与 @RequestMapping 的冲突问题
<mvc:default-servlet-handler/>
和 @RequestMapping 有冲突问题,需要加入注解驱动<mvc:annotation-driven/>
解决冲突问题- 没有
<mvc:annotation-driven/>
,<mvc:default-servlet-handler/>
会将所有的请求交给 Web 应用服务器默认的Servlet 处理。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!-- 组件扫描 --> <context:component-scan base-package="cw.springmvc"/> <!-- 注解驱动 --> <!-- <mvc:annotation-driven/> --> <!-- 默认servlet处理器 --> <mvc:default-servlet-handler/> </beans>
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!-- 组件扫描 --> <context:component-scan base-package="cw.springmvc"/> <!-- 注解驱动 --> <mvc:annotation-driven/> <!-- 默认servlet处理器 --> <mvc:default-servlet-handler/> </beans>
方式二:使用<mvc:resources/>
- 在 Spring3.0 版本后,Spring 定义了专门用于处理静态资源访问请求的处理器ResourceHttpRequestHandler。
- 并且添加了
<mvc:resources/>
标签,专门用于解决静态资源无法访问问题。 - 需要在 springmvc 配置文件中添加如下形式的配置:
<!-- 处理静态资源的方式二: 使用<mvc:resources/> 该标签加入配置文件后,框架会创建ResourceHttpRequestHandler处理器对象 让这个对象处理静态资源的访问,不依赖web服务器 --> <!-- mapping:访问静态资源的URI地址,使用通配符 ** location:静态资源在项目中的目录位置 --> <!-- /image/**:表示项目image目录下的任意文件或目录 --> <!-- 当前请求资源符合 /image/** 时,到 /image/ 寻找资源,第一个/表示项目webapp目录 --> <mvc:resources mapping="/image/**" location="/image/"/> <mvc:resources mapping="/html/**" location="/html/"/>
<mvc:resources/>
与 @RequestMapping 的冲突问题
<mvc:resources/>
和 @RequestMapping 有冲突问题,需要加入注解驱动<mvc:annotation-driven/>
解决冲突问题
方式三:一条配置处理所有静态资源
- 将所有的静态资源放到webapp目录下的static目录下
- springmvc配置文件的配置如下:
<!-- 注解驱动 --> <!-- 避免冲突问题 --> <mvc:annotation-driven/> <!-- 当前请求资源的路径符合 /static/** 时, 到 /static/ 寻找资源, 第一个 / 表示项目webapp目录 --> <mvc:resources mapping="/static/**" location="/static/"/>