Spring Web MVC 入门

news2024/10/28 18:29:27

1. 什么是 Spring Web MVC

Spring Web MVC 是基于 Servlet API 构建的原始 Web 框架,从从⼀开始就包含在Spring框架中。它的 正式名称“SpringWebMVC”来⾃其源模块的名称(Spring-webmvc),但它通常被称为"Spring MVC".

什么是Servlet呢?

Servlet 是⼀种实现动态⻚⾯的技术.准确来讲Servlet是⼀套JavaWeb开发的规范,或者说是⼀套 Java Web开发的技术标准.只有规范并不能做任何事情,必须要有⼈去实现它.所谓实现Servlet规 范,就是真正编写代码去实现Servlet规范提到的各种功能,包括类、⽅法、属性等. Servlet 规范是开放的,除了Sun公司,其它公司也可以实现Servlet规范,⽬前常⻅的实现了 Servlet 规范的产品包括Tomcat、Weblogic、Jetty、Jboss、WebSphere等,它们都被称 为"Servlet 容器".Servlet 容器⽤来管理程序员编写的Servlet类

从上述定义我们可以得出一个信息:Spring Web MVC 是一个 Web 框架

以下简称为 Spring MVC

2. MVC 定义

MVC是ModelViewController的缩写,它是软件⼯程中的⼀种软件架构设计模式,它把软件系统分 为模型、视图和控制器三个基本部分

View(视图):指在应用程序中专门用来与浏览器进行交互,展示数据的资源

Model(模型):是应用程序的主体部分,用来处理程序中数据逻辑的部分

Controller(控制器):可以理解为一个分发器,用来决定对于视图发来的请求,需要用哪一个模型来处理,以及处理完后需要跳回到哪一个视图,即用来连接视图和模型

⽐如去饭店吃饭

客⼾进店之后,服务员来接待客⼾点餐,客⼾点完餐之后,把客⼾菜单交给前厅,前厅根据客⼾菜单 给后厨下达命令.后厨负责做饭,做完之后,再根据菜单告诉服务员,这是X号餐桌客⼈的饭. 在这个过程中

服务员就是View(视图),负责接待客⼾,帮助客⼾点餐,以及给顾客端饭

前厅就是Controller(控制器),根据⽤⼾的点餐情况,来选择给哪个后厨下达命令

后厨就是Model(模型),根据前厅的要求来完成客⼾的⽤餐需求

3. Spring MVC

MVC是⼀种架构设计模式,也是⼀种思想, ⽽SpringMVC是对MVC思想的具体实现.除此之外, Spring MVC还是⼀个Web框架.

总结来说,SpringMVC是⼀个实现了MVC模式的Web框架.

所以,SpringMVC主要关注有两个点:

1. MVC

2. Web 框架

前面创建 Spring Boot 项目时,勾选的 Spring Web 框架就是 Spring MVC 框架

Spring Boot 是实现 Spring MVC 的一种方式,Spring Boot 可以添加很多依赖,借助这些依赖实现不同功能,其通过添加 Spring Web MVC 框架来实现 Web 功能

而 Spring 在实现 MVC 时,也结合自身项目特点,做了一些改变,相对而言,用下面这个图来描述 Soring MVC 更加合适一些:

4. 使用 Spring MVC

使用 Spring MVC 就是通过浏览器和用户程序进行交互

主要分为以下三个方面:

1. 建立连接:将用户(浏览器)和 Java 程序连接起来,也就是访问一个地址能够调用到我们的 Spring 程序

2. 请求:用户请求的时候会带一些参数,在程序中要想办法获取参数,所以请求主要是获取参数的功能

3. 响应:执行了业务逻辑之后,要把程序执行的结果返回给用户,也就是响应

4.1 项目准备

Spring MVC 项目创建和 Spring Boot 创建项目相同,在创建的时候选择 Spring Web 就相当于创建了 Spring MVC 的项目

4.2 建立连接

在 Spring MVC 中使用 @RequestMapping 来实现 URL 路由映射,也就是浏览器连接程序的作用

创建一个 UserController 类,实现用户通过浏览器和程序的交互,代码如下:

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {
    @RequestMapping("/sayHi")
    public String sayHi() {
        return "Hello Spring MVC";
    }
}

tip:方法名和路径名可以不同

接下来访问:http://127.0.0.1:8080/sayHi 就可以看到程序返回的数据了

4.2.1 @RequestMapping 注解介绍

@RequestMapping 是 Spring Web MVC 应用程序中最常被用到的注解之一,它是用来注册接口的路由映射的

表示服务收到请求时,路径为 /sayHi 的请求就会调用 sayHi 这个方法的代码

路由映射:当用户访问一个 URL 时,将用户的请求对应到程序中某个类的某个方法的过程叫做路由映射

其与 @RequestController 需要同时存在,若注释掉 @RequestController

一个项目中会有很多类,每个类中又有很多方法,当我们要访问 /sayHi 时,Spring 会对所有类进行扫描,只有类加了注解 @RequestController,Spring 才会去看这个类里面有没有 @RequestMapping("/sayHi"),如果有,就执行该方法;若是类没有加注解 @RequestController,Spring就不会进入该类,即使该类中有 @RequestMapping("/sayHi")

