The current HttpServiceProxyFactory only supports handling annotations based on @HttpExchange. However, in certain scenarios, users may need to generate HTTP request based on custom annotations. For example, some Spring Cloud OpenFeign users prefer or have already been accustomed to using annotations based on @RequestMapping, or some may want to generate HTTP request based on jakarta.ws.rs-api annotations.

If the framework allows users to determine the logic for generating request metadata instead of being "hardcoded" within the framework, it would greatly enhance its extensibility.

Comment From: rstoyanchev

In general nothing against flexibility, but specifically for @RequestMapping annotations, those were designed for server side use and their semantics are not neutral to client vs server use. Mapping with URL patterns vs declaring a concrete URL is just one example, but there are many others.

We created @HttpExchange annotations specifically for this reason. The method signature is largely compatible with @RequestMapping in terms of supported annotations and method arguments, but far from the same. The former is a subset of the latter, as it is focused on declaring a concrete request rather than on mapping requests to a server handler.

Theoretically, given that @HttpExchange is neutral to client vs server, and so it could be supported on the server side as an alternative to @RequestMapping. You would just need to accept working with more limited options for the method signature. The other way around, however, supporting @RequestMapping for client side use, is not something we are going to do. It's what Spring Cloud Feign supported and caused many issues.

Keep in mind also that the annotation and its attributes is just one side. The other is support for the specifics of the method signature. In that sense, you can't really only customize the method annotation. You also need to support its semantics, including method parameter types and method parameter annotations. In other words it wouldn't be that simple to support jakarta.ws.rs-api annotations.

Is it just a matter of being accustomed or is there some other reason for this?

Comment From: DanielLiu1123

Thank you for your response.

Firstly, I understand that @RequestMapping is specifically designed for server-side implementation, and it has numerous attributes that are not suitable for the client-side. However, my intention is not to support @RequestMapping as a client-side implementation, but rather to provide the capability to support other annotations.

Currently, HttpServiceProxyFactory supports custom parameter resolution by implementing HttpServiceArgumentResolver. Therefore, in theory, I can support all method parameter types and method parameter annotations within the jakarta.ws.rs-api annotations (although I am aware that it would require a significant amount of work to support them). At the very least, I can achieve this because Spring provides such an extension interface.

However, the question remains: how can I customize the request method and URL? (I understand that the URI and HttpMethod in method parameters can fulfill this functionality.) The answer is that there is currently no way to do so, as Spring does not provide an interface for extending this capability. Therefore, we are limited to using annotations based on @HttpExchange.

My main point is that providing an extension interface like HttpRequestValuesResolver would not have any negative consequences. Its functionality would be similar to that of HttpServiceArgumentResolver. Spring already provides default implementations internally to handle method parameter annotations, such as @RequestBody and @RequestParam. Similarly, within the framework, an internal implementation could be introduced to support annotations based on @HttpExchange (possibly named HttpExchangeHttpRequestValuesResolver).

Comment From: DanielLiu1123

Theoretically, given that @HttpExchange is neutral to client vs server, and so it could be supported on the server side as an alternative to @RequestMapping.

I agree as well that this is a very useful feature.

Comment From: rstoyanchev

@DanielLiu1123, providing flexibility isn't always a net positive. Sometimes it's important to understand the exact use case in order to shape the solution and understand where it belongs, and what the constraints and limitations are.

For example, you agree that server mapping support is useful, and if that's what you're actually after, then that's what we can focus our efforts on instead, and solve the problem in a way that is more broadly useful, and perhaps meets even more requirements.

Having an example of very concrete requirements is what would help the most to advance this issue.

Comment From: DanielLiu1123

My team is currently using Spring Boot OpenFeign and we have plans to embrace Spring Boot 3.

Typically, a service consists of two modules: an API module and a server implementation module.

foo
├── foo-api
└── foo-server

In the API module, we define interfaces that are used by other services:

public interface FooApi {
    @GetMapping("/foo/{id}")
    Foo getFoo(@PathVariable("id") String id);
}

At this point, the @GetMapping annotation is used both on the server side and the client side.

In the server module, we implement these interfaces:

@RestController
public class FooController implements FooApi {
    @Override
    public Foo getFoo(String id) {
        ...
    }
}

If we migrate to Spring Boot 3 and use the @HttpExchange annotation, we would need to make the following changes:

public interface FooApi {
    @GetMapping("/foo/{id}")
+   @GetExchange("/foo/{id}")
    Foo getFoo(@PathVariable("id") String id);
}

Now, the @GetMapping annotation is used only on the server side, while the @GetExchange annotation is used only on the client side.

Therefore, I tried to find an extension point in the HttpServiceProxyFactory to support the @RequestMapping annotation, in order to make the migration smoother with minimal code changes. Based on this, I created the httpexchange-spring-boot-starter project. However, since there was no extension point available, I implemented this functionality in a rather aggressive manner. This is the main reason why I raised this issue.

If the @HttpExchange annotation supports server mapping, I can use it for new APIs. However, for existing APIs, I would need to replace all @RequestMapping annotations with @HttpExchange, which would be a significant amount of work.

To summarize, there are two additional features that would effectively address the above issues:

  1. Provide an extension point in HttpServiceProxyFactory to support additional annotations. With this, I can migrate to Spring Boot 3 from Spring Cloud OpenFeign without any code changes. And it can also support WebFlux.
  2. Support server mapping for the @HttpExchange annotation. With this, I can use @HttpExchange as a neutral annotation for new APIs, which can be used on both the server side and the client side.

Comment From: rstoyanchev

Thanks for elaborating, that's very helpful. We'll consider what we can do.

Comment From: rstoyanchev

I've created #30913 to support server handling with @HttpExchange. We are targeting 6.1 M4 already for that.

For the extension point in HttpServiceProxyFactory, we don't want to make this a permanent feature what is meant to be a short term solution. The method annotation used is connected with the method signature in terms of its semantics, and supported inputs and outputs. I see how it helps you to achieve your goal, but it doesn't make sense broadly since @RequestMapping methods support many more arguments when used on the server side.

If your goal is to switch your existing applications to use the HTTP interface client, replacing @RequestMapping with @HttpExchange should be the easy part. How well it works from there is the bigger question, but that's unrelated to which method annotation is used. On the server side you can extend RequestMappingHandlerMapping and override getMappingForMethod to populate the RequestMappingInfo from @HttpExchange. That should be really straight forward and it's what we are going to do for 6.1.