day02 springmvc
创始人
2024-02-03 17:37:32

day02 springmvc

第一章 RESTFul风格交互方式

第一节 RESTFul概述

1. REST的概念

REST:Representational State Transfer,表现层资源状态转移。

  • 定位:互联网软件架构风格
  • 倡导者:Roy Thomas Fielding
  • 文献:Roy Thomas Fielding的博士论文

2. REST要解决的问题: 将针对功能设计系统转变成针对资源设计系统

传统的软件系统仅在本地工作,但随着项目规模的扩大和复杂化,不但整个项目会拓展为分布式架构,很多功能也会通过网络访问第三方接口来实现。在通过网络访问一个功能的情况下,我们不能轻易假设网络状况稳定可靠。所以当一个请求发出后没有接收到对方的回应,那我们该如何判定本次操作成功与否?

下面以保存操作为例来说明一下针对功能和针对资源进行操作的区别:

  • 针对功能设计系统

    保存一个 Employee 对象,没有接收到返回结果,判定操作失败,再保存一次。但是其实在服务器端保存操作已经成功了,只是返回结果在网络传输过程中丢失了。而第二次的补救行为则保存了重复、冗余但 id 不同的数据,这对整个系统数据来说是一种破坏。

  • 针对资源设计系统

    针对 id 为 3278 的资源执行操作,服务器端会判断指定 id 的资源是否存在。如果不存在,则执行保存操作新建数据;如果存在,则执行更新操作。所以这个操作不论执行几次,对系统的影响都是一样的。在网络状态不可靠的情况下可以多次重试,不会破坏系统数据。

幂等性:如果一个操作执行一次和执行 N 次对系统的影响相同,那么我们就说这个操作满足幂等性。而幂等性正是 REST 规范所倡导的。

3. RESTFul风格的架构特点

3.1 通过URL就知道要操作什么资源

REST是针对资源设计系统,所以在REST中一个URL就对应一个资源, 为实现操作幂等性奠定基础。

3.2 通过Http请求的方式就知道要对资源进行何种操作

在REST中,针对同一资源的增删改查操作的URL是完全相同的,它是通过Http协议的不同请求方式来区分不同操作的

REST 风格主张在项目设计、开发过程中,具体的操作符合 HTTP 协议定义的请求方式的语义

操作请求方式
查询操作GET
保存操作POST
删除操作DELETE
更新操作PUT

另有一种说法:

  • POST 操作针对功能执行,没有锁定资源 id,是非幂等性操作。
  • PUT 操作锁定资源 id,即使操作失败仍然可以针对原 id 重新执行,对整个系统来说满足幂等性。
    • id 对应的资源不存在:执行保存操作
    • id 对应的资源存在:执行更新操作
3.3 URL更加简洁也更加隐晦

REST风格提倡 URL 地址使用统一的风格设计,从前到后各个单词使用斜杠分开,不使用问号键值对方式携带请求参数,而是将要发送给服务器的数据作为 URL 地址的一部分,以保证整体风格的一致性。还有一点是不要使用请求扩展名。

使用问号键值对的方式给服务器传递数据太明显,容易被人利用来对系统进行破坏。使用 REST 风格携带数据不再需要明显的暴露数据的名称。

操作传统风格REST 风格
保存/CRUD/saveEmpURL 地址:/CRUD/emp 请求方式:POST
删除/CRUD/removeEmp?empId=2URL 地址:/CRUD/emp/2 请求方式:DELETE
更新/CRUD/updateEmpURL 地址:/CRUD/emp 请求方式:PUT
查询(表单回显)/CRUD/editEmp?empId=2URL 地址:/CRUD/emp/2 请求方式:GET

第二节 四种请求方式的映射

1. 为什么要进行请求方式的映射

在 HTML 中,GET 和 POST 请求可以天然实现,但是 DELETE 和 PUT 请求无法直接做到。SpringMVC 提供了 HiddenHttpMethodFilter 帮助我们将 POST 请求转换为 DELETE 或 PUT 请求

2. 具体执行映射操作

2.1 映射PUT 请求
2.1.1 修改web.xml文件

hiddenHttpMethodFilterorg.springframework.web.filter.HiddenHttpMethodFilter

