企业网站php源码,学校网站建设发展历程,运城市住房与城乡建设局网站,wordpress的小程序1. 什么是 Spring Web MVC
Spring Web MVC 是基于 Servlet API 构建的原始 Web 框架#xff0c;从从⼀开始就包含在Spring框架中。它的 正式名称“SpringWebMVC”来⾃其源模块的名称(Spring-webmvc)#xff0c;但它通常被称为Spring MVC. 什么是Servlet呢? Ser…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 会对所有类进行扫描只有类加了注解 RequestControllerSpring 才会去看这个类里面有没有 RequestMapping(/sayHi)如果有就执行该方法若是类没有加注解 RequestControllerSpring就不会进入该类即使该类中有 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 langen
headmeta charsetUTF-8titleTitle/title
/head
bodyform action/user/sayHi methodpostinput typesubmit 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;}Overridepublic 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 概念
JSONJavaScriptObjectNotation 【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 使用需要引入相关依赖如下 dependencygroupIdcom.fasterxml.jackson.core/groupIdartifactIdjackson-databind/artifactIdversion2.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用于处理 JSONUser user new User();user.setName(lisi);user.setAge(18);user.setGender(1); // 表示 男// 对象转 JSONString 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 字段中 ⽐如去医院挂号 看病之前先挂号.挂号时候需要提供⾝份证号,同时得到了⼀张就诊卡,这个就诊卡就相当于患 者的令牌.后续去各个科室进⾏检查,诊断,开药等操作,都不必再出⽰⾝份证了,只要凭就诊卡即可识别出当 前患者的⾝份.看完病了之后,不想要就诊卡了,就可以注销这个卡.此时患者的⾝份和就诊卡的关联就销毁了.(类 似于⽹站的注销操作)⼜来看病,可以办⼀张新的就诊卡,此时就得到了⼀个新的令牌 此时在服务器这边就需要记录 “令牌” 信息以及令牌对应的用户信息这个就是 Cookie机制所作的工作
6.11.2 Session
会话就是对话的意思 在计算机领域会话是一个客户与服务器之间的不中断的请求响应对客户的每个请求服务器能识别出请求来自于同一个客户
当一个位置的客户像 Web 应用程序发送第一个请求时就开始了一个会话
当客户明确结束会话或服务器在一个时限内没有接收到客户的任何请求时会话就结束了 ⽐如我们打客服电话 每次打客服电话,是⼀个会话.挂断电话,会话就结束了 下次再打客服电话,⼜是⼀个新的会话. 如果我们⻓时间不说话,没有新的请求,会话也会结束. 服务器同一时刻收到的请求是很多的服务器需要清楚的区分每个请求是属于哪个用户也就是属于哪个会话就需要在服务器这边记录每个会话以及与用户的信息的对应关系
Session 是服务器为了保存用户信息而创建的一个特殊的对象 Session 的本质就是一个 “哈希表”存储了一些键值对结构Key 就是 SessionIDValue 就是用户信息用户信息可以根据需求灵活设计 SessionID 是由服务器生成的一个 “唯一性字符串”从 Session 机制的角度来看这个唯一性字符串称为 “SessionID”但是站在整个登录流程中看待也可以把这个唯一性字符串称为 “token”
上述例子中的 令牌ID 就可以看作是 SessionID只不过令牌除了 ID 之外还会带有一些其他信息比如时间、签名等 当用户登录的时候服务器在 Session 中新增一个新记录并把 SessionID 返回给客户端通过 HTTP 响应中的 Set-Cookie 字段返回客户端后续再给服务器发送请求的时候需要在请求中带上 SessionID通过 HTTP 请求中的 Cookie 字段带上服务器收到请求之后根据请求中的 SessionID 在 Session 信息中获取到对应的用户信息再进行后续操作找不到则重新创建 Session并把 SessionID 返回
tipSession 默认是保存在内存中的如果重启服务器则 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) 传统方法获取 // 获取 CookieRequestMapping(/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;}
运行结果 两种方式对比
第一种方法可以获取多次参数
第二种方法每获取一个参数都需要加一个注解 tipPostman 设置 Cookie 6.11.5 获取 Session
1) Session 存储和获取
Session 是服务器端的机制我们需要先存储才能获取
Session 也是基于 HttpServletRequest 来存储和获取的
2) Session 存储 RequestMapping(/setSession)public String setSession(HttpServletRequest request) {// 先获取到 session 对象若没有则创建一个空 sessionHttpSession 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;}
tipSession 存储在服务器的内村上服务重启时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 / htmlbody 数据格式是 HTMLtext / cssbody 数据格式是 CSSapplication / javascriptbody 数据格式是 JavaScriptapplication / jsonbody 数据格式是 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 提供的方法来进行设置 ResponseBodyRequestMapping(/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-TypeLocal 等
这些信息通过 RequestMapping 注解的属性来实现其源码如下 7.6.1 通过设置 produces 属性的值设置响应的报头 Content-Type 7.6.2 通过 HttpServletResponse 内置方法设置 Header 7.6.3 通过 HttpServletResponse 内置方法设置 Cookie 8. 案例
8.1 加法计算器
需求输入两个整数点击 “点击相加” 按钮显示计算结果 1. 约定前后端交互接口
概念
约定前后端交互接⼝是进⾏Web开发中的关键环节.
接⼝⼜叫APIApplicationProgrammingInterface),我们⼀般讲到接⼝或者API指的都是同⼀个东 西.
是指应⽤程序对外提供的服务的描述,⽤于交换信息和执⾏任务
简单来说,就是允许客⼾端给服务器发送哪些HTTP请求,并且每种请求预期获取什么样的HTTP响应.
现在前后端分离模式开发,前端和后端代码通常由不同的团队负责开发.双⽅团队在开发之前,会提前 约定好交互的⽅式.客⼾端发起请求,服务器提供对应的服务.服务器提供的服务种类有很多,客⼾端按 照双⽅约定指定选择哪⼀个服务.
接⼝,其实也就是我们前⾯⽹络模块讲的的应⽤层协议.把约定的内容写在⽂档上,就是接⼝⽂档,接 ⼝⽂档也可以理解为是应⽤程序的操作说明书.
在项⽬开发前,根据需求先约定好前后端交互接⼝,双⽅按照接⼝⽂档进⾏开发. 接⼝⽂档通常由服务提供⽅来写,交由服务使⽤⽅确认,也就是客⼾端. 接⼝⽂档⼀旦写好,尽量不要轻易改变. 如若需要改变,必须要通知另⼀⽅知晓. 需求分析
加法计算器功能对两个整数进行相加需要客户端提供参与计算的两个数服务端返回这两个整数的计算结果
基于以上分析定义接口 接口定义 请求路径calc / sum请求方式GET / POST接口描述计算两个整数相加 请求参数
参数名类型是否必须备注num1Integer是参与计算的第一个数num2Integer是参与计算的第二个数 响应数据 Content-Typetext / 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 langen
headmeta charsetUTF-8meta nameviewport contentwidthdevice-width, initial-scale1.0titleDocument/title
/head
bodyform actioncalc/sum methodposth1计算器/h1数字1input namenum1 typetextbr数字2input namenum2 typetextbrinput typesubmit value 点击相加 /form
/body/html
运行 8.2 用户登录
需求用户输入账号和密码后端进行校验密码是否正确
如果不正确前端进行用户告知如果正确跳转到首页首先显示当前登录用户后续再访问首页可以获取到登录用户信息
1. 约定前后端交互接口
对于后端而言不涉及前端页面的展示只需提供两个功能
登录页面通过账号和密码校验输入的账号密码是否正确并告知前端首页告知前端当前登录用户如果当前已有用户登录返回登录的账号如果没有返回空
接口定义
(1) 校验接口
请求路径/user /login
请求方式POST
接口描述校验账号密码是否正确 请求参数
参数名类型是否必传备注userNameString是校验的账号passwordString是校验的密码 响应数据
Content-Typetest 、 html
响应内容true // 账号密码验证成功false // 账号密码验证失败 (2) 查询登录用户接口
请求路径/user /getLoginUser
请求方式GET
接口描述查询当前登录的用户
请求参数无
响应数据
Content-Typetext / 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); // 不填也行默认为 truesession.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 可以实现
(1) 登录页面 login.html
对于前端而言当点击登录按钮时需要把用户输入的信息传递到后端进行校验后端校验成功则跳转到首页index.html后端校验失败则直接弹窗报错
!DOCTYPE html
html langenheadmeta charsetUTF-8title登录页面/title
/headbodyh1用户登录/h1用户名input nameuserName typetext iduserNamebr密码input namepassword typepassword idpasswordbrinput typebutton value登录 onclicklogin()script srcjs/jquery-3.7.1.min.js/scriptscriptfunction login() {// 这里使用的是 jquery 封装过的 ajax更简洁不是原生的// 语法$.ajax({}); ajax 的参数是一个对象写在 {} 里主要内容就是完成接口文档信息$.ajax({type: post, // 请求类型url: /user/login, // 访问路径// 传参 key: valuedata: {userName: $(#userName).val(), // 用 # 获取 idpassword: $(#password).val()},// 返回结果success: function(body) { // 此处的 body 变量就是后端返回的 true 或 falseif (bodytrue) {// 跳转到 index 页面location.href index.html;} else {// 当前页面alert(密码错误);}}});}/script
/body/html
tip页面跳转的三种方式
window.location.href book_list.html;window.location.assign(book_list.html);window.location.replace(book_list.html); 以上写法通常把 window 省略比如window.location.href book_list.html; 写成 location.href book_list.html; 三者区别参考location.assign()、location.href、location.replace(url)的不同 (2) 首页代码 index.html
首页代码比较简单只显示当前登录用户即可
当前登录用户需要从后端获取并显示到前端
!doctype html
html langenheadmeta charsetUTF-8meta nameviewportcontentwidthdevice-width, user-scalableno, initial-scale1.0, maximum-scale1.0, minimum-scale1.0meta http-equivX-UA-Compatible contentieedgetitle用户登录首页/title
/headbody登录人: span idloginUser/spanscript srcjs/jquery-3.7.1.min.js/scriptscript$.ajax({type: get,url: /user/getLoginUser,success: function(userName) {$(#loginUser).text(userName);}});/script
/body/html
tip上述两个前端代码中的 jquery 存于本地
4. 运行测试 多次刷新 http://127.0.0.1:8080/index.html 发现依然可以获取到登录用户
如果重启服务器则登录人显示为空这是因为 Session 存在内存中如果不做任何处理默认服务器重启Session 数据就丢失了
8.3 留言板
需求
输入留言信息点击提交后端把数据存储起来
页面展示输入的表白墙的信息 1. 约定前后端交互接口
需求分析
后端需要提供两个服务
提交留言用户输入留言信息后后端需要把留言信息保存起来
展示留言页面展示时需要从后端后取到所有的留言信息
接口定义
(1) 获取全部留言
全部留言信息这里用 List 来表示可以用 JSON 来描述这个 List 数据
请求
GET /message/getList
响应JSON 格式
[{from: 黑猫to: 白猫message: 喵}, {from: 黑狗,to: 白狗,message: 汪}// ...
]
浏览器给服务器发送一个 GET /message/getList 这样的请求就能返回当前一共有哪些留言记录结果以 JSON 的格式返回回来
(2) 发表新留言
请求body 也为 JSON 格式
POST /message/publish{from: 黑猫to: 白猫message: 喵
}响应JSON 格式
{ok: 1
}
浏览器给服务器发送一个 POST /message/publish 这样的请求就能把当前的留言提交给服务器
2. 实现服务器端代码
定义 MessageInfo 对象
public class MessageInfo {private String from;private String to;private String message;public String getFrom() {return from;}public void setFrom(String from) {this.from from;}public String getTo() {return to;}public void setTo(String to) {this.to to;}public String getMessage() {return message;}public void setMessage(String message) {this.message message;}
}
MessageController
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.ArrayList;
import java.util.List;RequestMapping(/message)
RestController
public class MessageController {//存储留言信息ListMessageInfo messageInfos new ArrayList();// 获取接口RequestMapping(/getList)public ListMessageInfo getList() {return messageInfos;}// 发布接口RequestMapping(/publish)public String publish(RequestBody MessageInfo messageInfo) { // 请求是 JSON 格式参数需要加上 RequestBody// 参数都不为空将对象添加到 list 中if (StringUtils.hasLength(messageInfo.getFrom()) StringUtils.hasLength(messageInfo.getTo()) StringUtils.hasLength(messageInfo.getMessage())) {messageInfos.add(messageInfo);return {\ok\: 1};}return {\ok\: 0};}
}
后端测试 成功了但是此时的响应类型是 text 格式 需要将其转为 JSON 格式如下 3. lombok
3.1 介绍
lombok 是一个 Java goon工具库通过添加注解的方式简化 Java 的开发
引入依赖
在 pom.xml 文件中的 dependencies /dependencies 中添加以下语句
dependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactIdoptionaltrue/optional
/dependency
3.2 使用
前面定义 MessageInfo 时需要写每个参数的 get set 方法很麻烦
lombok 可以通过注解的方式帮我们消除一些冗长代码使代码看起来简洁一点如下
import lombok.Data;Data
public class MessageInfo {private String from;private String to;private String message;
}Data 注解会帮助我们自动生成一些方法
3.3 原理解释
可以观察加了 Data 注解之后Idea 反编译的 class 文件
这不是真正的字节码文件而是 Idea 根据字节码进行反编译后的文件
反编译是将可执行的程序代码转换为某种形式的高级编程语言使其具有更易读的格式
反编译是一种逆向工程它的作用和编译器的作用相反 可以看出lombok 是一款在编译时期生成代码的工具包
Java 程序的运行原理 lombok 的作用如下图 3.4 lombok 包含的方法
注解作用Getter自动添加 gerrer 方法Setter自动添加 setter 方法ToString自动添加 toString方法EqualsAndHashCode自动添加 equals 方法和 hashCode 方法NoArgsConstructor自动添加无参构造方法AllArgsConstructor自动添加全属性构造方法顺序按照属性的定义顺序NonNull属性不能为 nullRequiredArgsConstructor自动添加必须属性的构造方法final NonNull 的属性为必须
DataGetterSetterToStringEqualsAndHashCodeRequiredArgsConstructor NoArgsConstructor
当我们只需要其中某种方法时可以单独注解如下 4. 前端代码
!DOCTYPE html
html langenheadmeta charsetUTF-8meta nameviewport contentwidthdevice-width, initial-scale1.0title留言板/titlestyle.container {width: 350px;height: 300px;margin: 0 auto;/*水平方向居中*//* border: 1px black solid; */text-align: center;}.grey {color: grey;}.container .row {width: 350px;height: 40px;display: flex;justify-content: space-between;align-items: center;}.container .row input {width: 260px;height: 30px;}#submit {width: 350px;height: 40px;background-color: orange;color: white;border: none;margin: 10px;border-radius: 5px;font-size: 20px;}/style
/headbodydiv classcontainerh1留言板/h1p classgrey输入后点击提交, 会将信息显示下方空白处/pdiv classrowspan谁:/span input typetext name idfrom/divdiv classrowspan对谁:/span input typetext name idto/divdiv classrowspan说什么:/span input typetext name idsay/divinput typebutton value提交 idsubmit onclicksubmit()!-- divA 对 B 说: hello/div --/divscript srcjs/jquery-3.7.1.min.js/scriptscript// 给点击按钮注册点击事件function submit() {// 1. 获取到编辑框内容let from $(#from).val();let to $(#to).val();let say $(#say).val();if (from || to || say ) {alert(请检查输入内容);return;}// 检验完成发起请求$.ajax({url: /message/publish,type: post,contentType: application/json,data: {from: from,to: to,message: say},success: function (result) {if (result.ok 1) {// 成功alert(添加成功);} else {// 失败alert(添加失败);}}});}/script
/body/html
测试期望弹出一个失败或者成功的弹窗
结果并没有而是报了两个错误 错误一
第一个错误是因为图标问题暂不考虑
看第二个错误发现其状态码为 400 再看控制台给的提示 JSON解析错误无法识别的标记“from”应为JSON字符串、数字、数组、对象或标记“null”、“true”或“false”]
看这个描述可能是 JSON 格式的问题通过 Fiddler 抓包查看 可以看出正常情况下请求应该是一个 JSON 字段结果在浏览器请求却出现了字符串说明问题出现在前端代码中
将 data 转为 JSON 格式 修改后重启服务 可以添加成功但是并没有按照我们的要求页面展示输入的表白墙的信息
错误二
此时数据确实添加到后端了但是前端并没有向后端要这些数据页面中没有任何展示
我们希望页面在加载时就显示这些数据因此将向后端请求数据的 ajax 写到 script 的最开始如下 重启服务使用 Postman 构造两条数据然后浏览器刷新显示 出现了 undefined肯定是刚才写的代码有问题观察后端定义的 MessageInfo 发现 第三个参数有问题将其修改为 msg.message重新启动服务 成功了当我们在浏览器中添加数据时 错误三
只显示弹窗并没有添加数据这是因为添加完之后并没有请求 List没有重新加载页面因此成功后也应该请求 List 重启服务 错误四 重启服务 添加失败了并且浏览器的开发者工具没有错误信息后端控制台也没有错误日志 这时我们就需要自己打印错误日志
既然页面可以弹出 添加错误 的弹窗说明程序一定可以走到这里 通过这些日志可以知道result.ok 并没有获取到这个对象的属性后端传过来的不是一个对象而是一个字符串
这种情况下前端也可以主动将该字符串转为 JSON 对象如下 重启服务成功。
tip两种改为 JSON 的方法两者不能同时进行转换
前端方法如上图
后端方法 前端完整代码
!DOCTYPE html
html langenheadmeta charsetUTF-8meta nameviewport contentwidthdevice-width, initial-scale1.0title留言板/titlestyle.container {width: 350px;height: 300px;margin: 0 auto;/*水平方向居中*//* border: 1px black solid; */text-align: center;}.grey {color: grey;}.container .row {width: 350px;height: 40px;display: flex;justify-content: space-between;align-items: center;}.container .row input {width: 260px;height: 30px;}#submit {width: 350px;height: 40px;background-color: orange;color: white;border: none;margin: 10px;border-radius: 5px;font-size: 20px;}/style
/headbodydiv classcontainerh1留言板/h1p classgrey输入后点击提交, 会将信息显示下方空白处/pdiv classrowspan谁:/span input typetext name idfrom/divdiv classrowspan对谁:/span input typetext name idto/divdiv classrowspan说什么:/span input typetext name idsay/divinput typebutton value提交 idsubmit onclicksubmit()!-- divA 对 B 说: hello/div --/divscript srcjs/jquery-3.7.1.min.js/scriptscriptgetList();// 将 ajax 封装到一个方法中提高可读性function getList() {$.ajax({type: get,url: /message/getList,success: function (message) { // 由于后端返回的是一个 List因此使用 for 循环打印到页面上for (let msg of message) {// 构造 html 元素let html div msg.from 对 msg.to 说: msg.message /div;// 把构造好的元素添加进去$(.container).append(html);}}});}// 给点击按钮注册点击事件function submit() {// 1. 获取到编辑框内容let from $(#from).val();let to $(#to).val();let say $(#say).val();if (from || to || say ) {alert(请检查输入内容);return;}// 检验完成发起请求$.ajax({url: /message/publish,type: post,contentType: application/json,data: JSON.stringify({from: from,to: to,message: say}),success: function (result) {// 打印日志console.log(result);console.log(result.ok);// let o JSON.parse(result);if (result.ok 1) {// 成功alert(添加成功);// 构造 html 元素let html div from 对 to 说: say /div;// 把构造好的元素添加进去$(.container).append(html);// 同时清理之前输入框的内容$(:text).val();} else {// 失败alert(添加失败);}}});}/script
/body/html
总结 5. 插件 EditStarter
前面插入 lombok 是通过一段 dependency 来插入的以下有两种方式可以快速添加
方式一去 maven 中央仓库查 复制上述 dependency 到 pom.xml 中即可 粘贴过来的代码段会有版本信息而上面写的没有版本信息但那时按住 Ctrl 将鼠标移到 lombok 上显示出的版本信息也是 1.18.34
这是因为 Spring Boot 帮我们进行了版本管理 Spring Boot 不仅帮我们进行了版本管理还帮我们处理了这些 jar 包之间的冲突
若我们有喜好的版本也可以通过 version 来自己调整
方法二EditStartes 在 pom.xml 文件中单击右键选择 Generate如下图 进入 EditStarters 的编辑界面添加对应的依赖即可 tip不是所有的依赖都可以在这里添加这个界面和 Spring Boot 创建项目界面是一样的
在这里找不到的依赖还是需要去 Maven 仓库查找坐标来添加
8.4 图书管理系统
需求
登录用户输入账号密码完成登录
列表展示展示图书 8.4.1 Bootstrap
这里使用的是版本 4
官网介绍Bootstrap 是全球最受欢迎的前端框架用于构建响应式、移动设备优先的网站。利用 jsDelivr 和我们提供的入门模板帮助你快速掌握 Bootstrap。
具体不展开
8.4.2 约定前后端交互接口
需求分析
图书管理系统是一个相对较大的案例这里先实现其中一部分功能
根据需求可知后端需要提供两个接口
账号密码校验接口根据输入用户名和密码校验登录是否通过
图书列表提供图书列表信息
接口定义
1. 登录接口 URLPOST /user/longin 请求参数nameadminpasswordadmin 响应true // 账号密码验证成功false // 账号密码验证失败 2. 图书列表展示 URLPOST /book/getList 请求参数无 响应返回图书列表如下 [{id: 1,bookName: 活着,author: 余华,count: 270,price: 20,publish: 北京文艺出版社,status: 1,statusCN: 可借阅},...
] 字段说明 id图书 IDbookName图书名称author作者count数量price定价publish图书出版社status图书状态 1-可借阅其他-不可借阅statusCN图书状态中文含义