一、基础知识铺垫

Axios使用

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

属性含义
url请求的端点 URL
methodHTTP 请求方法(如 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

return request({
    url: '/test/users/123',
    method: 'get'
 });
 return request({
    url: '/test/users/345/info',
    method: 'post'
 });
@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 请求参数的实际名称。

return request({
    url: '/users',
    method: 'get',
    params:{
      type:"1",
      status:"2",
    }
 });
	@GetMapping("/users")
    public String getUser(@RequestParam String type, @RequestParam(name = "status") String userStatus) {
        return "Received GET request for" + type + " " + userStatus;
    }

@ModelAttribute

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

return request({
    url: '/users',
    method: 'get',
    params:{
      type:"1",
      status:"2",
    }
 });
	@GetMapping("/users")
    public String getUser(@ModelAttribute Query query) {
        return "Received GET request for" + query.toString();
    }
	@Data
	class Query{
	    String type;
	    String status;
	}

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

return request({
    url: '/users',
    method: 'delete',
    params:{
      type:"1",
      status:"2",
    }
 });
	@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

return request({
    url: '/users',
    method: 'post',
    data:{
      userId: 123,
      userName: "John Doe",
      userAge: 30,
      userSex: "Male"
    }
});
    @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

return request({
    url: '/users',
    method: 'put',
    data:{
      userId: 123,
      userName: "John Doe",
      userAge: 32,
      userSex: "Male"
    }
});
    @PutMapping("/users")
    public String getUser(@RequestBody UserVo userVo) {
        return userVo.toString();
    }
	@Data
	class UserVo{
	    Long userId;
	    @JsonProperty("userName")
	    String name;
	    Long userAge;
	    String userSex;
	}
return request({
    url: '/users',
    method: 'patch',
    data:{
      userId: 123,
      userAge: 34,
    }
});
    @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

@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");
    // 业务逻辑
}
@PutMapping("/users")
public R<Boolean> getUser(@RequestBody JsonNode body) {
    Long id = body.get("userId").asLong();
    String mind = body.get("name").asText();
    // 业务逻辑
}

三、稍微复杂一点的传递

(一)数组

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

return request({
    url: '/users',
    method: 'post',
    data:{
      userId: 123,
      userOrder: ['1223', '3445', '556'],
    }
});
    @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 的默认期望不匹配

return request({
    url: '/users',
    method: 'get',
    params:{
      userId: 123,
      userOrder: ['1223', '3445', '556'].join(','),
    }
});
	@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,可以省去很多麻烦。

return request({
    url: '/users',
    method: 'post',
    data:{
      userId: 123,
      userOrder: [
        { id: '1223', name: 'Order1' },
        { id: '3445', name: 'Order2' },
        { id: '556', name: 'Order3' }
      ]
    }
});
    @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注解来接收这两种类型的数据。

const pageParams = {
  page: 1,
  size: 10
};
return request({
    url: `/users?page=${pageParams.page}&size=${pageParams.size}`,
    method: 'post',
    data:{
      userId: 123,
      userName: "Jack"
    }
});
    @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)发送,每个部分可以包含文件内容或其他数据。这种方式非常适合文件上传,因为它支持在单个请求中发送文件数据及其他表单字段

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
  });
};
	@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 允许在一个请求中发送多部分数据,包括文本字段和文件,非常适合文件上传的场景

<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>

@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

cookies的传递和获取和Path Variables一样也无关乎getpost

document.cookie = "exampleCookie=exampleValue; path=/;";
// 输出当前域下的所有 Cookies
console.log("Current cookies:", document.cookie);

return request({
   url: '/users',
   method: 'get',
   withCredentials:true
});
	@GetMapping("/users")
    public String getUser(@CookieValue(value = "exampleCookie", defaultValue = "") String exampleCookie) {
        return exampleCookie;
    }

在这个示例中,用于从接收到的请求中提取名为 exampleCookie的 Cookie 值。如果请求中没有 exampleCookie Cookie,defaultValue指定的默认值 ""会被使用。

在这里插入图片描述

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