4.2.2 @RequestMapping 使用

@RequestMapping 既可以修饰类,也可以修饰方法,当修饰类和方法时,访问的地址是类路径 + 方法路径

@RequestMapping 标识一个类:设置映射请求的请求路径的初始信息

@RequestMapping 标识一个方法:设置映射请求路径的具体信息

@RequestMapping("/user")
@RestController
public class UserController {
    @RequestMapping("/sayHi")
    public String sayHi() {
        return "Hello Spring MVC";
    }
}

访问地址:http://127.0.0.1:8080/user/sayHi

tip:

1. @RequestMapping 的 URL 路径最前面加不加 / 都可以,Spring 程序启动时会进行判断,如果前面没有加 /,Spring 会拼接上一个 /通常情况下,建议加上 /

2. 注解没有先后顺序之分

4.2.3  @RequestMapping 是 GET 还是 POST 请求

GET 请求:

浏览器发送的请求类型都是 get,通过上面案例可知 @RequestMapping 支持 get 请求

通过 Fiddler 也可以观察到:

POST 请求:

通过 form 表单来构造请求:创建 test.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/user/sayHi" method="post">
        <input type="submit" value="提交">
    </form>
</body>
</html>

从运行结果可以看出:@RequestMapping 既支持 get 请求,又支持 post 请求,同理,也支持其他的请求方式

4.2.4 指定 GET/POST 方法类型

方法一:我们可以显式的指定 @RequestMapping 来接收 POST 的情况,如下:

方法二:使用 @GetMapping 或 @PostMapping 注解来设置

@PostMapping 使用方法同理


5. 使用 Postman 创建请求

界面介绍

6. 请求

6.1 传递单个参数

接收单个参数,在 Spring MVC 中直接用方法中的参数就可以,如下代码:

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RequestMapping("/param")
@RestController
public class ParamController {
    @RequestMapping("/p1")
    public String p1(String name) {
        return "接收到参数,name:" + name;
    }
}

使用 Postman 构建发送请求:

Spring MVC 根据方法的参数名,找到对应的参数,赋值给方法

如果参数不一致,是获取不到参数的,如下:

tip:

使用基本类型来接收参数时,参数必须传(除非是 boolean 类型),否则会报 500 错误

类型不匹配时,会报 400 错误

使用包装类型,如果不传参数,Spring 接收到的数据则为 null,因此在企业开发中,对于参数可能为空的数据,建议使用包装类型

6.2 传递多个参数

和接收单个参数一样,直接使用方法的参数接收即可,如下:

tip:当有多个参数时,前后端进行参数匹配时,是以参数的名称进行匹配的,因此参数的位置是不影响后端获取参数的结果

6.3 传递对象

当参数比较多时,方法声明就需要有很多形参,并且后续每新增一个参数,也需要修改方法声明

因此我们不妨将这些参数封装成一个对象

Spring MVC 也可以自动实现对象参数的赋值,如下:

创建一个 User 对象:

package com.example.sprignmvc_demo_20241021;

public class User {
    private String name;
    private int age;
    private Integer gender;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Integer getGender() {
        return gender;
    }

    public void setGender(Integer gender) {
        this.gender = gender;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", gender=" + gender +
                '}';
    }
}

传递对象代码:

Spring 会根据参数名称自动绑定到对象的各个属性上,如果某个属性未传递,则赋值为 null(基本类型则赋值为默认初始值,如 int 被赋值为 0)

6.4 后端参数重命名(后端参数映射)

某些特殊情况下,前端传递的参数 key 和我们后端接收的 key 可以不一致,如:前端传递了一个 userName,而后端是使用 name 字段来接收的,这样就会出现参数接收不到的情况,如果出现这种情况,我们就可以使用 @RequestParam 重命名前后端的参数值,如下:

若此时前端使用 name 来传参:

查看 @RequestParam 的源码:

可以看到,required 的默认值为 true,表示的含义是:该注解修饰的参数默认为必传参数

既然有默认,就可以更改:

再次运行:

不报错了,但是接收不到前端传的 name 参数,这是因为使用 @RequestParam 进行参数重命名时,请求参数只能和 @RequestParam 声明的名称一致,才能进行参数绑定和赋值

6.5 传递数组

Spring MVC 可以自动绑定数组参数的赋值

像第二种方式,会自动进行分割,如下:

6.6 传递集合

这种传参方式和传数组是一样的,HTTP 默认将其封装成了一个数组,相当于是传递了一个数组,而 List 无法用来接收该数组

此时就需要使用 @RequestParam 来进行参数绑定,将数绑定成 List

6.7 传递 JSON 数据

6.7.1 JSON 概念

JSON:JavaScriptObjectNotation 【JavaScript 对象表⽰法】

JSON是⼀种轻量级的数据交互格式.它基于ECMAScript(欧洲计算机协会制定的js规范)的⼀个⼦集, 采⽤完全独⽴于编程语⾔的⽂本格式来存储和表⽰数据。--百度百科

