目录
- 认识 Spring MVC
- 什么是 Spring MVC
- MVC 的定义
- Spring MVC 注解的运用
- 1. Spring MVC 的连接
- @RequestMapping 注解
- 2. 获取参数
- 获取单个参数
- 获取多个参数
- 传递对象
- 表单传参
- 后端参数重命名
- @RequestBody 接收 JSON 对象
- @PathVariable 获取 URL 中的参数
- 上传文件 @RequestPart
- 获取 Cookie/Session/Header
- 3. 返回数据
认识 Spring MVC
什么是 Spring MVC
Spring MVC(正式名称:Spring Web MVC) 是基于 Servlet API 构建的原始 Web 框架,从⼀开始就包含在 Spring 框架中。
- Spring MVC 是⼀个 Web 框架
- Spring MVC 是基于 Servlet API 构建的
MVC 的定义
MVC 是 Model View Controller 的缩写,它是软件⼯程中的⼀种软件架构模式,它把软件系统分为模型、视图和控制器三个基本部分。
MVC 执行流程:
- 用户的请求首先到 Controller
- Controller 将请求转发给 Model
- Model 处理业务并将数据结果返回给 Controller
- Controller 将处理的数据发给 View
- View 将数据转换成页面发送给用户
MVC 是⼀种思想,⽽ Spring MVC 是对 MVC 思想的具体实现。
总结来说,Spring MVC 是⼀个实现了 MVC 模式,并继承了 Servlet API 的 Web 框架。既然是 Web框架,那么当⽤户在浏览器中输⼊了 url 之后,我们的 Spring MVC 项⽬就可以感知到⽤户的请求.
在创建 Spring Boot 项⽬时,我们勾选的 Spring Web 框架其实就是 Spring MVC 框架。
Spring MVC 注解的运用
- 连接的功能:将⽤户(浏览器)和 Java 程序连接起来,也就是访问⼀个地址能够调⽤到我们的 Spring 程序。
- 获取参数的功能:⽤户访问的时候会带⼀些参数,在程序中要想办法获取到参数。
- 输出数据的功能:执⾏了业务逻辑之后,要把程序执⾏的结果返回给⽤户
1. Spring MVC 的连接
首先创建一个 TestController 类,来实现用户与 Spring 程序的交互:
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;@Controller //让该类随着 Spring 框架启动而加载
@ResponseBody //返回非页面数据(这个注解在返回数据中讲解)
//@RestController 相当于@Controller + @ResponseBody
@RequestMapping("/test") //路由器规则注册(一级路由)
public class TestController {@RequestMapping("/hi") //路由器规则注册(二级路由)public String sayHi() {System.out.println("hi Spring MVC");return "<h1> 你好 Spring MVC <h1>";}
}
通过浏览器地址访问,来与程序交互:
可以看到,通过我们访问地址 http://localhost:8080/test/hi 就可以执行sayHi 方法,并返回字符串到页面上了。
这里注意:
spring mvc 项目默认扫描路径是启动类所在的包下所有的子包,也就是说:我们新建的的类要想放入 IoC 中,就得在该包下创建类。(默认启动类是在 demo 包下)
@RequestMapping 注解
@RequestMapping 是 Spring Web 应⽤程序中最常被⽤到的注解之⼀,它是⽤来注册接⼝的路由映射的。
路由映射:所谓的路由映射指的是,当⽤户访问⼀个 url 时,将⽤户的请求对应到程序中某个类的某个⽅法的过程就叫路由映射。
@RequestMapping 即可修饰类,也可以修饰⽅法,当修饰类和⽅法时,访问的地址是类 + ⽅法。
@RequestMapping 也可以直接修饰⽅法,代码实现如下:
默认情况下,@RequestMapping 是支持 post 和 get 请求的,我们用 Postman 来验证一下:
get 请求:
post 请求:
在有些情况下,我们可能要该注解只支持其中一种请求,那要怎么实现呢?
- 只支持 get 请求的注解方式
@RestController
@RequestMapping("/test")
public class TestController {//方式一//下面 value 也可以改为 path@RequestMapping(value = "/hi",method = RequestMethod.GET)public String sayHi() {System.out.println("hi Spring MVC");return "<h1> 你好 Spring MVC <h1>";}//方式二@GetMapping("/hhh")public String hhh() {return "hello world";}
}
这里就只演示一下方式一:
注意,浏览器通过 url 来访问地址, 默认是 get 请求。
再通过 Postman 来构造一下 post 请求:
2. 只支持 post 请求的注解方式
@RestController
@RequestMapping("/test2")
public class Test2Controller {//方式一@RequestMapping(path = "/hi",method = RequestMethod.POST)public String sayHi() {return "你好";}//方式二@PostMapping("/hhh")public String hhh() {return "你好,世界!";}
}
这里通过浏览器直接访问就报错了:
通过 Postman 构建 post 请求:
2. 获取参数
获取单个参数
学习 servlet 时获取参数的写法:
@RestController
@RequestMapping("/user")
public class UserController {//传统写法获取请求中的参数@RequestMapping("/getname") //这里不建议使用大小写, 可以用下划线来区分public String getName(HttpServletRequest request) {return "Name : " + request.getParameter("name");}
}
通过 url 传递参数:
通过注解获取:
@RestController
@RequestMapping("/user")
public class UserController {//直接获取 url 中的参数//当该路由被触发后,执行到方法时//就会对 name 进行匹配,直接对 name 进行赋值@RequestMapping("/getname2")public String getName2(String name) {return "Name : " + name;}
}
获取多个参数
其实获取多个参数和获取单个参数差不多:
@RestController
@RequestMapping("/user")
public class UserController {//在传参时,注意参数的命名与要获取的参数名一致@RequestMapping("/getname3")public String getName3(String name, Integer age) {return "Name : " + name + "age : " + age;}
}
不传参数就默认为 null
传递对象
顾名思义,就是将参数当做一个对象的部分属性来接收,在接收时我们新建一个 model 层,来存放所需要的对象:
@RestController
@RequestMapping("/user")
public class UserController {@RequestMapping("/add")public User add(User user) {return user; //将得到的对象返回回去}
}
@Data 注解是个组合注解,它等于:@Getter + @Setter + @ToString + @EqualsAndHashCode + @RequiredArgsConstructor + @NoArgsConstructor
添加了它就不需要我们自己写 Getter 和 Setter 方法了,减少重复工作。
当后端进行接收数据时,就会将参数和 user 里的属性进行对比,发现属性名称与参数的 key 相同就会进行赋值。
当对象返回前端时,因为前端是用 json 来表示对象的,所以返回的对象就转化为 json 格式 :
表单传参
其实表单传参和 url 传参区别就是传递参数的位置不一样,对于后端来说都一样,我们可以用 Postman 来构造请求:
后端参数重命名
有时候前端传递的 key 你觉得不合理,想改个顺眼的名字,就可以对传递过来的参数重命名,当然前端的参数是不变的。(注意: 对象不能重命名)
@RestController
@RequestMapping("/user")
public class UserController {//将前端参数 y 改为 name,并由 name 接收@RequestMapping("/name")public String name(@RequestParam("y") String name) {return name;}
}
这里就有一个问题,如果我不传这个 y 就会报错:
在 @RequestParam 中,参数默认是必传的:
如果我们需求是参数非必传则可以进行如下修改:
@RestController
@RequestMapping("/user")
public class UserController {@RequestMapping("/name")public String name(@RequestParam(value = "y",required = false) String name) {return name;}
}
这样就不会报错了:
@RequestBody 接收 JSON 对象
@RestController
@RequestMapping("/user")
public class UserController {@RequestMapping("/get_json")public User getJson(@RequestBody User user) {return user;}
}
使用 Postman 构造对象并发送 :
@PathVariable 获取 URL 中的参数
@RestController
@RequestMapping("/user")
public class UserController {//{aid} 中的 aid 是用来接收参数的@RequestMapping("/get_url/{aid}")//下面的 "aid" 是将参数名为 aid 的参数赋值给后面的 aid//这里两个 aid 名字相同,可以省略参数名("aid")不写public Integer getUrl(@PathVariable("aid") Integer aid) {return aid;}
}
当然了, 还可以传递多个参数 :
@RestController
@RequestMapping("/user")
public class UserController {@RequestMapping("/get_url2/{aid}/{name}")public String getUrl2(@PathVariable() Integer aid, @PathVariable String name) {return "aid: " + aid + " name: " + name;}
}
上传文件 @RequestPart
@RestController
@RequestMapping("/user")
public class UserController {@RequestMapping("/upload")//myfile 是接收的参数名, 赋值给 filepublic String upload(@RequestPart("myfile") MultipartFile file) throws IOException {String path = "E:\\image\\img.png";//保存文件file.transferTo(new File(path));return path;}
}
该路径下确实保存了 img.png 图片
上面的代码写法是有问题的, 如果有很多用户都要保存文件, 那文件名就不能写死了, 必须保证每次保存的文件名都不一样, 可以使用 UUID :
@RestController
@RequestMapping("/user")
public class UserController {@RequestMapping("/upload")public String upload(@RequestPart("myfile") MultipartFile file) throws IOException {//得到 UUID 并去掉 "-"String name = UUID.randomUUID().toString().replace("-","");//file.getOriginalFilename() 得到文件名//file.getOriginalFilename().lastIndexOf(".") 得到最后一个"."的下标//整个就是 name 拼接上 .后缀名name += file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));String path = "E:\\image\\" + name;//保存文件file.transferTo(new File(path));return path;}
}
多次提交得到的文件 :
获取 Cookie/Session/Header
- 获取 Cookie
@RestController
@RequestMapping("/user")
public class UserController {@RequestMapping("/get_cookie")public String getCookie(@CookieValue(value = "myCookie", required = false) String ck) {return ck;}
}
没有输出 :
通过前端构建一个 key 为 myCookie 的 cookie :
- 获取 Session
要想获取 Session 首先要有 Session, 我们可以上传一个 Session :
@RestController
@RequestMapping("/user")
public class UserController {@RequestMapping("/set_session")public String setSession(HttpServletRequest request) {HttpSession session = request.getSession();if(session != null) {session.setAttribute("SESSION_KEY","张三");return "session set success";}return "session set fail";}@RequestMapping("/get_session")public String getSession(@SessionAttribute(required = false, value = "SESSION_KEY") String name) {return name;}
}
3. 获取 Header
@RestController
@RequestMapping("/user")
public class UserController {@RequestMapping("/get_header")public String getHeader(@RequestHeader("User-Agent") String userAgent) {return "UserAgent : " + userAgent;}
}
可以通过 fiddler 来抓包验证一下 :
3. 返回数据
默认请求下⽆论是 Spring MVC 或者是 Spring Boot 返回的都是 html 格式,如果需要返回非 html 格式数据, 就得使用 @ResponseBody 注解了, 我们之前一直使用的 @RestController 便是 @ResponseBody + @Controller 注解.
验证返回数据的默认格式 :
@Controller
public class Test {@RequestMapping("/b")public String t() {return "hello.html";}
}
因为没有这个前端页面, 所以返回出错 :
抓包(返回的是 html 格式) :
在静态文件中加入 hello.html 文件 :
再次访问 :
使用 @ResponseBody 返回字符串 :
@Controller
public class Test {@ResponseBody@RequestMapping("/a")public String t2() {return "hello.html";}
}
或者使用 @RestController 也可以.