Spring Web module providing web application infrastructure including HTTP integration, servlet filters, Spring web MVC framework, and reactive web stack support
Comprehensive HTTP client support for consuming REST APIs and web services. Spring Web provides both traditional template-based clients (RestTemplate) and modern fluent clients (RestClient), along with configurable client infrastructure.
Modern synchronous HTTP client with a fluent API design for type-safe and readable HTTP operations.
/**
* Fluent synchronous HTTP client interface
*/
interface RestClient {
// HTTP method specifications
RequestHeadersUriSpec<?> get();
RequestBodyUriSpec post();
RequestBodyUriSpec put();
RequestBodyUriSpec patch();
RequestHeadersUriSpec<?> delete();
RequestHeadersUriSpec<?> head();
RequestHeadersUriSpec<?> options();
RequestBodyUriSpec method(HttpMethod method);
// Factory methods
static RestClient create();
static RestClient create(String baseUrl);
static RestClient create(RestTemplate restTemplate);
static RestClient.Builder builder();
static RestClient.Builder builder(RestTemplate restTemplate);
}
/**
* RestClient builder for configuration
*/
interface RestClient.Builder {
RestClient.Builder baseUrl(String baseUrl);
RestClient.Builder defaultUriVariables(Map<String, ?> defaultUriVariables);
RestClient.Builder defaultHeader(String header, String... values);
RestClient.Builder defaultHeaders(Consumer<HttpHeaders> headersConsumer);
RestClient.Builder defaultRequest(Consumer<RequestHeadersSpec<?>> defaultRequest);
RestClient.Builder requestFactory(ClientHttpRequestFactory requestFactory);
RestClient.Builder messageConverters(Consumer<List<HttpMessageConverter<?>>> configurer);
RestClient.Builder requestInterceptor(ClientHttpRequestInterceptor interceptor);
RestClient.Builder requestInterceptors(Consumer<List<ClientHttpRequestInterceptor>> interceptorsConsumer);
RestClient.Builder requestInitializer(ClientHttpRequestInitializer initializer);
RestClient.Builder requestInitializers(Consumer<List<ClientHttpRequestInitializer>> initializersConsumer);
RestClient.Builder statusHandler(Predicate<HttpStatusCode> statusPredicate, ResponseErrorHandler errorHandler);
RestClient.Builder defaultStatusHandler(ResponseErrorHandler errorHandler);
RestClient.Builder observationRegistry(ObservationRegistry observationRegistry);
RestClient.Builder observationConvention(ClientRequestObservationConvention observationConvention);
RestClient build();
}
/**
* URI specification for requests without body
*/
interface RequestHeadersUriSpec<S extends RequestHeadersSpec<S>> extends UriSpec<S> {
// Inherited from UriSpec: uri methods
}
/**
* URI specification for requests with body
*/
interface RequestBodyUriSpec extends RequestBodySpec, RequestHeadersUriSpec<RequestBodySpec> {
// Combines URI and body specification
}
/**
* Headers specification for all request types
*/
interface RequestHeadersSpec<S extends RequestHeadersSpec<S>> {
S header(String headerName, String... headerValues);
S headers(Consumer<HttpHeaders> headersConsumer);
S accept(MediaType... acceptableMediaTypes);
S acceptCharset(Charset... acceptableCharsets);
S ifModifiedSince(ZonedDateTime ifModifiedSince);
S ifNoneMatch(String... ifNoneMatches);
// Execute and retrieve response
ResponseEntity<Void> retrieve();
<T> ResponseEntity<T> retrieve(Class<T> bodyType);
<T> ResponseEntity<T> retrieve(ParameterizedTypeReference<T> bodyType);
// Exchange for full control
<T> T exchange(ExchangeFunction<T> exchangeFunction);
}
/**
* Body specification for requests with content
*/
interface RequestBodySpec extends RequestHeadersSpec<RequestBodySpec> {
RequestBodySpec contentType(MediaType contentType);
RequestBodySpec contentLength(long contentLength);
RequestBodySpec body(Object body);
<T> RequestBodySpec body(T body, Class<T> bodyType);
<T> RequestBodySpec body(T body, ParameterizedTypeReference<T> bodyType);
}
/**
* URI specification methods
*/
interface UriSpec<S extends RequestHeadersSpec<S>> {
S uri(String uri, Object... uriVariables);
S uri(String uri, Map<String, ?> uriVariables);
S uri(URI uri);
S uri(Function<UriBuilder, URI> uriFunction);
}Usage Examples:
// Create RestClient
RestClient restClient = RestClient.create("https://api.example.com");
// Simple GET request
String response = restClient.get()
.uri("/users/{id}", 123)
.retrieve()
.body(String.class);
// POST request with JSON body
User user = new User("John", "john@example.com");
User createdUser = restClient.post()
.uri("/users")
.contentType(MediaType.APPLICATION_JSON)
.body(user)
.retrieve()
.body(User.class);
// GET with custom headers
List<User> users = restClient.get()
.uri("/users")
.header("Authorization", "Bearer token")
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.body(new ParameterizedTypeReference<List<User>>() {});
// Error handling with exchange
String result = restClient.get()
.uri("/users/{id}", 404)
.exchange((request, response) -> {
if (response.getStatusCode().is4xxClientError()) {
return "User not found";
}
return response.bodyTo(String.class);
});
// Builder configuration
RestClient configuredClient = RestClient.builder()
.baseUrl("https://api.example.com")
.defaultHeader("Authorization", "Bearer token")
.defaultHeader("User-Agent", "MyApp/1.0")
.statusHandler(HttpStatus::is4xxClientError, (request, response) -> {
throw new ClientErrorException(response.getStatusCode());
})
.build();Traditional synchronous HTTP client providing template method patterns for HTTP operations.
/**
* Traditional synchronous client to perform HTTP requests
*/
class RestTemplate extends InterceptingHttpAccessor implements RestOperations {
RestTemplate();
RestTemplate(ClientHttpRequestFactory requestFactory);
RestTemplate(List<HttpMessageConverter<?>> messageConverters);
// GET operations
<T> T getForObject(String url, Class<T> responseType, Object... uriVariables);
<T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables);
<T> T getForObject(URI url, Class<T> responseType);
<T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables);
<T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables);
<T> ResponseEntity<T> getForEntity(URI url, Class<T> responseType);
// HEAD operations
HttpHeaders headForHeaders(String url, Object... uriVariables);
HttpHeaders headForHeaders(String url, Map<String, ?> uriVariables);
HttpHeaders headForHeaders(URI url);
// POST operations
<T> T postForObject(String url, Object request, Class<T> responseType, Object... uriVariables);
<T> T postForObject(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables);
<T> T postForObject(URI url, Object request, Class<T> responseType);
<T> ResponseEntity<T> postForEntity(String url, Object request, Class<T> responseType, Object... uriVariables);
<T> ResponseEntity<T> postForEntity(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables);
<T> ResponseEntity<T> postForEntity(URI url, Object request, Class<T> responseType);
URI postForLocation(String url, Object request, Object... uriVariables);
URI postForLocation(String url, Object request, Map<String, ?> uriVariables);
URI postForLocation(URI url, Object request);
// PUT operations
void put(String url, Object request, Object... uriVariables);
void put(String url, Object request, Map<String, ?> uriVariables);
void put(URI url, Object request);
// PATCH operations
<T> T patchForObject(String url, Object request, Class<T> responseType, Object... uriVariables);
<T> T patchForObject(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables);
<T> T patchForObject(URI url, Object request, Class<T> responseType);
// DELETE operations
void delete(String url, Object... uriVariables);
void delete(String url, Map<String, ?> uriVariables);
void delete(URI url);
// OPTIONS operations
Set<HttpMethod> optionsForAllow(String url, Object... uriVariables);
Set<HttpMethod> optionsForAllow(String url, Map<String, ?> uriVariables);
Set<HttpMethod> optionsForAllow(URI url);
// Generic exchange operations
<T> ResponseEntity<T> exchange(String url, HttpMethod method, HttpEntity<?> requestEntity,
Class<T> responseType, Object... uriVariables);
<T> ResponseEntity<T> exchange(String url, HttpMethod method, HttpEntity<?> requestEntity,
Class<T> responseType, Map<String, ?> uriVariables);
<T> ResponseEntity<T> exchange(URI url, HttpMethod method, HttpEntity<?> requestEntity,
Class<T> responseType);
<T> ResponseEntity<T> exchange(RequestEntity<?> requestEntity, Class<T> responseType);
<T> ResponseEntity<T> exchange(RequestEntity<?> requestEntity, ParameterizedTypeReference<T> responseType);
// Async execute methods
<T> ListenableFuture<ResponseEntity<T>> execute(String url, HttpMethod method,
RequestCallback requestCallback,
ResponseExtractor<ResponseEntity<T>> responseExtractor,
Object... uriVariables);
}
/**
* RestOperations interface defining the template contract
*/
interface RestOperations {
// All the same methods as RestTemplate class
// This interface is implemented by RestTemplate
}Usage Examples:
RestTemplate restTemplate = new RestTemplate();
// Simple GET operations
String result = restTemplate.getForObject("https://api.example.com/users/{id}", String.class, 123);
ResponseEntity<User> userResponse = restTemplate.getForEntity("https://api.example.com/users/{id}", User.class, 123);
// POST operations
User newUser = new User("John", "john@example.com");
User createdUser = restTemplate.postForObject("https://api.example.com/users", newUser, User.class);
URI location = restTemplate.postForLocation("https://api.example.com/users", newUser);
// Exchange for full control
HttpHeaders headers = new HttpHeaders();
headers.setBearerAuth("token");
HttpEntity<User> requestEntity = new HttpEntity<>(newUser, headers);
ResponseEntity<User> response = restTemplate.exchange(
"https://api.example.com/users",
HttpMethod.POST,
requestEntity,
User.class
);
// Using RequestEntity
RequestEntity<User> request = RequestEntity.post("https://api.example.com/users")
.contentType(MediaType.APPLICATION_JSON)
.body(newUser);
ResponseEntity<User> response = restTemplate.exchange(request, User.class);Low-level HTTP client infrastructure for creating and configuring HTTP requests and responses.
/**
* Factory for creating ClientHttpRequest objects
*/
interface ClientHttpRequestFactory {
/** Create a new request for the given URI and HTTP method */
ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException;
}
/**
* Client-side HTTP request
*/
interface ClientHttpRequest extends HttpRequest, HttpOutputMessage {
/** Execute this request and return the response */
ClientHttpResponse execute() throws IOException;
}
/**
* Client-side HTTP response
*/
interface ClientHttpResponse extends HttpInputMessage, Closeable {
/** Get the HTTP status code */
HttpStatusCode getStatusCode() throws IOException;
/** Get the status text */
String getStatusText() throws IOException;
/** Close this response */
void close();
}
/**
* Execution context for client HTTP requests
*/
interface ClientHttpRequestExecution {
/** Execute the request with the given body */
ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException;
}
/**
* Interceptor for client-side HTTP requests
*/
interface ClientHttpRequestInterceptor {
/** Intercept the request before execution */
ClientHttpResponse intercept(HttpRequest request, byte[] body,
ClientHttpRequestExecution execution) throws IOException;
}
/**
* Initializer for client HTTP requests
*/
interface ClientHttpRequestInitializer {
/** Initialize the request before execution */
void initialize(ClientHttpRequest request);
}Concrete implementations of ClientHttpRequestFactory for different HTTP client libraries.
/**
* Simple factory based on standard JDK HTTP facilities
*/
class SimpleClientHttpRequestFactory implements ClientHttpRequestFactory {
SimpleClientHttpRequestFactory();
/** Set the proxy to use for connections */
void setProxy(Proxy proxy);
/** Set whether to buffer request body in memory */
void setBufferRequestBody(boolean bufferRequestBody);
/** Set the chunk size for streaming */
void setChunkSize(int chunkSize);
/** Set the connection timeout */
void setConnectTimeout(int connectTimeout);
/** Set the read timeout */
void setReadTimeout(int readTimeout);
ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException;
}
/**
* Factory based on Apache HttpComponents HttpClient
*/
class HttpComponentsClientHttpRequestFactory implements ClientHttpRequestFactory, DisposableBean {
HttpComponentsClientHttpRequestFactory();
HttpComponentsClientHttpRequestFactory(HttpClient httpClient);
/** Set the HttpClient instance to use */
void setHttpClient(HttpClient httpClient);
/** Set connection timeout */
void setConnectTimeout(int connectTimeout);
/** Set connection request timeout */
void setConnectionRequestTimeout(int connectionRequestTimeout);
/** Set socket read timeout */
void setReadTimeout(int readTimeout);
/** Set buffer request body */
void setBufferRequestBody(boolean bufferRequestBody);
ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException;
void destroy() throws Exception;
}
/**
* Factory based on OkHttp 3.x
*/
class OkHttp3ClientHttpRequestFactory implements ClientHttpRequestFactory, DisposableBean {
OkHttp3ClientHttpRequestFactory();
OkHttp3ClientHttpRequestFactory(OkHttpClient client);
/** Set the OkHttpClient instance to use */
void setClient(OkHttpClient client);
/** Set connection timeout */
void setConnectTimeout(int connectTimeout);
/** Set read timeout */
void setReadTimeout(int readTimeout);
/** Set write timeout */
void setWriteTimeout(int writeTimeout);
ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException;
void destroy() throws Exception;
}
/**
* Factory based on JDK 11+ HTTP Client
*/
class JdkClientHttpRequestFactory implements ClientHttpRequestFactory {
JdkClientHttpRequestFactory();
JdkClientHttpRequestFactory(HttpClient httpClient);
JdkClientHttpRequestFactory(HttpClient httpClient, Executor executor);
/** Set the HttpClient instance to use */
void setHttpClient(HttpClient httpClient);
/** Set executor for async operations */
void setExecutor(Executor executor);
/** Set read timeout */
void setReadTimeout(Duration readTimeout);
ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException;
}
/**
* Factory wrapper that buffers all outgoing and incoming streams
*/
class BufferingClientHttpRequestFactory extends AbstractClientHttpRequestFactoryWrapper {
BufferingClientHttpRequestFactory(ClientHttpRequestFactory requestFactory);
ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod, ClientHttpRequestFactory requestFactory) throws IOException;
}
/**
* Factory wrapper with support for request interceptors
*/
class InterceptingClientHttpRequestFactory extends AbstractClientHttpRequestFactoryWrapper {
InterceptingClientHttpRequestFactory(ClientHttpRequestFactory requestFactory,
List<ClientHttpRequestInterceptor> interceptors);
ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod, ClientHttpRequestFactory requestFactory) throws IOException;
}Usage Examples:
// Configure different HTTP client factories
SimpleClientHttpRequestFactory simpleFactory = new SimpleClientHttpRequestFactory();
simpleFactory.setConnectTimeout(5000);
simpleFactory.setReadTimeout(10000);
HttpComponentsClientHttpRequestFactory httpComponentsFactory = new HttpComponentsClientHttpRequestFactory();
httpComponentsFactory.setConnectTimeout(5000);
httpComponentsFactory.setReadTimeout(10000);
// Use with RestTemplate
RestTemplate restTemplate = new RestTemplate(simpleFactory);
// Configure with interceptors
List<ClientHttpRequestInterceptor> interceptors = Arrays.asList(
new BasicAuthenticationInterceptor("username", "password"),
new LoggingClientHttpRequestInterceptor()
);
restTemplate.setInterceptors(interceptors);Built-in interceptors for common cross-cutting concerns like authentication and logging.
/**
* Interceptor to apply Basic Authentication headers
*/
class BasicAuthenticationInterceptor implements ClientHttpRequestInterceptor {
BasicAuthenticationInterceptor(String username, String password);
BasicAuthenticationInterceptor(String username, String password, Charset charset);
ClientHttpResponse intercept(HttpRequest request, byte[] body,
ClientHttpRequestExecution execution) throws IOException;
}
/**
* Interceptor to apply Bearer token authentication
*/
class BearerTokenAuthenticationInterceptor implements ClientHttpRequestInterceptor {
BearerTokenAuthenticationInterceptor(String token);
ClientHttpResponse intercept(HttpRequest request, byte[] body,
ClientHttpRequestExecution execution) throws IOException;
}Usage Examples:
// Basic authentication interceptor
BasicAuthenticationInterceptor basicAuth = new BasicAuthenticationInterceptor("user", "pass");
// Bearer token interceptor
BearerTokenAuthenticationInterceptor bearerAuth = new BearerTokenAuthenticationInterceptor("jwt-token");
// Custom interceptor
ClientHttpRequestInterceptor customInterceptor = (request, body, execution) -> {
request.getHeaders().add("X-Custom-Header", "custom-value");
ClientHttpResponse response = execution.execute(request, body);
// Log response or modify headers
return response;
};
// Apply interceptors to RestTemplate
RestTemplate restTemplate = new RestTemplate();
restTemplate.setInterceptors(Arrays.asList(basicAuth, customInterceptor));
// Apply interceptors to RestClient
RestClient restClient = RestClient.builder()
.requestInterceptor(bearerAuth)
.requestInterceptor(customInterceptor)
.build();Configure custom error handling for HTTP client responses.
/**
* Strategy interface used by RestTemplate to determine whether a response has an error
*/
interface ResponseErrorHandler {
/** Determine if the given response has an error */
boolean hasError(ClientHttpResponse response) throws IOException;
/** Handle the error in the given response */
void handleError(ClientHttpResponse response) throws IOException;
/** Handle the error for the given request and response */
default void handleError(URI url, HttpMethod method, ClientHttpResponse response) throws IOException {
handleError(response);
}
}
/**
* Default implementation of ResponseErrorHandler
*/
class DefaultResponseErrorHandler implements ResponseErrorHandler {
DefaultResponseErrorHandler();
/** Check if response has client (4xx) or server (5xx) error */
boolean hasError(ClientHttpResponse response) throws IOException;
/** Throw appropriate exception based on status code */
void handleError(ClientHttpResponse response) throws IOException;
void handleError(URI url, HttpMethod method, ClientHttpResponse response) throws IOException;
// Template methods for customization
protected boolean hasError(HttpStatusCode statusCode);
protected void handleError(ClientHttpResponse response, HttpStatusCode statusCode) throws IOException;
}Usage Examples:
// Custom error handler
ResponseErrorHandler customErrorHandler = new ResponseErrorHandler() {
@Override
public boolean hasError(ClientHttpResponse response) throws IOException {
return response.getStatusCode().is4xxClientError() ||
response.getStatusCode().is5xxServerError();
}
@Override
public void handleError(ClientHttpResponse response) throws IOException {
HttpStatusCode statusCode = response.getStatusCode();
if (statusCode.is4xxClientError()) {
throw new ClientErrorException("Client error: " + statusCode);
}
if (statusCode.is5xxServerError()) {
throw new ServerErrorException("Server error: " + statusCode);
}
}
};
// Apply to RestTemplate
RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(customErrorHandler);
// Apply to RestClient
RestClient restClient = RestClient.builder()
.defaultStatusHandler(HttpStatus::is4xxClientError,
(request, response) -> {
throw new ClientErrorException(response.getStatusCode());
})
.build();Install with Tessl CLI
npx tessl i tessl/maven-org-springframework--spring-web