当前位置:   article > 正文

【Spring MVC】这一篇,带你从入门到进阶(附 Restful)_springmvc

springmvc

目录

1、什么是MVC?

2、什么是 Spring MVC

3、实战开发

3.1、如何创建 Spring MVC 项目

3.2、将 Spring 程序与用户(浏览器)联通

3.3、基础注解

3.3.1、@RequestMapping

3.3.2、GET/POST方法、

3.4、获取参数注解

3.4.1、传递单个/多个参数

3.4.2、传递对象

3.4.3、参数重命名 @RequestParam

3.4.4、接收 JSON 对象 @RequestBody

3.4.5、获取URL中参数 @PathVariable

3.4.6、上传文件 @RequestPart

3.5.7、获取Cookie数据 @CookieValue

更简单的读取Cookie数据如下方式:

3.4.8、存储Session数据

3.4.9、读取Session数据 @SessionAttribute

 更简单的读取Session数据如下方式:

3.5、返回其他格式的数据

3.5.1、返回静态页面

3.5.2、返回单个JSON对象

3.5.3、返回多个JSON对象 

3.5.4、多个字段 + 文件(图片、视频)

3.6、Restful 接口风格

3.6.1、@GetMapping

3.6.2、@PostMapping

3.6.3、@PutMapping

3.6.4、@PatchMapping

3.6.5、@DeleteMapping


1、什么是MVC?


MVC是 Model View Controller 的缩写,是一种软件设计模式,将软件分为模型、视图和控制器三部分,大体工作流程:客户端向服务器发起请 HTTP 请求,被 Controller 接收,从 Model 中请求信息(通常模型对象负责在数据库中存取数据),并将响应信息返回给 Controller,接着交由给 View 显示数据,最后将 HTTP 响应传递给用户。

2、什么是 Spring MVC


简单来说,是一个构建在 Servlet(API)之上,来自于 Spring Web 模块的的 Web框架(HTTP)

通常我们所说的 SSM 就等于 Spring + Spring MVC + MyBatis,后来又有了一种更新的说法:SSM = Spring Boot + Spring Web(Spring MVC) + MyBatis

3、实战开发


3.1、如何创建 Spring MVC 项目

a)如果你是使用的IDEA社区版,需要安装以下插件(不是社区版可以跳过此步骤)

所需插件:Spring Boot Helper(如下图)

注意:此插件在IDEA社区版2022.1.x之前都免费,IDEA社区版2022.2.x之后开始收费,所以IDEA社区版版本的选择,大家自行选择。

这个插件就是用来构建 Spring Boot 框架的,之后,你在创建一个项目的时候可以看到这样一个选项(就是通过来创建Spring Boot框架),如下图:

b)接下来就是创建Spring-MVC项目的步骤: 

关键是要选择Spring Web选项,这里就包含了MVC,如下:

最后一路Next即可,最终目录结构如下:

如果你得到了上图这个目录结构,说明你的项目已经创建好了,但是还没有初始化,因此还需要以下步骤:

然后点击OK,接着就是一个,漫长的等待过程,当你的项目出现了下图变化,才说明你的Spring-Boot项目已经创建并初始化完成:

3.2、将 Spring 程序与用户(浏览器)联通

具体代码如下:

  1. import org.springframework.stereotype.Controller;
  2. import org.springframework.web.bind.annotation.RequestMapping;
  3. import org.springframework.web.bind.annotation.ResponseBody;
  4. @Controller //让 Spring 项目启动时,将这个注解下的类加载到容器中
  5. @ResponseBody //使其返回的响应是数据,而非页面
  6. @RequestMapping("/user") //注册路由
  7. public class UserController {
  8. @RequestMapping("/hi") //路由注册
  9. public String sayHi() {
  10. return "hi! Spring MVC!";
  11. }
  12. }

输入URl即可访问到sayHi方法,如下结果:

3.3、基础注解

3.3.1、@RequestMapping

@RequestMapping 是⽤来注册接⼝的路由映射的,当用户输入一个url,就可以通过url访问程序某个类的某个方法(@RequestMapping 即可修饰类,也可以修饰⽅法,当修饰类和⽅法时,访问的地址是类 + 方法。),如下图:

3.3.2、GET/POST方法、

可以通过 @RequestMapping 或 @GetMapping/@PostMapping 获取请求的数据。

