Spring Boot 接收前端请求的四种参数方式

一、HTTP 请求的结构

在讨论怎么取参数之前,先看清参数藏在哪里。一个 HTTP 请求由三部分组成:请求行、请求头、请求体。

1.1 请求行

复制代码
POST /api/v1/users/42/orders?page=1&size=20 HTTP/1.1

请求行包含三要素:

**请求方法:**POST,指定对资源的操作语义。

请求URL:/api/v1/users/42/orders?page=1&size=20,包含路径和查询字符串。

**协议版本:**HTTP/1.1,HTTP 协议版本号。

URL 中携带两种参数:
路径参数: 嵌在路径段中,如 /users/42 中的 42。
查询参数:? 后面的 key=value 键值对,多个用 & 连接。

1.2 请求头

复制代码
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...
Accept: application/json
User-Agent: Mozilla/5.0
Cookie: sessionId=abc123
X-Forwarded-For: 192.168.1.100

请求头是 Key: Value 形式的元数据,不传输业务数据,但携带了格式声明、认证凭据、客户端信息等控制信息。常见请求头:

|-----------------|------------------------------|
| 请求头 | 作用 |
| Content-Type | 声明请求体的数据格式 |
| Authorization | 携带认证凭据(Bearer Token、Basic 等) |
| Accept | 告知服务器客户端期望的响应格式 |
| Cookie | 携带会话信息 |
| X-Forwarded-For | 代理场景下传递真实客户端 IP |

1.3 请求体

请求体是可选部分,GET 和 DELETE 请求通常没有请求体,POST / PUT / PATCH 通常有。请求体的格式由 Content-Type 请求头声明:

|-----------------------------------|-------------|------------------------|
| Content-Type | 格式 | 示例 |
| application/json | JSON | {"name":"张三","age":25} |
| application/x-www-form-urlencoded | 表单键值对 | name=张三&age=25 |
| multipart/form-data | 多部分表单(文件上传) | 二进制分片 |
| text/plain | 纯文本 | hello world |

1.4 完整请求结构

复制代码
┌──────────────────────── 请求行 ────────────────────────┐
│  POST /api/v1/users/42/orders?page=1&size=20 HTTP/1.1 │
├──────────────────────── 请求头 ────────────────────────┤
│  Content-Type: application/json                        │
│  Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...        │
│  Accept: application/json                              │
├──────────────────────── 空行 ──────────────────────────┤
│                                                        │
├──────────────────────── 请求体 ────────────────────────┤
│  {                                                     │
│    "productId": 1001,                                  │
│    "quantity": 2,                                      │
│    "remark": "不要辣"                                  │
│  }                                                     │
└────────────────────────────────────────────────────────┘

我们一般会将参数放在以下四种位置:

路径参数 → 请求行 URL 路径段;查询参数 → 请求行 ? 后面;请求头参数 → 请求头区域;请求体参数 → 请求体区域。

二、方式一:路径参数 ------ @PathVariable

路径参数是 URL 路径中通过 {} 占位声明的变量,Spring 通过 @PathVariable 将其提取并绑定到方法参数。

2.1 基本用法

java 复制代码
@GetMapping("/users/{id}")
public User getUser(@PathVariable("id") Long id) {
    return userService.getById(id);
}

请求:GET /users/42 → id = 42

当方法参数名与占位符名一致时,可以省略 value(需编译时开启 -parameters):

java 复制代码
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) { ... }

2.2 多个路径参数

java 复制代码
@DeleteMapping("/users/{userId}/orders/{orderId}")
public Result deleteOrder(@PathVariable Long userId,
                          @PathVariable Long orderId) {
    orderService.delete(userId, orderId);
    return Result.success();
}

请求:DELETE /users/42/orders/1001 → userId = 42, orderId = 1001

2.3 适用场景

路径参数用于标识资源或资源状态,属于 URL 路径的一部分,语义上是"你要操作哪个东西":

