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 pathmethod: 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);
}
}