当前位置: 首页 > news >正文

Spring MVC

Spring MVC

  • 什么是 MVC
    • MVC 定义
    • MVC 和 Spring MVC 的关系
    • Spring MVC 的影响
  • 创建 Spring MVC 项目
  • Spring MVC 作用
    • 实现用户 和 程序的映射
    • 获取用户请求参数
      • 获取单个参数
      • 获取多个参数
    • 获取对象
    • 后端参数重命名
    • 接收 JSON 对象
    • 从 URL 地址当中获取参数
    • 上传文件
      • 配置文件保存路径
      • 图片名不能重复
      • 获取图片的格式
  • 获取 Cookie/Session/header
    • 获取 Request 和 Response 对象
    • 获取 Cookie
    • 获取 Header
    • 存储和获取 Session
      • 设置 Session
      • 获取 Session
  • 返回数据
    • 使用 @RestController
  • 练习
    • form 表单
    • Ajax
      • 通过 JSON 来传输
  • 请求转发和请求重定向
    • 请求转发
    • 请求重定向

什么是 MVC

Spring MVC 其实是 Spring Web MVC,是基于 Servlet API,所以 Servlet 是 Spring MVC 的 “父亲”。因此,Servlet 那一套编程方法,在 Spring MVC 中,也是可以使用的。但是 Spring MVC 比 servlet 要简单很多。

Spring MVC :一开始就在 Spring 框架当中。只是属于 Spring 中的一个 web 模块。

一个 web 项目,只做三件事

  1. 实现用户请求 到 程序 的 链接,就是用户请求可以被 程序接收到。
  2. 在前后端建立联系的情况下,拿到用户请求的参数。
  3. 拿到参数之后,进行业务处理,并将其结果返回给前端。
  4. 使用 Spring MVC 的话,只需要前后端的名称一样就行了。

MVC 定义

MVC 是 Model View Controller (模型视图控制器)的缩写,它是软件⼯程中的⼀种软件架构模式,它把软件系统分为模型、视图和控制器三个基本部分。
在这里插入图片描述

  1. Model(模型) 是应用程序中用于处理应用程序数据逻辑的部分。通常模型对象负责在数据库中存取数据。
  2. View(视图) 是应用程序中处理数据显示的部分,通常视图是依据模型数据创建的。
  3. Controller(控制器) 是应⽤程序中处理⽤户交互的部分。通常控制器负责从视图读取数据,控制⽤户输⼊,并向模型发送数据。

四者之间的关系如下
在这里插入图片描述

  1. 可以把 View,Controller,Model 看作是一个软件(项目)。
  2. 然后用户通过浏览器发送一个 HTTP 请求到 Controller ,然后 Controller 完成对数据的校验,校验数据的正确性和合法性。
  3. Controller 校验数据通过之后,就会将请求数据发送到 model,然后 model 对数据库进行处理之后,再把结果发给 Controller
  4. 然后 COntroller 在把得到的数据交给 View
  5. View 是服务器的视图层,然后和前端的模板相结合,再加上渲染,然后发给客户,客户就能看懂了。

MVC 和 Spring MVC 的关系

两者的关系,就像 IoC 和 DI 的关系。前者是思想,后者是实现。

Spring MVC 是⼀个实现了 MVC 模式,并继承了 Servlet API 的 Web 框架。因为是 Web 框架,所以⽤户在浏览器中输⼊了 url 之后,我们的 Spring MVC 项 ⽬就可以感知到⽤户的请求。

Spring MVC 的影响

现在绝大部分的 Java 项目都是基于 Spring(或 Spring Boot)的,而 Spring 的核心就是 Spring MVC。现在大部分的 Java 项目都是 Spring MVC 项目。

创建 Spring MVC 项目

创建项目和我们之前创建 Spring Boot 项目一样,只是在添加依赖的时候,选择 Spring web 就好了:
在这里插入图片描述
这样就完成了 Spring MVC 项目的创建了。

Spring MVC 作用

  1. 实现 用户 和 程序 的映射(在浏览器输入 URL 之后,能够在程序中匹配到相应的方法)
  2. 服务器端要得到用户的请求参数
  3. 服务器端要将结果返回给用户(前端)

