目录
Spring Web Mvc是基于Servlet ApI构建的原始Web框架,从一开始就包含在Spring框架中
Spring MVC是一个web框架,是和Spring框架一起诞生的,是Spring框架的Web模块,而SpringBoot是为了更加方便高效的使用Spring框架诞生的
什么是MVC
MVC是Model View Controller的缩写,是软件工程中的一种软件架构模式,将软件系统分为模型,视图,控制器三个部分
1、Model(模型)是应用程序中用于处理应用程序数据逻辑的部分,通常模型对象负责在数据库中存取数据
2、View(视图)是应用程序中处理数据显示的部分,通常视图是依据模型数据创建的
3、Controller(控制器)是应用程序中处理用户交互的部分,通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据
MVC和Spring MVC的关系
MVC是一种思想,而Spring MVC是对MVC思想的具体实现
Spring MVC项目
在创建Spring Boot项目时,选择了Spring Web依赖,就相当于创建了Spring MVC项目
去掉勾选,就可以将目录展开了
Spring MVC 连接客户端与服务器
@Controller//在框架启动时,创建对象 并注册入框架
public class Hello {
@ResponseBody
@RequestMapping("/hello")//设置路由地址
public String print() {
return "hello.html";
}
}
我们可以使用http://127.0.0.1:8080/hello访问到服务器,客户端和服务器之间建立了连接
这里,要介绍两个注解:@ResponseBody @RequestMapping
@ResponseBody注解
Spring MVC 默认返回的是视图名称(xxx.html)@ResponseBody告诉浏览器,服务器返回的是一个数据,而不是静态页面
没有@ResponseBody注解,返回静态页面的名称
加了@ResponseBody,返回的是数据,这个数据存储在Body部分
@RequestMapping注解
1、注解作用
这个注解用于注册接口的路由映射
路由映射:当用户访问一个url时,将用户的请求对应到程序某一个类的某个方法
@RequestMapping注解可以加到类上,一定加到方法上
注解如果加到类上,就设计了一级路由;但是方法一定有对应路由映射,否则无法被访问
2、请求类型
1、@RequestMapping注解默认支持POST请求 GET请求
2、method属性可以指定请求类型
@Controller//在框架启动时,创建对象 并注册入框架
public class Hello {
@ResponseBody
@RequestMapping(value = "/hello",method = RequestMethod.POST)//设置路由地址
public String print() {
return "hello.html";
}
}
只可以接收POST请求,不可以接收其他请求类型
3、为了简化对请求方法限制 的写法,提供了其他注解:例如@GetMapping和@PostMapping
@GetMapping:请求方法类型为GET
@Controller//在框架启动时,创建对象 并注册入框架
public class Hello {
@ResponseBody
@GetMapping("/hello")
public String print() {
return "hello.html";
}
}
获取请求参数
获取单个参数
在之前的Servlet项目内,获取请求的参数,通过继承HttpServlet类重写doxxx方法,通过HttpServletRequest内置方法实现
其中get请求的参数在QueryString中,使用getParameter()方法来获取参数
其中post请求的参数在body中,使用getReader()方法来获取body内容
@WebServlet("/getinf")
public class GetInf extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
int id = Integer.parseInt(req.getParameter("id"));
resp.getWriter().write("id= " + id);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
BufferedReader reader = req.getReader();//读取body内容
String ret = reader.readLine();
resp.getWriter().write( ret);
}
}
在Springmvc项目,方法传入参数即可,可以同时支持get和post方法
@Controller
public class GetInfo {
@ResponseBody
@RequestMapping("/getinfo")
public String print(Integer id) {//这里 使用Integer类型 可以防止参数不存在为null,从而报错
if (id == null)
return null;
return "id= " + id;
}
}
获取对象类型的参数
前端给后端发送的也可能是一个对象
在servelt时期,代码如下:
public class Student {
private int id;
private String name;
private int age;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
@WebServlet("/getstudent")
public class GetStudent extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Student student = new Student();
Integer id = Integer.valueOf(req.getParameter("id"));
String name = String.valueOf(req.getParameter("name"));
Integer age = Integer.valueOf(req.getParameter("age"));
student.setId(id);
student.setName(name);
student.setAge(age);
resp.setContentType("text/html;charset=utf-8");
resp.getWriter().write(String.valueOf(student));
}
}
在Spring MVC中:方法参数设置为对象类型即可
@Data
public class Student {
private int id;
private String name;
private int age;
}
@Controller
public class GetStudent {
@RequestMapping("/getstudent")
@ResponseBody
public Student getStudent(Student student) {
return student;
}
}
也就是说,sevlet时期,实现同一个功能,要分别重写post和get请求,调用不同的方法实现,比较复杂
Spring mvc只需要一个方法,默认同时支持POST、GET请求,同时SpringMVC会自己调整数据返回的类型(json/html等)
后端参数重命名
前端传递的参数名称要和后端匹配,但是我们采用前后端分离写项目,很有可能产生参数命名不匹配的问题,这时,可以使用@RequestParma注解来重命名前后端的参数值
@Controller
public class GetInfo {
@ResponseBody
@RequestMapping("/getinfo")
public String print(@RequestParam("userid") Integer id) {
if (id == null)
return null;
return "id= " + id;
}
}
@RequestParam("userid") Integer id 让前端的参数名userid,可以被替换为后端的参数名id,从而正常使用后端程序
但是,如果此时前端传递的参数名是id,程序会报错,因为此时后端可以接受的前端参数名是userid,且要求这个参数是必须要传的;
可以通过@RequestParam注解的requied参数设置是否一定要传入userid参数:如果requied=true,表示前端一定要传入userid参数,如果不传入,后端程序报错;如果requied=false,表示前端不一定要传入userid参数,如果传入,后端程序正常使用,如果不传入,后端程序也不会报错,只不过不能使用到后端对应方法而已
@Controller
public class GetInfo {
@ResponseBody
@RequestMapping("/getinfo")
public String print(@RequestParam(value = "userid",required = false) Integer id) {
if (id == null)
return null;
return "id= " + id;
}
}
接收json格式数据
在servlet中,接收json格式的数据,要手动引入jackson依赖,调用第三方库提供的方法
class Stu {
public int id;
public String name;
public int age;
@Override
public String toString() {
return "Stu{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
@WebServlet("/studentjson")
public class GetStudentJson extends HttpServlet {
ObjectMapper objectMapper = new ObjectMapper();
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//解析body部分的数据 生成对象
Stu stu = objectMapper.readValue(req.getInputStream(), Stu.class);
resp.setContentType("application/json;charset=UTF-8");
resp.getWriter().write(stu.toString());
}
}
在SpringMVC中,前端发出json格式的数据(字符串格式),后端使用@RequestBody注解表示参数为json格式,框架会自己按照json数据来处理
@Controller
public class GetInfo {
@ResponseBody
@RequestMapping("/getinfo")
public String print(@RequestBody String id) {
if (id == null)
return null;
return id;
}
}
如果不使用@RequestBody注解,前端参数就无法被后端获取
获取URL参数
有的需要获取的参数不是URL的QueryString或者body部分,而是其他部分的数据
比如:https://mp.csdn.net/mp_blog/creation/editor
可能会获取参数my_blog或者creation部分
需要使用@PathVariable注解
@Controller
public class GetUrl {
@RequestMapping("/get/{id}/{name}/{address}")//路由地址是 get/id/name/address
@ResponseBody
public String get(@PathVariable Integer id, @PathVariable String name) {
return id + name;
}
}
上传文件
先创建post请求,用于上传文件
当前端的form表单包含有文件上传时;form表单中的数据编码类型要写 enctype="multipart/form-data" 才能上传文件,该编码类型将会以二进制的形式上传数据。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!-- 根据form表单 实现文件上传 -->
<form action=UploadServlet" method="post" enctype="multipart/form-data"><!-- 数据类型是文件 -->
<input type="file" name="filename" >
<input type="submit" value="上传">
<!-- form表单点击后提交请求 -->
</form>
</body>
</html>
在servlet时,上传文件如下所示:
我们先尝试改掉post请求的action,让其跳转到其他网页,和后端暂时分离,执行前端后抓包观察请求信息
浏览器发送上传文件请求时将文件名存储在Request Head里的Content-Disposition里,但Content-Disposition得值里除了文件名信息还有一些其他信息,所以只能通过字符串截取的方式获取文件的后缀名。
@WebServlet("/UploadServlet")
@MultipartConfig//Servlet3.0 给HttpServletRequest 类 上传文件的功能提供支持,表示现在处理的请求是 multipart/from-data 类型//Servlet3.0将multipart/form-data的POST请求封装成Part,通过Part对上传的文件进行操作。
public class FileUpLoad extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=utf-8");
//通过表单file控件(<input type="file" name="filename">)的名字直接获取Part对象
Part part = req.getPart("filename");//根据获取上传组件 传入input标签的name值
//我们此时需要获取的是文件名称
// Servlet3没有提供直接获取文件名的方法,需要从请求头中解析出来
String header = part.getHeader("Content-Disposition");
//得到文件名称
String filename = getFileName(header);
System.out.println(filename);
//指定文件 上传的路径
String savePath = "C:\\Users\\30283\\Pictures\\picture";
System.out.println(savePath);
//得到新的路径 UUID会保证生成不重复的数据
String root = savePath + "\\" + UUID.randomUUID().toString() + filename;
System.out.println("文件路径" + root);
part.write(root);
part.delete();//删除产生的额外内容
resp.getWriter().write("上传成功");
}
/**
* 根据请求头解析出文件名
* 请求头的格式:
* 火狐和google浏览器下:
* form-data; name="file"; filename="snmp4j--api.zip"
* IE浏览器下:form-data; name="file"; filename="E:\snmp4j--api.zip"
* <p>
* <p>
* header:请求头
*
* @return 文件名
*/
private String getFileName(String header) {
//兼顾各种浏览器 第一步先获取filename后的内容
String filename = header.substring(header.lastIndexOf("=") + 2,header.length()-1);
System.out.println(filename);
//火狐和google浏览器下: 得到 snmp4j--api.zip IE浏览器下 得到E:\snmp4j--api.zip
//第二步 获取不同的浏览器的名称
int index = filename.lastIndexOf('\\');
filename = filename.substring(index + 1);//从路径中获取名称
// IE浏览器:snmp4j--api.zip"
//火狐和google浏览器下: "snmp4j--api.zip"
System.out.println(filename);
return filename;
}
浏览器上传一个文件之后,会在 指定目录C:\\Users\\30283\\Pictures\\picture下生成文件
在SpringMVC 中,@RequestPart这个注解用在multipart/form-data表单提交请求的方法上,主要用来搭配springboot接收MultipartFile类型的文件。
@Controller
@Slf4j
public class UploadServlet {
@ResponseBody
@PostMapping("/uploadservlet")
public Boolean upload(@RequestPart("filename") MultipartFile file) {
Boolean result = false;
try {
file.transferTo(new File("C:\\Users\\30283\\Pictures\\picture\\new.jpg"));
result = true;
log.info("上传成功");
} catch (IOException e) {
e.printStackTrace();
}
return true;
}
}
在这个代码中,存在问题:
1、文件上传的路径是写死的,每一次新上传,就会覆盖之前的内容
2、文件类型也是写死的
第一步:让平台可以变化:
在windows系统,是从C盘或者D盘等开始的,Linux系统,是从root这个根目录开始的;一个项目,可以运行在多个平台,我们要对此进行配置,让多个平台都可以使用
创建本地和Linux的配置文件,命名规定application-开头
有三个yml文件
- application.yml:主配置文件,可以设置使用哪一个平台的信
# 设置到底运行的是哪一个平台的配置文件
spring:
profiles:
active :
dev
- 第二个配置文件 application-dev.yml,设置本地运行平台的配置信息
# 本地运行配置文件
file:
path:
C:\\Users\\30283\\Pictures\\picture\\
- 第三个配置文件 application-run.yml,设置Linux运行平台的配置信息
# Linux运行环境配置
#文件路径 是自定义的路径
file:
path:
root\\save
@Controller
public class Choose {
@Value("${file.path}")//读取配置文件信息 看运行的是一个配合文件信息
private String choose;
@RequestMapping("/choose")
@ResponseBody
public String choose() {
return choose;
}
}
在application.yml中选择dev时,运行本地配置文件
在application.yml中选择run时,运行linux配置文件
第二步:让文件地址可以变化
第三步:让文件类型可以变化
获取原来的文件名称,截取类型,通过UUID构建新的文件名称
@Controller
@Slf4j
public class UploadServlet {
@Value("${file.path}")//读取配置文件信息 看运行的是哪一个配合文件信息 获取文件上传的目录
private String dir;
@ResponseBody
@PostMapping("/uploadservlet")
public String upload(@RequestPart("filename") MultipartFile file) {
//得到源文件名称
String filename = file.getOriginalFilename();
log.info(filename);
//截取得到文件类型
String style = filename.substring(filename.lastIndexOf("."));
log.info(style);
//根据UUID 生成一个不重复的数字 拼接为新的文件名
filename = dir + UUID.randomUUID().toString() + filename + style;
log.info(filename);
try {
file.transferTo(new File(filename));//生成新文件 并拷贝内容
} catch (IOException e) {
e.printStackTrace();
log.error("上传失败");
return "上传失败";
}
return "上传成功";
}
}
获取cookie
servlet中创建cookie,并获取cookie
@WebServlet("/setcookie")
public class SetCookie extends HttpServlet {
/**
* 给服务器返回一个Cookie
*/
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
Cookie cookie = new Cookie("username", "张三");
cookie.setMaxAge(60 * 60 );//设置cookie的存储时间
resp.addCookie(cookie);//给浏览器返回cookie
resp.getWriter().write("cookie创建成功");
}
}
@WebServlet("/getcookie")
public class GetCookie extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取服务器传递的Cookie
req.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=utf-8");
Cookie[] cookies = req.getCookies();
if (cookies != null) {
for (int i = 0; i < cookies.length; i++) {
System.out.println(cookies[i].getName());
if (cookies[i].getName().equals("username"))
resp.getWriter().write("username= " + cookies[i].getValue());
}
}
}
}
SpringMVC中,创建Cookie的方式和servlet一样,通过HttpServletRequest类的方法实现
获取Cookie,有两种方式(1)在控制器中通过注解@CookieValue(键值名),获取指定某个cookie。(2)通过HttpServletRequest中的getcookies方法获取cookie数组,然后迭代里面的每一个cookie键值对。
(1)、SpringMVC项目的每一个方法,隐藏了两个参数
HttpServletRequest request, HttpServletResponse response
@Controller
public class GetCookie {
@RequestMapping("/getcookie")
@ResponseBody
public String getCookie(HttpServletRequest request, HttpServletResponse response) {
String name="username";
String value=null;
Cookie[]cookies=request.getCookies();
for (int i = 0; i < cookies.length; i++) {
if(cookies[i].getName().equals("username")){
value=cookies[i].getValue();
break;
}
}
if(value==null)
return null;
return name+value;
}
}
(2)、在控制器中通过注解@CookieValue(键值名),获取指定某个cookie
@Controller
public class CookieInfo {
@ResponseBody
@RequestMapping("/getCookieByName")
public String getCookie(@CookieValue("username") String value) {
//value存放 cookie名称是username 对应的value值
return "username= " + value;
}
}
获取Session
1、SpringMVC创建session和servlet是一样的
@Controller
public class SetSession {
@RequestMapping("/setsession")
@ResponseBody
//创建Session
public String SetSession(HttpServletRequest request) {
HttpSession session = request.getSession(true);//参数为true,如果不存在session,可以创建session
if (session != null) {
session.setAttribute("username", "张三");
return "创建成功";
}
return "失败";
}
}
获取Session,有两种方式(1)在控制器中通过注解@SessionAttribute,获取指定某个Session。(2)通过HttpServletRequest中的方法获取
(1)、通过HttpServletRequest中的方法获取
@Controller
public class GetSession {
@ResponseBody
@RequestMapping("/getsession")
public String getSession(HttpServletRequest request) {
HttpSession session = request.getSession(false);//在会话不存在时,不可以重写建立会话
if (session != null && session.getAttribute("username") != null) {
return (String) session.getAttribute("username");
}
return null;
}
}
(2)、在控制器中通过注解@SessionAttribute,获取Session
@Controller
public class GetSession {
@ResponseBody
@RequestMapping("/getsession")
public String getSession(@SessionAttribute(value = "username")String value) {
//将Session中 key是username 对应的value赋值给变量
//required 默认为true 表示key是username 的键值对一定存在,如果真的不存在,会报错
//required 为false,表示key是username 的键值对不一定存在,如果真的不存在,不会报错
return value;
}
}
required参数 默认为true 表示key是username 的键值对一定存在,如果真的不存在,会报错 当 key为username的 键值对并不存在,报错
@Controller
public class GetSession {
@ResponseBody
@RequestMapping("/getsession")
public String getSession(@SessionAttribute(value = "username",required = false)String value) {
//将Session中 key是username 对应的value赋值给变量
//required 默认为true 表示key是username 的键值对一定存在,如果真的不存在,会报错
//required 为false,表示key是username 的键值对不一定存在,如果真的不存在,不会报错
return value;
}
}
当 key为username的 键值对并不存在,不会报错,只是没有显示结果
获取Header
1、创建Header
@Controller
public class SetHeader {
@ResponseBody
@RequestMapping("/setheader")
public void setHeader(HttpServletRequest request, HttpServletResponse response) {
response.setCharacterEncoding("utf-8");
response.setHeader("SetName", "zhangsan");
}
}
2、 获取Header,有两种方式(1)在控制器中通过注解@RequestHeader,获取指定某个Header。(2)通过HttpServletRequest中的方法获取
(1)、通过HttpServletRequest中的方法获取
@Controller
public class GetHeader {
@RequestMapping("/getheader")
@ResponseBody
public String getHeader(HttpServletRequest request) {
String value = request.getHeader("Accept-Encoding");
return "Accept-Encoding : " + value ;
}
}
(2)、通过@RequestHeader注解获取
requried参数起到和之前一样的作用
@Controller
public class GetHeader {
@RequestMapping("/getheader")
@ResponseBody
public String getHeader(@RequestHeader(value = "Date",required = false) String value) {
return value;
}
}
返回数据
返回静态页面
返回json数据
@Controller
public class SetStudent {
@ResponseBody
@RequestMapping("/setstudent")
public Student student() {
Student student = new Student();
student.setAge(10);
student.setName("张三");
student.setId(2);
return student;
}
}
请求转发
在java后端中,实现页面跳转的两种方式:请求转发和请求重定向
1、在servlet时期,实现请求转发:
@WebServlet("/login")
public class Login extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
String username = req.getParameter("user_name");
String password = req.getParameter("user_password");
if (username != null && password != null && username.equals("张三") && password.equals("123"))
req.getRequestDispatcher("success.html").forward(req, resp);
}
}
2、在SpringMVC,在使用HttpServletRequest类的方法之外,还直接使用关键字forward实现请求转发
@Controller
public class Forward {
@RequestMapping("/login")
public String print() {
return "forward:/success.html";
}
}
请求重定向
1、servlet中实现:
@WebServlet("/redirect")
public class Redirect extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
String username = req.getParameter("user_name");
String password = req.getParameter("user_password");
if (username != null && password != null && username.equals("张三") && password.equals("123"))
resp.sendRedirect("success.html");
}
}
2、spring MVC还可以使用关键字redirect实现
@Controller
public class Redirect {
@RequestMapping("/redirect")
public String reDirect() {
return "redirect:/success.html";
}
}
请求转发和请求重定向的区别
也就是 请求转发,客户端只会有一次请求,内部页面跳转由服务器进行,因为只有一次请求,涉及到的资源是共享的,要求资源都在同一个目录之下,否则无法访问到某些资源
请求重定向,客户端会有多次请求,内部页面跳转由客户端进行,因为有多次请求,涉及到的资源是不共享的