Skip to content

Add support for @RequestParam, @RequestHeader, @PathVariable at field level #32597

Closed as not planned
@vcruzmj

Description

@vcruzmj

This was discussed already in:

  1. Allow @RequestParam, @PathVariable, and their siblings on fields #23618
  2. Provide mechanism to map request parameters to fields of DTO object #23094
  3. Add support for @BeanParam like JAX-RS in Spring MVC [SPR-17237] #21770

But it was closed by @jhoeller alongside this comment.

We have no plans to introduce field-level request binding annotations. @RequestParam, @PathVariable and co are meant to be used at the parameter level, that's a pretty central part of our annotated handler method arrangement. Binding to objects is a separate mechanism based on bean property setters, property-style fields or constructor arguments, matched against request parameters and path variables. If different binding names are intended there, you could use constructor binding and declare the constructor parameter names with the abbreviated binding names, or with setter methods and declare the setter method names according to your binding names, all while keeping the field names themselves readable.

But IMHO, this answer is just a business decision instead of a technical decision; it could be a feature request that can be handled with a low priority, but discarding it just because "it is not in plans" is not a good approach at all.

First of all, why do we need this? Mainly because some GET endpoints start to get so many arguments that they surpass an acceptable amount. So, creating a DTO is a must, but when you need to specify a different name or property (like required) in every field, this starts to get messy.

Our current solution uses @ConstructorProperties, but this approach leads to human errors and a considerable degradation of maintainability. (I'm going to use Kotlin examples, but applies to Java also)

Example 1:

Expected with the requested feature
data class DTOWithRequestParams(
    @field:RequestParam(name= "some_field_a", required = true)
    val someFieldA: String,

    @field:RequestParam(name= "some_field_b", defaultValue = "B")
    val someFieldB: String?,

    @field:RequestParam(name= "some_field_c", required = true)
    val someFieldC: String?,

    // ... more fields in between

    @field:RequestParam(name= "some_field_x", required = true)
    val someFieldX: String?,

    @field:RequestParam(name= "some_field_y", required = true)
    val someFieldY: String?,

    @field:RequestParam(name= "some_field_z", required = true)
    val someFieldZ: String?,
)
Actual option with `@ConstructorProperties`
data class DTOWithRequestParams
@ConstructorProperties(
    "some_field_a",
    "some_field_b",
    "some_field_c",
    // more field names
    "some_field_x",
    "some_field_y",
    "some_field_z",
)
constructor(
    val someFieldA: String,

    val someFieldB: String?,

    val someFieldC: String?,

    // ... more fields in between

    val someFieldX: String?,

    val someFieldY: String?,

    val someFieldZ: String?,
)

Clearly, with the current approach, you need to always consider the order of the fields. If you don't, the code will compile and be run anyway, but for example, you can have the value of someFieldA inside someFieldB without notice until it fails in other code ahead.

With a new approach at the field level, you can have the annotation with the name and the other properties attached to the field, and it is hard to write something wrong and easier to maintain.

Please consider this feature, even with low priority, and leave the community with an opinion on this matter.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions