Master Spring Boot 3 Http Interface: Declarative Remote Calls with WebFlux
This article explains how Spring Boot 3's Http Interface feature enables declarative HTTP service calls by defining Java interfaces, covering dependency setup, interface definition, token handling, configuration, controller integration, testing with Postman, and a brief discussion of its WebFlux dependency.
Introduction
Http Interface, introduced in Spring Boot 3.0, allows you to declare HTTP services as Java interfaces, generating proxy implementations based on WebFlux's WebClient.
Usage
Dependency Integration
Set Spring Boot version 3.0.0 in
pom.xml.
Use JDK 17.
Add
spring-boot-starter-webfluxdependency.
<code><parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.0</version>
</parent></code> <code><dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency></code>Basic Usage
Define an interface for the remote service using
@HttpExchangeand method‑level annotations such as
@PostExchangeor
@GetExchange.
<code>/**
* Define Http interface for remote UmsAdmin service
*/
@HttpExchange
public interface UmsAdminApi {
@PostExchange("admin/login")
CommonResult<LoginInfo> login(@RequestParam("username") String username,
@RequestParam("password") String password);
}</code> <code>/**
* Define Http interface for remote PmsBrand service
*/
@HttpExchange
public interface PmsBrandApi {
@GetExchange("brand/list")
CommonResult<CommonPage<PmsBrand>> list(@RequestParam("pageNum") Integer pageNum,
@RequestParam("pageSize") Integer pageSize);
@GetExchange("brand/{id}")
CommonResult<PmsBrand> detail(@PathVariable("id") Long id);
@PostExchange("brand/create")
CommonResult create(@RequestBody PmsBrand pmsBrand);
@PostExchange("brand/update/{id}")
CommonResult update(@PathVariable("id") Long id, @RequestBody PmsBrand pmsBrand);
@GetExchange("brand/delete/{id}")
CommonResult delete(@PathVariable("id") Long id);
}</code>Token Storage
Implement a
TokenHoldercomponent that stores the authentication token in the HTTP session.
<code>/**
* Login token storage (in Session)
*/
@Component
public class TokenHolder {
public void putToken(String token) {
RequestAttributes ra = RequestContextHolder.getRequestAttributes();
HttpServletRequest request = ((ServletRequestAttributes) ra).getRequest();
request.getSession().setAttribute("token", token);
}
public String getToken() {
RequestAttributes ra = RequestContextHolder.getRequestAttributes();
HttpServletRequest request = ((ServletRequestAttributes) ra).getRequest();
Object token = request.getSession().getAttribute("token");
return token != null ? (String) token : null;
}
}</code>Configuration
Create a configuration class that provides a
WebClientbean with default headers and a filter that adds the token to each request, then creates proxy beans for the declared interfaces.
<code>@Configuration
public class HttpInterfaceConfig {
@Value("${remote.baseUrl}")
private String baseUrl;
@Autowired
private TokenHolder tokenHolder;
@Bean
WebClient webClient() {
return WebClient.builder()
.defaultHeader("source", "http-interface")
.filter((request, next) -> {
ClientRequest filtered = ClientRequest.from(request)
.header("Authorization", tokenHolder.getToken())
.build();
return next.exchange(filtered);
})
.baseUrl(baseUrl)
.build();
}
@Bean
UmsAdminApi umsAdminApi(WebClient client) {
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builder(WebClientAdapter.forClient(client)).build();
return factory.createClient(UmsAdminApi.class);
}
@Bean
PmsBrandApi pmsBrandApi(WebClient client) {
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builder(WebClientAdapter.forClient(client)).build();
return factory.createClient(PmsBrandApi.class);
}
}</code>Controller
Inject the generated proxy beans and call them from a REST controller to perform login, list brands, get brand details, create, update, and delete operations.
<code>@RestController
@RequestMapping("/remote")
public class HttpInterfaceController {
@Autowired
private UmsAdminApi umsAdminApi;
@Autowired
private PmsBrandApi pmsBrandApi;
@Autowired
private TokenHolder tokenHolder;
@PostMapping("/admin/login")
public CommonResult<LoginInfo> login(@RequestParam String username, @RequestParam String password) {
CommonResult<LoginInfo> result = umsAdminApi.login(username, password);
if (result.getData() != null) {
tokenHolder.putToken(result.getData().getTokenHead() + " " + result.getData().getToken());
}
return result;
}
@GetMapping("/brand/list")
public CommonResult<CommonPage<PmsBrand>> listBrand(@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "3") Integer pageSize) {
return pmsBrandApi.list(pageNum, pageSize);
}
@GetMapping("/brand/{id}")
public CommonResult<PmsBrand> brand(@PathVariable("id") Long id) {
return pmsBrandApi.detail(id);
}
@PostMapping("/brand/create")
public CommonResult createBrand(@RequestBody PmsBrand pmsBrand) {
return pmsBrandApi.create(pmsBrand);
}
@PostMapping("/brand/update/{id}")
public CommonResult updateBrand(@PathVariable("id") Long id, @RequestBody PmsBrand pmsBrand) {
return pmsBrandApi.update(id, pmsBrand);
}
@GetMapping("/delete/{id}")
public CommonResult deleteBrand(@PathVariable("id") Long id) {
return pmsBrandApi.delete(id);
}
}</code>Testing
Use Postman to call the login endpoint, obtain the token, and then invoke the authenticated brand‑list endpoint successfully.
Conclusion
Http Interface lets you call remote HTTP services by simply defining interfaces, but it relies on WebFlux’s WebClient, which may cause friction when using traditional Spring MVC.
macrozheng
Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.