简单来说:JSON就是⼀种数据格式,有⾃⼰的格式和语法,使⽤⽂本表⽰⼀个对象或数组的信息,因此 JSON本质是字符串. 主要负责在不同的语⾔中数据传递和交换.

6.7.2 JSON 和 JavaScript 的关系

没有关系,只是语法相似

6.7.3 JSON 语法

JSON 是一个字符串,其格式非常类似于 JavaScript 对象字面量的格式

看一段 JSON 数据:

{
	"squadName": "Super hero squad",
	"homeTown": "Metro City",
	"formed": 2016,
	"secretBase": "Super tower",
	"active": true,
	"members": [{
		"name": "Molecule Man",
		"age": 29,
		"secretIdentity": "Dan Jukes",
		"powers": ["Radiation resistance", "Turning tiny", "Radiation blast"]
	},
	{
		"name": "Madame Uppercut",
		"age": 39,
		"secretIdentity": "Jane Wilson",
		"powers": ["Million tonne punch", "Damage resistance", "Superhuman reflexes"]
	},
	{
		"name": "Eternal Flame",
		"age": 1000000,
		"secretIdentity": "Unknown",
		"powers": ["Immortality", "Heat Immunity", "Inferno", "Teleportation", "Interdimensional travel"]
	}]
}

也可以压缩表示:(和上面数据一样,只不过上面数据进行了格式化,更易读)

{"squadName":"Super hero squad","homeTown":"Metro City","formed":2016,"secretBase":"Super tower","active":true,"members":[{"name":"Molecule Man","age":29,"secretIdentity":"Dan Jukes","powers":["Radiation resistance","Turning tiny","Radiation blast"]},{"name":"Madame Uppercut","age":39,"secretIdentity":"Jane Wilson","powers":["Million tonne punch","Damage resistance","Superhuman reflexes"]},{"name":"Eternal Flame","age":1000000,"secretIdentity":"Unknown","powers":["Immortality","Heat Immunity","Inferno","Teleportation","Interdimensional travel"]}]}

JSON 的语法:

数据在 键值对(Key / Value)

数据由 逗号 分隔

对象用 { } 表示

数组用 [ ] 表示

值可以为对象,也可以为数组,数组中可以包含多个对象

JSON 的两种结构:

对象:大括号 { } 保存的对象是一个无序的 键值对 集合,一个对象以 左括号 { 开始,右括号 } 结束,每个 后跟一个冒号 : ,键值对使用 逗号 分隔

数组:中括号 [ ] 保存的数组是 值(Value) 的有序集合,一个数组以 左中括号 [ 开始,右中括号 ] 结束,值之间使用 逗号 分隔

6.7.4 JSON 字符串和 Java 对象互转

JSON 本质上是一个字符串,通过文本来存储和描述数据

Spring MVC 框架也集成了 JSON 的转换工具,我们可以直接使用,来完成 JSON 字符串和 Java 对象的互转

本质上是 jackson-databind 提供的转换功能,Spring MVC 框架中已经把该工具包引入了进来,我们可以直接使用,若要脱离 Spring MVC 使用,需要引入相关依赖,如下:

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.13.5</version>
</dependency>

JSON 的转换工具包有很多,jackson-databind 只是其中的一种

后端实现:

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class JsonTest {
    public static void main(String[] args) throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper(); // 该类来自于 jackson-databind,用于处理 JSON

        User user = new User();
        user.setName("lisi");
        user.setAge(18);
        user.setGender(1); // 表示 男

        // 对象转 JSON
        String s = objectMapper.writeValueAsString(user);
        System.out.println(s);

        // JSON 转对象
        User user1 = objectMapper.readValue(s, User.class);
        System.out.println(user1);
    }
}

使用 ObjectMapper 对象提供的两个方法,可以完成对象和 JSON 字符串的互转

writeValueAsString:把对象转为 JSON 字符串

readValue:把字符串转为对象

6.7.5 JSON 优点

简单易⽤:语法简单,易于理解和编写,可以快速地进⾏数据交换

跨平台⽀持: JSON可以被多种编程语⾔解析和⽣成,可以在不同的平台和语⾔之间进⾏数据交换和 传输

轻量级:相较于XML格式,JSON数据格式更加轻量级,传输数据时占⽤带宽较⼩,可以提⾼数据传输 速度

易于扩展: JSON的数据结构灵活,⽀持嵌套对象和数组等复杂的数据结构,便于扩展和使⽤

安全性:JSON数据格式是⼀种纯⽂本格式,不包含可执⾏代码,不会执⾏恶意代码,因此具有较⾼ 的安全性

6.8 传递 JSON 对象

接收 JSON 对象,需要使用 @RequestBody 注解

RequestBody:请求正文,这个注解作用在请求正文的数据绑定,请求参数必须写在请求正文中

后端实现:

    @RequestMapping("/p8")
    public String p8(@RequestBody User user) {
        return "user:" + user;
    }