get请求的写法有如下三种:

  1. // 写法1
  2. @RequestMapping("/index")
  3. // 写法2
  4. @RequestMapping(value = "/index",method = RequestMethod.GET)
  5. // 写法3
  6. @GetMapping("/index")

post请求的写法有如下两种:

  1. // 写法1
  2. @RequestMapping(value = "/index",method = RequestMethod.POST)
  3. // 写法2
  4. @PostMapping("/index")

3.4、获取参数注解

Spring MVC中可以直接通过方法中的参数来传参,要注意的有以下几点:

Ps:

1、当有多个参数进行参数匹配时,是按照参数的名称进行匹配的,因此参数的位置不影响参数的获取;

2、Spring MVC 中通过前端传参时,方法的参数一定要是包装类类型,而非基础类型(因为如果方法的参数是基础类型,但又忘记传递时,程序就会报500错误,而保证类型为包装类类型忘记传参时,只会值为null);

3.4.1、传递单个/多个参数

由于传递单个和多个参数的写法方式一样,所以这里就以多参数为例~

代码如下:

  1. import org.springframework.stereotype.Controller;
  2. import org.springframework.web.bind.annotation.RequestMapping;
  3. import org.springframework.web.bind.annotation.ResponseBody;
  4. @Controller //让 Spring 项目启动时,将这个注解下的类加载到容器中
  5. @ResponseBody //使其返回的响应是数据,而非页面
  6. @RequestMapping("/user") //注册路由
  7. public class UserController {
  8. @RequestMapping("/message")
  9. public void getUserMessage(String name, String age) {
  10. System.out.println("name:" + name);
  11. System.out.println("age:" + age);
  12. }

方法参数对应 url 中 query string 的 key 值,如下使用 Postman 访问方法:

执行结果如下:

值得一提的是,以上这种后端的参数写法也支持另一种数据格式的传输,如下:

Fiddler抓包请求:

3.4.2、传递对象

在 Spring MVC 中可以创建一个实体类对象(含getter和setter方法),最后通过方法的参数对象的getter方法获取属性。

如下实体类对象:

  1. @Controller //让 Spring 项目启动时,将这个注解下的类加载到容器中
  2. @ResponseBody //使其返回的响应是数据,而非页面
  3. @RequestMapping("/user") //注册路由
  4. public class UserController {
  5. @RequestMapping("/message")
  6. public void getMessage(User user) {
  7. System.out.println("id:" + user.getId());
  8. System.out.println("name:" + user.getName());
  9. System.out.println("age:" + user.getAge());
  10. }
  11. }

前端访问:

执行结果:

3.4.3、参数重命名 @RequestParam

情况一:必传参数设置

有些情况下,前端传递的参数 key 的字段和后端接收的 key 的字段可以不一致,例如前端传输一个 time ,后端就可以使用 @RequestParam 来重命名后端的参数为 startTime 字段来接收。

例如前端两个参数key字段分别为t1,t2,后端使用 startTime 和 endTime 字段来接收如下代码:

  1. import org.springframework.stereotype.Controller;
  2. import org.springframework.web.bind.annotation.RequestMapping;
  3. import org.springframework.web.bind.annotation.RequestParam;
  4. import org.springframework.web.bind.annotation.ResponseBody;
  5. @Controller //让 Spring 项目启动时,将这个注解下的类加载到容器中
  6. @ResponseBody //使其返回的响应是数据,而非页面
  7. @RequestMapping("/user") //注册路由
  8. public class UserController {
  9. @RequestMapping("/time")
  10. public void getTime(@RequestParam("t1") String startTime,
  11. @RequestParam("t2") String endTime) {
  12. System.out.println("起始时间:" + startTime);
  13. System.out.println("结束时间" + endTime);
  14. }
  15. }

 前端访问地址如下:

执行结果:

情况二:非必传参数设置

情况一中,若前端传递了一个非time的参数,程序就会出现报错的情况,我们可以通过设置 @RequestParam 中的 required=false 来避免不传递时报错,具体实现如下:

  1. @RequestMapping("/time")
  2. public void getTime(@RequestParam(value = "t1", required = false) String startTime,
  3. @RequestParam(value = "t2", required = false) String endTime) {
  4. System.out.println("起始时间:" + startTime);
  5. System.out.println("结束时间" + endTime);
  6. }

3.4.4、接收 JSON 对象 @RequestBody

代码如下:

  1. import com.example.demo.model.User;
  2. import org.springframework.stereotype.Controller;
  3. import org.springframework.web.bind.annotation.RequestBody;
  4. import org.springframework.web.bind.annotation.RequestMapping;
  5. import org.springframework.web.bind.annotation.RequestParam;
  6. import org.springframework.web.bind.annotation.ResponseBody;
  7. @Controller //让 Spring 项目启动时,将这个注解下的类加载到容器中
  8. @ResponseBody //使其返回的响应是数据,而非页面
  9. @RequestMapping("/user") //注册路由
  10. public class UserController {
  11. @PostMapping("/message")
  12. public String getMessage(@RequestBody User user) {
  13. return user.toString();
  14. }
  15. }

前端发送请求,结果如下:

Ps:注意这里传递的是JSON对象,向后端提交数据,需要搭配着POST请求。

3.4.5、获取URL中参数 @PathVariable

前端地址:

后端接收:

  1. @PostMapping("/urlValue/{name}/{age}")
  2. public String getUrlValue(@PathVariable("name") String name,
  3. @PathVariable("age") String age) {
  4. return "name:" + name + ", age:" + age;
  5. }

执行结果:

注意:以上写法@PostMapue/{name}/{ageping("/urlVal}")中{name}和{age}是必须要传的参数,若只传age,不传name,则可以使用如下写法:

  1. @PostMapping("/urlValue/{name}/{age}")
  2. public String getUrlValue(@PathVariable(value = "name", required = false) String name,
  3. @PathVariable("age") String age) {
  4. return "name:" + name + ", age:" + age;
  5. }

Ps:

1.需要获取的参数要在 @PostMapping 注解中用大括号{}括起来。

2.这种写法的URl对比之前的 query string 在SEO上效果更好(浏览器搜索以后会排在更前面)!

3.4.6、上传文件 @RequestPart

前端通过from表单请求的请求文件数据:

后端接收:

  1. @PostMapping("/upfile")
  2. public String upfile(@RequestPart("myfile") MultipartFile file) throws IOException {
  3. String path = "D:\\其他\\滑稽2.webp";
  4. file.transferTo(new File(path));
  5. return path;
  6. }

3.5.7、获取Cookie数据 @CookieValue

由于 Spring MVC(Spring Web)是基于 Servlet 实现的,所以获取 Cooike 数据也是通过 HttpServletResponse 的 getCookie 方法获取到的,如下后端代码(这里为了方便观察结果,使用 @Slf4j 来打印日志信息):

  1. @GetMapping("/getCookie")
  2. public String getCookie(HttpServletRequest request) {
  3. Cookie[] cookies = request.getCookies();
  4. for(Cookie ans : cookies) {
  5. log.error("key:" + ans.getName() + ",value:" + ans.getValue());
  6. }
  7. return "get Cookie Success!";
  8. }

前端可以通过F12检测里的Application中的Cookie自定义数据,如下:

前端请求如下:

 访问结果:

更简单的读取Cookie数据如下方式:

  1. @RequestMapping("/cookie")
  2. @ResponseBody
  3. public String cookie(@CookieValue("zhangsan") String name) {
  4. return "cookie:" + name;
  5. }

Ps:如果 zhangsan 这个参数是一个非必要的参数,那么也可以再在@CookieValue注解后面加上一个false参数;

3.4.8、存储Session数据

由于 Spring MVC(Spring Web)是基于 Servlet 实现的,所以获取 Session 数据也是通过 HttpServletResponse 的 getSesion 方法获取到的,如下后端代码:

  1. @RequestMapping("setSession")
  2. public String setSession(HttpServletRequest request) {
  3. HttpSession session = request.getSession(true);
  4. session.setAttribute("jay chou", "nb");
  5. return "set Session Success!";
  6. }

前端请求:

3.4.9、读取Session数据 @SessionAttribute

由于 Spring MVC(Spring Web)是基于 Servlet 实现的,所以获取 Session 数据也是通过 HttpServletResponse 的 getSesion 方法获取到的,如下后端代码:

  1. @RequestMapping("getSession")
  2. public String getSession(HttpServletRequest request) {
  3. HttpSession session = request.getSession(false);
  4. if(session == null || session.getAttribute("jay chou") == null) {
  5. return "当前会话参数错误";
  6. }
  7. return "get Session Success!";
  8. }

前端先通过2.5.7中的后端代码存储Session数据,就可以观察到生成如下SessionId:

再进行前端访问就可以得到如下结果:

 更简单的读取Session数据如下方式:

  1. @RequestMapping("getSessionEasy")
  2. public String getSession(@SessionAttribute(value = "jay chou", required = false) String name) {
  3. return name;
  4. }

Ps:这里表示 jay chou 这个字符串是一个非必要参数,去掉false参数他就是一个必要参数。

3.5、返回其他格式的数据

3.5.1、返回静态页面

Spring MVC 和 Spring Boot 默认情况下都是返回View视图(xxx.html),通过@ResponseBody注解就可以让后端返回的是数据,而非页面。那么如果现在需要返回一个页面,我们应该怎么做?

a)创建一个前端页面 index.html

前端代码如下:

  1. <!doctype html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport"
  6. content="width=device-width, user-scalable=no, initial-scale=1.
  7. 0, maximum-scale=1.0, minimum-scale=1.0">
  8. <meta http-equiv="X-UA-Compatible" content="ie=edge">
  9. <title>hello,spring mvc</title>
  10. <script src="index.js"></script>
  11. </head>
  12. <body>
  13. <h1>Hello,Spring MVC.</h1>
  14. </body>
  15. </html>

后端代码如下:

  1. @Controller
  2. @RequestMapping("/html")
  3. public class IndexController {
  4. @RequestMapping("/index")
  5. public String retIndex() {
  6. //执行业务逻辑...
  7. return "/index.html";
  8. }
  9. }

客户发送请求后的结果:

3.5.2、返回单个JSON对象

在 Spring MVC 中返回 HashMap 对象,实际上就是在返回JSON对象,如下后端代码:

  1. @RequestMapping("getJson")
  2. public HashMap<String, Integer> resJson() {
  3. HashMap<String, Integer> map = new HashMap<>();
  4. map.put("zhangsan", 1);
  5. map.put("lisi", 2);
  6. map.put("wangwu", 3);
  7. return map;
  8. }

客户端输入url得到如下结果:

3.5.3、返回多个JSON对象 

通过使用泛型为实体类的 List 作为返回值(注意要在类上加 @RestController 或 @ResponseBody 注解,或者在对应方法上使用 @ResponseBody 注解),就可以实现多个 JSON 对象返回给前端,如下后端代码示例:

  1. @ResponseBody
  2. public List<Blog> getBlogList() throws JsonProcessingException {
  3. List<Blog> list = blogService.getBlogListById();
  4. return list;
  5. }

3.5.4、多个字段 + 文件(图片、视频)

a)在Spring Boot中,当一个HTTP请求包含了普通字段和图片文件时,你可以使用 @ModelAttribute 来绑定普通字段,同时使用@RequestParam来接收文件。这样,你可以在一个方法中同时处理表单数据和文件上传。