|--------|--------------------------------|--------------------------------|
| 场景 | 示例 URL | 路径参数 |
| 获取单个资源 | GET /users/42 | id=42 |
| 切换状态 | PUT /orders/1001/status/2 | id=1001, status=2 |
| 嵌套资源操作 | DELETE /categories/5/dishes/10 | DELETE /categories/5/dishes/10 |

三、方式二:查询参数 ------ @RequestParam / 省略注解

查询参数是 URL 中 ? 后面的 key=value 键值对,Spring 提供了多种接收方式。

3.1 显式指定:@RequestParam

java 复制代码
@GetMapping("/products")
public List<Product> list(@RequestParam("category") String category,
                          @RequestParam("page") int page) {
    return productService.list(category, page);
}

请求:GET /products?category=electronics&page=2

@RequestParam 的三个常用属性:

java 复制代码
@GetMapping("/search")
public Result search(
    @RequestParam(value = "keyword", required = true) String keyword,
    @RequestParam(value = "page", defaultValue = "1") int page,
    @RequestParam(value = "size", defaultValue = "20") int size
) { ... }

3.2 省略注解

当方法参数是简单类型(基本类型及其包装类、String、Date 等)且不加任何注解时,Spring 默认按查询参数名匹配:

java 复制代码
@GetMapping("/products")
public List<Product> list(String keyword, int page) {
    // 等价于 @RequestParam("keyword") 和 @RequestParam("page")
    return productService.list(keyword, page);
}

**注意:**省略注解要求方法参数名与 URL 查询参数名一致。这依赖编译时的 -parameters 选项。如果未开启,Spring 无法通过反射获取参数名,会报错。Maven 配置:

XML 复制代码
> <plugin>
>     <groupId>org.apache.maven.plugins</groupId>
>     <artifactId>maven-compiler-plugin</artifactId>
>     <configuration>
>         <parameters>true</parameters>
>     </configuration>
> </plugin>
>

3.3 对象封装:POJO 接收多个查询参数

查询参数多的时候,逐个声明方法参数会很冗长。Spring 支持将查询参数自动封装到 POJO 中:

java 复制代码
@GetMapping("/products")
public PageResult<Product> list(ProductQueryDTO query) {
    return productService.pageQuery(query);
}
java 复制代码
@Data
public class ProductQueryDTO {
    private String keyword;
    private Integer categoryId;
    private int page = 1;
    private int size = 20;
    private String sortField;
    private String sortOrder;
}

请求:GET /products?keyword=手机&categoryId=5&page=2&size=10

Spring 遍历查询参数,按名称匹配 POJO 的字段,通过 setter 注入。POJO 要求:有无参构造器 + 有标准 setter(Lombok @Data 满足)。

3.4 接收数组和集合

查询参数允许同名多值,Spring 可以用数组或 List 接收:

java 复制代码
// 请求:GET /products?tag=电子&tag=热销&tag=新品
@GetMapping("/products")
public List<Product> list(@RequestParam("tag") List<String> tags) {
    return productService.findByTags(tags);
}

也可以用 @RequestParam 接收 Map<String, String> 获取所有查询参数:

java 复制代码
@GetMapping("/search")
public Result search(@RequestParam Map<String, String> params) {
    params.forEach((k, v) -> System.out.println(k + "=" + v));
    return Result.success();
}

3.5 适用场景

查询参数用于筛选、分页、排序、搜索等,语义上是"你要什么样的资源":

|-------|------------------------------------------|
| 场景 | 示例 URL |
| 分页查询 | GET /users?page=1&size=20 |
| 条件筛选 | GET /products?category=电子&priceMax=5000 |
| 关键字搜索 | GET /search?keyword=手机 |
| 排序 | GET /products?sort=price&order=desc |

四、方式三:请求体参数 ------ @RequestBody

请求体参数位于 HTTP 请求的 body 区域,通常是 JSON 格式。Spring 通过 @RequestBody 触发 HttpMessageConverter(默认 Jackson 的 MappingJackson2HttpMessageConverter)将 JSON 反序列化为 Java 对象。