tip:区分传递对象时使用 get 和 post 方式

区分传递对象与传递 JSON 对象

6.9 获取 URL 中参数(@PathVariable)

path variable:路径变量

这个注解主要作用在请求 URL 路径上的数据绑定

默认传递餐宿写在 URL 上,Spring MVC 就可以获取到

多个参数的情况:

tip:如果方法参数名称和需要绑定的 URL 中的变量名称一致时,可以简写,不用给 @PathVariable 的属性赋值,如上述例子中的 name 变量;反之则需要

6.10 传递文件(@RequestPart)

重命名:

6.11 获取 Cookie/Session

6.11.1 Cookie

HTTP 协议自身是属于 “无状态” 协议

无状态是指:默认情况下 HTTP 协议的客户端和服务器之间的这次通信和下次通信之间没有直接的联系

但是实际开发中,我们很多时候是需要知道请求之间的关联关系的

例如:登录网站成功后,第二次访问的时候,服务器就能知道该请求是否已经登录过了

上述途中的 “令牌” 通常就存储在 Cookie 字段中

⽐如去医院挂号

  1. 看病之前先挂号.挂号时候需要提供⾝份证号,同时得到了⼀张"就诊卡",这个就诊卡就相当于患 者的"令牌".
  2. 后续去各个科室进⾏检查,诊断,开药等操作,都不必再出⽰⾝份证了,只要凭就诊卡即可识别出当 前患者的⾝份.
  3. 看完病了之后,不想要就诊卡了,就可以注销这个卡.此时患者的⾝份和就诊卡的关联就销毁了.(类 似于⽹站的注销操作)
  4. ⼜来看病,可以办⼀张新的就诊卡,此时就得到了⼀个新的"令牌"

此时在服务器这边就需要记录 “令牌” 信息,以及令牌对应的用户信息,这个就是 Cookie机制所作的工作

6.11.2 Session

会话:就是对话的意思

在计算机领域,会话是一个客户与服务器之间的不中断的请求响应,对客户的每个请求,服务器能识别出请求来自于同一个客户

当一个位置的客户像 Web 应用程序发送第一个请求时,就开始了一个会话

当客户明确结束会话或服务器在一个时限内没有接收到客户的任何请求时,会话就结束了

⽐如我们打客服电话

每次打客服电话,是⼀个会话.挂断电话,会话就结束了

下次再打客服电话,⼜是⼀个新的会话.

如果我们⻓时间不说话,没有新的请求,会话也会结束.

服务器同一时刻收到的请求是很多的,服务器需要清楚的区分每个请求是属于哪个用户,也就是属于哪个会话,就需要在服务器这边记录每个会话以及与用户的信息的对应关系

Session 是服务器为了保存用户信息而创建的一个特殊的对象

Session 的本质就是一个 “哈希表”,存储了一些键值对结构,Key 就是 SessionID,Value 就是用户信息(用户信息可以根据需求灵活设计)

SessionID 是由服务器生成的一个 “唯一性字符串”,从 Session 机制的角度来看,这个唯一性字符串称为 “SessionID”,但是站在整个登录流程中看待,也可以把这个唯一性字符串称为 “token”

上述例子中的 令牌ID 就可以看作是 SessionID,只不过令牌除了 ID 之外,还会带有一些其他信息,比如时间、签名等

  1. 当用户登录的时候,服务器在 Session 中新增一个新记录,并把 SessionID 返回给客户端(通过 HTTP 响应中的 Set-Cookie 字段返回)
  2. 客户端后续再给服务器发送请求的时候,需要在请求中带上 SessionID(通过 HTTP 请求中的 Cookie 字段带上)
  3. 服务器收到请求之后,根据请求中的 SessionID 在 Session 信息中获取到对应的用户信息,再进行后续操作,找不到则重新创建 Session,并把 SessionID 返回

tip:Session 默认是保存在内存中的,如果重启服务器则 Session 数据就会丢失

6.11.3 Cookie 和 Session 的区别

  • Cookie 是客户端保存用户信息的一种机制;Session 是服务器端保存用户信息的一种机制
  • Cookie 和 Session 之间主要是通过 SessionID 关联起来的,SessionID 是 Cookie 和 Session 之间的桥梁
  • Cookie 和 Session 经常会在一起配合使用,但不是必须配合

完全可以用 Cookie 来保存一些数据在客户端,这些数据不一定是用户身份信息,也不一定是 SessionID

Session 中的 SessionID 也不是非得通过 Cookie / Set-Ckkoie 传递,比如通过 URL 传递

6.11.4 获取 Cookie