实现用户 和 程序的映射

  1. 使用 @RequestMapping(“/xxx”) 映射,可以加在 类上面,就是一级目录,然后方法上面再加的话,就是二级目录:

    @Controller
    @ResponseBody
    @RequestMapping("/user")
    public class UserController {
    
        @RequestMapping("/sayhi")
        public String sayHi() {
            return "hello world";
        }
    }
    

    访问方法如下:
    在这里插入图片描述

    user 是一级目录,sayhi 是二级目录。默认是 GET 请求:
    在这里插入图片描述

  2. 使用 POST 请求来访问资源,通过 method 参数来设置:

    @Controller
    @ResponseBody
    @RequestMapping("/user")
    public class UserController {
    
        @RequestMapping(method = RequestMethod.POST, value = "/sayhi")
        public String sayHi() {
            return "hello world";
        }
    }
    

    访问结果如下:
    在这里插入图片描述
    就支持 POST 访问了。

  3. 使用 @PostMapping 来指定 POST 访问:

    @Controller
    @ResponseBody
    @RequestMapping("/user")
    public class UserController {
    
        @PostMapping("/sayhi")
        public String sayHi() {
            return "hello world";
        }
    }
    

    访问结果如下:
    在这里插入图片描述

  4. 通过 GetMapping 来进行 GET 访问:

    @Controller
    @ResponseBody
    @RequestMapping("/user")
    public class UserController {
    
        @GetMapping("/sayhi")
        public String sayHi() {
            return "hello world";
        }
    }
    

    访问结果如下:
    在这里插入图片描述

获取用户请求参数

获取单个参数

通过传入参数,然后返回对象:

@Controller
@ResponseBody
@RequestMapping("/user")
public class UserController {

    @GetMapping("/getuserbyid")
    public UserInfo getUserById(Integer id) {
        UserInfo userInfo = new UserInfo();
        userInfo.setId(id);
        userInfo.setUsername("zhangsan");
        userInfo.setAge(18);
        return userInfo;
    }
}

传入 id = 1,只要保证传入的参数和接收的参数一样就好了,运行结果如下:
在这里插入图片描述
使用 Integer 是可以接收 null 的。

获取多个参数

通过传多个参数来获取:

@Controller
@ResponseBody
@RequestMapping("/user")
public class UserController {

    @RequestMapping("/login")
    public String login(String username, String password) {
        return "用户名:" + username + " 密码:" + password;
    }
}

运行结果如下:
在这里插入图片描述
也就是获取多个参数和获取一个参数的方法是一样的,只需要传递多个参数就好了。

获取对象

如果有多个参数的时候,通过传对象可以让结果看的更简洁:

@Controller
@ResponseBody
@RequestMapping("/user")
public class UserController {

    @RequestMapping("/reg")
    public String login(UserInfo userInfo) {
        return "用户信息:" + userInfo;
    }
}

前端传这样的参数:

http://127.0.0.1:8080/user/reg?username=zhangsan&password=123&age=18

运行结果如下:
在这里插入图片描述

后端参数重命名

某些特殊情况下,前端传递的参数 key 和我们后端接收的 key 可能不一致比如前端传递了 name 之后,后端是用 username 来接收的,那么就会报错。通过 @RequestParam 来对存在念书重命名:

@Controller
@ResponseBody
@RequestMapping("/user")
public class UserController {

    @RequestMapping("/reg")
    public String login(@RequestParam("name") String username) {
        return "用户名:" + username;
    }
}

前端参数:

http://127.0.0.1:8080/user/reg?name=zhangsan

运行结果如下:

在这里插入图片描述
也就是当使用了这个注解之后,前端必须要传一个 name 参数。否则就报错。如果加了 required 之后,设置 required 的参数为 false,因为默认是 true,就不会报错了,会显示用户名是 null:

@Controller
@ResponseBody
@RequestMapping("/user")
public class UserController {

    @RequestMapping("/reg")
    public String login(@RequestParam(value = "name", required = false) String username) {
        return "用户名:" + username;
    }
}

运行结果如下:
在这里插入图片描述

接收 JSON 对象

使用 RequestBody 注解来完成对象的接收,后端代码如下:

@Controller
@ResponseBody
@RequestMapping("/user")
public class UserController {

    @RequestMapping("/reg")
    public String login(@RequestBody UserInfo userInfo) {
        return "用户信息:" + userInfo;
    }
}

然后通过 Postman 来模拟:
在这里插入图片描述

从 URL 地址当中获取参数

这里并不是从 URL 地址中的参数部分获取参数,而是从 / 来作为分隔符获取参数,通过 @PathVariable 注解来实现

@Controller
@ResponseBody
@RequestMapping("/user")
public class UserController {

    @RequestMapping("/info/{id}/{name}")
    public String getURLInfo(@PathVariable Integer id, @PathVariable String name) {
        return "ID:" + id + " name:" + name;
    }
}

前端 URL:

http://127.0.0.1:8080/user/info/2/zhangsan

运行结果如下:
在这里插入图片描述
在这里插入图片描述

上传文件

通过 @RequestPart 注解来实现对文件的上传,只需要对参数加上注解就好了。然后通过 Spring 的 MultipartFile 来接收文件就好了:

@Slf4j
@Controller
@ResponseBody
@RequestMapping("/user")
public class UserController {

    @RequestMapping("/upload")
    public boolean upImg(Integer id, @RequestPart("image") MultipartFile file) {
        boolean result = false;
        //保存图片到本地目录
        try {
            file.transferTo(new File("D:/img.png"));
        } catch (IOException e) {
            log.error("上传图片失败: " + e.getMessage());
        }
        return result;
    }
}

Postman 模拟如下:
在这里插入图片描述
D 盘的文件如下:
在这里插入图片描述
但是如果这样每次的名字都一样的话,就会导致文件覆盖。

配置文件保存路径

直接把路径放在配置文件当中,这样的话,如果项目上线也很容易修改,直接修改为线上环境就好了,application 是必不可少的,不过可以通过 - 在 application 后面 加上名称。通过 application.yml 来配置 配置文件的运行环境:

# 设置配置文件的运行平台
spring:
  profiles:
    active: dev

在这里插入图片描述
开发环境配置
在这里插入图片描述
生产环境如下
在这里插入图片描述
文件路径

@Controller
@ResponseBody
@RequestMapping("/user")
public class UserController {

    //从配置文件中读取图片的保存路径
    @Value("${img.path}")
    private String imgPath;

    @RequestMapping("/sayHi")
    public String sayHi() {

        return "文件路径:" + imgPath;
    }
}

访问结果如下:
在这里插入图片描述
这样就完成了对图片路径的访问。换成生产环境也一样可以。

图片名不能重复

让图片名不能重复,通过 UUID 来让图片永远不重复。UUID 是自动生成一个全球不同的一串数字。如果用时间戳的话,总会有两个人同时传数据,所以就会导致内容覆盖。

获取图片的格式

就是获取原图片的格式,不能向我们上面这样,改变图片的格式。从最后一个点,向后截取,就能获取到文件的后缀名了:

@Slf4j
@Controller
@ResponseBody
@RequestMapping("/user")
public class UserController {

    //从配置文件中读取图片的保存路径
    @Value("${img.path}")
    private String imgPath;

    @RequestMapping("/upload")
    public boolean upImg(Integer id, @RequestPart("image") MultipartFile file) {
        boolean result = false;
        //保存图片到本地目录

        String fileName = file.getOriginalFilename();
        fileName = fileName.substring(fileName.lastIndexOf("."));
        fileName = UUID.randomUUID().toString() + fileName;
        try {
            file.transferTo(new File(imgPath + fileName));
            result = true;
        } catch (IOException e) {
            log.error("上传图片失败: " + e.getMessage());
        }
        return result;
    }
}

Postman 模拟如下:
在这里插入图片描述
保存路径内容如下:
在这里插入图片描述

获取 Cookie/Session/header

获取 Request 和 Response 对象

web 程序里面都会有 Request 和 Response 参数,只需要在需要的时候,直接使用就好了:

@Controller
@ResponseBody
@RequestMapping("/user")
public class UserController {

    @RequestMapping("/sayhi")
    public String sayHi(HttpServletRequest request) {
        return "hello world " + request.getParameter("name");
    }
}

运行结果如下:
在这里插入图片描述

获取 Cookie

直接使用 @CookieValue 就好了,先设置好 Cookie:
在这里插入图片描述
然后输出:

@Controller
@ResponseBody
@RequestMapping("/user")
public class UserController {

    @RequestMapping("/cookie")
    public String getCookie(@CookieValue("lisi") String cookie) {
        return "Cookie Value" + cookie;
    }
}

运行结果如下:
在这里插入图片描述
如果要读取多个 cookie 的话,就设置多个 Cookie 参数。

获取 Header

直接通过 @RequestHeader 的注解来实现获取 Header,我们获取 Header 里面的 user-agent:

@Controller
@ResponseBody
@RequestMapping("/user")
public class UserController {

    @RequestMapping("/header")
    public String getCookie(@RequestHeader("User-Agent") String userAgent) {
        return "user-Agent: " + userAgent;
    }
}

运行结果如下:
在这里插入图片描述

存储和获取 Session

设置 Session

听过 HttpSession 来设置 session:

@Controller
@ResponseBody
@RequestMapping("/user")
public class UserController {

    @RequestMapping("/header")
    public boolean setSession(HttpServletRequest request) {
        boolean result = false;
        //得到 Session 
        HttpSession session = request.getSession(true);//true 是创建会话
        //使用 setAtt 设置值
        session.setAttribute("userinfo", "userinfo");
        return true;
    }
}

运行结果如下,设置 Session 就是多了一个 JSESSIONID :
在这里插入图片描述

获取 Session

Spring Boot 通过 @SessionAttribute 注解就可以获取到 Session 了:

@Controller
@ResponseBody
@RequestMapping("/user")
public class UserController {

    @RequestMapping("/getsession")
    public String getSession(@SessionAttribute(value = "userinfo",required = false) String userinfo) {
        return "会话:" + userinfo;
    }
}

访问结构如下:
在这里插入图片描述
加上 required 的时候,如果 session 当中没有 此属性 的时候,就不会报错了。

返回数据

如下代码:

@Controller
@RequestMapping("/user")
public class UserController {

    @RequestMapping("/sayhi")
    public String sayHi() {
        return "hello";
    }
}

这样返回的其实是一个 静态页面 的结果:
在这里插入图片描述
把代码当中换成 hello.html ,然后写一个页面,就可以了:

@Controller
public class UserController {

    @RequestMapping("/sayhi")
    public String sayHi() {
        return "hello.html";
    }
}

运行结果如下:
在这里插入图片描述
@ResponseBody,就表示返回非静态页面的数据。

使用 @RestController

通过 @RestController 注解,就可以直接返回非静态页面的数据了:

@RestController
public class UserController {

    @RequestMapping("/sayhi")
    public String sayHi() {
        return "hello.html";
    }
}

运行结果如下:
在这里插入图片描述

练习

form 表单

通过 MVC 和 form 表单来实现。前端代码:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>add</title>
    </head>
    <body>
        <form action="/calc">
            <h1>计算器</h1>
            数字1:<input name="num1" type="text"><br>
            数字2:<input name="num2" type="text"><br>
            <input type="submit" value=" 点击相加 ">
        </form>
    </body>
</html>

后端接受的时候,路由也是 /calc 来接收,然后返回页面数据就好了:

@RestController
public class CalcController {

     @RequestMapping("/calc")
    public String calc(Integer num1, Integer num2) {
         if (num1 == null || num2 == null) {
             return "<h1>参数错误!</h1><a href='javascript:history.go(-1);'>返回</a>";
         }
         return "<h1>结果:" + (num1 + num2) + "</h1><a href='javascript:history.go(-1);'>返回</a>";
    }
}

后端返回的结果,这里写成了 h1 标签。运行结果如下:
在这里插入图片描述
点击之后效果如下:
在这里插入图片描述

Ajax

通过 Ajax 来完成用户的登录,通过模拟登录来实现用户交互,前端代码如下:

<!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">
    <script src="js/jquery-1.9.1.min.js"></script>
    <title>Document</title>
    <script>
      //ajax 提交
      function mysub(){
        //1.判空   也可以把 jQuery 符号换成 $
        var username = jQuery("#username");
        var password = jQuery("#password");
        //使用 trim 方法来去掉空格
        if(jQuery.trim(username.val())==""){
          alert("请先输入用户名!");
          //把光标重新放到元素上面
          username.focus();
          return;
        }
        if(jQuery.trim(password.val())==""){
          alert("请先输入密码!");
          password.focus(); //光标重制到此元素
          return;
        }
        jQuery.ajax({
          url:"/login",
          type:"POST",
          data:{"username":username.val(),
            "password":password.val()},
          success:function(result){
            alert(JSON.stringify(result));
          }
        });
      }
    </script>
  </head>
  <body>
    <div style="text-align: center;">
      <h1>登录</h1>
      用户:<input id="username">
      <br>
      密码:<input id="password" type="password">
      <br>
      <input type="button" value=" 提交 " onclick="mysub()" style="margin-top: 20px;margin-left: 50px;">
    </div>
  </body>
</html>

后端代码如下:

@RestController
public class UserController {

    @RequestMapping("/login")
    public HashMap<String, Object> login(String username, String password) {
        HashMap<String, Object> result = new HashMap<>();
        int state = 200;
        int data = -1;//等于 1 的话,就表述登陆成功
        String msg = "";
        if (StringUtils.hasLength(username) && StringUtils.hasLength(password)) {
            if (username.equals("admin") && password.equals("admin")) {
                data = 1;
                msg = "";
            }
        } else {
            msg = "非法参数";
        }
        result.put("state", state);
        result.put("data", data);
        result.put("msg", msg);
        return result;
    }
}

运行结果如下:
在这里插入图片描述
故意填错密码:
在这里插入图片描述
填对密码的时候,就和我们约定的结果一样了:
在这里插入图片描述

通过 JSON 来传输

通过 JSON 来传输数据的时候,后端就要通过 @RequeestBody 来接收了。然后前端也要指定为 JSON 格式发送。就是转化为 JSON 字符串就好了。前端代码如下:

<!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">
    <script src="js/jquery-1.9.1.min.js"></script>
    <title>Document</title>
    <script>
      //ajax 提交
      function mysub(){
        //1.判空   也可以把 jQuery 符号换成 $
        var username = jQuery("#username");
        var password = jQuery("#password");
        //使用 trim 方法来去掉空格
        if(jQuery.trim(username.val())==""){
          alert("请先输入用户名!");
          //把光标重新放到元素上面
          username.focus();
          return;
        }
        if(jQuery.trim(password.val())==""){
          alert("请先输入密码!");
          password.focus(); //光标重制到此元素
          return;
        }
        jQuery.ajax({
          url:"login",
          type:"POST",
          contentType:"application/json",
          data:JSON.stringify({"username":username.val(),
            "password":password.val()}),
          success:function(result){
            alert(JSON.stringify(result));
          }
        });
      }
    </script>
  </head>
  <body>
    <div style="text-align: center;">
      <h1>登录</h1>
      用户:<input id="username">
      <br>
      密码:<input id="password" type="password">
      <br>
      <input type="button" value=" 提交 " onclick="mysub()" style="margin-top: 20px;margin-left: 50px;">
    </div>
  </body>
</html>

后端代码如下:

@RestController
public class UserController {

    @RequestMapping("/login")
    public HashMap<String, Object> login(@RequestBody UserInfo userInfo) {
        HashMap<String, Object> result = new HashMap<>();
        int state = 200;
        int data = -1;//等于 1 的话,就表述登陆成功
        String msg = "";
        if (StringUtils.hasLength(userInfo.getUsername()) && StringUtils.hasLength(userInfo.getPassword())) {
            if (userInfo.getUsername().equals("admin") && userInfo.getPassword().equals("admin")) {
                data = 1;
                msg = "";
            } else {
                msg = "用户名或密码不正确";
            }
        } else {
            msg = "用户名或密码不正确";
        }
        result.put("state", state);
        result.put("data", data);
        result.put("msg", msg);
        return result;
    }
}

运行结果如下:
在这里插入图片描述

请求转发和请求重定向

请求转发是 forward,请求重定向是 redirect。

请求转发

