Spring MVC

什么是 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 页面了。重定向是发生在客户端的行为,不会经过服务器端。

  • 10
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Lockey-s

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值