1) 传统方法获取:

    // 获取 Cookie
    @RequestMapping("/getCookie")
    public String getCookie(HttpServletRequest request) {
        Cookie[] cookies = request.getCookies();
        // cookie 为空判断
        // if (cookies == null) return "Cookie 为 null";
        for (Cookie cookie : cookies) {
            System.out.println(cookie.getName() + ":" + cookie.getValue());
        }
        return "Cookie 获取成功";
    }
  • Spring MVC是基于ServletAPI构建的原始Web框架,也是在Servlet的基础上实现的
  • HttpServletRequest , HttpServletResponse 是Servlet提供的两个类,是Spring MVC⽅法的内置对象.需要时直接在⽅法中添加声明即可.
  • HttpServletRequest 对象代表客⼾端的请求,当客⼾端通过HTTP协议访问服务器时,HTTP请 求头中的所有信息都封装在这个对象中,通过这个对象提供的⽅法,可以获得客⼾端请求的所有信 息.
  • HttpServletResponse 对象代表服务器的响应.HTTP响应的信息都在这个对象中,⽐如向客⼾ 端发送的数据,响应头,状态码等.通过这个对象提供的⽅法,可以获得服务器响应的所有内容
  • Spring MVC在这两个对象的基础上进⾏了封装,给我们提供更加简单的使⽤⽅法.

此时没有设置 Cookie,通过浏览器访问 http://127.0.0.1:8080/header/getCookie,得到 Cookie 为 null

在浏览其中设置 Cookie 的值(手动添加 Cookie)

再次访问:

2) 简洁方法获取:

    @RequestMapping("/getCookie2")
    public String getCookie2(@CookieValue("zhangsan") String value) {
        return "从 Cookie 中获取信息:" + value;
    }

运行结果:

两种方式对比:

第一种方法可以获取多次参数

第二种方法每获取一个参数都需要加一个注解


tip:Postman 设置 Cookie


6.11.5 获取 Session

1) Session 存储和获取

Session 是服务器端的机制,我们需要先存储,才能获取

Session 也是基于 HttpServletRequest 来存储和获取的

2) Session 存储

    @RequestMapping("/setSession")
    public String setSession(HttpServletRequest request) {
        // 先获取到 session 对象,若没有,则创建一个空 session
        HttpSession session = request.getSession();
        session.setAttribute("userName", "zhangsan");
        session.setAttribute("age", 18);
        return "设置 session 成功";
    }

第一次运行上述程序,是没有 session 对象的,可以在 Fiddler 中观察到 Set-Cookie 生成 SessionID:

3) Session 读取(使用 HttpServletRequest)

    @RequestMapping("/getSession")
    public String getSession(HttpServletRequest request) {
        HttpSession session = request.getSession();
        // session 是类似 map 的结构
        // 判空
        if (session.getAttribute("userName") == null) return "session 为 null";

        String userName = (String)session.getAttribute("userName");
        System.out.println(session.getAttribute("age"));
        return "从 session 中获取信息,userName:" + userName;
    }

request.getSession() 共做了两件事:先从 Cookie 中拿到 SessionID,然后从 SessionID 中拿到 Session

设置完 session 后,再获取 session:

可以看到,Http 请求时,将 SessionID 通过 Cookie 传递到了服务器

4) 简洁获取①

    @RequestMapping("/getSession2")
    public String getSession2(HttpSession session) {
        String userName = (String)session.getAttribute("userName");
        System.out.println(session.getAttribute("age"));
        return "从 session 中获取信息,userName:" + userName;
    }

tip:Session 存储在服务器的内村上,服务重启时,Session 会丢失

先设置 session,再获取

5) 简洁获取②

    @RequestMapping("/getSession3")
    public String getSession3(@SessionAttribute String userName) {
        return "从 session 中获取信息,userName:" + userName;
    }

tip:由于 Session 是服务器端的,只能由代码去构建,不能用 Postman 构建


6.11.6 获取 Header

1) 传统方式:

获取 Header 也是从 HttpServletRequest 中获取

    @RequestMapping("/getHeader")
    public String getHeader(HttpServletRequest request) {
        String userAgent = request.getHeader("User-Agent");
        return "User-Agent:" + userAgent;
    }

2) 简洁方式:

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

post 方式:在 Headers 选项中根据需求添加即可

7. 响应

7.1 返回静态页面

创建前端页面 hello.html

html 代码:

后端代码:

@RequestMapping("/response")
@RestController
public class ResponseController {
    @RequestMapping("/returnHtmlPage")
    public String returnHtmlPage() {
        return "/hello.html";
    }
}

我们期待返回如下界面:

结果却是:

这时需要将 @RestController 改为 @Controller:

7.1.1 @RestController 和 @Controller

二者的差异就在 @RequestBody 上,@ResponseBody表示返回数据

早期前后端未分离时,后端需要返回视图,就用到 @Controller

现在前后端分离,后端仅需给前端返回数据,具体展示哪个页面给用户由前端决定,因此用到 @ResponseBody,再加上 @Controller 中其他的作用,就成了现在的 @RequestController

tip:

@Controller:定义一个控制器,Spring 框架启动时加载,把这个对象交给 Spring 管理

@ResponseBody:定义返回的数据格式为非视图,返回一个 text/html 信息

7.2 返回数据 @ResponseBody

@ResponseBody 既是类注解,又是方法注解

如果作用在类上,表示该类的所有方法返回的都是数据

如果作用在方法上,表示该方法返回的是数据,其他方法不受影响