通过 forward 来实现请求转发:

@Controller
public class UserController {

    @RequestMapping("/myforward")
    public String myForward() {
        return "forward:/hello.html";
    }
}

forward 后面加的是请求转发路径。运行结果如下:
在这里插入图片描述
直接到了 hello.html 页面了。

请求重定向

请求重定向是 redirect :

@Controller
public class UserController {

    @RequestMapping("/myredirect")
    public String myRedirect() {
        return "redirect:/hello.html";
    }
}

运行结果如下:
在这里插入图片描述
这里就是直接访问 hello.html 页面了。重定向是发生在客户端的行为,不会经过服务器端。

相关文章:

  • MySQL 日志管理
  • 机器学习之特征选择
  • 高薪程序员面试题精讲系列149之你熟悉单点登录吗?说说单点登录的实现原理及流程
  • 【Unity3D】顶点和片段着色器
  • jmeter实战
  • 【零基础学QT】第十章 项目打包,利用Inno Setup制作软件安装包
  • LeetCode合并有序数组
  • 微信小程序分享一个视频给好友
  • 南大通用GBase8s 常用SQL语句(260)
  • Windows11 VMware-Ubuntu-Android12 源码下载和编译
  • javaMVC土特产交易平台系统计算机毕业设计MyBatis+系统+LW文档+源码+调试部署
  • 南大通用GBase8s 常用SQL语句(256)
  • 01-JVM-类加载篇
  • C#基础--泛型
  • 【云原生kubernetes从入门到实践系列教程 ] 四.docker volumes持久化
  • Google 是如何开发 Web 框架的
  • CSS3 聊天气泡框以及 inherit、currentColor 关键字
  • gops —— Go 程序诊断分析工具
  • iOS编译提示和导航提示
  • iOS高仿微信项目、阴影圆角渐变色效果、卡片动画、波浪动画、路由框架等源码...
  • node学习系列之简单文件上传
  • Spring核心 Bean的高级装配
  • STAR法则
  • 从零开始的webpack生活-0x009:FilesLoader装载文件
  • 第三十一到第三十三天:我是精明的小卖家(一)
  • 对话 CTO〡听神策数据 CTO 曹犟描绘数据分析行业的无限可能
  • 巧用 TypeScript (一)
  • 视频flv转mp4最快的几种方法(就是不用格式工厂)
  • 异步
  • HanLP分词命名实体提取详解
  • 直播平台建设千万不要忘记流媒体服务器的存在 ...
  • ​软考-高级-信息系统项目管理师教程 第四版【第23章-组织通用管理-思维导图】​
  • #【QT 5 调试软件后,发布相关:软件生成exe文件 + 文件打包】
  • #define 用法
  • #周末课堂# 【Linux + JVM + Mysql高级性能优化班】(火热报名中~~~)
  • (webRTC、RecordRTC):navigator.mediaDevices undefined
  • (二)换源+apt-get基础配置+搜狗拼音
  • (翻译)Quartz官方教程——第一课:Quartz入门
  • (附源码)springboot家庭装修管理系统 毕业设计 613205
  • (三分钟)速览传统边缘检测算子
  • (四)模仿学习-完成后台管理页面查询
  • (一)kafka实战——kafka源码编译启动
  • (转)Scala的“=”符号简介
  • (转)拼包函数及网络封包的异常处理(含代码)
  • (转载)VS2010/MFC编程入门之三十四(菜单:VS2010菜单资源详解)
  • ./和../以及/和~之间的区别
  • .NET CORE 2.0发布后没有 VIEWS视图页面文件
  • .Net core 6.0 升8.0
  • .net core 源码_ASP.NET Core之Identity源码学习
  • .NET Core实战项目之CMS 第一章 入门篇-开篇及总体规划
  • .net 提取注释生成API文档 帮助文档
  • .NET 中小心嵌套等待的 Task,它可能会耗尽你线程池的现有资源,出现类似死锁的情况
  • .NET/C# 编译期能确定的字符串会在字符串暂存池中不会被 GC 垃圾回收掉
  • .NET开源全面方便的第三方登录组件集合 - MrHuo.OAuth
  • .net流程开发平台的一些难点(1)