Skip to content

Spring Boot 中的 @RequestParam 注解详解 🚀

什么是 @RequestParam?

@RequestParam 是 Spring MVC 中用于绑定 HTTP 请求参数的核心注解。它能够将 URL 查询参数或表单数据自动映射到控制器方法的参数上,让我们轻松获取客户端传递的数据。

NOTE

@RequestParam 主要用于处理两种类型的请求参数:

  • 查询参数:如 GET /pets?petId=123 中的 petId=123
  • 表单数据:如 POST 请求中的表单字段

为什么需要 @RequestParam?🤔

解决的核心痛点

在没有 @RequestParam 之前,开发者需要手动从 HttpServletRequest 中获取参数:

kotlin
@GetMapping("/pets")
fun getPet(request: HttpServletRequest): String {
    // 手动获取参数,容易出错
    val petIdStr = request.getParameter("petId") 
    if (petIdStr == null) { 
        throw IllegalArgumentException("petId is required") 
    } 
    
    val petId = try { 
        petIdStr.toInt() 
    } catch (e: NumberFormatException) { 
        throw IllegalArgumentException("petId must be a number") 
    } 
    
    // 业务逻辑...
    return "petForm"
}
kotlin
@GetMapping("/pets")
fun getPet(@RequestParam("petId") petId: Int): String { 
    // Spring 自动处理参数获取、类型转换和验证
    // 直接使用 petId,无需手动处理
    return "petForm"
}

设计哲学

@RequestParam 体现了 Spring 框架的核心设计理念:

  • 约定优于配置:默认行为满足大多数场景
  • 声明式编程:通过注解声明意图,框架处理细节
  • 类型安全:自动类型转换,减少运行时错误

基础用法示例 📝

1. 简单参数绑定

kotlin
@RestController
@RequestMapping("/api/pets")
class PetController {
    
    @GetMapping
    fun getPet(@RequestParam("petId") petId: Int): ResponseEntity<Pet> {
        // Spring 自动将字符串 "123" 转换为 Int 类型
        val pet = petService.findById(petId)
        return ResponseEntity.ok(pet)
    }
}

请求示例GET /api/pets?petId=123

2. 可选参数处理

kotlin
@GetMapping("/search")
fun searchPets(
    @RequestParam(value = "name", required = false) name: String?,
    @RequestParam(value = "age", required = false) age: Int?
): List<Pet> {
    return petService.search(name, age)
}
kotlin
@GetMapping("/search")
fun searchPets(
    @RequestParam(value = "name", defaultValue = "") name: String,
    @RequestParam(value = "page", defaultValue = "0") page: Int,
    @RequestParam(value = "size", defaultValue = "10") size: Int
): Page<Pet> {
    return petService.findAll(PageRequest.of(page, size))
}

TIP

使用 defaultValuerequired = false 更安全,因为它避免了空值处理的复杂性。

3. 多值参数处理

kotlin
@GetMapping("/filter")
fun filterPets(
    @RequestParam("tags") tags: List<String>, 
    @RequestParam("ages") ages: IntArray
): List<Pet> {
    // 处理多个相同名称的参数
    // 请求: /filter?tags=cute&tags=friendly&ages=1&ages=2&ages=3
    return petService.filterByTagsAndAges(tags, ages)
}

请求示例GET /api/pets/filter?tags=cute&tags=friendly&ages=1&ages=2&ages=3

高级用法 🔥

1. 获取所有请求参数

kotlin
@PostMapping("/process")
fun processForm(
    @RequestParam params: MultiValueMap<String, String> 
): ResponseEntity<String> {
    // 获取所有请求参数,包括重复的参数名
    params.forEach { (key, values) ->
        println("参数 $key: $values")
    }
    
    // 处理特定参数
    val name = params.getFirst("name")
    val hobbies = params["hobbies"] // 获取所有 hobby 值
    
    return ResponseEntity.ok("处理完成")
}

2. 表单数据处理

kotlin
@PostMapping("/pets", consumes = [MediaType.APPLICATION_FORM_URLENCODED_VALUE])
fun createPet(
    @RequestParam("name") name: String,
    @RequestParam("age") age: Int,
    @RequestParam("breed") breed: String,
    @RequestParam(value = "description", required = false) description: String?
): ResponseEntity<Pet> {
    val pet = Pet(name = name, age = age, breed = breed, description = description)
    val savedPet = petService.save(pet)
    return ResponseEntity.status(HttpStatus.CREATED).body(savedPet)
}

3. 复杂参数映射

点击查看复杂表单处理示例
kotlin
@RestController
@RequestMapping("/api/orders")
class OrderController {
    
    @PostMapping("/create")
    fun createOrder(
        @RequestParam("customerId") customerId: Long,
        @RequestParam("items") itemIds: List<Long>,
        @RequestParam("quantities") quantities: List<Int>,
        @RequestParam(value = "couponCode", required = false) couponCode: String?,
        @RequestParam(value = "notes", defaultValue = "") notes: String,
        @RequestParam("deliveryDate") 
        @DateTimeFormat(pattern = "yyyy-MM-dd") deliveryDate: LocalDate
    ): ResponseEntity<Order> {
        
        // 验证参数
        if (itemIds.size != quantities.size) {
            throw IllegalArgumentException("商品数量与数量列表长度不匹配")
        }
        
        // 构建订单项
        val orderItems = itemIds.zip(quantities) { itemId, quantity ->
            OrderItem(itemId = itemId, quantity = quantity)
        }
        
        // 创建订单
        val order = Order(
            customerId = customerId,
            items = orderItems,
            couponCode = couponCode,
            notes = notes,
            deliveryDate = deliveryDate
        )
        
        val savedOrder = orderService.create(order)
        return ResponseEntity.status(HttpStatus.CREATED).body(savedOrder)
    }
}

类型转换与验证 ⚡

自动类型转换

Spring 支持多种类型的自动转换:

kotlin
@GetMapping("/demo")
fun typeConversionDemo(
    @RequestParam("id") id: Long,                    // String → Long
    @RequestParam("active") active: Boolean,         // String → Boolean
    @RequestParam("price") price: BigDecimal,        // String → BigDecimal
    @RequestParam("tags") tags: Set<String>,         // String[] → Set<String>
    @RequestParam("date") 
    @DateTimeFormat(pattern = "yyyy-MM-dd") date: LocalDate  // String → LocalDate
): Map<String, Any> {
    return mapOf(
        "id" to id,
        "active" to active,
        "price" to price,
        "tags" to tags,
        "date" to date
    )
}

参数验证

结合 Bean Validation 进行参数验证:

kotlin
@GetMapping("/validate")
fun validateParams(
    @RequestParam("email") 
    @Email(message = "邮箱格式不正确") email: String,
    
    @RequestParam("age") 
    @Min(value = 0, message = "年龄不能小于0")
    @Max(value = 150, message = "年龄不能大于150") age: Int,
    
    @RequestParam("phone") 
    @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确") phone: String
): ResponseEntity<String> {
    return ResponseEntity.ok("验证通过")
}

最佳实践 ✅

1. 参数命名规范

kotlin
// ✅ 推荐:使用有意义的参数名
@GetMapping("/search")
fun searchUsers(
    @RequestParam("keyword") searchKeyword: String,
    @RequestParam("page") pageNumber: Int = 0,
    @RequestParam("size") pageSize: Int = 20
): Page<User>

// ❌ 不推荐:参数名不明确
@GetMapping("/search")
fun searchUsers(
    @RequestParam("q") q: String,
    @RequestParam("p") p: Int,
    @RequestParam("s") s: Int
): Page<User>

2. 合理设置默认值

kotlin
@GetMapping("/products")
fun getProducts(
    @RequestParam(value = "category", defaultValue = "all") category: String,
    @RequestParam(value = "sortBy", defaultValue = "name") sortBy: String,
    @RequestParam(value = "order", defaultValue = "asc") order: String,
    @RequestParam(value = "page", defaultValue = "0") page: Int,
    @RequestParam(value = "size", defaultValue = "10") size: Int
): Page<Product> {
    // 业务逻辑
}

3. 错误处理

kotlin
@ControllerAdvice
class GlobalExceptionHandler {
    
    @ExceptionHandler(MissingServletRequestParameterException::class)
    fun handleMissingParam(ex: MissingServletRequestParameterException): ResponseEntity<ErrorResponse> {
        val error = ErrorResponse(
            code = "MISSING_PARAMETER",
            message = "缺少必需参数: ${ex.parameterName}",
            timestamp = LocalDateTime.now()
        )
        return ResponseEntity.badRequest().body(error)
    }
    
    @ExceptionHandler(MethodArgumentTypeMismatchException::class)
    fun handleTypeMismatch(ex: MethodArgumentTypeMismatchException): ResponseEntity<ErrorResponse> {
        val error = ErrorResponse(
            code = "TYPE_MISMATCH",
            message = "参数 ${ex.name} 类型错误,期望: ${ex.requiredType?.simpleName}",
            timestamp = LocalDateTime.now()
        )
        return ResponseEntity.badRequest().body(error)
    }
}

常见陷阱与注意事项 ⚠️

1. 参数名不匹配

WARNING

@RequestParamvalue 与实际请求参数名不匹配时,会抛出异常。

kotlin
// 请求: GET /api/users?userId=123
@GetMapping("/users")
fun getUser(@RequestParam("id") userId: Long) 
// 错误:请求参数是 userId,但注解中写的是 id

正确做法:

kotlin
@GetMapping("/users")
fun getUser(@RequestParam("userId") userId: Long) 

2. 类型转换失败

kotlin
// 请求: GET /api/products?price=abc
@GetMapping("/products")
fun getProducts(@RequestParam("price") price: Double) {
    // 会抛出 MethodArgumentTypeMismatchException
}

TIP

建议使用自定义转换器或在全局异常处理器中处理类型转换异常。

3. 数组参数的空值处理

kotlin
@GetMapping("/filter")
fun filterItems(@RequestParam("tags") tags: List<String>) {
    // 如果请求中没有 tags 参数,会抛出异常
    // 解决方案:设置 required = false 或 defaultValue
}

总结 🎯

@RequestParam 是 Spring MVC 中处理请求参数的强大工具,它:

  • 简化开发:自动处理参数绑定和类型转换
  • 提高安全性:减少手动参数处理的错误
  • 增强可读性:声明式的参数定义更清晰
  • 支持灵活配置:可选参数、默认值、多值处理等

IMPORTANT

掌握 @RequestParam 的正确使用方法,能够让你的 Spring Boot 应用更加健壮和易维护。记住要合理设置默认值、处理异常情况,并遵循良好的命名规范。

通过本文的学习,相信你已经对 @RequestParam 有了深入的理解。在实际项目中,结合业务需求灵活运用这些知识,你将能够构建出更加优雅和可靠的 Web 应用! 🚀