Behrang Saeedzadeh opened SPR-17515 and commented

Currently, the correct handler signature to handle a request such as

POST http://localhost:8080/points?x=640&y=480
Content-Type: application/json

{ "circle": { "radius": "3.14" } } 

is something like:

@PostMapping
public 
CreateShapResponse
createShape(CreateShapeParams params, @RequestBody CreateShapeBody body) {

}

@Data
public class CreateShapeParams {

    private Double x;

    private Double y;

}

@Data
public class CreateShapeBody {

    private Circle;

    @Data
    public static class Circle {
        private Double radius;
    }
}

It would be nice if it was possible to merge the CreateShapeParams and CreateShapeBody POJOs into one CreateShapeRequest and have a single argument handler method. Then we could rewrite the controller as:

@PostMapping 
public 
CreateShapResponse
createShape(CreateShapeRequest request) {

} 

@Data
public class CreateShapeRequest {

    private Double x;

    private Double y;

    @RequestBody
    private Body body;

    @Data 
    public static class Body { 

        private Circle; 

        @Data
        public static class Circle {
            private Double radius;
        } 
    }
}

This would enable an API structure that is becoming more and more common.

For example, AWS SDKs have XyzRequest classes that encapsulate all the request details, from parameters to body and, IIRC, headers too (which means org.springframework.web.bind.annotation.RequestHeader should be allowed on POJO fields and setters too).

Response-side annotations (e.g. ResponseBody, ResponseStatus, etc.) should be allowed on fields (and setters) as well.


No further details from SPR-17515

Comment From: jhoeller

Closing this one along with #23618 since we have no intentions to introduce another first-class variant of binding. With our constructor binding against Java record classes, there are new options already, and for combining parameters/headers and a converted request body, you could declare the individual arguments as request parameters and then programmatically combine them into a custom record that you proceed with - which is just a one-line call against a one-line record type declaration.