hiddenHttpMethodFilter/*

2.1.2 页面的表单
  • 要点1:原请求方式必须是 post
  • 要点2:新的请求方式名称通过请求参数发送
  • 要点3:请求参数名称必须是 _method
  • 要点4:请求参数的值就是要改成的请求方式
2.1.3 handler方法
@PutMapping("/movie")
public String updateMovie(){logger.debug("PUT请求....");return "target";
}
2.2 映射DELETE请求
2.2.1 web.xml中要维持之前映射PUT请求的配置
2.2.2 前端页面

通常删除超链接会出现在列表页面:

将XXX请求转换为DELETE请求

姓名年龄删除
张三40删除
李四30删除

创建负责转换的表单

使用vue给删除超链接绑定单击响应函数:

  1. 引入vue

在这里插入图片描述



  1. 绑定单击响应函数
var vue = new Vue({"el":"#app","methods":{deleteMovie(){console.log("aaaaaaa")//真正发送删除请求://1. 阻止标签的默认行为event.preventDefault()//2. 创建一个空表单:先动态设置表单的actionvar myForm = document.getElementById("myForm");myForm.action = event.target.href//并且使用js代码提交表单myForm.submit()}}
});
2.2.3 handler方法
@DeleteMapping("/movie")
public String deleteMovieById(){logger.debug("DELETE请求....");return "target";
}

第三节 PathVariable注解获取路径参数

1. REST 风格路径参数

请看下面链接:

/emp/20

/shop/product/iphone

如果我们想要获取链接地址中的某个部分的值,就可以使用 @PathVariable 注解,例如上面地址中的20、iphone部分。

2. 具体操作

2.1 单个路径参数
2.1.1 发送请求携带参数
携带参数movieId
2.1.2 handler方法获取路径参数
//注意:{movieId是一个占位符},表示获取该位置的值,@PathVariable("movieId")表示获取占位符为"movieId"的值
@GetMapping("/movie/{movieId}")
public String findMovieById(@PathVariable("movieId") Integer movieId){logger.debug("GET请求...."+movieId);return "target";
}
2.2 多个路径参数
2.2.1 发送请求携带参数
携带多个参数
2.2.2 handler方法获取路径参数
@GetMapping("/movie/{categoryId}/{groupId}/{movieId}")
public String findMovieById(@PathVariable("categoryId") Integer categoryId,@PathVariable("groupId")Integer groupId,@PathVariable("movieId") Integer movieId){logger.debug("GET请求...."+categoryId+":"+groupId+":"+movieId);return "target";
}

第四节 RESTFul综合案例

1.案例准备

将前面的传统CRUD案例复制并且重新导入

2. 功能清单

功能URL 地址请求方式
访问首页/GET
查询全部数据/soldierGET
删除/soldier/2DELETE
跳转到添加数据的表单/soldier/add.htmlGET
执行保存/soldierPOST
跳转到更新数据的表单/soldier/2GET
执行更新/soldierPUT

3.访问首页

不需要做修改

4.查询全部

4.1 处理器方法需修改 注解改为@GetMapping
    @GetMappingpublic String findAll(Model model){List soldierList = soldierService.findAll();model.addAttribute("soldierList", soldierList);return PAGE_LIST;}
4.2 index页面修改路径


首页


欢迎来到首页

展示士兵列表
4.3 list页面展示 不需修改

5. 删除数据

重点在于将 GET 请求转换为 DELETE。基本思路是:通过一个通用表单,使用 Vue 代码先把 GET 请求转换为 POST,然后再借助 hiddenHttpMethodFilter 在服务器端把 POST 请求转为 DELETE。

5.2.1 前端代码
删除	


5.2.2 处理器方法
    public static final String LIST_ACTION = "redirect:/soldier";public static final String PAGE_EDIT = "edit";public static final String PAGE_LIST = "list";@Autowiredprivate SoldierService soldierService;@DeleteMapping("/{id}")public String deleteById(@PathVariable("id") Integer soldierId){soldierService.deleteById(soldierId);// 重新查询所有,重定向查询所有方法return LIST_ACTION;}
5.2.3 web.xml中添加HiddenHttpMethodFilter 请求方式映射
    hiddenHttpMethodFilterorg.springframework.web.filter.HiddenHttpMethodFilterhiddenHttpMethodFilter/*

6.跳转到添加数据的页面

list.html修改跳转add.html的路径 处理器方法无需修改

    添加士兵

7.执行添加

7.2.1 处理器方法修改 添加和更新整合成一个方法
    @PutMappingpublic String addOrUpdate(Soldier soldier){soldierService.addOrUpdate(soldier);return LIST_ACTION;}
7.2.3 SoldierService接口中添加方法
    /*** 新增或修改* @param soldier*/void addOrUpdate(Soldier soldier);