tip:将 returnData 方法上的 @ResponseBody 去掉,程序会报 404 错误

程序会认为需要返回的是视图,根据内容 "hahahahahahaha" 去查找文件,但是查找不到,路径不存在,报 404 异常

7.3 返回 HTML 代码片段

直接返回即可,当后端返回数据,若数据中有 HTML 代码,会被浏览器直接解析

通过 Fiddler 观察响应结果 Content-Type: text/html

响应中的 Content-Type 常见取值有以下几种:

  • text / html:body 数据格式是 HTML
  • text / css:body 数据格式是 CSS
  • application / javascript:body 数据格式是 JavaScript
  • application / json:body 数据格式是 JSON

如果请求的是 js 文件,Spring MVC 会自动设置 Content-Type 为 application / javascript

如果请求的是 css 文件,Spring MVC 会自动设置 Content-Type 为 text / css

7.4 返回 JSON

7.5 设置状态码

Spring MVC 会根据我们方法的返回结果自动设置响应状态码,也可以手动指定状态码

通过 Spring MVC 的内置对象 HttpServletResponse 提供的方法来进行设置

    @ResponseBody
    @RequestMapping("/setStatus")
    public User setStatus(HttpServletResponse response) {
        User user = new User();
        user.setName("zhangsan");
        user.setAge(18);
        response.setStatus(500);
        return user;
    }

7.6 设置 Header

http 响应报头也会向客户端传递一些附加信息,比如服务程序的名称;请求的资源已移动到新地址等,比如:Content-Type,Local 等

这些信息通过 @RequestMapping 注解的属性来实现,其源码如下:

7.6.1 通过设置 produces 属性的值,设置响应的报头 Content-Type

7.6.2 通过 HttpServletResponse 内置方法设置 Header

7.6.3 通过 HttpServletResponse 内置方法设置 Cookie

8. 案例

8.1 加法计算器

需求:输入两个整数,点击 “点击相加” 按钮,显示计算结果

1. 约定前后端交互接口

概念:

约定"前后端交互接⼝"是进⾏Web开发中的关键环节.

接⼝⼜叫API(ApplicationProgrammingInterface),我们⼀般讲到接⼝或者API,指的都是同⼀个东 西.

是指应⽤程序对外提供的服务的描述,⽤于交换信息和执⾏任务

简单来说,就是允许客⼾端给服务器发送哪些HTTP请求,并且每种请求预期获取什么样的HTTP响应.

现在"前后端分离"模式开发,前端和后端代码通常由不同的团队负责开发.双⽅团队在开发之前,会提前 约定好交互的⽅式.客⼾端发起请求,服务器提供对应的服务.服务器提供的服务种类有很多,客⼾端按 照双⽅约定指定选择哪⼀个服务.

接⼝,其实也就是我们前⾯⽹络模块讲的的"应⽤层协议".把约定的内容写在⽂档上,就是"接⼝⽂档",接 ⼝⽂档也可以理解为是应⽤程序的"操作说明书".

在项⽬开发前,根据需求先约定好前后端交互接⼝,双⽅按照接⼝⽂档进⾏开发.

接⼝⽂档通常由服务提供⽅来写,交由服务使⽤⽅确认,也就是客⼾端.

接⼝⽂档⼀旦写好,尽量不要轻易改变.

如若需要改变,必须要通知另⼀⽅知晓.


需求分析

加法计算器功能,对两个整数进行相加,需要客户端提供参与计算的两个数,服务端返回这两个整数的计算结果

基于以上分析,定义接口


接口定义

  • 请求路径:calc / sum
  • 请求方式:GET / POST
  • 接口描述:计算两个整数相加

请求参数:

参数名类型是否必须备注
num1Integer参与计算的第一个数
num2Integer参与计算的第二个数

响应数据

  • Content-Type:text / html
  • 响应内容:计算机计算结果:8

2. 服务器代码

@RequestMapping("/calc")
@RestController
public class CalcController {
    @RequestMapping("/sum")
    public String sum(@RequestParam("num1") Integer num1,
                      @RequestParam("num2") Integer num2) {
        Integer sum = num1 + num2;
        return "计算机计算结果:" + sum;
    }
}

tip:使用 @RequestParam 限定参数为必传(方法一,其他两种在下面示例)

使用 Postman 测试:

3. 前端代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
     <form action="calc/sum" method="post">
        <h1>计算器</h1>
        数字1:<input name="num1" type="text"><br>
        数字2:<input name="num2" type="text"><br>
        <input type="submit" value=" 点击相加 ">
    </form>
</body>

</html>

运行:

8.2 用户登录

需求:用户输入账号和密码,后端进行校验密码是否正确

  • 如果不正确,前端进行用户告知
  • 如果正确,跳转到首页,首先显示当前登录用户
  • 后续再访问首页,可以获取到登录用户信息

1. 约定前后端交互接口

对于后端而言,不涉及前端页面的展示,只需提供两个功能

  1. 登录页面:通过账号和密码,校验输入的账号密码是否正确,并告知前端
  2. 首页:告知前端当前登录用户,如果当前已有用户登录,返回登录的账号;如果没有,返回空

