spring-projects / spring-framework

Spring Framework
https://spring.io/projects/spring-framework
Apache License 2.0
56.5k stars 38.11k forks source link

Introduce RestClient #29552

Closed poutsma closed 1 year ago

poutsma commented 1 year ago

In Spring Framework 5.0, we introduced the WebClient: a new, reactive HTTP client. Compared to RestTemplate, the previous generation, WebClient improved HTTP access on two fronts:

With the advent of Project Loom, asynchrony can be obtained through standard, synchronous APIs. We should therefore introduce a version of WebClient that offers the same improved APIs as the current reactive version, but exposes them in a synchronous manner.

poutsma commented 1 year ago

After team discussion, it has been decided that the 6.1 timeline will be too early for us to fully implement a Loom-ready WebClient. There are simply too may unknowns: underlying ClientHttpRequestFactory implementations that yet have to reveal their plans for Loom support, it is unclear what structured concurrency will look like in a Loom environment, and so on.

Implementing a new, Loom-ready WebClient in 6.1 could mean that we end up with something that is not ready for the future. Therefore, we have decided to do experimental work on this issue in parallel to the 6.1 release, but not make the issue part of this release. Further progress will be provided through this issue.

Note that we are still considering other areas for Loom compatibility for 6.1, including the existing (reactive) WebClient and RestTemplate.

nightswimmings commented 1 year ago

This is amazing, but please, keep the package server(arch)-agnostic this time (reactive/servlet)

poutsma commented 1 year ago

Spring 6.1 M2 will see the introduction of RestClient, a synchronous HTTP client that offers an API similar to WebClient, using the same infrastructure (i.e. request factory, error handler, interceptors, etc) as RestTemplate.

ThomasKasene commented 1 year ago

Out of curiosity, why use "Rest" as part of the name if it can be used for anything HTTP-related, such as GraphQL? I admit it's tough to come up with a good, descriptive name that also rolls easily off the tongue, though, especially since "HttpClient" is already taken.

poutsma commented 1 year ago

The prefix Rest reflects that this new client conceptually sits in between RestTemplate and WebClient. Initially we considered having two WebClient classes: a reactive and non-reactive one. But that would cause confusion, so we went for RestClient.

In retrospect, I would not have have given RestTemplate that prefix when it was introduced in Spring 3, because it has little to do with REST. But here we are, and like you said: HttpClient is already taken by Apache, OkHttp, Jetty, and the JDK.

anbusampath commented 1 year ago

The RestTemplate will be deprecated in a future version and will not have major new features added going forward. https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html

with RestClient in place, will RestTemplate get deprecated in 6.1 timeframe?

poutsma commented 1 year ago

The RestTemplate will be deprecated in a future version and will not have major new features added going forward.

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html

@anbusampath Please read that page you linked to again, as it does not contain the text you quoted.

with RestClient in place, will RestTemplate get deprecated in 6.1 timeframe?

RestTemplate will not be deprecated; it is in maintenance mode, meaning that it will not have new features.

anbusampath commented 1 year ago

@anbusampath Please read that page you linked to again, as it does not contain the text you quoted.

sorry my bad, I was reading old doc - https://docs.spring.io/spring-framework/docs/5.2.1.RELEASE/javadoc-api/index.html?org/springframework/web/client/RestTemplate.html and attached latest doc link. Latest version does not contain the text.

RestTemplate will not be deprecated; it is in maintenance mode, meaning that it will not have new features.

Thanks for the clarification.

quaff commented 1 year ago

RestTemplate will not be deprecated; it is in maintenance mode, meaning that it will not have new features.

@poutsma Could you review this PR https://github.com/spring-projects/spring-framework/pull/27188? It's not new feature, I think it's a fix or improvement.

Sineaggi commented 1 year ago

Will this new RestClient support the HttpServiceProxyFactory class along with the @HttpExchange annotated interfaces?

anbusampath commented 1 year ago

Will this new RestClient support the HttpServiceProxyFactory class along with the @HttpExchange annotated interfaces?

watch this issue - https://github.com/spring-projects/spring-framework/issues/30117

vpavic commented 1 year ago

Thanks for making this happen @poutsma (and the team). With RestTemplate being in the maintenance mode, something like this was much needed for all of us that aren't (for whatever the reason) keen on using WebClient.