7.2.4 SoldierServiceImpl实现类添加方法
    @Transactional@Overridepublic void addOrUpdate(Soldier soldier) {// 判断是否携带id 未携带id是新增士兵if (null == soldier.getSoldierId()){Soldier soldierDaoByName = null;try {soldierDaoByName = soldierDao.findByName(soldier.getSoldierName());} catch (Exception e) {e.printStackTrace();}// 判断是否存在 不存在则添加if (null == soldierDaoByName){soldierDao.add(soldier);}else {return;}}else{// 更新士兵soldierDao.update(soldier);}}
7.2.5 修改add.html post方式通过服务端请求方式映射为put方式


添加士兵页面


士兵名称:
士兵武器:

8.跳转到更新数据页面

8.1 处理器方法
    @GetMapping("/{id}")public String getSoldierById(@PathVariable("id") Integer soldierId, Model model){Soldier soldier = soldierService.getSoldierById(soldierId);model.addAttribute("soldier", soldier);return PAGE_EDIT;}
8.2 list.html页面修改跳转edit.html路径
        修改
8.3 页面回显数据 post方式通过服务端请求方式映射为put方式


编辑士兵页面


士兵姓名:
士兵武器:
9.执行更新

处理器方法无需修改 和添加功能共用一个方法

    @PutMappingpublic String addOrUpdate(Soldier soldier){soldierService.addOrUpdate(soldier);return LIST_ACTION;}

第五节 REST的小结

  1. 解决什么问题: 从根据功能设计url变成根据资源设计url
  2. 是什么:资源状态转移Representational State Transfer
  3. 特征:
    1. 一个url就代表一个资源,也就是说通过url我们可以知道当前请求操作的是什么资源
    2. 请求方式代表操作,也就是说通过请求方式我们知道当前请求对当前资源做的是何种操作
  4. 效果:
    1. url更加简洁
    2. url更加隐晦
    3. 操作的幂等性
  5. 怎么实现:
    1. 请求方式映射:HTML默认只能发送GET和POST请求,SpringMVC在某些时候需要将POST请求映射成PUT或者DELETE请求
    2. GetMapping、PostMapping、DeleteMapping、PutMapping要选择情况使用
    3. 合理设计url
    4. 使用Pathvariable注解获取REST风格的url上的路径参数

第二章 Ajax交互

第一节 获取请求参数

1.获取普通类型参数(与之前获取请求参数的方式一致)

1.1 引入JavaScript库

在这里插入图片描述

1.2 前端代码 WEB-INF下创建index.html


Title




1.3 后端代码
1.3.1 导入依赖
  org.springframeworkspring-webmvc5.3.1ch.qos.logbacklogback-classic1.2.3javax.servletjavax.servlet-api3.1.0providedorg.projectlomboklombok1.18.24providedcom.fasterxml.jackson.corejackson-databind2.12.1
1.3.2 创建springmvc配置文件




1.3.3 创建logback.xml文件

[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger] [%line] [%msg]%n

1.3.4 web.xml文件种添加dispatch处理器和处理乱码问题配置

dispatcherServletorg.springframework.web.servlet.DispatcherServletcontextConfigLocationclasspath:spring.xml1dispatcherServlet/CharacterEncodingFilterorg.springframework.web.filter.CharacterEncodingFilterencodingUTF-8forceRequestEncodingtrueforceResponseEncodingtrueCharacterEncodingFilter/*
1.3.5 创建User实体类
package com.atguigu.pojo;import lombok.Data;@Data
public class User {private Integer age;private String name;private String address;
}
1.3.6 创建UserController类
package com.atguigu.controller;import com.atguigu.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;@Controller
@RequestMapping("/user")
public class UserController {@RequestMapping("/commonParams")public String commonParameter(User user){// 获取异步请求携带的普通类型参数,和以前获取同步请求携带的参数是一样的System.out.println("user========>" + user);return "success";}
}

在这里插入图片描述

2. 获取JSON请求体参数

2.1 前端代码
    发送异步请求携带JSON类型的参数 
// 使用axios发送异步请求,携带JSON类型的请求参数sendJsonParams(){axios({"url":"/user/jsonParams","method":"POST","data":{"age":30,"name":"ls","address":"sz"}}).then(response=>{console.log(response.data.data)})}
2.2 后端代码
2.2.1 jackson依赖

如果忘记导入jackson依赖,会看到下面的错误页面
在这里插入图片描述

关于 SpringMVC 和 Jackson jar包之间的关系,需要注意:当 SpringMVC 需要解析 JSON 数据时就需要使用 Jackson 的支持。但是 SpringMVC 的 jar 包并没有依赖 Jackson,所以需要我们自己导入。

我们自己导入时需要注意:SpringMVC 和 Jackson 配合使用有版本的要求。二者中任何一个版本太高或太低都不行。

SpringMVC 解析 JSON 数据包括两个方向:

  • 从 JSON 字符串到 Java 实体类。
  • 从 Java 实体类到 JSON 字符串。

另外,如果导入了 Jackson 依赖,但是没有开启 mvc:annotation-driven 功能,那么仍然会返回上面的错误页面。

也就是说,我们可以这么总结 SpringMVC 想要解析 JSON 数据需要两方面支持:

  • mvc:annotation-driven
  • 引入 Jackson 依赖

还有一点,如果运行环境是 Tomcat7,那么在 Web 应用启动时会抛出下面异常:

org.apache.tomcat.util.bcel.classfile.ClassFormatException: Invalid byte tag in constant pool: 19

解决办法是使用 Tomcat8 或更高版本。

2.2.2 处理器方法
    @RequestMapping("/jsonParams")public String jsonParams(@RequestBody User user){// 1.获取Json请求体类型的参数必须封装到POJO对象或者是Map中// 2.POJO参数或者Map参数的前面一定要加入@RequestBody注解,不然获取的值都为null// 3.项目中一定要引入jackson的依赖System.out.println("user========>" + user);return "success";}

在这里插入图片描述

第二节 响应JSON类型数据

1.前端代码



Title




2.后端代码

前提是项目中引入了jackson的依赖

创建result包及类

package com.atguigu.controller;import com.atguigu.pojo.User;
import com.atguigu.result.Result;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;@Controller
@RequestMapping("/user")
public class UserController {@ResponseBody@RequestMapping("/commonParams")public Result commonParameter(User user){// 获取异步请求携带的普通类型参数,和以前获取同步请求携带的参数是一样的System.out.println("user========>" + user);return Result.ok(user);}@ResponseBody@RequestMapping("/jsonParams")public Result jsonParams(@RequestBody User user){// 1.获取Json请求体类型的参数必须封装到POJO对象或者是Map中// 2.POJO参数或者Map参数的前面一定要加入@RequestBody注解,不然获取的值都为null// 3.项目中一定要引入jackson的依赖System.out.println("user========>" + user);// 将user封装到Result中 返回给客户端 必须添加ResponseBody注解return Result.ok(user);}
}

返回给客户端json格式的数据

在这里插入图片描述

3. 常见错误

3.1 500 错误

在这里插入图片描述

出现上面的错误页面,表示SpringMVC 为了将 实体类对象转换为 JSON 数据, 需要转换器。但是现在找不到转换器。它想要成功完成转换需要两方面支持:

  • mvc:annotation-driven
  • 引入Jackson依赖
3.2 406 错误(了解)

问题出现的原因:

  • 请求地址扩展名:html
  • 服务器端打算返回的数据格式:JSON

上面二者不一致。SpringMVC 要坚守一个商人的良心,不能干『挂羊头,卖狗肉』的事儿。解决办法有三种思路:

  • 第一种方法:不使用请求扩展名
  • 第二种方法:使用和实际返回的数据格式一致的扩展名
dispatcherServlet*.html*.json

  • 第三种方法:使用一个 HTTP 协议中没有被定义的扩展名,例如:*.do

4. RestController注解

4.1 提取ResponseBody注解

如果类中每个方法上都标记了 @ResponseBody 注解,那么这些注解就可以提取到类上。

4.2 合并注解

类上的ResponseBody 注解可以和Controller 注解合并为RestController 注解。所以使用了RestController 注解就相当于给类中的每个方法都加了ResponseBody 注解。

4.3 RestController源码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {/*** The value may indicate a suggestion for a logical component name,* to be turned into a Spring bean in case of an autodetected component.* @return the suggested component name, if any (or empty String otherwise)* @since 4.0.1*/@AliasFor(annotation = Controller.class)String value() default "";
}

相关内容

热门资讯

埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...