Skip to main content

HTTP Port Definition

Basic Structure

port:
PortServiceName:
meta: http @url("baseUrl") [@auth("token")]
methods:
methodName:
meta: method @HttpEndpoint(url="endpoint", method="HTTP_METHOD", contentType="content-type")
# or shorthand form
meta: method @get("/endpoint") # GET method
meta: method @post("/endpoint") # POST method
params:
paramName: ParamType [@body]
return: ReturnType

HTTP Annotations

Port-level Annotations

  • @url("baseUrl"): Base URL of the HTTP server
  • @auth("token"): Default authentication token (optional)

Method-level Annotations

  • @HttpEndpoint: Detailed HTTP configuration
    • url: API endpoint path
    • method: HTTP method (GET, POST, PUT, DELETE, PATCH)
    • contentType: Content-Type header (default: application/json)
    • paramNames: Parameter name list (auto-generated)

Shorthand Annotations

  • @get("url"): GET method shorthand
  • @post("url"): POST method shorthand
  • @put("url"): PUT method shorthand
  • @delete("url"): DELETE method shorthand

Parameter Annotations

  • @body: Designates the parameter to be sent as the request body

HTTP Port Examples

1. Basic JSON API Call

dto:
UserRequest:
meta: dto
fields:
name: string
email: string

UserResponse:
meta: dto
fields:
id: long
name: string
status: string

port:
UserApiAdapter:
meta: http @url("https://api.example.com")
methods:
createUser:
meta: method @post("/users")
params:
body: UserRequest @body
return: UserResponse

getUser:
meta: method @get("/users/{id}")
params:
id: long
return: UserResponse

updateUser:
meta: method @HttpEndpoint(url="/users/{id}", method="PUT", contentType="application/json")
params:
id: long
body: UserRequest @body
return: UserResponse

2. File Upload (Multipart)

port:
FileUploadAdapter:
meta: http @url("https://api.fileservice.com") @auth("Token abc123")
methods:
uploadFile:
meta: method @HttpEndpoint(url="/upload", method="POST", contentType="multipart/form-data")
params:
body: org.springframework.util.MultiValueMap @body
return: String

uploadUserDocument:
meta: method @post("/users/{userId}/documents")
params:
userId: long
body: org.springframework.util.MultiValueMap @body
return: Map<String, Object>

3. API with External Authentication

port:
PaymentApiAdapter:
meta: http @url("https://api.payment.com") @auth("Bearer sk_test_123")
methods:
processPayment:
meta: method @post("/v1/payments")
params:
body: PaymentRequest @body
return: PaymentResponse

getPaymentStatus:
meta: method @get("/v1/payments/{paymentId}")
params:
paymentId: string
return: PaymentStatusResponse

4. Real-World External API Integration Example (Parts Catalog)

port:
PartsCatalogAdapter:
meta: http @url("https://api.parts-catalogs.com") @auth("TDTDTD-9800-2A6D1DB6")
methods:
getCatalogs:
meta: method @get("/v1/catalogs/") -- Vehicle catalog (brand) list
params:
return: java.util.List

getCarModelList:
meta: method @get("/v1/catalogs/{catalogId}/models") -- Vehicle model list by brand
params:
catalogId: String
return: java.util.List<PartInfo>

getCarInfoByVIN:
meta: method @get("/v1/car/info") -- Vehicle info by VIN
params:
q: String -- VIN or FRAME
return: java.util.List

HTTP Authentication

1. Basic Token Authentication

port:
ApiService:
meta: http @url("https://api.example.com") @auth("Bearer your-token-here")

2. Configuration via Properties File

# domainName and adapterName should be replaced according to the port configuration
domainName.adapterName.authKey=auth_key_info

3. Complex (Dynamic) Authentication - HttpAuthProvider Implementation

@Configuration
public class CustomHttpAuthProvider {

@Bean("payment.PaymentApiAdapter.httpAuthProvider")
public HttpAuthProvider getHttpAuthProvider() {
return (inputObj, headers) -> {
// Dynamic token generation
String token = getTokenFromExternalService();
headers.add(Map.of("Authorization", "Bearer " + token));
headers.add(Map.of("X-API-Key", "custom-api-key"));
headers.add(Map.of("X-Client-Version", "1.0"));
return null;
};
}
}