以下是一个简单的示例,展示了如何在Spring Boot控制器中解析包含普通字段和图片文件的multipart/form-data请求:

  1. import org.springframework.web.bind.annotation.*;
  2. import org.springframework.web.multipart.MultipartFile;
  3. @RestController
  4. public class FormDataController {
  5. @PostMapping("/submit-form")
  6. public String handleFormSubmit(@ModelAttribute MyFormData formData, @RequestParam("image") MultipartFile image) {
  7. // 获取普通字段的值
  8. String name = formData.getName();
  9. String email = formData.getEmail();
  10. // 处理图片文件
  11. if (!image.isEmpty()) {
  12. // 获取文件名
  13. String fileName = image.getOriginalFilename();
  14. // 保存文件到服务器或进行其他处理
  15. // ...
  16. }
  17. // 执行其他业务逻辑
  18. return "表单提交成功";
  19. }
  20. // 用于绑定表单数据的模型类
  21. public static class MyFormData {
  22. private String name;
  23. private String email;
  24. // 获取和设置name字段的值
  25. public String getName() {
  26. return name;
  27. }
  28. public void setName(String name) {
  29. this.name = name;
  30. }
  31. // 获取和设置email字段的值
  32. public String getEmail() {
  33. return email;
  34. }
  35. public void setEmail(String email) {
  36. this.email = email;
  37. }
  38. }
  39. }

