day02 springmvc

news2025/1/11 20:57:37

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文件
<!--一定要配置在解决乱码的Filter之后-->
<filter>
    <filter-name>hiddenHttpMethodFilter</filter-name>
    <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>hiddenHttpMethodFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
2.1.2 页面的表单
  • 要点1:原请求方式必须是 post
  • 要点2:新的请求方式名称通过请求参数发送
  • 要点3:请求参数名称必须是 _method
  • 要点4:请求参数的值就是要改成的请求方式
<form th:action="@{/rest/movie}" method="post">
    <input type="hidden" name="_method" value="put"/>
    <button>发送请求</button>
</form>
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 前端页面

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

<h3>将XXX请求转换为DELETE请求</h3>
<div id="app">
    <table id="dataTable">
        <tr>
            <th>姓名</th>
            <th>年龄</th>
            <th>删除</th>
        </tr>
        <tr>
            <td>张三</td>
            <td>40</td>
            <td>
                <a th:href="@{/rest/movie}" @click="deleteMovie">删除</a>
            </td>
        </tr>
        <tr>
            <td>李四</td>
            <td>30</td>
            <td>
                <a th:href="@{/rest/movie}" @click="deleteMovie">删除</a>
            </td>
        </tr>
    </table>
</div>

创建负责转换的表单

<form id="myForm" method="post">
    <input type="hidden" name="_method" value="delete"/>
</form>

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

  1. 引入vue

在这里插入图片描述

<!--引入vue-->
<script th:src="@{/static/javaScript/vue.js}"></script>
  1. 绑定单击响应函数