接口定义

(1) 校验接口

请求路径:/user /login

请求方式:POST

接口描述:校验账号密码是否正确


请求参数

参数名类型是否必传备注
userNameString校验的账号
passwordString校验的密码

响应数据

Content-Type:test 、 html

响应内容:true // 账号密码验证成功;false // 账号密码验证失败


(2) 查询登录用户接口

请求路径:/user /getLoginUser

请求方式:GET

接口描述:查询当前登录的用户

请求参数:无

响应数据

Content-Type:text / html

响应内容:zhangsan(返回当前登录的用户名)

2. 服务端代码

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RequestMapping("/user")
@RestController
public class UserController {
    @RequestMapping(value = "/login", method = RequestMethod.POST)
    public Boolean login(String userName, String password, HttpServletRequest request) {
        // 方式二:
//        if (userName == null || "".equals(userName)) return false; // 使用常量.equals,避免空指针异常
        // 方式三:Spring 提供检测字符串是否有长度
        if (!StringUtils.hasLength(userName) || !StringUtils.hasLength(password)) return false;

        // 不为空,校验账号和密码是否正确
        if ("admin".equals(userName) && "admin".equals(password)) {
            // 设置 Session,为了下面获取用户名
            HttpSession session = request.getSession(true); // 不填也行,默认为 true
            session.setAttribute("userName", userName);

            return true;
        }

        return false;
    }

    @RequestMapping (value = "/getLoginUser", method = RequestMethod.GET)
    public String getLoginUser(HttpSession session) {
        if (session.getAttribute("userName") != null) {
            return (String)session.getAttribute("userName");
        }
        return "";
    }
}

tip:限定必传参数的另外两种方式,上述代码中展现

Postman 测试:

3. 前端代码

ajax 是异步的,form 表单是同步的

当用户无输入时,form 表单会一直等待,输入后会跳转新页面

而 ajax 可以不跳转新页面,如下例:

当我们鼠标从账号框转移到密码框,并点击后

他会进行重复检测,这个过程肯定是在后端进行的,浏览器不能存储很大量的信息,这个检测过程并不会跳转新页面,ajax 可以实现

// 各种错误。。。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2225598.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

OpenAI被爆12月发布其Orion AI模型!波兰“OFF”电台解雇所有记者,启用AI“主持人”|AI日报

文章推荐 Stability AI一口气推出3款图像生成模型系列&#xff01;升级版Claude 3.5 Sonnet能像人类一样操控电脑&#xff5c;AI日报 今日热点 据报道&#xff0c;OpenAI计划于12月发布其Orion AI模型 据The Verge昨日报道&#xff0c;OpenAI计划在今年12月之前发布其下一个…

ctfshow(171,172,173)--SQL注入--联合注入

Web171 进入靶场&#xff0c;是一个SQL查询界面&#xff1a; 审计&#xff1a; 查询语句如下&#xff1a; $sql "select username,password from user where username !flag and id ".$_GET[id]." limit 1;";语句功能从数据表user中查询username,pa…

MATLAB生态环境数据处理与分析

原文链接&#xff1a;MATLAB在生态环境数据处理与分析https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247624407&idx4&sn39c8119bba0797e6bf5cc35eea1c6767&chksmfa8da730cdfa2e266dac5221af101230d7ded29576a34856b31f736a89dbb2e3e481a5e94e8a&to…

日常笔记记录

1、Http 1.1 概念 HTTP 是 HyperText Transfer Protocol&#xff08;超文本传输协议&#xff09;的简写&#xff0c;它是 TCP/IP 协议集中的一个应用层协议&#xff0c;是客户端与服务端进行交互时必须遵循的规则。它用于定义 Web 浏览器与 Web 服务器之间交换数据的过程以及…

【Docker】在AlmaLinux 8.10系统中安装Docker-ce过程分享

随着2024年6月30日&#xff0c;官方停止了对CentOS 7的维护&#xff0c;属于CentOS 7的时代终于结束了。当然&#xff0c;对于CentOS 7的维护停止&#xff0c;大家也不用过度紧张&#xff0c;目前仍有部分Linux版本可以提供企业级的生产可用系统服务&#xff0c;比如&#xff1…

Python记录-字典

定义 Python 中的字典&#xff08;dictionary&#xff09;是一种内置的数据结构&#xff0c;用于存储键值对&#xff08;key-value pairs&#xff09;。字典中的每个键&#xff08;key&#xff09;都是唯一的&#xff0c;并且与一个值&#xff08;value&#xff09;相关联。键…

vue3学习(二)一款优秀的编辑器

开源项目&#xff1a; https://github.com/Leecason/element-tiptap doc需要改成document&#xff0c;改完之后依然有问题&#xff0c;应该是对vue3兼容不好&#xff0c; 所以在issue中有人回复使用 https://github.com/okijhhyu/element-tiptap-vue3 经过测试&#xff0c;确实…

shiro会话管理和加密

