SpringBoot和Axios数据的传递和接收-Restful完全版

SpringBoot和Axios数据的传递和接收-Restful完全版

一、基础知识铺垫

Axios使用

使用axios发送请求,一般有三个最常用的属性。

属性 含义
url 请求的端点 URL
method HTTP 请求方法(如 get, post, put, delete, patch 等)。
params / data 如果 methodgetdelete,使用 params 来传递 URL 查询参数。如果是 post, put, patch,则使用 data 传递请求体数据。通常是一个对象 {}。

HTTP请求方式

Restful风格定义了多种请求方式。

方式 简介 常用场景
GET 请求指定的资源。通常用来获取或查询资源。 读取或查询资源,如获取用户列表或特定用户的详细信息。
POST 向指定资源提交数据,请求服务器进行处理(如创建或修改)。数据包含在请求体中。 创建新资源(如新用户、新帖子),或提交用户数据表单。
PUT 用请求体中的数据替换目标资源的所有当前表示。 更新现有资源的全部内容,如编辑用户的完整个人信息。
PATCH 对资源应用部分修改。 更新资源的一部分,如修改用户的邮箱地址或密码。
DELETE 删除指定的资源。 删除资源,如删除用户账户或帖子。

数据传输方式

方式 介绍
URL路径参数(Path Variables 通过 URL 的路径部分传递数据。在 Spring Boot 中使用 @PathVariable 注解获取。适用于 RESTful 风格的 API,例如获取特定资源的详情。
查询参数(Query Parameters 通过 URL 的查询字符串(?key=value 形式)传递数据。在 Spring Boot 中使用 @RequestParam 注解获取。适用于 GETDELETE 请求。
请求体(Request Body 通过 HTTP 请求的 body 部分传递数据。在 Spring Boot 中使用 @RequestBody 注解获取。适用于 POST , PUTPATCH请求,发送复杂的数据结构。

SpringBoot获取数据的方式

需要提及的是,单单从”获取数据”的角度,我们可以把DeleteGet归为一类,把PutPatchPost归为一类。

  • 前者在axios中使用params传递参数,属于Query Parameters
  • 后者在axios中使用data传递参数,属于Request Body
  • 无论是哪一种,都可以有Path Variables

在 Spring Boot(及一般的 HTTP 服务开发)中,将请求分为”GET 体系”和”POST 体系”可能会导致一些混淆,因为每种 HTTP 方法(GET、POST、PUT、PATCH、DELETE 等)都设计有其独特的用途和语义。不过,如果我们从”如何获取请求中的数据”这个角度来看,可以有一种比较宽泛的分类方式,尤其是关注于数据是通过 URL 还是请求体传递。

体系 获取数据的常用注解
Path Variables @PathVariable
Get、Delete类 @RequestParam@ModelAttribute
Post、Put、Patch类 @RequestBody

二、基础传递代码示例

除了特殊的数据类型,普通的数据传递,默认以axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8';为策略。

(一)Path Variables

Path Variables数据在url上,无关乎get还是post

language-javascript
1
2
3
4
5
6
7
8
9
10
return request({

url: '/test/users/123',
method: 'get'
});
return request({

url: '/test/users/345/info',
method: 'post'
});
language-java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@RestController
@RequestMapping("/test")
public class CourseTestController {


@GetMapping("/users/{userId}")
public String getUser(@PathVariable String userId) {

return "Received GET request for User ID: " + userId;
}

@PostMapping("/users/{userId}/info")
public String updateUser(@PathVariable String userId) {

return "Received POST request for User ID: " + userId;
}

}

(二)Get、Delete

@RequestParam

@RequestParam 主要用于将单个请求参数绑定到方法的参数上,通过指定 valuename 属性,你可以明确告诉 Spring Boot 请求参数的实际名称。

language-javascript
1
2
3
4
5
6
7
8
9
10
return request({

url: '/users',
method: 'get',
params:{

type:"1",
status:"2",
}
});
language-java
1
2
3
4
5
@GetMapping("/users")
public String getUser(@RequestParam String type, @RequestParam(name = "status") String userStatus) {

return "Received GET request for" + type + " " + userStatus;
}

@ModelAttribute

利用 @ModelAttribute 注解。这个注解会告诉 Spring Boot,应该将请求中的查询参数自动绑定到方法参数对象的属性上。

language-javascript
1
2
3
4
5
6
7
8
9
10
return request({

url: '/users',
method: 'get',
params:{

type:"1",
status:"2",
}
});
language-java
1
2
3
4
5
6
7
8
9
10
11
@GetMapping("/users")
public String getUser(@ModelAttribute Query query) {

return "Received GET request for" + query.toString();
}
@Data
class Query{

String type;
String status;
}

通常情况下,我们会将所有的查询参数封装到一个对象中,而不是分开为两个对象,除非这两个对象在逻辑上代表着完全不同的东西,且您希望显式地区分它们。如果您确实有特定的理由需要这样做,也是可行的。比如第二个Query2表示分页查询时的分页参数

language-javascript
1
2
3
4
5
6
7
8
9
10
return request({

url: '/users',
method: 'delete',
params:{

type:"1",
status:"2",
}
});
language-java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
	@DeleteMapping("/users")
public String deleteUser(@ModelAttribute Query1 query1, @ModelAttribute Query2 query2) {

return "Received GET request for" + query1.toString() + query2.toString();
}
@Data
class Query1{

String type;
}
@Data
class Query2{

String userStatus;
// 如果您希望整个对象通过 @ModelAttribute 来绑定,同时又有个别属性名不匹配
// 您可以在后端对象中添加 setter 方法,并在其中处理名称不匹配的问题
// 注意:Lombok @Data 注解会生成默认的 setter 方法,
// 所以如果使用 Lombok,您需要手动添加一个额外的 setter 方法来处理不匹配的情况
public void setStatus(String status) {

this.userStatus = status;
}
}

(三)Post、Put、Patch

@RequestBody

language-javascript
1
2
3
4
5
6
7
8
9
10
11
12
return request({

url: '/users',
method: 'post',
data:{

userId: 123,
userName: "John Doe",
userAge: 30,
userSex: "Male"
}
});
language-java
1
2
3
4
5
6
7
8
9
10
11
12
13
   @PostMapping("/users")
public String getUser(@RequestBody UserVo userVo) {

return userVo.toString();
}
@Data
class UserVo{

Long userId;
String userName;
Long userAge;
String userSex;
}

Spring Boot 后端的 UserVo 类中的属性名和前端传递的 JSON 对象的键名不一致时,可以使用@JsonProperty

language-javascript
1
2
3
4
5
6
7
8
9
10
11
12
return request({

url: '/users',
method: 'put',
data:{

userId: 123,
userName: "John Doe",
userAge: 32,
userSex: "Male"
}
});
language-java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
   @PutMapping("/users")
public String getUser(@RequestBody UserVo userVo) {

return userVo.toString();
}
@Data
class UserVo{

Long userId;
@JsonProperty("userName")
String name;
Long userAge;
String userSex;
}
language-javascript
1
2
3
4
5
6
7
8
9
10
return request({

url: '/users',
method: 'patch',
data:{

userId: 123,
userAge: 34,
}
});
language-java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
   @PatchMapping("/users")
public String getUser(@RequestBody UserVo userVo) {

return userVo.toString();
}
@Data
class UserVo{

Long userId;
@JsonProperty("userName")
String name;
Long userAge;
String userSex;
}

如果你不想额外写一个类作为@RequestBody的参数,你可以选择使用Map或者JsonNode

language-java
1
2
3
4
5
6
7
@PutMapping("/users")
public R<Boolean> getUser(@RequestBody Map<String, Object> body) {

Long id = Long.valueOf(body.get("userId").toString());
String mind = (String) body.get("name");
// 业务逻辑
}
language-java
1
2
3
4
5
6
7
@PutMapping("/users")
public R<Boolean> getUser(@RequestBody JsonNode body) {

Long id = body.get("userId").asLong();
String mind = body.get("name").asText();
// 业务逻辑
}

三、稍微复杂一点的传递

(一)数组

如果用的是Post,那么一般一切安好。

language-javascript
1
2
3
4
5
6
7
8
9
10
return request({

url: '/users',
method: 'post',
data:{

userId: 123,
userOrder: ['1223', '3445', '556'],
}
});
language-java
1
2
3
4
5
6
7
8
9
10
11
   @PostMapping("/users")
public String getUser(@RequestBody UserVo userVo) {

return userVo.toString();
}
@Data
class UserVo{

Long userId;
List<String> userOrder;
}

如果用的是Get,就需要注意默认情况下,Spring Boot 期望列表或数组类型的查询参数以特定的格式传递,例如:userOrder=1223&userOrder=3445&userOrder=556。但在你的例子中,由于是通过 axios 发送请求,并且当你在请求的 params 中包含一个数组时,axios 会将数组转换为 userOrder[0]=1223&userOrder[1]=3445&userOrder[2]=556 的格式,这与 Spring Boot 的默认期望不匹配

language-javascript
1
2
3
4
5
6
7
8
9
10
return request({

url: '/users',
method: 'get',
params:{

userId: 123,
userOrder: ['1223', '3445', '556'].join(','),
}
});
language-java
1
2
3
4
5
@GetMapping("/users")
public String getUser(@RequestParam String userId, @RequestParam List<String> userOrder) {

return userId + "\n" + userOrder.toString();
}

对于数组元素是简单类型,直接用字符’,’拼接即可,变成userOrder=1223,3445,556,Springboot也能匹配。或者可以去参考qs.stringify也就是qs库的用法。

如果数组元素比较复杂呢?如果你仍然坚持使用get,建议去阅读qs使用。一般的做法是用post,可以省去很多麻烦。

language-javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
return request({

url: '/users',
method: 'post',
data:{

userId: 123,
userOrder: [
{
id: '1223', name: 'Order1' },
{
id: '3445', name: 'Order2' },
{
id: '556', name: 'Order3' }
]
}
});
language-java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
   @PostMapping("/users")
public String getUser(@RequestBody UserVo userVo) {

return userVo.toString();
}

@Data
class UserVo{

Long userId;
List<Item> userOrder;
}
@Data
class Item{

String id;
String name;
}

(二)GET/POST复合型

对于复合型请求,即在URL中通过查询参数(例如分页信息)传递部分数据,同时在请求体中通过JSON传递更复杂的数据(例如筛选条件),Spring Boot可以通过同时使用@RequestParam@RequestBody注解来接收这两种类型的数据。

language-javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const pageParams = {

page: 1,
size: 10
};
return request({

url: `/users?page=${
pageParams.page}&size=${
pageParams.size}`,
method: 'post',
data:{

userId: 123,
userName: "Jack"
}
});
language-java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
   @PostMapping("/users")
public String getUser(
@RequestBody UserVo userVo,
@RequestParam("page") int page,
@RequestParam("size") int size
) {

return userVo.toString();
}
@Data
class UserVo{

Long userId;
String userName;
}

四、特殊数据

(一)文件

上传文件时,使用的 Content-Type 通常是 multipart/form-data。这种类型允许将请求体中的数据作为一系列部分(parts)发送,每个部分可以包含文件内容或其他数据。这种方式非常适合文件上传,因为它支持在单个请求中发送文件数据及其他表单字段

language-javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const formData = new FormData();
formData.append('file', file);
formData.append('filename', file.name);
formData.append('chunkIndex', i.toString());
formData.append('totalChunks', totalChunks.toString());
formData.append('md5', md5);
const rsp = await addChunk(formData);

export const addChunk = (data: any) => {

return request({

url: '/video/chunk',
method: 'post',
data: data
});
};
language-java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@PostMapping(value = "/video/chunk")
public R<String> handleChunkUpload(
@RequestParam("file") MultipartFile file,
@RequestParam("md5") String md5,
@RequestParam("filename") String filename,
@RequestParam("chunkIndex") int chunkIndex,
@RequestParam("totalChunks") int totalChunks) {

if (ObjectUtil.isNull(file)) {

return R.fail("上传文件不能为空");
}
Boolean b = mediaFilesService.handleChunkUpload(file, md5);
if (b){

return R.ok();
}else {

return R.fail();
}
}

Vue 3 的框架 Element Plus中,el-upload 组件用于文件上传,它底层使用的也是 multipart/form-data 这种 Content-Type 来上传文件。这是因为 multipart/form-data 允许在一个请求中发送多部分数据,包括文本字段和文件,非常适合文件上传的场景

language-html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<template>
<el-upload
action="http://example.com/upload"
:data="extraData"
:on-success="handleSuccess"
:on-error="handleError"
>
<el-button size="small" type="primary">点击上传</el-button>
</el-upload>
</template>

<script setup>
import {
ElMessage } from 'element-plus';
import {
ref } from 'vue';

// 额外数据
const extraData = ref({

userId: "123",
description: "这是一个文件描述"
});

const handleSuccess = (response, file, fileList) => {

// 文件上传成功的回调
ElMessage.success('文件上传成功');
};

const handleError = (err, file, fileList) => {

// 文件上传失败的回调
ElMessage.error('文件上传失败');
};
</script>

language-java
1
2
3
4
5
6
7
8
9
10
11
12
@RestController
public class FileUploadController {

@PostMapping("/upload")
public String handleFileUpload(
@RequestParam("file") MultipartFile file,
@RequestParam("userId") String userId,
@RequestParam("description") String description) {

return "文件上传成功,用户ID: " + userId + ",描述: " + description;
}
}

(二)Cookies

s
aultValue`指定的默认值 “”会被使用。