var vue = new Vue({
    "el":"#app",
    "methods":{
        deleteMovie(){
            console.log("aaaaaaa")
            //真正发送删除请求:
            //1. 阻止标签的默认行为
            event.preventDefault()
            //2. 创建一个空表单:先动态设置表单的action
            var 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 发送请求携带参数
<a th:href="@{/rest/movie/2}">携带参数movieId</a>
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 发送请求携带参数
<a th:href="@{/rest/movie/2/22/123}">携带多个参数</a>
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
    @GetMapping
    public String findAll(Model model){
        List<Soldier> soldierList = soldierService.findAll();
        model.addAttribute("soldierList", soldierList);
        return PAGE_LIST;
    }
4.2 index页面修改路径
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
<h1>欢迎来到首页</h1>
<a th:href="@{/soldier}">展示士兵列表</a>
</body>
</html>
4.3 list页面展示 不需修改

5. 删除数据

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

5.2.1 前端代码
<td>
    <a th:href="@{/soldier/}+${soldier.soldierId}" onclick="deleteSoldier()">删除	</a>
    
    
</td>
<!-- 组件名称:通用表单 -->
<!-- 组件作用:把删除超链接的 GET 请求转换为 POST,并携带 _method 请求参数 -->
<form id="deleteById" method="post">
    
    <!-- 请求参数作用:告诉服务器端 hiddenHttpMethodFilter 要转换的目标请求方式 -->
    <!-- 请求参数名:_method,这是 hiddenHttpMethodFilter 中规定的 -->
    <!-- 请求参数值:delete,这是因为我们希望服务器端将请求方式最终转换为 delete -->
    <input type="hidden" name="_method" value="delete"/>
</form>
<script>
    function deleteSoldier() {
        // 阻止a标签的默认发送请求
        event.preventDefault();
        // 获取form标签
        var element = document.getElementById("deleteById");
        // 设置form标签的action值为a标签的href
        element.setAttribute("action", event.target.href)
        // 进行发送请求
        element.submit();
    }
</script>
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";

    @Autowired
    private SoldierService soldierService;

    @DeleteMapping("/{id}")
    public String deleteById(@PathVariable("id") Integer soldierId){
        soldierService.deleteById(soldierId);
        // 重新查询所有,重定向查询所有方法
        return LIST_ACTION;
    }
5.2.3 web.xml中添加HiddenHttpMethodFilter 请求方式映射
    <!--一定要配置在解决乱码的Filter之后-->
    <filter>
        <filter-name>hiddenHttpMethodFilter</filter-name>
        <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>hiddenHttpMethodFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

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

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

    <tr>
        <td colspan="5">
            <a th:href="@{/add.html}">添加士兵</a>
        </td>
    </tr>

7.执行添加

7.2.1 处理器方法修改 添加和更新整合成一个方法
    @PutMapping
    public 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
    @Override
    public 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方式
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>添加士兵页面</title>
</head>
<body>
<form th:action="@{/soldier}" method="post">
    <input type="hidden" name="_method" value="put">
    士兵名称: <input type="text" name="soldierName"> <br>
    士兵武器:<input type="text" name="soldierWeapon"> <br>
    <button>提交</button>
</form>
</body>
</html>

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路径
        <td>
            <a th:href="@{/soldier/}+${soldier.soldierId}">修改</a>
        </td>
8.3 页面回显数据 post方式通过服务端请求方式映射为put方式
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>编辑士兵页面</title>
</head>
<body>
<form th:action="@{/soldier}" method="post">
    <input type="hidden" name="_method" value="put">
    <input type="hidden" name="soldierId" th:value="${soldier.soldierId}">
    士兵姓名:<input type="text" name="soldierName" th:value="${soldier.soldierName}"> <br>
    士兵武器:<input type="text" name="soldierWeapon" th:value="${soldier.soldierWeapon}"> <br>
    <button>提交</button>
</form>

</body>
</html>
9.执行更新

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

    @PutMapping
    public 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
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="/static/js/vue.js"></script>
    <script src="/static/js/axios.js"></script>
</head>
<body>

<div id="app">
    <a href="javascript:;" @click="sendCommonParams">发送异步请求携带普通类型的参数</a> <br>

</div>

<script>
    new Vue({
        el:"#app",
        data:{

        },
        methods:{
            // 使用axios发送异步请求,并且携带普通类型的请求参数
            sendCommonParams(){
                axios({
                    "url":"/user/commonParams",
                    "method":"POST",
                    "params":{
                        "age":20,
                        "name":"zs",
                        "address":"sz"
                    }
                }).then(response=>{
                    console.log(response.data.data)
                })
            }
        }
    })
</script>
</body>
</html>
1.3 后端代码
1.3.1 导入依赖
  <dependencies>
    <!-- SpringMVC -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.3.1</version>
    </dependency>

    <!-- 日志 -->
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>1.2.3</version>
    </dependency>

    <!-- ServletAPI -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
      <scope>provided</scope>
    </dependency>

    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.24</version>
      <scope>provided</scope>
    </dependency>

    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.12.1</version>
    </dependency>
  </dependencies>
1.3.2 创建springmvc配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">

<!--    包扫描-->
    <context:component-scan base-package="com.atguigu"></context:component-scan>
<!--    mvc-->
    <mvc:annotation-driven></mvc:annotation-driven>
    <mvc:default-servlet-handler></mvc:default-servlet-handler>
</beans>
1.3.3 创建logback.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
    <!-- 指定日志输出的位置 -->
    <appender name="STDOUT"
              class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <!-- 日志输出的格式 -->
            <!-- 按照顺序分别是:时间、日志级别、线程名称、打印日志的类、日志主体内容、行数、换行 -->
            <pattern>[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger] [%line] [%msg]%n</pattern>
        </encoder>
    </appender>

    <!-- 设置全局日志级别。日志级别按顺序分别是:DEBUG、INFO、WARN、ERROR -->
    <!-- 指定任何一个日志级别都只打印当前级别和后面级别的日志。 -->
    <root level="DEBUG">
        <!-- 指定打印日志的appender,这里通过“STDOUT”引用了前面配置的appender -->
        <appender-ref ref="STDOUT"/>
    </root>

    <!-- 根据特殊需求指定局部日志级别 -->
    <logger name="org.springframework.web.servlet.DispatcherServlet" level="DEBUG"/>
</configuration>
1.3.4 web.xml文件种添加dispatch处理器和处理乱码问题配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
	version="4.0">

	<servlet>
			<servlet-name>dispatcherServlet</servlet-name>
			<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
			<init-param>
				<param-name>contextConfigLocation</param-name>
				<param-value>classpath:spring.xml</param-value>
			</init-param>
			<load-on-startup>1</load-on-startup>
		</servlet>

		<servlet-mapping>
			<servlet-name>dispatcherServlet</servlet-name>
			<url-pattern>/</url-pattern>
		</servlet-mapping>

	    <!--CharacterEncodingFilter-->
	        <!-- 配置过滤器解决 POST 请求的字符乱码问题 -->
	        <filter>
	            <filter-name>CharacterEncodingFilter</filter-name>
	            <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
	            <!-- encoding参数指定要使用的字符集名称 -->
	            <init-param>
	                <param-name>encoding</param-name>
	                <param-value>UTF-8</param-value>
	            </init-param>
	            <!-- 请求强制编码 -->
	            <init-param>
	                <param-name>forceRequestEncoding</param-name>
	                <param-value>true</param-value>
	            </init-param>
	            <!-- 响应强制编码 -->
	            <init-param>
	                <param-name>forceResponseEncoding</param-name>
	                <param-value>true</param-value>
	            </init-param>
	        </filter>
	        <filter-mapping>
	            <filter-name>CharacterEncodingFilter</filter-name>
	            <url-pattern>/*</url-pattern>
	        </filter-mapping>

</web-app>
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 前端代码
    <a href="javascript:;" @click="sendJsonParams">发送异步请求携带JSON类型的参数</a> <br>

            // 使用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.前端代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="/static/js/vue.js"></script>
    <script src="/static/js/axios.js"></script>
</head>
<body>

<div id="app">
    <a href="javascript:;" @click="sendCommonParams">发送异步请求携带普通类型的参数</a> <br>
    <a href="javascript:;" @click="sendJsonParams">发送异步请求携带JSON类型的参数</a> <br>

</div>

<script>
    new Vue({
        el:"#app",
        data:{

        },
        methods:{
            // 使用axios发送异步请求,并且携带普通类型的请求参数
            sendCommonParams(){
                axios({
                    "url":"/user/commonParams",
                    "method":"POST",
                    "params":{
                        "age":20,
                        "name":"zs",
                        "address":"sz"
                    }
                }).then(response=>{
                    console.log(response.data.data)
                })
            },

            // 使用axios发送异步请求,携带JSON类型的请求参数
            sendJsonParams(){
                axios({
                    "url":"/user/jsonParams",
                    "method":"POST",
                    "data":{
                        "age":30,
                        "name":"ls",
                        "address":"sz"
                    }
                }).then(response=>{
                    console.log(response.data.data)
                })
            }
        }
    })
</script>
</body>
</html>

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 要坚守一个商人的良心,不能干『挂羊头,卖狗肉』的事儿。解决办法有三种思路:

  • 第一种方法:不使用请求扩展名
  • 第二种方法:使用和实际返回的数据格式一致的扩展名
<servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <url-pattern>*.html</url-pattern>
    <url-pattern>*.json</url-pattern>
</servlet-mapping>
  • 第三种方法:使用一个 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 "";
}

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

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

相关文章

Android源码学习---init

init&#xff0c;是linux系统中用户空间的第一个进程&#xff0c;也是Android系统中用户空间的第一个进程。 位于/system/core/init目录下。 分析init int main(int argc, char **argv) { //设置子进程退出的信号处理函数 sigchld_handler act.sa_handler sigchld_handler;…

【博学谷学习记录】超强总结,用心分享丨人工智能 Python面向对象 学习总结之Python与Java的区别

目录前言简述面向对象类对象特性前言 经过学习&#xff0c;对Python面向对象部分有了一定的了解。 总结记录&#xff1a;面向对象上Python与Java的部分区别 简述 从类、对象、特性三个层面来简述其部分区别 面向对象 类 PythonJava定义class ClassName(object):passpubl…

2000-2020年各省固定资本存量数据

2000-2020年各省资本存量数据 1&#xff1a;来源&#xff1a;统计NJ、各省统计NJ 2、时间&#xff1a;2000-2020年 3、包括&#xff1a;30个省 4、数据说明&#xff1a;含原始数据和计算过程及最终结果 4、指标说明&#xff1a; 参考文献&#xff1a; 单豪杰&#xff08;…

【微服务架构组件之注册中心】注册中心选型-我只选nacos

注册中心的产生是基于用来解耦服务提供者(Provider)与消费者&#xff08;Consumer&#xff09;的关系&#xff0c;分布式设计架构下&#xff0c;众多的服务提供者的数量并不是动态不变的&#xff0c;在传统的静态LB的方案中&#xff0c;无法很好感知这种变化&#xff1b; 在分…

[附源码]java毕业设计网上宠物商店

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

进度条——不仅仅是语言层面上的小程序

文章目录\r和\n进度条完整代码\r和\n 在老式键盘上&#xff0c;回车键是这样的形状 但是该键的功能它不仅仅是回车&#xff0c;而是回车换行&#xff01; 这里需要明白两个概念&#xff1a; 回车&#xff1a;光标移动到当前行的行首 换行&#xff1a;光标移动到当前位置的…

跟艾文学编程《Python基础》Anaconda 安装

作者&#xff1a;艾文&#xff0c;计算机硕士学位&#xff0c;企业内训讲师和金牌面试官&#xff0c;公司资深算法专家&#xff0c;现就职BAT一线大厂。 邮箱&#xff1a;1121025745qq.com 博客&#xff1a;https://edu.csdn.net/lecturer/894?spm1003.2001.3001.4144 内容&am…

原生AJAX

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;微微的猪食小窝 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 微微的猪食小窝 原创 1、AJAX 简介 AJAX 全称为Asynchronous Javascript And XML,就是异步的JS 和 XML. 通过AJAX可以在浏览器中向服务器…

Vue3留言墙项目——主体部分静态、mock

文章目录主体头部主体关键部分小卡片组件创建mock数据以及使用主体头部 主体部分显示的内容&#xff0c;根据头部点击的是留言墙还是照片墙的按钮&#xff0c;显示不同的内容。 将照片墙和留言墙要渲染的数据抽取到一个js中&#xff0c;在导入的Main.vue&#xff08;主体页面&…

[go]汇编ASM简介

文章目录汇编(ASM)寄存器帧指针FP常见指令函数示例生成汇编Go汇编代码主要用于优化和与底层系统交互&#xff0c;并不会像其它的经典汇编代码那样独立运行。汇编(ASM) Go ASM是一种被Go编译器使用的特殊形式的汇编语言&#xff08;伪汇编&#xff09;&#xff0c;它基于Plan9输…

记录一次Powerjob踩的坑(Failed to deserialize message)

一. 问题描述: 在本地开发环境, server端和worker都运行正常. 但是发布到SIT环境(容器)的时候, 服务端却监测不到worker(worker可以找到服务端) 二. 问题表现: 1.服务端看不到Worker信息 2. 服务端日志信息 : Failed to deserialize message from [akka://oms111.111.111…

ECMAScript modules规范示例详解

引言 很多编程语言都有模块这一概念&#xff0c;JavaScript 也不例外&#xff0c;但在 ECMAScript 2015 规范发布之前&#xff0c;JavaScript 没有语言层面的模块语法。模块实际上是一种代码重用机制&#xff0c;要实现代码重用&#xff0c;将不同的功能划分到不同的文件中是必…

pycharm安装并加载编译器,设置背景图片,手把手详细操作

pycharm安装并加载编译器&#xff0c;设置背景图片&#xff0c;手把手详细操作 pycharm社区版&#xff08;免费&#xff09;下载官网 双击安装包&#xff0c;选择安装路径 勾选这两个&#xff0c;其实全不勾也没事 下一步默认就行&#xff0c;点install 安装完成后&#xf…

mimikatz抓取密码实战

必须下载最新版本 Releases gentilkiwi/mimikatz GitHubhttps://github.com/gentilkiwi/mimikatz/releases 有32和64之分&#xff0c;systeminfo查看自己版本 首先我们用后门得到权限&#xff0c;在用getsystem提权&#xff0c;因为mimikatz要system权限&#xff0c;getuid…

Python基础-1-环境搭建(初体验)

一&#xff1a;开发环境 Linux-5.15.0&#xff08;Ubuntu22.04&#xff09; 二&#xff1a;安装Python3 1、安装&#xff1a;sudo apt-get install python3 2、版本查询&#xff1a; python3 --version python3进入python解释器也可查询对应版本&#xff0c;按CtrlD或执行…

力扣(LeetCode)20. 有效的括号(C++)

栈模拟 一次遍历字符串 sss &#xff0c; 遇到左括号则入栈&#xff0c;遇到右括号则匹配栈顶。如果右括号匹配成功 &#xff0c; 栈顶元素弹栈 &#xff0c; 匹配不成功 &#xff0c; 则 returnfalsereturn\ \ falsereturn false 。 提示 : 当遍历完所有字符&#xff0c;记…

【计算机网络】扩展以太网方法总结

注&#xff1a;最后有面试挑战&#xff0c;看看自己掌握了吗 文章目录物理层扩展以太网链路层扩展以太网网桥网桥分类透明网桥源路由网桥多接口网桥----以太网交换机直通式交换机存储转发式交换机冲突域与广播域&#x1f343;博主昵称&#xff1a;一拳必胜客 &#x1f338;博主…

LinkedList详解

介绍 众所周知ArrayList底层数据结构是数组&#xff0c;但是数组有个缺点&#xff0c;虽然查询快&#xff0c;但是增删改会慢因为数组是在连续的位置上面储存对象的应用。当我们删除某一个元素的时候在他后面的元素的索引都会左移&#xff0c;导致开销会很大。所以LinkedList应…

Linux系统下交叉编译工具的安装实现

大家好&#xff0c;今天主要和大家聊一聊&#xff0c;如何使用Linux系统下的交叉编译工具链的方法。 目录 第一:交叉编译工具链基本简介 ​第二&#xff1a;交叉编译工具安装方法 ​第三&#xff1a;安装相关库 ​第四&#xff1a;交叉编译工具验证 第一:交叉编译工具链基…

0100 蓝桥杯真题03

import java.util.Scanner; /* * 题目描述 * 如下图所示&#xff0c;3 x 3 的格子中填写了一些整数。 --*---- |10* 1|52| --****-- |20|30* 1| *******-- | 1| 2| 3| ------ *我们沿着图中的星号线剪开&#xf…