文章目录
问答:
请求携带日期类型的数据如何接收?
请求参数名与形参名不一致时如何接收参数?
如何获取请求携带的cookie的值?
如何处理post请求的乱码问题?
SpringMVC生成响应的方式有哪些?
SpringMVC生成响应时如何共享数据?
@RequestBody和@ResponseBody的区别?
静态资源过滤的三种方式?
什么是SpringMVC的统一异常处理机制? 如何实现?
文件上传前端三要素是什么?
编程:
响应的多种方式实现
响应的多种方式返回值实现
ajax请求与响应实现
文件上传
RestFul风格的路径实现
一、响应与封装返回值
SpringMVC返回值的方式可以分为两大类:
1.同步请求的响应:
请求转发 或 重定向
2.异步请求的响应
异步指的是前端的请求的方式:ajax
ajax请求直接返回响应结果字符串
1、SpringMVC中响应的多种方式
pom.xml配置
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.10</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.8</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.8</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- tomcat7插件,运行命令: mvn tomcat7:run -DskipTests -->
<!-- 配置Tomcat插件 -->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<uriEncoding>UTF-8</uriEncoding>
<port>8080</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
web.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<!--配置springmvc提供的中文乱码过滤器-->
<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>
<!-- <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>
<!--配置核心控制器(前端控制器/调度控制器/总控制器) servlet-->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--配置springmvc资源的路径,dispatcherServlet初始化,就加载springmvc.xml资源-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!--配置servlert加载时机-->
<load-on-startup>1</load-on-startup>
</servlet>
<!--配置映射规则-->
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<!--拦截所有请求(除了jsp后缀请求外)-->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
springmvc.xml
<!--扫包加载bean资源-->
<context:component-scan base-package="com.ahcfl"/>
<!--配置视图解析器:将逻辑视图转换成物理视图-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--配置资源前缀-->
<property name="prefix" value="/WEB-INF/pages/"/>
<!--配置后缀-->
<property name="suffix" value=".jsp"/>
</bean>
<!--开启mvc注解驱动-->
<!--
开启注解驱动后,可以使用springmvc扩展的注解
@DateTimeFormat
@RequestBody
@ResponseBody
。。。。。
-->
<mvc:annotation-driven/>
<!--使用springmvc提供的默认决绝方式-->
<mvc:default-servlet-handler/>
【1】原生API与SpringMVC【实现请求转发】
package com.ahcfl.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Controller
public class ForwardController01 {
/**
* 请求转发与重定向的区别:
* 请求转发是一次请求的延续
* 请求转发可以共享Request对象
* 请求转发是服务器内部行为,只能转发到服务器内部的资源上
* 请求转发时地址栏地址不变
* 请求转发是Request的行为
*
* 重定向,改变方向发送多次请求
* 重定向不可以共享Request对象
* 重定向可以跳转到任何路径上
* 重定向时浏览器地址栏显示最后一次请求的路径
* 重定向是Response对象的行为
*
* SpringMVC的响应:
* 同步响应:
* 请求转发: 一次请求的延续,浏览器地址栏地址不会发生改变
* 原生API: HttpServletRequest
* forward:物理视图路径
* 逻辑视图 + 视图解析器
* 重定向:
* 原生API: HttpServletResponse
* redirect:重定向的物理逻辑
*
* 以流的形式写回给浏览器
* 原生API: HTTPServletResponse
* @ResponseBody + String
* @RequestMapping(value = "/handler07",
* produces = "text/html;charset=utf-8")
*/
/**1、原生API实现请求转发*/
@RequestMapping("/forward01")
public void basic(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("basic forward---->run");
request.getRequestDispatcher("/m01").forward(request,response);
}
/**2、springmvc+视图解析器[默认的请求方式:请求转发]*/
@RequestMapping("/forward02")
public String forward02(){
System.out.println("forward02---->run");
return "success2";
}
/**3、forward:+路径
* 路径的方式:
* 逻辑视图
* 物理视图的路径
* */
@RequestMapping("/forward03")
public String forward03(){
System.out.println("forward03-->run");
return "forward:/forward04"; // "forward:/success3"; 逻辑转发只能转发给处理器,不能转发到页面
}
@RequestMapping("/forward04")
public String forward04(){
System.out.println("forward04-->run");
return "forward://WEB-INF/pages/success4.jsp";
}
【2】原生API与SpringMVC【实现重定向】
package com.ahcfl.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @Author: cfl
* @CreateTime: 2021-05-30 15:19
* @Description: 重定向
* 1、原生API实现重定向 response.sendRedirect("/index.jsp");//jsp资源在WEB-INF之外
* 2、redirect:+资源(可以是服务器内部资源+也可以是其他服务资源)
* 方式1:服务器内部的jsp要在WEB-INF之外 return "redirect:/index.jsp";
* 方式2:重定向到某一个handler下的某个访问方 return "redirect:/test06";
* 方式3:重定向的外部服务器下 return redirect:http://www.baidu.com
*/
@Controller
public class RedirectController01 {
@RequestMapping("redirect01")
public void redirect01(HttpServletResponse response) throws IOException {
System.out.println("redirect01-->run");
response.sendRedirect("/success5.jsp");
}
@RequestMapping("redirect02")
public String redirect02(){
System.out.println("redirect02 run...");
//方式1:服务器内部的jsp要在WEB-INF之外
return "redirect:/success5.jsp";
}
@RequestMapping("redirect03")
public String redirect03(){
System.out.println("redirect03 run...");
//方式2:重定向到某一个handler下的某个访问方法
return "redirect:/redirect02";
}
@RequestMapping("redirect04")
public String redirect04(){
System.out.println("redirect04 run...");
//方式3:重定向的外部服务器下
return "redirect:http://www.baidu.com";
}
}
}
【3】原生API与SpringMVC 【以流的形式响应字符串】
/**
* 以流的形式写回字符串
* mime常用类型:
* *.html text/html
* *.js text/JavaScript
* *.png image/png
* *.json application/json
* 以流的形式写回字符串
* SpringMVC方式:
* @ResponseBody + String
* RequestMapping参数:
* produces: 设置响应参数的mime类型
*/
package com.ahcfl.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @Author: cfl
* @CreateTime: 2021-05-30 15:21
* @Description:
* 设置响应数据点的类型
* 设置纯文本的格式
* response.setContentType("text/plain;charset=UTF-8");
* 设置html格式
* response.setContentType("text/html;charset=UTF-8");
* 设置json格式
* response.setContentType("application/json;charset=UTF-8");
*/
@Controller
public class ResponseStringController01 {
/**
* @return void
* @Param [response]
* @Description:
* 使用远程API实现响应的字符串 【了解】
*/
@RequestMapping(value = "/m01")
public void method01(HttpServletResponse response) throws IOException {
// 处理响应乱码
response.setContentType("text/html;charset=utf-8");
response.getWriter().write("<h1>你若安好,便是晴天霹雳</h1>");
}
/**
* @Param:[]
* @return:java.lang.String
* @Description:
* 使用springmvc实现响应字符串【开发主流方式】
* produces : 指定响应字符串的数据类型,告诉浏览器解析字符串的规则
*/
@RequestMapping(value = "/m02",produces = "text/html;charset=utf-8")
public String method02() {
System.out.println("method---->run");
return "success1";
}
}
了解几个常用的数据类型:
常用的Content-Type类型:
【text/html :HTML格式】
【text/plain :纯文本格式】
text/xml :XML格式
image/gif :gif图片格式
image/jpeg :jpg图片格式
image/png :png图片格式
application/xml : XML数据格式
【application/json : JSON数据格式】
application/pdf : pdf格式
application/msword : Word文档格式
application/octet-stream : 二进制流数据(如文件下载)
【application/x-www-form-urlencoded :
<form encType="">中默认的encType,
form表单数据被编码为key/value格式发送到服务器(表单默认的提交数据的格式)】
【multipart/form-data : 表单上传文件】
2、响应时数据共享问题
请求转发: 数据都会存放到Request域中
重定向: 将共享的数据拼接在请求路径的后面
说明:
ModelAndView和Model是框架给我们准备好的,我们直接拿过来使用的即可,
我们称这些对象为【隐式对象】。
【1】使用request域对象封装响应结果
从http请求到服务器处理结束的整个过程中,多次请求转发过程中request域下的变量共享
**使用这种方式需要导入servlet的包 **
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
</dependency>
public String demo1(HttpServletRequest request){
request.setAttribute("user","admin");
return "success";
}
【2】使用Model封装结果
public String demo2(Model model){
model.addAttribute("user","admin");
return "success";
}
【3】使用ModelAndView封装结果
public ModelAndView demo3(ModelAndView modelAndView){
modelAndView.addObject("user","admin");
modelAndView.setViewName("success");
return modelAndView;
}
【4】代码演示
success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>${msg}</h1>
</body>
</html>
【1】转发响应时共享数据
通过Request域,Model、ModelAndView都可实现;
package com.ahcfl.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
@Controller
@RequestMapping("/params")
public class Demo3Controller {
/**
* 请求转发:
* 携带数据给目标资源 -- HttpServletRequest
* @return
*/
@RequestMapping("/test01")
public String test01(HttpServletRequest request){
System.out.println("test01方法执行了...");
request.setAttribute("msg","如今的现在早已不是曾经说好的以后");
return "success";
}
/**
* 请求转发:
* SpringMVC携带共享的资源
* Model : model对象是SpringMVC内置对象,当前对象可以直接使用
* 当我们向model中存放数据后,最终model中的数据会转存到Request域中
* @return
*/
@RequestMapping("/test02")
public String test02(Model model){
System.out.println("test01方法执行了...");
model.addAttribute("msg","你若安好,便是晴天");
return "success";
}
/**
* 请求转发:
* SpringMVC携带共享的资源
* ModelAndView: 封装视图和转发共享的数据
* @return
*/
@RequestMapping("/test03")
public ModelAndView test03(ModelAndView modelAndView){
System.out.println("test01方法执行了...");
// 封装转发携带的数据
modelAndView.addObject("msg","情绪是智慧不够的产物!");
// 封装视图
modelAndView.setViewName("success");
return modelAndView;
}
}
【2】重定向时共享数据
数据存放到session域下,会占用服务器内存空间;【不可取】
重定向时,request域下的数据不能共享,
但是使用Model或者ModelAndView可以自动将数据以参数的方式拼接到请求路径中。
/**
* 重定向-共享数据:
* 如果需要重定向,SpringMVC会自动将Model中的数据
* 拼接到重定向的路径后面
* @param model
* @return
*/
@RequestMapping("/test04")
public String test04(Model model){
System.out.println("test04方法执行了...");
model.addAttribute("msg","你若安好,便是晴天");
return "redirect:/pages/success.jsp";
}
@RequestMapping("/test05")
public ModelAndView test05(ModelAndView modelAndView){
System.out.println("test05方法执行了...");
// 封装转发携带的数据
modelAndView.addObject("msg1","情绪是智慧不够的产物!");
modelAndView.addObject("msg2","哈哈哈哈");
// 封装视图
modelAndView.setViewName("redirect:/pages/success.jsp");
return modelAndView;
}
3、异步请求的响应
测试环境:vue+springmvc
1、 使用Response 原生API响应结果
/**
* 原生API生成响应信息
* @param name
* @param age
* @param response
* @throws IOException
*/
@RequestMapping("/demo1")
public void demo1(String name, Integer age, HttpServletResponse response)
throws IOException {
response.setContentType("application/json;charset=utf-8");
System.out.println("demo1方法执行了.."+name+" : "+age);
response.getWriter().print("hello..."+name);
}
2、使用@ResponseBody注解响应结果
【1】响应普通字符串(逻辑视图名)
避免视图解析,直接响应字符串数据(返回普通字符串)
/**
* 使用原生API响应前端的异步请求[了解]
* @param response
*/
@RequestMapping("/demo01")
public void demo01(HttpServletResponse response) throws IOException {
response.setContentType("text/plain;charset=utf-8");
response.getWriter().write("一直敲一扇不愿意为你开的门是不礼貌的");
}
/**
* springmvc实现
* @param
* @throws IOException
*/
@RequestMapping(value = "/demo02",produces = "text/plain;charset=utf-8")
@ResponseBody
public String demo02() {
return "一直敲一扇不愿意为你开的门是不礼貌的";
}
@RequestMapping(value = "/demo03",produces = "text/plain;charset=utf-8")
@ResponseBody
public String demo03(String name,String age) {
return "name:"+name+",age:"+age;
}
【2】响应json字符串
先要导入JSON转换需要的包
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.8</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.8</version>
</dependency>
==========将返回结果转成json字符串返回给浏览器===========
/**
* json数据的序列化和反序列化
* -- json的反序列化
* "{'name:'"+userVO.getName()+",'age:'"+userVO.getAge()+"}"---》UserVo
* -- 序列化
* UserVo----》"{'name:'"+userVO.getName()+",'age:'"+userVO.getAge()+"}"
* @param userVo
* @return
*/
//@RequestMapping(value = "/demo04",produces = "application/json;charset=utf-8")
@RequestMapping(value = "/demo04")
@ResponseBody
public UserVo demo03(UserVo userVo) {
System.out.println(userVo);
// return "{'name:'"+userVO.getName()+",'age:'"+userVO.getAge()+"}";
// 序列化【可以直接将对象转成json字符串写回给浏览器】
return userVo;
}
/**
* 响应json格式的数据给浏览器
* @ResponseBody + 对象
* 【可以直接将对象转成json字符串写回给浏览器】
* @RequestBody 获取请求体中的数据并反序列化成Vo对象
* @ResponseBody 将对象序列化成json格式的字符串
*/
@RequestMapping(value = "/demo05",method = RequestMethod.POST)
@ResponseBody
public UserVo demo04(@RequestBody UserVo userVo) {
System.out.println(userVo);
// return "{'name:'"+userVO.getName()+",'age:'"+userVO.getAge()+"}";
return userVo;
}
@RequestMapping(value = "/demo06",method = RequestMethod.POST)
@ResponseBody
public UserVo demo04(String name, Integer age) {
UserVo userVO = new UserVo();
userVO.setAge(age);
userVO.setName(name);
System.out.println(userVO);
//return "{'name:'"+userVO.getName()+",'age:'"+userVO.getAge()+"}";
return userVO;
}
3、@RequestBody和@ResponseBody的区别
【1】@RequestBody
1.@RequestBody核心是接收客户端发送的json格式字符串,并封装到某个vo下【反序列化过程】
2.get请求是没有请求体,而请求携带的json格式数据必须存放在请求体中携带给服务器
3.编写位置: 参数列表中参数的前面
作用:
在post请求时,获取请求携带的json字符串,自动解析json字符串,
并将解析到的内容封装到对应的java对象中(导入Jackson的jar包)
【2】@ResponseBody
@ResponseBody:
作用1: 以流的形式写回字符串给浏览器
作用2: 将对象转成json字符串,将转完后的字符串以流的形式写回给浏览器
需要设置返回数据的mime类型: produces = "application/json;charset=utf-8"
4、小结
注意事项:
在SpringMvc配置文件中添加
<mvn:default-servlet-handler/> 防止访问静态资源时被核心控制器拦截;
# 面试题:@RequestBody和@ResponseBody都是用来干什么的?
@RequestBody : 使用在post请求中
1.获取请求体的参数(获取的是一个字符串)
2.获取请求携带的json格式字符串,并解析json格式的字符串,封装到对象中
3.使用在参数的前面
@ResponseBody: 生成响应字符串的
1.直接返回一个普通字符串
2.如果返回的是对象,则将对象转出json字符串并返回
3.使用在方法上或方法的返回值前面
【前端代码】
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="js/vue.js"></script>
<script src="js/axios.min.js"></script>
</head>
<body>
<h1>发送ajax请求</h1>
<!-- V:视图 -->
<div id="app">
用户名: <input type="text" v-model="user.username"> <br>
年龄: <input type="text" v-model="user.age"> <br>
<button @click="sendAjax1()">get方式-发送ajax请求1</button> <br>
<button @click="sendAjax2()">get方式-发送ajax请求2</button> <br>
<button @click="sendAjax3()">post方式-发送ajax请求3</button> <br>
<button @click="sendAjax4()">post方式-发送ajax请求4</button> <br>
<button @click="sendAjax5()">post方式-发送ajax请求5-返回json格式的数据</button> <br>
<br>
<h3>{{msg}} == {{resultUser.username}} == {{resultUser.age}}</h3>
</div>
</body>
</html>
<script>
new Vue({
el:"#app", // 将vue对象挂载到指定的视图上
data:{ // model,存放数据的位置
msg:"默认值",
user:{
username:"",
age:0
},
resultUser:{
}
},
methods:{
sendAjax1(){
// 发送ajax请求
//alert("函数执行了,即将发送ajax请求...");
let url = "/ajax/test01";
axios.get(url,{
params: this.user
}).then(resp=>{
this.msg = resp.data;
});
},
sendAjax2(){
// 发送ajax请求
//alert("函数执行了,即将发送ajax请求...");
let url = "/ajax/test02";
axios.get(url,{
params: this.user
}).then(resp=>{
this.msg = resp.data;
});
},
sendAjax3(){
// axios的post请求默认携带的参数类为 json 字符串
// 发送ajax请求
//alert("函数执行了,即将发送ajax请求...");
let url = "/ajax/test03";
axios.post(url,this.user).then(resp=>{
this.msg = resp.data;
});
},
sendAjax4(){
// axios的post请求默认携带的参数类为 json 字符串
// 发送ajax请求
//alert("函数执行了,即将发送ajax请求...");
let url = "/ajax/test04";
axios.post(url,this.user).then(resp=>{
this.msg = resp.data;
});
},
sendAjax5(){
// axios的post请求默认携带的参数类为 json 字符串
// 发送ajax请求
//alert("函数执行了,即将发送ajax请求...");
let url = "/ajax/test05";
axios.post(url,this.user).then(resp=>{
console.log(resp.data);
this.resultUser = resp.data;
});
}
}
});
</script>
【后台代码】
package com.itheima.controller;
import com.itheima.vo.UserVo;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("/ajax")
public class Demo4Controller {
@RequestMapping(value = "/test01",produces = "text/html;charset=utf-8")
@ResponseBody
public String test01(String username,Integer age){
System.out.println("test01方法执行了" + username +" : "+age);
return "你好美女";
}
@RequestMapping(value = "/test02",produces = "text/html;charset=utf-8")
@ResponseBody
public String test02(UserVo userVo){
System.out.println("test02方法执行了" + userVo);
return "你好美女";
}
/**
* 获取请求携带的json字符串,将字符串存放到一个变量中
* @RequestBody:
* 获取请求体中的字符串
* @param str
* @return
*/
@RequestMapping(value = "/test03",produces = "text/html;charset=utf-8")
@ResponseBody
public String test03(@RequestBody String str){
System.out.println("test03方法执行了");
System.out.println(str);
return "你好浏览器";
}
/**
* 获取请求携带的json字符串,将字符串存放到一个变量中
* @RequestBody:
* 注意: 需要导入jackson的jar包
* 解析请求体中的json字符串,并将数据封装到vo对象中
* @param userVo
* @return
*/
@RequestMapping(value = "/test04",produces = "application/json;charset=utf-8")
@ResponseBody
public String test04(@RequestBody UserVo userVo){
System.out.println("test04方法执行了");
System.out.println(userVo);
return "你好浏览器";
}
/**
* 响应数据:
* 响应json格式的数据给浏览器
* @ResponseBody + 对象
* 可以直接将对象转成json字符串写回给浏览器
* @param userVo
* @return
*/
@RequestMapping(value = "/test05",produces = "application/json;charset=utf-8")
@ResponseBody
public UserVo test05(@RequestBody UserVo userVo){
System.out.println("test04方法执行了");
System.out.println(userVo);
userVo.setAge(21);
userVo.setUsername("笑雯");
return userVo;
}
}
二、RESTFul风格的路径
1、什么是REST
Rest( Representational State Transfer 即表述性状态传递)
一种网络资源的访问风格,定义了网络资源的访问方式;
传统风格访问路径
http://localhost/user/getUser?id=1
http://localhost/deleteUser?id=1
Rest风格访问路径
http://localhost/user/1 请求方式:get
http://localhost/user/1 请求方式:delete
Restful是按照Rest风格访问网络资源
优点
<font color='red'>隐藏资源的访问行为</font>,通过地址无法得知做的是何种操作
书写简化
2、REST行为约定方式
GET(查询) http://localhost/user/1 GET
POST(保存) http://localhost/user POST
PUT(更新) http://localhost/user PUT
DELETE(删除) http://localhost/user DELETE
注意:
1.上述行为是约定方式,约定不是规范,可以打破,所以称Rest风格,而不是Rest规范。
2.RESTFul就是路径编写的另一种格式,
这种格式面向的是资源(核心思想:资源即路径),而非具体的功能。
3.在springmvc中使用@PathVariable注解就可以从Rest风格的URL中获取参数的值。
3、代码演示
/**
*
* @PathVariable:
*/
package com.ahcfl.controller;
import com.ahcfl.vo.UserVo;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
/**
* @Author: cfl
* @CreateTime: 2021-05-30 15:22
* @Description:
* rest风格:
* 1.路径即资源;
* 2.使用http请求的方法表达对资源的操纵行为;
* 注解 @RestController 等价于@Controller + @ResponseBody 加入IOC和获取参数
* 注解 @PathVariable("") 绑定URL中的参数值 解析请求路径中的值,并将 值赋给形参变量
* 请求路径中 {占位符} 接收请求路径中当前位置的值
*/
//@Controller
@RestController
@RequestMapping("/user")
public class RestFulController01 {
//@RequestMapping(value = "/user/{userName}/{userAge}",method = RequestMethod.GET)
// url: http://localhost:8080/user/ahcfl/16
@GetMapping("/{userName}/{userAge}")
//@ResponseBody
public UserVo getUserVo1(@PathVariable("userName") String name,
@PathVariable("userAge") Integer age){
System.out.println("GET");
UserVo userVo = new UserVo();
userVo.setAge(age);
userVo.setName(name+"GET");
return userVo;
}
@PostMapping("/{userName}/{userAge}")
//@ResponseBody
public UserVo getUserVo2(@PathVariable("userName") String name,
@PathVariable("userAge") Integer age){
System.out.println("name = " + name + ", age = " + age);
UserVo userVo = new UserVo();
userVo.setAge(age);
userVo.setName(name+"POST");
return userVo;
}
//@RequestMapping(value = "/{id}",method = RequestMethod.DELETE)
@DeleteMapping(value = "/{id}")
//@ResponseBody
public String delete(@PathVariable Integer id){
System.out.println("id="+id+"的用户被删除了");
return "ok!!!";
}
//等价于@RequestMapping+method = RequestMethod.PUT
@PutMapping("/{id}/{userName}/{userAge}")
//@ResponseBody
public UserVo updateUserVO(@PathVariable("userName") String name,
@PathVariable("userAge") Integer age,
@PathVariable("id") Integer id){
System.out.println("id="+id+"的用户被修改了");
UserVo userVo = new UserVo();
userVo.setName(name+"PUT");
userVo.setAge(age);
return userVo;
}
}
}
三、静态资源映射
静态资源访问不到的原因
总之,通过资源URL 根据处理器映射器找不到对应的Handler处理器
【方案一】配置web.xml
激活Tomcat的defaultServlet来处理静态文件 ==》用原生servlet方式在web.xml中配置:
<!-- 配置静态资源处理 -->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.html</url-pattern>
<url-pattern>*.css</url-pattern>
<url-pattern>*.js</url-pattern>
<url-pattern>*.png</url-pattern>
</servlet-mapping>
【方案二】配置springmvc.xml
在springmvc.xml文件中配置
< mvc:resources mapping="匹配请求路径" location="真实路径">
<!-- 设置静态资源不过滤 -->
<mvc:resources mapping="/css/**" location="/css/" /> <!-- 样式 -->
<mvc:resources mapping="/images/**" location="/images/" /> <!-- 图片 -->
<mvc:resources mapping="/js/**" location="/js/" /> <!-- javascript -->
<mvc:resources mapping="/html/**" location="/html/" /> <!-- html资源 -->
【方案三】配置springmvc.xml【推荐】
在springmvc.xml文件中配置
<!-- 统一配置 ★ -->
<!-- 代替以上所有的配置 -->
<mvc:default-servlet-handler/>
四、异常处理机制
1、异常概念
【1】异常分类
Throwable类有两个直接子类:
Exception:出现的问题是可以被捕获的;
Error:系统错误,通常由JVM处理
可捕获的异常又可以分为两类:
check异常:直接派生自Exception的异常类,必须被捕获或再次声明抛出
runtime异常:派生自RuntimeException的异常类。使用throw语句可以随时抛出这种异常对象:
【2】异常处理
在编写代码处理异常时,对于检查异常,有2种不同的处理方式:
使用try…catch…finally语句块处理它。
在函数签名中使用throws 声明交给函数调用者caller去解决
# 1、try…catch…finally语句块
try{
//try块中放可能发生异常的代码。
}catch(Exception exception){
//发生异常时候处理的方式
//如果try中没有发生异常,则所有的catch块将被忽略。
}finally{
//无论异常是否发生,异常是否匹配被处理,finally都会执行。
//finally主要做一些清理工作,如流的关闭,数据库连接的关闭等。
}
# 2、throw exceptionObject
public void save(User user){
if(user == null)
throw new IllegalArgumentException("User对象为空");
//......
}
2、springMVC异常处理源码
在最初说springMVC执行流程时候,我们知道整个流程如下:
也就是说调用处理器映射器==>处理器适配器==>视图解析器的整个过程中,
M层(service、dao)发生异常时候,我们把异常throw出来,在抛到dispatcherServlet的时候
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
......
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
......
}
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
=========处理异常===========
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
.......
}
最后调用HandlerExceptionResolver异常处理器来处理。
@Nullable
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,@Nullable Object handler, Exception ex) throws Exception {
========// Success and error responses may use different content types======
request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
=======// Check registered HandlerExceptionResolvers...========
ModelAndView exMv = null;
if (this.handlerExceptionResolvers != null) {
for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
exMv = resolver.resolveException(request, response, handler, ex);
if (exMv != null) {
break;
}
}
}
if (exMv != null) {
if (exMv.isEmpty()) {
request.setAttribute(EXCEPTION_ATTRIBUTE, ex);
return null;
}
======//We might still need view name translation for a plain error model...====
if (!exMv.hasView()) {
String defaultViewName = getDefaultViewName(request);
if (defaultViewName != null) {
exMv.setViewName(defaultViewName);
}
}
if (logger.isTraceEnabled()) {
logger.trace("Using resolved error view: " + exMv, ex);
}
if (logger.isDebugEnabled()) {
logger.debug("Using resolved error view: " + exMv);
}
WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
return exMv;
}
throw ex;
}
由此可以提供一个统一处理的思路
1、dao->service->controller逐层向上抛出异常
不建议在dao、service使用try catch
如果dao、service使用try catch,必须抛出异常
2、自定义一个实现HandlerExceptionResolver接口的类统一处理
解析异常信息==》自定义异常类
跳转错误提示页面==》错误统一处理的页面(错误码、错误信息)
3、为什么使用异常处理机制
【1】在我们项目中避免不了有异常的情况,当异常的情况发生时,我们不能将异常信息直接返回给用户
(浏览器), 采用友好的方式告知用户. 告知的方式就是我们要用的统一异常处理;
【处理方式】
项目中所有的异常都可以向上抛出,最终抛给异常处理类,
然后在异常处理类中定义友好的提示信息,并返回给用户.
【2】在Java中所有的异常最终的归宿都是被抓取的。
抓取后进行处理,而对于异常的处理一般有两种方式:
一种是当前方法处理,这种处理方式会造成业务代码和异常处理代码的耦合。
另一种是抛给调用者处理,在这种方法的基础上,衍生除了SpringMVC的异常处理机制。
由于我们的代码最后都是有Spring框架来调用的,所以我们可以直接将异常抛给框架,然后由框架统一处理。
4、 统一异常处理实现
SpringMVC的异常处理思路:【所有异常都往上抛,然后指定一个统一的异常处理器进行处理】
【方式一】基于HandlerExceptionResolver接口实现
自定义异常处理器
a).编写一个类,实现 HandlerExceptionResolver 接口
b).重写 resolveException方法:完成异常处理
1.跳转到公共的错误页面
2.携带错误信息
返回:ModelAndView
addObject: 携带参数
setViewName:指定页面跳转
c).配置异常处理器(交给spring容器管理)
<bean id="exceptionResolver" class="com.itheima.exception.MyHandlerExceptionResolver" />
或者使用注解加入spring的ioc容器中;
代码实现:
自定义异常处理类
package com.ahcfl.resolver;
import com.itheima.exception.ProjectException;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class MyHandlerExceptionResolver implements HandlerExceptionResolver {
/**
* @param request : 请求对象
* @param response : 响应对象
* @param handler : 处理器对象(哪个处理器方法抛出的异常)
* @param ex : 抛出的异常是什么
* @return
*/
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response,Object handler, Exception ex) {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("msg",ex.getMessage());
// 设置视图 转发到error.jsp
modelAndView.setViewName("forward:/pages/error.jsp");
return modelAndView;
}
}
测试类:
package com.ahcfl.controller;
import com.itheima.exception.ProjectException;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class ExceptionController {
@RequestMapping("/testException")
public String testException() throws Exception{
System.out.println(1/0);
return "success";
}
}
【方式二】注解开发异常处理器(推荐)
- 使用注解实现异常分类管理
名称: @ControllerAdvice
类型: 类注解
位置:异常处理器类上方
作用:设置当前类为异常处理器类
@Component
@ControllerAdvice
public class ExceptionAdvice {
}
- 使用注解实现异常分类管理
名称: @ExceptionHandler
类型: 方法注解
位置:异常处理器类中针对指定异常进行处理的方法上方
作用:设置指定异常的处理方式
说明:处理器方法可以设定多个
@ExceptionHandler(Exception.class)
@ResponseBody
public String doOtherException(Exception ex){
return "出错啦,请联系管理员! ";
}
5、理想的异常处理解决方案
【1】异常处理方案
- 业务异常:
发送对应消息传递给用户,提醒规范操作 - 系统异常:
发送固定消息传递给用户,安稳用户
发送特定消息给运维人员,提醒维护
记录日志 - 其他异常:
发送固定消息传递给用户,安稳用户
发送特定消息给编程人员,提醒维护
纳入预期范围内
记录日志
【2】自定义异常
1.以业务异常为例说明
//自定义异常继承RuntimeException,覆盖父类所有的构造方法
public class BusinessException extends RuntimeException {
public BusinessException() {
}
public BusinessException(String message) {
super(message);
}
public BusinessException(String message, Throwable cause) {
super(message, cause);
}
public BusinessException(Throwable cause) {
super(cause);
}
public BusinessException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
2.定义分类异常管理器
@Component
@ControllerAdvice
public class MyExceptionAdvice {
@ExceptionHandler(BusinessException.class)
public ModelAndView handlerException(BusinessException exception){
ModelAndView mv = new ModelAndView();
mv.setViewName("forward:/pages/error.jsp");
mv.addObject("msg",exception.getMessage());
return mv;
}
// @ExceptionHandler(otherDefinerException.class)
// public ModelAndView handlerException(BusinessException exception){
// ModelAndView mv = new ModelAndView();
// mv.setViewName("forward:/pages/error.jsp");
// mv.addObject("msg",exception.getMessage());
// return mv;
// }
}
3.触发业务异常示例
@Controller
public class TestExceptionController {
@GetMapping("/user")
public String getUser(String name){
//举例业务异常说明
if(name.trim().length()<4 ){
throw new BusinessException("用户名长度必须在2-4位之间,请重新输入!");
}
return "hello";
}
}
总之,通过自定义异常将所有的异常现象进行分类管理,以统一的格式对外呈现异常消息
五、SpringMVC文件上传
案例需求:用户通过客户端,将用户本地文件传递到服务器上,并在服务器上将用户上传的文件进行保存;
1、文件上传三要素
【1】请求方式必须是Post ,因为get请求携带的数据量受限
【2】form表单的enctype取值必须是:multipart/form-data
form默认content-type是:application/x-www-form-urlencoded
enctype属性:是表单请求正文的类型
值:multipart/form-data 【多部分表单项】
作用:
1.将普通表单项与文件上传项分离,普通表单项可以直接被解析.
2.而上传文件项需要对上传的文件进行解析,并保存文件
【3】提供一个文件输入框
<form method="post" enctype="multipart/form-data">
用户名: <input type="text" name="username"/> 【普通表单项】
密码: <input type="text" name="password" /> 【普通表单项】
头像: <input type="file" name="filename" /> 【上传文件项】
<input type="submit" value="提交表单" />
</form>
2、代码实现
1.导入Fileupload的jar包
<!--普通文件上传-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
2.配置文件解析器
配置到springmvc.xml文件中
<!-- 配置文件上传解析器 id为固定值,不能变-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--最大文件大小,单位字节 5m= 1024*1024*5-->
<property name="maxUploadSize" value="5000000"/>
<!--指定默认编码格式-->
<property name="defaultEncoding" value="UTF-8"/>
</bean>
3.编写前端页面(发送上传文件的请求)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="" method="post" enctype="multipart/form-data">
<%-- TODO:普通表单项 --%>
用户名: <input type="text" name="username" value="tom"> <br>
<%-- TODO:普通表单项 --%>
年龄: <input type="text" name="age" value="18"> <br>
<%-- TODO:文件上传项 --%>
头像: <input type="file" name="filename"> <br>
<input type="submit" value="提交表单">
</form>
</body>
</html>
4.上传代码(单文件)
package com.ahcfl.web;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.util.Random;
import java.util.UUID;
@RequestMapping("/file")
@Controller
public class FileUploadController {
/**
* @param username : 普通表单项
* @param age : 普通表单项
* @param filename : 文件上传项,在此对象中存放有上传的文件的所有信息
* @return
*/
@RequestMapping("/fileUpload")
public String handler01(String username, Integer age, MultipartFile filename) throws IOException {
System.out.println(username+" : "+age);
// 文件对象,需要处理
// 获取上传文件向的名称 name="filename"(获取name属性的值)
String name = filename.getName();
System.out.println(name);
// 获取上传的文件的名称
String originalName = filename.getOriginalFilename();
System.out.println(originalName);
// 截取文件的扩展名
int startIndex = originalName.lastIndexOf(".");
String fileStr = originalName.substring(startIndex, originalName.length());
System.out.println(fileStr);
// 生成唯一的文件名称 java.util.UUID
originalName = UUID.randomUUID().toString()+fileStr;
// 将上传的文件保存到本地
File file = new File("G:/upload116/"+originalName);
filename.transferTo(file);
return "success";
}
}
5、上传代码(多文件)
@RequestMapping("/fileUpload1")
public String handler02(String username, Integer age, MultipartFile[] filename) throws IOException {
System.out.println(username+" : "+age);
for (MultipartFile multipartFile:filename) {
// 文件对象,需要处理
// 获取上传的文件的名称
String originalName = multipartFile.getOriginalFilename();
System.out.println(originalName);
// 截取文件的扩展名
String fileStr = originalName.substring(originalName.lastIndexOf("."), originalName.length());
System.out.println(fileStr);
// 生成唯一的文件名称
originalName = UUID.randomUUID().toString()+fileStr;
// 多文件夹存储图片
Random random = new Random();
int i = random.nextInt(10);
int j = random.nextInt(10);
File file = new File("G:/upload116/" + i + "/" + j);
if (!file.exists()){
// 创建多层文件夹
file.mkdirs();
}
// 将上传的文件保存到本地
File file1 = new File("G:/upload116/"+i+"/"+j+"/"+originalName);
multipartFile.transferTo(file1);
}
return "success";
}
总结
特殊的请求参数:
日期类型: @DateTimeFormat(pattern="日期格式")
自定义类型转换器
中文乱码: 过滤器
请求参数名与形参名称不一致:
@RequestParam("请求参数名")
获取请求头: @RequestHeader("头名称")
获取cookie的值: @CookieValue("Cookie的名称")
响应:
请求转发:
String + forward:物理视图路径
String + 逻辑视图 + 视图解析器
重定向:
String + redirect:物理视图路径
以流的形式写回字符串:
@ResponseBody + String
注解写在方法上,用于描述当前方法返回的内容需要放在响应体中
以流的形式将结果返回
请求转发和重定向如何携带数据?
原生API
Model: 模型
ModelAndView: 模式和视图