UniHttp – A Declarative HTTP Interface Integration Framework for Java
This article introduces UniHttp, a declarative Java HTTP client framework that simplifies third‑party API integration by using annotations to generate request metadata, supports various parameter types, lifecycle hooks, custom processors, and provides practical enterprise‑level examples with full code snippets.
Introduction
In enterprise projects, using traditional programmatic HTTP clients such as HttpClient or OkHttp often leads to scattered, hard‑to‑maintain code when integrating many third‑party APIs. UniHttp offers a declarative approach that lets developers call remote HTTP services as if they were local methods.
Quick Start
1. Add Dependency
<dependency>
<groupId>io.github.burukeyou</groupId>
<artifactId>uniapi-http</artifactId>
<version>0.1.0</version>
</dependency>2. Define Interface
@HttpApi(url = "http://localhost:8080")
public interface UserHttpApi {
@GetHttpInterface("/getUser")
BaseRsp
getUser(@QueryPar("name") String param, @HeaderPar("userId") Integer id);
@PostHttpInterface("/addUser")
BaseRsp
addUser(@BodyJsonPar Add4DTO req);
}3. Use the API
@Service
public class UserAppService {
@Autowired
private UserHttpApi userHttpApi;
public void doSomething() {
userHttpApi.getUser("jay", 3);
}
}Annotations Overview
UniHttp provides a family of annotations to describe request parts:
@HttpApi – marks an interface as an HTTP API and sets the base URL.
@GetHttpInterface / @PostHttpInterface / @PutHttpInterface / @DeleteHttpInterface – specify HTTP method and path.
@QueryPar – puts a parameter into the query string.
@HeaderPar – adds a request header.
@CookiePar – adds a cookie.
@BodyJsonPar – sends a JSON body (content‑type application/json ).
@BodyFormPar – sends a form‑urlencoded body (content‑type application/x-www-form-urlencoded ).
@BodyMultiPartPar – sends multipart/form‑data, useful for file uploads.
@BodyBinaryPar – sends raw binary data (content‑type application/octet-stream ).
@ComposePar – groups multiple @Par annotations inside a single object to reduce method parameters.
Lifecycle Hooks (HttpApiProcessor)
Implement HttpApiProcessor<YourAnnotation> to customize the request/response lifecycle. The main hooks are:
postBeforeHttpMetadata – modify the request before it is sent (e.g., add signatures, extra query parameters).
postSendHttpRequest – replace the default sending logic, log requests, or inject dynamic tokens.
postAfterHttpResponseBodyString – process the raw response body (e.g., decrypt).
postAfterHttpResponseBodyResult – manipulate the deserialized result.
postAfterMethodReturnValue – AOP‑style post‑processing of the proxy method’s return value.
Custom HTTP Client Configuration
@Configuration
public class CustomConfiguration {
@Bean
public OkHttpClient myOkHttpClient(){
return new OkHttpClient.Builder()
.readTimeout(50, TimeUnit.SECONDS)
.writeTimeout(50, TimeUnit.SECONDS)
.connectTimeout(10, TimeUnit.SECONDS)
.connectionPool(new ConnectionPool(20,10, TimeUnit.MINUTES))
.build();
}
}Enterprise‑Level Integration Example
The article demonstrates how to create a channel‑specific annotation ( @MTuanHttpApi ) that automatically adds an appId query parameter, generates a cryptographic sign header, and injects a token and sessionId obtained from a separate authentication call.
@Inherited
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@HttpApi(processor = MTuanHttpApiProcessor.class)
public @interface MTuanHttpApi {
@AliasFor(annotation = HttpApi.class)
String url() default "${channel.mtuan.url}";
String appId() default "${channel.mtuan.appId}";
}
@Slf4j
@Component
public class MTuanHttpApiProcessor implements HttpApiProcessor
{
@Value("${channel.mtuan.publicKey}") private String publicKey;
@Value("${channel.mtuan.appId}") private String appId;
@Autowired private Environment environment;
@Autowired private WeatherApi weatherApi;
@Override
public HttpMetadata postBeforeHttpMetadata(HttpMetadata meta, HttpApiMethodInvocation
inv) {
String resolvedAppId = environment.resolvePlaceholders(inv.getProxyApiAnnotation().appId());
meta.putQueryParam("appId", resolvedAppId);
String sign = createSignKey(meta.getHttpUrl().getQueryParam(), meta.getBody());
meta.putHeader("sign", sign);
return meta;
}
private String createSignKey(Map
query, HttpBody body){
// simplified pseudo‑code for signature generation
String qs = query.entrySet().stream()
.map(e -> e.getKey()+"="+e.getValue())
.collect(Collectors.joining(";"));
String bodyStr = (body instanceof HttpBodyJSON) ? body.toStringBody() : "";
String raw = publicKey + qs + bodyStr;
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
return new String(md.digest(raw.getBytes()));
} catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); }
}
@Override
public HttpResponse
postSendHttpRequest(HttpSender sender, HttpMetadata meta) {
// obtain token and sessionId before the real request
HttpResponse
tokenResp = weatherApi.getToken(appId, publicKey);
String token = tokenResp.getBodyResult();
String sessionId = tokenResp.getHeader("sessionId");
meta.addCookie(new Cookie("token", token));
meta.addCookie(new Cookie("sessionId", sessionId));
log.info("Sending request {} {}", meta.getHttpUrl().toUrl(), meta.toHttpProtocol());
return sender.sendHttpRequest(meta);
}
@Override
public Object postAfterHttpResponseBodyResult(Object body, HttpResponse
resp, Method method, HttpMetadata meta) {
if (body instanceof BaseRsp) {
((BaseRsp)body).setCode(999);
}
return body;
}
}With this processor, every request to the weather service automatically includes the required appId , a cryptographic sign , and dynamic authentication cookies, while the response code is normalized to 999 .
Conclusion
UniHttp provides a high‑level, annotation‑driven way to integrate third‑party HTTP services, reduces boilerplate, supports rich parameter binding, and offers extensible lifecycle hooks for enterprise scenarios such as signing, token management, and custom client configuration.
Architecture Digest
Focusing on Java backend development, covering application architecture from top-tier internet companies (high availability, high performance, high stability), big data, machine learning, Java architecture, and other popular fields.
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.