4. HttpAuthProviderFactory Usage

@Configuration
public class HttpAuthConfig {

@Bean("domainName.adapterName.httpAuthProvider")
public HttpAuthProvider createBearerAuthProvider() {
return HttpAuthProviderFactory.createBearerToken("your-dynamic-token");
}

@Bean("domainName.adapterName2.httpAuthProvider")
public HttpAuthProvider createCustomAuthProvider() {
return HttpAuthProviderFactory.create("X-API-Key", "your-api-key");
}
}

Generated Java Interface

package com.example.domain.port;

import io.elasticore.runtime.port.*;
import com.example.domain.dto.*;

@ExternalService(protocol="http", id="demo.UserApiAdapter", url="https://api.example.com")
public interface UserApiAdapter {

@HttpEndpoint(url="/users", method="POST", contentType="application/json", paramNames="body")
UserResponse createUser(UserRequest body);

@HttpEndpoint(url="/users/{id}", method="GET", contentType="application/json", paramNames="id")
UserResponse getUser(Long id);
}

Spring Boot Configuration

HTTP Client Settings

# HTTP connection timeout settings
elasticore.http.connect-timeout=5000
elasticore.http.read-timeout=30000
elasticore.http.write-timeout=30000

# HTTP connection pool settings
elasticore.http.max-connections=100
elasticore.http.max-connections-per-route=20

# Logging settings
logging.level.io.elasticore.springboot3.http=DEBUG
logging.level.org.springframework.web.reactive.function.client=DEBUG

Cache Settings

elasticore.cache.http.enabled=true
elasticore.cache.http.max-size=1000
elasticore.cache.http.expire-after-write=300s

Advanced Features

HTTP Request/Response Interceptor

@Component
public class HttpRequestInterceptor {

@EventListener
public void handleHttpRequest(HttpRequestEvent event) {
log.info("HTTP Request: {} {}", event.getMethod(), event.getUrl());
event.getHeaders().add("X-Request-ID", UUID.randomUUID().toString());
}

@EventListener
public void handleHttpResponse(HttpResponseEvent event) {
log.info("HTTP Response: {} - {}", event.getStatusCode(), event.getResponseTime());
}
}

Error Handling and Retry

@Service
public class ResilientApiService {

@Autowired
private PaymentApiAdapter paymentApiAdapter;

@Retryable(value = {HttpTimeoutException.class}, maxAttempts = 3, backoff = @Backoff(delay = 1000))
public PaymentResponse processPaymentWithRetry(PaymentRequest request) {
try {
return paymentApiAdapter.processPayment(request);
} catch (HttpClientErrorException e) {
if (e.getStatusCode() == HttpStatus.BAD_REQUEST) {
throw new PaymentValidationException("Invalid payment request", e);
}
throw e;
}
}

@Recover
public PaymentResponse recover(Exception ex, PaymentRequest request) {
log.error("Payment processing failed after retries", ex);
return PaymentResponse.failed("Service temporarily unavailable");
}
}

Security Considerations

Authentication Security

# Good example: Use environment variables
port:
PaymentApiAdapter:
meta: http @url("https://api.payment.com") @auth("${PAYMENT_API_KEY}")

# Bad example: Hardcoded values
port:
PaymentApiAdapter:
meta: http @url("https://api.payment.com") @auth("sk_live_12345...") # Security risk

Preventing Sensitive Data Logging

@Component
public class SecureHttpLoggingInterceptor {

private static final Set<String> SENSITIVE_HEADERS = Set.of(
"authorization", "x-api-key", "cookie"
);

@EventListener
public void handleHttpRequest(HttpRequestEvent event) {
Map<String, String> safeHeaders = event.getHeaders().entrySet().stream()
.filter(entry -> !SENSITIVE_HEADERS.contains(entry.getKey().toLowerCase()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

log.info("HTTP Request: {} {} Headers: {}",
event.getMethod(), event.getUrl(), safeHeaders);
}
}