接口参数校验之路径变量:@PathVariable(二):多个路径变量校验

一、引言

在上一篇文章《接口参数校验之路径变量:@PathVariable》中,我们深入探讨了Spring MVC框架中的一个重要特性——路径变量的使用和校验。文章详细阐述了如何通过@PathVariable注解从请求URL中提取路径变量,并对单个路径变量进行合法性校验。强调了这一功能在实现RESTful API设计原则,提升API资源定位精确性以及保证系统安全性和稳定性方面的关键作用。

然而,在实际项目开发过程中,接口设计往往更为复杂多样,我们可能会遇到需要同时处理多个路径变量的情况。例如,在处理具有层级关系的资源时(如获取某个用户的某个订单信息)。

因此,本文将在此基础上进一步展开,详细介绍如何在Spring MVC中优雅且高效地处理和校验多个路径变量,从而帮助开发者更好地应对这类复杂场景下的接口设计与实现挑战。

二、多个路径变量的应用场景分析

  1. 资源层级关系
    在RESTful API设计中,资源通常是通过URL来唯一标识和访问的。当资源之间存在明显的层级关系时,通常会在API路由中体现出来,这时就可能出现多个路径变量。例如,在一个博客系统中,文章评论是依附于具体的文章之下的,所以要获取某个文章下的特定评论,可能会设计如下的API接口:

    GET /articles/{articleId}/comments/{commentId}
    

    在这个例子中,“articleId”和“commentId”就是两个路径变量,分别代表了文章ID和评论ID,它们共同确定了一个具体的评论资源。

  2. 路径变量的特点及其对API路由的影响

    • 可读性强:多个路径变量有助于构建语义明确且直观的API URL结构,使得开发者和使用者能够通过URL直接理解资源间的关系。
    • 路由灵活:通过灵活组合路径变量,API能够支持复杂的查询需求,实现对深层次、关联紧密的数据资源的精确访问。
    • 易于客户端使用:客户端可以根据资源层次直接构造请求URL,无需额外传递参数或在查询字符串中编码复杂关系。
  3. 对业务逻辑的影响

    • 耦合度降低:路径变量明确表达了资源之间的关联,使服务端的路由处理逻辑与业务逻辑更加解耦,提高了代码的可维护性和扩展性。
    • 校验与权限控制:服务端在处理带有多个路径变量的请求时,需要对每一个变量进行有效性验证,并可能涉及到基于不同资源层级的权限控制策略,确保只有授权的用户能访问相应的资源。
    • 缓存优化:清晰的资源层级路径也有助于HTTP缓存机制更有效地识别和区分不同的资源实例,从而提升性能。

    这一段是AI介绍的,作者并没有实际经历过,仅供参考。

三、@PathVariable处理多个路径变量

  1. 定义与注解语法
    在Spring MVC中,我们可以轻松地在一个URL路径模板中定义多个@PathVariable。该注解用于将URL路径中的占位符映射到控制器方法的参数上。例如,在一个URL模板 /users/{userId}/orders/{orderId} 中,userIdorderId 都是路径变量。在对应的控制器方法签名中,可以使用 @PathVariable 注解来标记这两个参数:

    @GetMapping("/users/{userId}/orders/{orderId}")
    public ResponseEntity<Order> getOrder(@PathVariable String userId, @PathVariable String orderId) {
        // 通过 userId 和 orderId 获取并返回订单信息
    }
    

    上述代码中,@PathVariable 注解表明了方法参数应当从请求路径中获取相应的值,并根据类型自动转换(在这个例子中,转换为String类型)。

  2. 实例代码演示
    假设我们有一个简单的电商应用,用户可以查看自己的订单详情。以下是具体的Controller层示例代码:

    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class OrderController {
    
        @GetMapping("/users/{userId}/orders/{orderId}")
        public ResponseEntity<Order> getOrderDetails(@PathVariable("userId") String userId, 
                                                    @PathVariable("orderId") String orderId) {
            // 此处应调用服务层逻辑,根据userId和orderId查询订单
            // 以下仅为模拟查询结果
            Order order = getOrderFromDatabase(userId, orderId);
            if (order != null) {
                return ResponseEntity.ok(order);
            } else {
                return ResponseEntity.notFound().build();
            }
        }
    
        private Order getOrderFromDatabase(String userId, String orderId) {
            // 实现从数据库或其他存储系统根据userId和orderId获取订单的逻辑
            // 这里仅作为示例,实际项目中会替换为真实的服务调用
            return new Order(userId, orderId, "Sample Product", "Delivered");
        }
    }
    
    class Order {
        // 省略构造函数、getter/setter等
        private String userId;
        private String id;
        private String productName;
        private String status;
    
        // ...
    }
    

    在上述示例中,当客户端发起如 GET /users/1/orders/200 的请求时,Spring MVC会自动解析路径中的userId为1,orderId为200,并将其传递给getOrderDetails方法进行处理。

四、多个路径变量校验

校验的关键注解任然是三个:@Validated@PathVariable校验注解(如@Pattern)

核心代码

package com.example.web.user.controller;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.constraints.Pattern;

@Slf4j
@RestController
@RequestMapping
@Tag(name = "用户角色管理")
@Validated
public class UserRoleController {

    @DeleteMapping("users/{userId}/roles/{roleId}")
    @Operation(summary = "删除用户的角色")
    @Parameter(name = "userId", description = "用户ID", example = "1234567890123456789")
    @Parameter(name = "roleId", description = "角色ID", example = "9876543210123456789")
    public void deleteUserRole(@PathVariable @Pattern(regexp = "^\\d{19}$", message = "用户ID,应为19位数字") String userId,
                               @PathVariable @Pattern(regexp = "^\\d{19}$", message = "角色ID,应为19位数字") String roleId) {
        log.info("测试,删除用户的角色");
    }

}

校验不通过

在这里插入图片描述

校验不通过

在这里插入图片描述

  • 56
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宋冠巡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值