Backend Development 21 min read

UniHttp – Declarative HTTP Client Framework for Java Enterprise Projects

UniHttp is a declarative HTTP interface integration framework that simplifies third‑party API calls in Java projects by generating proxy beans, supporting rich annotations for request parameters, lifecycle hooks, custom processors, and seamless Spring integration, thereby improving code cohesion and maintainability.

Architect
Architect
Architect
UniHttp – Declarative HTTP Client Framework for Java Enterprise Projects

In enterprise Java projects, using traditional programmatic HTTP clients such as HttpClient or OkHttp often leads to scattered integration logic, duplicated code, and maintenance difficulties when multiple developers implement their own wrappers. UniHttp addresses these problems by providing a declarative HTTP interface framework that lets developers define API contracts as Java interfaces and automatically generates proxy objects that behave like local method calls.

1. Introduction

UniHttp abstracts the entire request lifecycle—building request parameters, sending the request, handling the response, and deserialization—so developers no longer need to manually manage URL construction, headers, query parameters, or body serialization. The framework internally uses OkHttp for network communication while exposing a Spring‑style configuration experience.

2. Quick Start

Add the Maven dependency:

<dependency>
  <groupId>io.github.burukeyou</groupId>
  <artifactId>uniapi-http</artifactId>
  <version>0.0.4</version>
</dependency>

Define an interface and annotate it with @HttpApi (or a custom meta‑annotation) to specify the base URL, then annotate each method with request‑type annotations such as @GetHttpInterface or @PostHttpInterface :

@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);
}

Use the generated bean in a Spring service:

@Service
public class UserAppService {
    @Autowired
    private UserHttpApi userHttpApi;

    public void doSomething() {
        userHttpApi.getUser("jay", 3);
    }
}

3. Annotation Reference

The framework provides a family of @*Par annotations to map method parameters to different parts of an HTTP request:

@QueryPar – query string

@PathPar – URL path variable

@HeaderPar – request header

@CookiePar – cookie header

@BodyJsonPar – JSON body (content‑type: application/json)

@BodyFormPar – form‑urlencoded body (content‑type: application/x-www-form-urlencoded)

@BodyMultiPartPar – multipart/form‑data body

@BodyBinaryPar – binary body (content‑type: application/octet-stream)

@ComposePar – composite object that contains other @*Par fields

Each annotation can be applied to simple values, collections, objects, or maps, with the framework handling name resolution and serialization automatically.

4. Response Types

Methods can return a plain deserialized object (e.g., BaseRsp<WeatherDTO> ) or the raw HttpResponse<T> when the full HTTP status, headers, or cookies are needed. For file downloads, the framework offers HttpBinaryResponse , HttpFileResponse , and HttpInputStreamResponse .

5. HttpApiProcessor Lifecycle Hooks

Implement HttpApiProcessor to customize the request/response flow. The available hooks are executed in the following order:

postBeforeHttpMetadata → postSendingHttpRequest → postAfterHttpResponseBodyString → postAfterHttpResponseBodyResult → postAfterMethodReturnValue

Typical use cases include adding signatures, injecting dynamic tokens, logging, or modifying the deserialized result.

6. Custom Processor Example

The article demonstrates a custom processor for a weather‑service channel that:

Injects a channel‑specific appId into query parameters.

Generates a SHA‑256 sign header from query parameters and request body.

Calls a token‑acquisition endpoint before each request, extracts the token from the response body and sessionId from response headers, and adds them as cookies.

Logs request and response details.

Modifies the deserialized BaseRsp to set a custom status code.

@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) {
        String qp = query.entrySet().stream()
            .map(e -> e.getKey() + "=" + e.getValue())
            .collect(Collectors.joining(";"));
        String bodyStr = (body instanceof HttpBodyJSON) ? body.toStringBody() : "";
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-256");
            return new String(md.digest((publicKey + qp + bodyStr).getBytes()));
        } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); }
    }

    @Override
    public HttpResponse
postSendHttpRequest(HttpSender sender, HttpMetadata meta) {
        HttpResponse
tokenResp = weatherApi.getToken(appId, publicKey);
        meta.addCookie(new Cookie("token", tokenResp.getBodyResult()));
        meta.addCookie(new Cookie("sessionId", tokenResp.getHeader("sessionId")));
        return sender.sendHttpRequest(meta);
    }

    @Override
    public Object postAfterHttpResponseBodyResult(Object body, HttpResponse
rsp, Method method, HttpMetadata meta) {
        if (body instanceof BaseRsp) { ((BaseRsp)body).setCode(999); }
        return body;
    }
}

By wiring this processor with a custom meta‑annotation @MTuanHttpApi , all APIs that target the weather channel automatically inherit the token handling, signing, and logging logic.

7. Custom HTTP Client Configuration

If the default OkHttp client needs tuning, define a Spring @Bean of type OkHttpClient with custom timeouts or connection pools, and UniHttp will pick it up automatically.

@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();
    }
}

The article concludes with a link to the GitHub repository ( https://github.com/burukeYou/UniAPI ) and references to additional test modules.

JavaSpringHTTPAPIframeworkDeclarativeUniHttp
Architect
Written by

Architect

Professional architect sharing high‑quality architecture insights. Topics include high‑availability, high‑performance, high‑stability architectures, big data, machine learning, Java, system and distributed architecture, AI, and practical large‑scale architecture case studies. Open to ideas‑driven architects who enjoy sharing and learning.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.