SpringMVC程序开发
文章目录
- SpringMVC程序开发
- :one:认识SpringMVC
- 什么是SpringMVC
- MVC思想
- :two:获取请求参数
- 创建SpringMVC项目
- 建立路由连接
- 获取请求参数
- 获取urlEncoded格式参数
- 获取表单标签参数
- 获取Json格式参数
- 获取URL中的参数
- 上传图片
- 获取请求信息
- 获取请求header
- 获取cookie
- 创建session
- 获取session中存储的信息
- :three:返回数据给前端
- 返回页面
- 返回Json格式数据
- 请求转发和请求重定向
1️⃣认识SpringMVC
什么是SpringMVC
SpringMVC是基于servlet API构建的web框架,SpringMVC是spring框架的一个模块,用于快速开发web APP。SpringMVC正式名称是Spring Web MVC,一般叫作Spring MVC,也可以叫作Spring Web。
MVC思想
MVC是 model(模型) view(视图) controller(控制器)单词的缩写,是之前web开发的一种设计模式。
model,view,controller在web后端中所担任的职务:
Model(模型):主要任务是与数据库打交道,从数据库读取数据,将数据写入到数据库,
View(视图):主要是将从数据库取出的数据构建成页面,即将原始数据构建成页面。
Controller(控制器):这是后端程序中直接与用户交互的部分,连接View,和Model的桥梁
- 这是原始的web开发思想,并没有前后端分离。因为View也是在后端实现的,直接返回给浏览器的就是页面
- 现在主流的开发方式是前后端分离方式,直接将原始数据返回给前端,前端拿到原始数据构建出页面
了解了MVC的思想,就能理解SpringMVC,SpringMVC就是一个web框架,采取的是MVC的思想,所以SpringMVC是MVC思想的具体实现。
2️⃣获取请求参数
创建SpringMVC项目
SpringMVC只是Spring框架中的一个模块,而SpringBoot是Spring开发的脚手架。相当于Spring和SpringBoot都是基石,SpringBoot是基石之上的一个web后端开发模块。
SpringBoot是在Spring和SpringMVC之后出现的,所以最开始创建SpringMVC项目并不是基于SpringBoot项目来创建的,是基于Spring项目来创建的。不过现在有了SpringBoot了,就都用SpringBoot来创建SpringMVC项目了,因为SpringBoot更简便。
基于SpringBoot创建SpringMVC项目,只需要在创建项目之初引入Spring Web依赖即可:
建立路由连接
在servlet项目中,需要加@WebServlet注解,来建立路由,而在SpringMVC项目中,也是需要加注解来建立路由的:
package com.example.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@ResponseBody
@RequestMapping("/web")
public class WebController {
@RequestMapping("/hi")
public String sayHi(){
return "Hi,SpringMVC";
}
}
==@RequsetMapping(请求映射)注解:==建立路由,加在方法上,也可以加在类上:
- 加在类上相当于多了一级路径,如下代码中的请求URL应该是:http://127.0.0.1:8080/web/hi
- 不加在类上则只有一级路径,去掉@RequestMapping(“/web”)则请求的URL:http://127.0.0.1:8080/hi
- 该注解既可以支持Get请求,也可以支持Post请求,如果要想设置只支持其中一种请求,则可以加一个参数:@RequestMapping(value = “/hi”,method = RequestMethod.GET)或者使用@GetMapping注解(只支持Get请求)效果是一样的
==@GetMapping:==和@RequestMapping注解功能类似,但是只支持Get请求
==@PostMapping:==和@RequestMapping注解功能类似,但是只支持Post请求
要想前端发来的请求能映射到代码,除了@RequestMapping,还需要@Controller注解:
==@Controller:==这个注解是Spring中的一个注解,用于将类注册到容器中,
- 如果不将该类注册到容器中,是没法调用到sayHi()方法的,也就是Spring是感知不到WebController这个类的,当前端发来请求的时候,容器中没有这个类,找不到/web/hi这个路由,就会返回404
- 当该类上没有加@RequsetMapping的时候,建立路由的类只能加@Controller注解将类注册到容器中,使用其他注解是没法正确访问到的,因为MVC的思想中Controller层就是和前端交互的第一站,而SpringMVC就这么设计,规定死了必须加@Controller注解
- 但是当类上加了@RequsetMapping注解的时候,使用五大类注解都是可以的
@ReponseBody:(可以加在类上也可以加在方法上)这个注解加在类上,代表类中的所有方法返回的不是静态页面,而基本的数据。因为之前开发web app前后端不分离,后端返回的直接就是页面,那其实是可以不加这个注解的,用法如下:
- 先在static目录下建一个index.html文件
- 不加@ResponseBody注解,return “/index.html”;则返回的是页面:index.html
这是之前没有前后端分离的时候的开发方式,现在都是前后端分离的方式了,返回的不再是页面,而是基本的数据,所以得加@ReponseBody注解,才能返回基本的数据。
==@RestController:==RestController注解是一个组合注解,可以代替@Controller+@ResponseBody这两个注解。
获取请求参数
最基本的连接目前已经建立好了,但是很多时候请求中有参数,该如何获取呢?
一般Get请求中,参数在URL的queryString中;一般Post请求,参数在请求的body中,body中的参数的组织格式有多种,最常用的一般有:urlencoded(也就是URL中的queryString的格式),Json格式。
获取urlEncoded格式参数
在学习servlet时,URL中的queryString中的参数可以使用req.getParameter()方法获取到,body中的数据如果和queryString的组织格式一样,也是可以使用这个方法获取到的。如果body中的数据组织格式是Json,还需要jackson将Json字符串转换为java对象来获取参数。
那在SpringMVC中,URL中的queryString中的数据或者是body中的数据(如果以urlencoded格式组织的数据)都是可以使用以下三种方式来获取到的:
1️⃣方式一:原始的servlet方式:
package com.example.demo.controller;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Controller
@ResponseBody
@RequestMapping("/web")
public class WebController {
@RequestMapping("/get1")
public String get1(HttpServletRequest request, HttpServletResponse response){
String name = request.getParameter("name");
int age = Integer.parseInt(request.getParameter("age"));
return "name:"+name+" age:"+age;
}
}
- 因为SpringMVC是基于servlet的一个框架,所以它还支持servlet中获取参数的一些方法,在get1方法中加上两个参数HttpServletRequest request ,HttpServletResponse response,就可以使用原始的servlet中的方式获取请求中的参数了。
- 需要注意的是servlet这种方式只能获取URL中queryString中的参数,以及body中的urlEncoded格式的参数,如果body中的数据组织格式是json,getParameter()方法是获取不到的。
2️⃣方式二:通过方法参数获取
上面这种原始的servlet的方式还是比较复杂,到了SpringMVC时代就有了更简单的方式:
package com.example.demo.controller;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Controller
@ResponseBody
@RequestMapping("/web")
public class WebController {
@RequestMapping("/get2")
public String get2(String name,Integer age){
return "name:"+name+" age:"+age;
}
}
- 前端传来的参数,直接写在方法形参中,SpringMVC会将参数获取到,然后在调用get2方法的时候,会将参数传进来,不用自己再手动获取参数了。
- 需要注意的是方法的形参名需要和请求中参数的key保持一致,否则会获取不到参数
⭕️参数重命名:有些情况下,前端传递的参数的key不太合理,比如 :n=张三,n这个key不如使用name替换,前端参数不合理,但是我们后端又不想被前端限制住,我们不想我们方法的形参也使用n,这个时候就可以重命名:使用@RequestParam注解来重命名:
@RequestMapping("/get2")
public String get2(@RequestParam(value = "n",required = false) String name,Integer age){
return name+":"+age;
}
- 使用注解后形参name的就可以任意取了,有注解中的value = "n"来和前端参数对应,就不是形参名称:name和前端参数去对饮了
- required = false代表如果获取不到这个参数就给name赋值null
- required = true代表如果获取不到这个参数就返回状态码为400(Bad Request)的响应
3️⃣方式三:将参数封装到对象中,再通过对象获取
使用第二种方式确实简单很多,不过如果请求中的参数太多了,假如有十个参数,那还能在方法中写十个形参吗?可以倒是可以,不过不合理。更合理的方式是将前端传来的参数封装为一个对象,每个参数作为这个对象中的属性。在代码中通过调用这个对象拿到参数:
Student类:
package com.example.demo.model;
import lombok.Data;
@Data
public class Student {
private String name;
private Integer age;
private String sex;
private String classId;
}
路由类:
@Controller
@ResponseBody
@RequestMapping("/web")
public class WebController {
@RequestMapping("/get3")
public String get3(Student student){
return student.getName()+student.getAge()+student.getSex()+student.getClassId();
}
}
- Spring帮我们获取到参数,封装成对象,然后我们直接调用对象属性就可以
- Student类中的属性名需要和请求参数中的名字对应,否则获取不到对应的参数,则属性的值为null
获取表单标签参数
form表单标签也可以用来构造http请求,只支持get和post请求,并且参数的组织格式依然是urlencoded格式。
-
如果是get请求,则参数在url的queryString中;
-
如果是post请求,则参数在请求的body中,数据的组织格式依然是urlencoded。
既然form标签提交的参数都是urlencoded格式的数据,那么就仍然可以使用上面的三种方式来获取参数。
1️⃣方式一:通过方法形参获取到前端参数
🔑前端代码:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>登录</title>
</head>
<body>
<h1>登录</h1>
<form action="/web/login" method="post">
<span>名字:</span>
<input type="text" name="name">
<br>
<span>密码:</span>
<input type="password" name="password">
<br>
<input type="submit" value="提交">
</form>
</body>
</html>
🔑页面:
🔑后端代码:
package com.example.demo.controller;
import com.example.demo.model.Student;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Controller
@ResponseBody
@RequestMapping("/web")
public class WebController {
@RequestMapping("/login")
public String login(String name ,String password){
return "name:"+name+" password:"+password;
}
}
🔑返回结果:
2️⃣方式二:将前端参数构建成对象,再通过对象获取到参数
获取Json格式参数
对于urlencoded格式的参数,可以使用上面的三种方式来获取,那对于Json格式的参数呢,如果前端传递的参数是Json格式来组织的呢?该怎么获取到参数?
在servlet中,对于请求的body中的Json格式的数据,需要借助jackson,来实现json字符串转换为java对象,再通过对象拿到参数。
其实在SpringBoot中已经内置了jackson依赖,并且为了更方便的使用jsckson将json字符串转换为java对象,SpringMVC还提供了一个注解:@RequsetBody
🔑路由类:
package com.example.demo.controller;
import com.example.demo.model.Student;
import com.example.demo.model.User;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Controller
@ResponseBody
@RequestMapping("/web")
public class WebController {
@RequestMapping("/get2")
public String get2(@RequestBody User user){
return user.toString();
}
}
🔑User类:
package com.example.demo.model;
import lombok.Data;
@Data
public class User {
private String name;
private Integer age;
private Integer classId;
private String sex;
}
🔑前端请求:
- 在参数列表中加上一个注解:@RequsetBody,就可以实现自动获取json字符串中的参数并转为User对象。json字符串中的key需要和实体类User中的属性名对应,如果名字不对应,参数是不能赋值给User对象的属性的
- 当加上@RequsetBody后,请求中的body就必须得是json字符串,就不能是urlencoded格式的数据,否则无法正确转换为User对象
- 当加上@RequsetBody注解后,就不能再使用name,age等变量一个参数一个参数获取了,就必须得封装成一个User类来获取参数,否则参数无法正确获取:
package com.example.demo.controller;
import com.example.demo.model.Student;
import com.example.demo.model.User;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Controller
@ResponseBody
@RequestMapping("/web")
public class WebController {
@RequestMapping("/get2")
//加了@RequsetBody后,使用单个形参无法正确获取前端参数
public String get2(@RequestBody String name,Integer age){
return user.toString();
}
}
获取URL中的参数
URL中的参数可以在queryString中,这是最常见的,但是其实URL中queryString前的路径也是可以传递参数的:http://127.0.0.1:8080/web/get2/zhangsan/123(这里的zhagnsan/123就不作为路径了,作为参数)
参数其实是作为路径了,这种方式比较少见,但是SpringMVC也是支持获取路径中的参数了
@RequestMapping("/get2/{name}/{password}")
//这里的形参名需要和注解中的路径名对应
public String get2( @PathVariable String name,@PathVariable String password){
return name+":"+password;
}
上传图片
有时候前端需要上传一个图片到服务器,比如更改用户头像,那服务器端就得有相应的代码来接收图片,并保存图片到服务器的某一个目录下。
package com.example.demo.controller;
import com.example.demo.model.Student;
import com.example.demo.model.User;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
@Controller
@ResponseBody
@RequestMapping("/web")
public class WebController {
@RequestMapping("/upload")
public String upload(@RequestPart("myfile") MultipartFile file) throws IOException {
//最终会将图片保存到d盘根目录下
file.transferTo(new File("d:/img.jpg"));
return "upload ok.";
}
}
使用postman构造一个上传图片的请求:
获取请求信息
获取请求header
1️⃣方式一:传统的servlet方式
package com.example.demo.controller;
import com.example.demo.model.Student;
import com.example.demo.model.User;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
@Controller
@ResponseBody
@RequestMapping("/web")
public class WebController {
@RequestMapping("/getHeader")
public String getHeader(HttpServletRequest request,HttpServletResponse response){
String str = request.getHeader("User-Agent");
return str;
}
}
2️⃣方式二:SpringMVC的方式:
@RequestMapping("/getHeader")
public String getHeader(@RequestHeader("User-Agent") String userAgent){
return userAgent;
}
- 使用@RequestHeader注解,参数就是要获取的Header的key
获取cookie
1️⃣方式一:传统servlet方式:
package com.example.demo.controller;
import com.example.demo.model.Student;
import com.example.demo.model.User;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
@Controller
@ResponseBody
@RequestMapping("/web")
public class WebController {
@RequestMapping("/getco")
public String getCookie(HttpServletRequest request,HttpServletResponse response){
Cookie[] cookies = request.getCookies();
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < cookies.length; i++) {
//获取cookie的key值
String name = cookies[i].getName();
stringBuilder.append(name);
stringBuilder.append(":");
//获取cookie的value值
String value = cookies[i].getValue();
stringBuilder.append(value);
}
return stringBuilder.toString();
}
}
2️⃣方式二:SpringMVC的方式:
@RequestMapping("/getco")
public String getCookie(@CookieValue("java") String cookieValue){
return cookieValue;
}
- 使用@CookieValue注解,参数是cookie的key,就可以获取到cookie的value
创建session
当用户登录时,后端检查用户名和密码都正确的之后,后端要创建sesssion,来存储用户的身份信息。
登录页面后端实现创建session,并存储用户信息方式:
@RequestMapping("/login")
public String login(String name ,String password,HttpServletRequest request,HttpServletResponse response){
//从数据库查询数据检查用户名和密码是否正确
//正确无误后,创建session来保存用户身份信息
//参数为true,服务器中有sessionId对应的session则直接返回,无则创建session
HttpSession session = request.getSession(true);
//在session中存储用户信息
session.setAttribute("userName","张三");
return "login ok";
}
获取session中存储的信息
==方式一:==传统servlet方式:
@RequestMapping("/index")
public String index(HttpServletRequest request,HttpServletResponse response) throws IOException {
//参数为false,从服务器查询sessionId对饮的session,有则返回,无则返回null
HttpSession session = request.getSession(false);
//session为null说明没有会话信息,重定向到登录页面
if (session == null){
response.sendRedirect("/login.html");
return "未登录状态";
}
//拿到session中的用户身份信息
String userName = (String) session.getAttribute("userName");
return "ok";
}
==方式二:==SpringMVC使用注解的方式:
@RequestMapping("/sess")
public String getSession(@SessionAttribute(value = "userName",required = false) String userName){
return "userName:"+userName;
}
-
使用@SessionAttribute注解,可以自动获取到session中存储区的参数信息,并赋值给变量userName。
-
value = “userName” 代表要获取session中参数的key的名称为userName,
-
required = false代表如果从session中获取不到userName参数就给变量userName赋值为null。使用注解的方式获取session中的参数其实还是使用的原始的servlet方式一,不过代码是SpringMVC帮我们实现的,我们不用写复杂的获取HttpSession对象,再用这个对象获取session中存储的用户信息。
-
required = true代表如果从session中获取不到userName这个参数,就返回状态码为400(Bad Request)的响应
-
获取不到userName参数有以下几种情况:
- 客户端发送的请求的cookie中没有sessionId(可能原因是存储sessionId的cookie过期了)
- 请求中的cookie中有sessionId,但是服务器重启了(服务器重启后session在内存中就没有了)
- 请求的cookie中的sessionId和服务器这边存储的session的sessionId对应不上
- 请求中的cookie中的sessionId在服务器存储的session中可以对应上,顺利获取到了HttpSession对象,但是HttpSession对象中并没有userName这个参数(可能原因是登录成功后创建session时就没有给HttpSession中设置userName这个参数)
3️⃣返回数据给前端
返回页面
在最开始前后端不分离的时候,后端直接返回的就是html页面,当不加@ReponseBody这个注解的时候,就可以返回一个页面:
@RequestMapping("/get4")
public Object get4(){
return "/index.html";
}
返回Json格式数据
当前后端分离开之后,后端返回的数据就不再是页面了,而是构建页面的数据,如果想在返回的响应中数据以Json格式来组织,在servlet时期,需要jackson工具,手动将java对象转换为Json字符串。
但是在SpringMVC时期,当返回的是一个HashMap的时候,就会自动实现在响应中以Json格式组织数据,这是SpringMVC帮我们做好的(其实是内置了Jackson工具):
@RequestMapping("get5")
public HashMap<String,Integer> get5(String name,Integer password){
HashMap<String,Integer> hashMap = new HashMap<>();
hashMap.put(name,password);
return hashMap;
}
- 在响应中也可以看到Content-Type:application/json。
- 从响应中可以看到成功返回了json格式数据,比servlet时代的方式简单了很多
- 使用TreeMap和使用HashMap的效果是一样的。
使用HashSet也可以返回json格式数据:
@RequestMapping("get6")
public HashSet<String> get6(){
HashSet<String> hashSet = new HashSet<>();
hashSet.add("java");
hashSet.add("bit");
return hashSet;
}
- 使用HashSet和TreeSet的效果是一样的。
请求转发和请求重定向
请求转发和请求重定向都是用来实现跳转的,但实现方式不同。
1️⃣定义上的区别:
请求转发:发生在服务器端内部,当服务器接收到一个请求后,服务器会将请求转发给目标地址,再将目标地址返回的结果返回给客户端。
请求重定向:当服务器接收到请求后,会先返回一个响应,这个响应会告诉浏览器再给目标地址发送一个请求(目标地址在响应的header中)。然后浏览器就会向目标地址发送第二个请求,服务器再返回一个响应。
🏮举个栗子:
小明:妈妈,我想吃烤山药 妈妈:好,我去给你买(请求转发)
小明:妈妈,我想吃烤山药 妈妈:你自己去买吧(请求重定向)
2️⃣请求转发和请求重定向的过程:
3️⃣浏览器地址栏最终显示的URL不同:
请求转发整个过程,只有一次交互,地址栏中的URL不会变
请求重定向整个过程,有两次交互,所以地址栏最终会显示第二次请求的URL,不再是第一次请求的URL。
4️⃣代码实现不同:
在SpringMVC中,
请求转发和请求重定向的代码实现略有差别:
@Controller
public class TestController {
//请求重定向
@RequestMapping("/hello")
public String hello(){
return "redirect:/index.html";
}
//请求转发
@RequestMapping("/hi")
public String hi(){
return "forward:login.html";
}
}
虽然后端可以实现页面跳转,但是页面跳转一般都是由前端来完成的,一般不由后端来完成。