I'm curious whether the team considered making the new client a part of separate module, rather then having it again (as with RestTemplate) packaged in spring-web that's mostly focused on server-side components? I know that something along these lines was previously discussed (https://github.com/spring-projects/spring-framework/issues/21301 comes to mind), but I'm in the camp with those that firmly believe that I shouldn't get server-side components if client is all I'm interested in. Besides the fact it has become quite common for frameworks that react on presence of things on classpath, I would say that the likelihood of setups that need just the client in on the rise - Spring Batch comes to mind as it recommends job per JVM approach these days, if I'm not mistaken.

Regarding naming, did you consider going with HttpOperations (implemented by HttpTemplate)? This is more aligned with other places in Spring where we have ...Operations/...Template combination, and avoids using the term REST where it really shouldn't be used. Additionally, it feels to me that RestClient itself isn't really the client itself but rather the convenience layer that abstracts the underlying HTTP client, similar like other ...Operations/...Template combinations do.

poutsma commented 1 year ago

I'm curious whether the team considered making the new client a part of separate module, rather then having it again (as with RestTemplate) packaged in spring-web that's mostly focused on server-side components?

This was not considered. RestClient is closely aligned to RestTemplate, as it uses the same underlying infrastructure and throws the same exceptions. Putting RestClient in a separate module would therefore mean either moving RestTemplate and related types out of the spring-web module—breaking backward compatibility from 6.0, or putting RestClient in a new module all by itself, which is equally unappealing.

Regarding naming, did you consider going with HttpOperations (implemented by HttpTemplate)? This is more aligned with other places in Spring where we have ...Operations/...Template combination, and avoids using the term REST where it really shouldn't be used.

A HttpClient based the Operations/Template design is what we created in RestTemplate 14 years ago, and it quickly became apparent that the API-style that Templates offer is not suitable for HTTP clients, because exposing every capability of HTTP in a Template results in too many overloaded (see RestTemplate). As for the Rest prefix, see my comment above.

Additionally, it feels to me that RestClient itself isn't really the client itself but rather the convenience layer that abstracts the underlying HTTP client, similar like other ...Operations/...Template combinations do.

I am not sure what gave you that feeling, because the WebClient was introduced in 5.0 and "abstract the underlying HTTP clients" in pretty much the same way as RestClient does.

vpavic commented 1 year ago

This was not considered. RestClient is closely aligned to RestTemplate, as it uses the same underlying infrastructure and throws the same exceptions. Putting RestClient in a separate module would therefore mean either moving RestTemplate and related types out of the spring-web module—breaking backward compatibility from 6.0, or putting RestClient in a new module all by itself, which is equally unappealing.

I understand that this would be a significant undertaking, and one that wouldn't be fully possible without breaking changes and therefore a new major release. However, IMO RestClient in a new module sounds quite nice, assuming other clients are moved there as well with time (if RestTemplate won't eventually get deprecated and removed).

With the introduction of a modern HTTP client to the JDK itself, it's even more difficult to swallow the extra components you need to put to your classpath in order to use the new RestClient, if you don't need server-side support. Quite honestly, I'd prefer to use the thinnest possible convenience layer around java.net.http.HttpClient (be it from Spring or someone else) simply because I can have the same experience on every type of project while also having minimal impact on the classpath.

I am not sure what gave you that feeling, because the WebClient was introduced in 5.0 and "abstract the underlying HTTP clients" in pretty much the same way as RestClient does.

The same concerns from my end apply to WebClient as well, I'm just not focusing on it as I generally try to avoid reactive, but I think it should've been named (reactive-)HTTP-something. Naming truly is hard, but I would've definitely preferred something that has HTTP in its name and expresses well that it's an abstraction of the underlying client, regardless of the legacy and how others name their clients.

Anyhow, this is (to me) a less important concern to how things are laid out across Spring modules.

rstoyanchev commented 1 year ago

Naming is hard, and it's not always about picking the most logical name. The choice has to be made in a specific context, and in the given context the chosen name is good. The RestTemplate, even if imperfectly named, has been used for so long and widely that it carries very deep associations. RestClient is good as a layer around the same infrastructure and in the same package.

The Spring Framework has many clients, two HTTP clients, two WebSocket clients, SockJsClient, TCP client, STOMP client, RSocket client, DatabaseClient, and not even counting the various templates for JDBC, JMS. Across Spring projects there are many more. There would be many new jars down this road, tempting as it might look for one case, I'm not sure the overall end result is a better place.

Many of those clients support different underlying libraries. It's not clear if you actually want this split even further, e.g. one HTTP client jar for the JDK client, another for Jetty, another for Apache HTTP Components, and so on, but clearly that would be even more jars, or otherwise you still have a jar with some optional dependencies at least. Our view is that we try to structure modules but any optional dependencies within should not have impact you in any way as long as the classes are not used.

vpavic commented 1 year ago

Many of those clients support different underlying libraries. It's not clear if you actually want this split even further, e.g. one HTTP client jar for the JDK client, another for Jetty, another for Apache HTTP Components, and so on, but clearly that would be even more jars, or otherwise you still have a jar with some optional dependencies at least. Our view is that we try to structure modules but any optional dependencies within should not have impact you in any way as long as the classes are not used.

My preference would be to have a single Spring module that carries all the HTTP-client related code, with supported underlying libraries being expressed through optional dependencies.

On projects I work on I'm really picky about what I put on the classpath and really like to avoid having anything that's not really necessary. So I'm never thrilled to see server- and client-side components mixed together.

My own preferences aside, there are numerous benefits of keeping the two apart:

wimdetroyer commented 11 months ago

Does RestClient have a variant for use in testing like RestTemplate had with TestRestTemplate ?

See also: https://github.com/spring-projects/spring-boot/blob/6fd691af589f1b85a492e3d6660dac5d15ba18c6/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/testing.adoc#testresttemplate

Is the alternative using the WebTestClient here?

EDIT: issue tracking this https://github.com/spring-projects/spring-framework/issues/31275

rstoyanchev commented 11 months ago

@wimdetroyer, TestRestTemplate is part of the Spring Boot test support. It's a simple helper to configure a RestTemplate for integration testing against your server. It's up to the Boot team to consider what similar support for RestClient looks like.

In the mean time, you can bridge TestRestTemplate to RestClient:

@Test
void exampleTest(@Autowired TestRestTemplate restTemplate) {
    RestClient restClient = RestClient.create(restTemplate.getRestTemplate());
    // ...    
}
ViktorDimitrovFT commented 9 months ago

Does RestClient have a retry mechanism support? How we can configure the client to retry requests when specific exception is thrown?

wimdetroyer commented 9 months ago

Does RestClient have a retry mechanism support? How we can configure the client to retry requests when specific exception is thrown?

Use spring retry

making commented 9 months ago

RestClient can use the same interface as RestTemplate for inserting retry processes, which is the ClientHttpRequestInterceptor. For an example of a ClientHttpRequestInterceptor with retry logic, you can refer to this GitHub repository: https://github.com/making/retryable-client-http-request-interceptor