Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support HttpRequestValues as a method parameter for HTTP Interface #34227

Open
ciscoo opened this issue Jan 9, 2025 · 3 comments
Open

Support HttpRequestValues as a method parameter for HTTP Interface #34227

ciscoo opened this issue Jan 9, 2025 · 3 comments
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) status: waiting-for-feedback We need additional information before we can continue status: waiting-for-triage An issue we've not yet triaged or decided on

Comments

@ciscoo
Copy link

ciscoo commented Jan 9, 2025

When an API requires multiple values, the number of arguments for HTTP service method can be a bit painful. For example:

interface ExampleService {

    @PostMapping(path = "/example", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
    void example(@RequestBody Object example, @RequestHeader("X-Custom-Requirement") String foo,
                    @RequestHeader("X-Another-Custom-Requirement") String bar,
                    @RequestParam String one,
                    @RequestParam String two,
                    @RequestParam String three);

}

The service method can be simplified to:

@PostMapping(path = "/example", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
void example(@RequestBody Object example, @RequestHeader HttpHeaders httpHeaders, @RequestParam MultiValueMap<String, Object> params);

Which is better, but I think it would be easier or nicer even to allow a single argument that encapsulates all of the request details.

That argument could also override the predefined ones such as the consumes or path from the exchange annotation. This is more or less what UriBuilderFactory does today for the URL.

If I'm not mistaken, RestTemplate provided something similar through RequestEntity.

Note I know that you can drop down to the 'lower level' RestClient to fully customize the request as indicated in this table in the documentation. But providing that control at the HTTP interface level I think will be a welcome addition.

Looking at Framework's code, HttpServiceArgumentResolver is responsible for consuming the various annotations and applying those values to HttpRequestValues. So a HttpServiceArgumentResolver that accepts a HttpRequestValues or similar may work I think.


My company has variety of modern and legacy APIs and Web services. While we try to conform to some standard when modernizing legacy services or APIs, there still exists APIs that require a lot of information in their request.

To work around this, I started wrapping the request details into an intermediary object and then delegate to the service method. This leads to cleaner code (IMO). For example:

record RequestSpecification(Object body, String param, String header) {}

interface ExampleService {
    @PostMapping(path = "/example", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
    void example(@RequestBody Object example, @RequestHeader HttpHeaders httpHeaders, @RequestParam MultiValueMap<String, Object> params);

    default void example(RequestSpecification specification) {
        HttpHeaders headers = new HttpHeaders();
        headers.add("X-Custom-Requirement", specification.header());
        MultiValueMap<String, Object> params = MultiValueMap.fromSingleValue(Map.of("one", specification.param()));
        example(specification.body(), headers, params);
    }
}
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Jan 9, 2025
@rstoyanchev rstoyanchev added the in: web Issues in web modules (web, webmvc, webflux, websocket) label Jan 13, 2025
@rstoyanchev
Copy link
Contributor

rstoyanchev commented Jan 13, 2025

HttpRequestValues is mainly for use by arguments resolvers to have a place to add values to. It's not intended for direct use by applications. RequestEntity is much more along the lines of a public API for generalized request input, but then there is not much value in passing that to an HTTP interface. You could just as well pass it directly to RestClient.

If you want something in between like a dedicated RequestSpecification type that both indicates the necessary input specific to the service and is more concise, then could create a custom argument resolver for it, and use it to add the necessary values to HttpRequestValues. You can register such custom resolvers on the HttpServiceProxyFactory builder.

@rstoyanchev rstoyanchev added the status: waiting-for-feedback We need additional information before we can continue label Jan 13, 2025
@ciscoo
Copy link
Author

ciscoo commented Jan 13, 2025

HttpRequestValues is mainly for use by arguments resolvers to have a place to add values to. It's not intended for direct use by applications.

Yes, I did not find anything else used for HTTP interfaces.

RequestEntity is much more along the lines of a public API for generalized request input, but then there is not much value in passing that to an HTTP interface. You could just as well pass it directly to RestClient.

But it does not work that way though, at least from what I can see. There does not exist any methods on RestClient where you can just provide a RequestEntity and have it extract all values from it internally. Instead, you must use the DSL provided by RestClient.

If you want something in between like a dedicated RequestSpecification type that both indicates the necessary input specific to the service and is more concise, then could create a custom argument resolver for it, and use it to add the necessary values to HttpRequestValues. You can register such custom resolvers on the HttpServiceProxyFactory builder.

This is what this issue is requesting from Spring Framework. To provide the ability to provide an object (RequestEntity for example), and extract all values from it.

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Jan 13, 2025
@rstoyanchev
Copy link
Contributor

Sorry, my bad. It doesn't make sense to pass RequestEntity into RestClient as the two provide a similar API for building the request. RequestEntity is useful to provide a similar experience with the RestTemplate.

We could support RequestEntity as an argument, but the main value of an HTTP interface is to indicate the inputs required for the endpoint. If you generalize that how would does user has to know what to pass? If you generalize it all the way into RequestEntity then what value do you get for an HTTP interface?

@bclozel bclozel added status: waiting-for-feedback We need additional information before we can continue and removed status: feedback-provided Feedback has been provided labels Jan 15, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) status: waiting-for-feedback We need additional information before we can continue status: waiting-for-triage An issue we've not yet triaged or decided on
Projects
None yet
Development

No branches or pull requests

4 participants