Ps:前端发送请求时,需要确保表单的 enctype 属性设置为 multipart/form-data,这样浏览器才会以正确的格式发送文件 

b)当然,方便起见,也可以直接把 MultipartFile 文件作为一个类的字段一起传入,如下:

  1. import org.springframework.web.bind.annotation.*;
  2. import org.springframework.web.multipart.MultipartFile;
  3. @RestController
  4. public class FileAndDataUploadController {
  5. @PostMapping("/upload")
  6. public String handleFileUpload(@ModelAttribute MyData data) {
  7. // 处理普通字段
  8. String name = data.getName();
  9. String description = data.getDescription();
  10. // 处理文件上传
  11. MultipartFile file = data.getFile();
  12. if (!file.isEmpty()) {
  13. try {
  14. // 保存文件到服务器(这里仅为示例,实际中你需要处理文件路径和文件名)
  15. byte[] bytes = file.getBytes();
  16. // 你可以在这里添加代码将文件保存到数据库或文件系统中
  17. // 返回成功消息
  18. return "文件上传成功,普通字段也已接收";
  19. } catch (Exception e) {
  20. e.printStackTrace();
  21. // 返回错误消息
  22. return "文件上传失败:" + e.getMessage();
  23. }
  24. } else {
  25. // 文件为空时的处理
  26. return "上传的文件为空";
  27. }
  28. }
  29. // 用于接收普通字段和文件的数据模型
  30. public static class MyData {
  31. private String name;
  32. private String description;
  33. private MultipartFile file; // 用于接收文件的字段
  34. // 省略getter和setter方法...
  35. public String getName() {
  36. return name;
  37. }
  38. public void setName(String name) {
  39. this.name = name;
  40. }
  41. public String getDescription() {
  42. return description;
  43. }
  44. public void setDescription(String description) {
  45. this.description = description;
  46. }
  47. public MultipartFile getFile() {
  48. return file;
  49. }
  50. public void setFile(MultipartFile file) {
  51. this.file = file;
  52. }
  53. }
  54. }

 

3.6、Restful 接口风格

3.6.1、@GetMapping

a)使用场景:

  • 从服务器上获取数据.
  • 幂等,不会改变服务器上任何资源.

b)路径参数:

  • 路径参数一般为唯一标识符,例如 /users/{id},使用 @PathVariable 获取对应值.

c)请求携带数据位置:

  • GET 请求一般没有请求体.  数据都在 query string 中,使用 @RequestParam 获取对应的值.

3.6.2、@PostMapping

a)使用场景:

  • 向服务器提交数据.
  • 不幂等,多次发送相同 Post 请求可能会产生不同结果.

b)路径参数:

  • Post 请求可以包含路径参数,一般用来指定资源的唯一标识,例如 /users/{userId}/posts,使用 @RequestParam 获取对应的值.

c)请求携带数据位置:

  • POST 请求数据一般在请求体 body 中,使用 @RequestBody 接收.

3.6.3、@PutMapping

a)使用场景:

  • PUT 请求一般用户更新整个资源,而不仅仅是其中一部分.
  • 幂等,多次执行相同的 PUT 请求产生的结果相同.

b)路径参数:

  • PUT 请求通常使用路径参数来指定要更新的资源的唯一标识符(如 /user/{id}).

c)请求携带数据位置:

  • PUT 请求数据一般放在请求体 body 中.

3.6.4、@PatchMapping

a)使用场景:

  • 与 PUT 请求不同,PATCH 请求一般只对现有资源进行部分更新,而不是这个资源.

b)路径参数:

  • PATCH 请求通常使用路径参数来指定要更新的资源的唯一标识符(如 /user/{id}).

c)请求携带数据位置:

  • PATHCH 请求的数据通常放在请求体 body 中.

3.6.5、@DeleteMapping

a)使用场景:

  • 表示删除服务器中的指定资源.
  • 幂等,多次执行相同请求结果一致.

b)路径参数:

  • DELETE 请求通常使用路径参数来指定要删除的资源的唯一标识符(如 /user/{id}).

c)请求携带数据位置:

  • DELETE 一般不需要请求体,而是通过 query string 或 路径 进行传参.

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/正经夜光杯/article/detail/862314
推荐阅读
相关标签
  

闽ICP备14008679号