Shiro 会话管理和加密 会话管理 缓存 加密 会话管理 Shiro提供了完整的企业级会话管理功能&#xff0c;不依赖于底层容器&#xff08;如Tomcat&#xff09;&#xff0c;不管是J2SE还是J2EE环境都可以使用&#xff0c;提供了会话管理&#xff0c;会话事件监听&#xff0c;会话存…

【自动化测试之oracle数据库】MacOs如何安装oracle- client

操作系统为Mac OS&#xff0c;本地在pycharm上跑自动化脚本时&#xff0c;因为有操作oracle数据库的部分&#xff0c;所以需要安装oracle数据库的客户端&#xff0c;并install cx_oracle,本文主要介绍如何在macOS上完成安装&#xff0c;并在python自动化测试代码中配置&#xf…

vue3项目中引入阿里图标库

开篇 本篇的主题是在vue3项目中引入阿里图标库 步骤 注册阿里图标库账号(阿里图标)&#xff0c;并创建项目 将图标加入项目中 将需要的图标先加入购物车&#xff0c;随后加入到项目中 生成项目代码 在项目中生成项目代码&#xff0c;便于后续复制到vue项目中 ## 在vue3项目…

信息安全入门——网络安全威胁

目录 前言网络安全威胁概览悄无声息的数据泄露——被动攻击明目张胆的破坏行为——主动攻击网路世界的瘟疫——病毒总结 前言 在数字化时代&#xff0c;信息安全成为了我们每个人都不得不面对的重要议题。网络安全威胁无处不在&#xff0c;它们可能来自网络的暗角&#xff0c;…

MySQL 9从入门到性能优化-慢查询日志

【图书推荐】《MySQL 9从入门到性能优化&#xff08;视频教学版&#xff09;》-CSDN博客 《MySQL 9从入门到性能优化&#xff08;视频教学版&#xff09;&#xff08;数据库技术丛书&#xff09;》(王英英)【摘要 书评 试读】- 京东图书 (jd.com) MySQL9数据库技术_夏天又到了…

【51单片机】第一个小程序 —— 点亮LED灯

学习使用的开发板&#xff1a;STC89C52RC/LE52RC 编程软件&#xff1a;Keil5 烧录软件&#xff1a;stc-isp 开发板实图&#xff1a; 文章目录 单片机介绍LED灯介绍练习创建第一个项目点亮LED灯LED周期闪烁 单片机介绍 单片机&#xff0c;英文Micro Controller Unit&#xff0…

信息安全工程师(68)可信计算技术与应用

前言 可信计算技术是一种计算机安全体系结构&#xff0c;旨在提高计算机系统在面临各种攻击和威胁时的安全性和保密性。 一、可信计算技术的定义与原理 可信计算技术通过包括硬件加密、受限访问以及计算机系统本身的完整性验证等技术手段&#xff0c;确保计算机系统在各种攻击和…

力扣hot100-->递归/回溯

递归/回溯 1. 17. 电话号码的字母组合 中等 给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映射如下&#xff08;与电话按键相同&#xff09;。注意 1 不对应任何字母。 示例 1&#xff1a; 输入&…

c语言中整数在内存中的存储

整数的二进制表示有三种&#xff1a;原码&#xff0c;反码&#xff0c;补码 有符号的整数&#xff0c;三种表示方法均有符号位和数值位两部分&#xff0c;符号位都是用‘0’表示“正&#xff0c;用1表示‘负’ 最高位的以为被当作符号位&#xff0c;剩余的都是数值位。 整数…

智慧旅游微信小程序平台

作者介绍&#xff1a;✌️大厂全栈码农|毕设实战开发&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。 &#x1f345;获取源码联系方式请查看文末&#x1f345; 推荐订阅精彩专栏 &#x1f447;&#x1f3fb; 避免错过下次更新 Springboot项目精选实战案例 更多项目…

Vue3.js - 数据代理方法

1. Vue导入 最简单的方式&#xff0c;通过联网接入Vue3的接口 <script type"text/javascript" src"https://unpkg.com/vue3"></script> 2. Vue实例 2.1 创建Vue实例 const vm Vue.createApp({}) 使用Vue中的createApp方法创建对应实例&a…

115页PPT华为管理变革:制度创新与文化塑造的核心实践

集成供应链&#xff08;ISC&#xff09;体系 集成供应链&#xff08;ISC&#xff09;体系是英文Integrated Supply Chain的缩写&#xff0c;是一种先进的管理思想&#xff0c;它指的是由相互间提供原材料、零部件、产品和服务的供应商、合作商、制造商、分销商、零售商、顾客等…

AI 提示词(Prompt)入门 :ChatGPT 4.0 高级功能指南

这段时间 GPT4 多了很多功能&#xff0c;今天主要是增加了 GPTs Store 的介绍和 创建 GPTs 的简单方法&#xff0c;那么我们开始吧&#xff0c;文末有彩蛋。 这里主要讲解如下几个点&#xff1a; 1&#xff1a; ChatGPT 4.0 插件的使用 2&#xff1a;ChatGPT 4.0 高级数据分…