4.1 基本用法

java 复制代码
@PostMapping("/users")
public Result createUser(@RequestBody UserDTO userDTO) {
    userService.create(userDTO);
    return Result.success();
}

请求:

java 复制代码
POST /users
Content-Type: application/json

{
    "username": "zhangsan",
    "email": "zhangsan@example.com",
    "age": 25
}

Spring 读取请求体中的 JSON 字节流,通过 Jackson 将其反序列化为 UserDTO 对象。反序列化要求:

POJO 有无参构造器

字段名与 JSON key 一致(或通过 @JsonProperty 映射)

有标准 setter(或使用 Lombok @Data)

4.2 JSON 字段名与 Java 字段名不一致时

用 @JsonProperty 做映射:

java 复制代码
@Data
public class UserDTO {
    @JsonProperty("user_name")
    private String userName;

    @JsonProperty("email_address")
    private String emailAddress;
}

前端传:

java 复制代码
{
    "user_name": "张三",
    "email_address": "zhangsan@qq.com"
}

4.3 接收嵌套 JSON

java 复制代码
@Data
public class OrderDTO {
    private Long addressId;
    private String remark;
    private List<OrderItemDTO> items;  // 嵌套对象列表
}

@Data
public class OrderItemDTO {
    private Long productId;
    private Integer quantity;
}

前端传:

java 复制代码
{
    "addressId": 1,
    "remark": "不要辣",
    "items": [
        {"productId": 1001, "quantity": 2},
        {"productId": 1002, "quantity": 1}
    ]
}

Jackson 会递归反序列化嵌套对象,无需额外注解。

4.4 接收非 JSON 格式的请求体

@RequestBody 不仅仅用于 JSON。Spring 会根据 Content-Type 选择对应的 HttpMessageConverter:

|------------------|----------------------------------------|------------|
| Content-Type | 使用的 Converter | 用法 |
| application/json | MappingJackson2HttpMessageConverter | 反序列化为 POJO |
| application/xml | MappingJackson2XmlHttpMessageConverter | 反序列化为 POJO |
| text/plain | StringHttpMessageConverter | 接收为 String |

java 复制代码
// 接收纯文本请求体
@PostMapping("/notify")
public Result handleNotify(@RequestBody String payload) {
    log.info("收到通知:{}", payload);
    return Result.success();
}

4.5 @RequestBody 不能省略

这是一个高频踩坑点。如果省略 @RequestBody:

java 复制代码
// 错误:省略了 @RequestBody
@PostMapping("/users")
public Result createUser(UserDTO userDTO) {
    // userDTO 的所有字段都是 null
    return Result.success();
}

Spring 对没有 @RequestBody 注解的复杂类型参数,执行的是表单参数绑定 (WebDataBinder),即从 application/x-www-form-urlencoded 格式的查询参数 / 表单参数中按字段名匹配。前端传的是 JSON 请求体,Spring 根本不会去读它,结果所有字段为 null。
**规则:**只要前端用 application/json 传数据,Controller 方法参数必须加 @RequestBody,没有例外。

4.6 一个方法只能有一个 @RequestBody

HTTP 请求体只能被读取一次,Spring 不支持用多个 @RequestBody 参数分别接收不同部分。如果需要传递多个逻辑对象,封装到一个外层 DTO:

java 复制代码
// 错误:两个 @RequestBody
@PostMapping("/order")
public Result create(@RequestBody OrderDTO order, @RequestBody PaymentDTO payment) { ... }

// 正确:封装到外层 DTO
@Data
public class CreateOrderRequest {
    private OrderDTO order;
    private PaymentDTO payment;
}

@PostMapping("/order")
public Result create(@RequestBody CreateOrderRequest request) { ... }

4.7 适用场景

请求体参数用于提交、修改复杂数据,语义上是**"我要给你什么数据"**:

|------|--------------------|-------------|
| 场景 | HTTP 方法 | 说明 |
| 新增资源 | POST + JSON | 创建用户、提交订单 |
| 修改资源 | PUT / PATCH + JSON | 修改个人信息、更新配置 |
| 批量操作 | POST + JSON 数组 | 批量导入、批量删除 |

五、方式四:请求头参数 ------ @RequestHeader

请求头中的值通过 @RequestHeader 提取。虽然业务数据一般不放在请求头中,但认证凭据、追踪标识、客户端信息等经常通过请求头传递。

5.1 基本用法

java 复制代码
@GetMapping("/profile")
public Result getProfile(@RequestHeader("Authorization") String authHeader) {
    String token = authHeader.replace("Bearer ", "");
    Long userId = jwtUtil.parseUserId(token);
    return Result.success(userService.getById(userId));
}

请求:

java 复制代码
GET /profile HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...

5.2 常用属性

@RequestHeader 与 @RequestParam 的属性完全一致:

|--------------|----------|----------------|
| 属性 | 作用 | 默认值 |
| value / name | 请求头的 key | 方法参数名 |
| required | 是否必传 | true(不存在时抛 400 |
| defaultValue | 未传时的默认值 | 无 |

5.3 获取所有请求头

如果需要获取多值请求头(如 Accept 可以有多个值),使用 MultiValueMap:

java 复制代码
@GetMapping("/headers")
public Result headers(@RequestHeader MultiValueMap<String, String> headers) {
    List<String> acceptValues = headers.get("Accept");
    return Result.success();
}

5.4 拦截器中手动获取请求头

在认证拦截器中,通常不用 @RequestHeader,而是直接从 HttpServletRequest 获取:

java 复制代码
@Component
public class AuthInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) {
        String token = request.getHeader("Authorization");
        if (token == null || !jwtUtil.validate(token)) {
            response.setStatus(401);
            return false;
        }
        return true;
    }
}

5.5 适用场景

请求头参数不是业务数据,而是控制信息:

|-------|------------------------------|
| 场景 | 请求头 |
| 身份认证 | Authorization: Bearer xxx |
| 请求追踪 | X-Trace-Id: abc-123 |
| 多语言支持 | Accept-Language: zh-CN |
| 内容协商 | Accept: application/json |
| 客户端标识 | X-Device-Type: iOS |
| 幂等性控制 | Idempotency-Key: unique-uuid |

六、总结

|-------|--------------------|---------|------------|
| 参数方式 | 注解 | 从哪取 | 传什么 |
| 路径参数 | @PathVariable | URL 路径段 | 资源标识 |
| 查询参数 | @RequestParam / 省略 | URL ? 后 | 筛选、分页、排序 |
| 请求体参数 | @RequestBody | 请求体 | 提交的复杂数据 |
| 请求头参数 | @RequestHeader | 请求头 | 认证、追踪等控制信息 |

记住一个核心判断逻辑:

要定位资源 → 路径参数

要筛选资源 → 查询参数

要提交数据 → 请求体参数(加 @RequestBody)

要传递控制信息 → 请求头参数

相关推荐
时空系1 小时前
第13篇:综合实战——制作我的小游戏 Rust中文编程
开发语言·后端·rust
咸鱼咸鱼1 小时前
RustDesk 自建服务端教程:开源远程桌面,完全掌控你的数据
后端
0xDevNull2 小时前
JDK多版本切换安装与配置
java·后端
是安迪吖2 小时前
企业资产管理系统练习
前端·ai
zhouwy1132 小时前
AI 编程工具结合 Figma MCP 实现前端设计高保真还原
前端·人工智能·figma
kyriewen2 小时前
WebAssembly:前端界的“外挂”,让C++代码在浏览器里跑起来
前端·c++·webassembly
悟空和大王2 小时前
核心 SDK 详细设计文档 (Visual-Render-SDK)
前端
傻瓜搬砖人2 小时前
SpringBoot整合Junit-Redis-打包
spring boot·redis·junit
Java编程爱好者2 小时前
1-5 线程池:Thread+阻塞队列+循环
后端