KNETMiddleware i KNETMiddlewarePipeline
oferuj膮 prosty pattern do kompozycji logiki przetwarzania request贸w.
Middleware to prostsza alternatywa dla interceptor贸w. Idealne do szybkich transformacji request贸w/response bez potrzeby implementacji pe艂nego interfejsu.
import rip.nerd.kitsunenet.util.KNETMiddleware
import rip.nerd.kitsunenet.util.KNETMiddlewarePipeline
import rip.nerd.kitsunenet.util.through
import rip.nerd.kitsunenet.util.then
// Definiuj middleware
val authMiddleware = KNETMiddleware.request { request ->
request.copy(
headers = request.headers + ("Authorization" to "Bearer $token")
)
}
// Stw贸rz pipeline
val pipeline = KNETMiddlewarePipeline()
.use(authMiddleware)
// Wykonaj request przez pipeline
val response = pipeline.execute(client, request)
Modyfikuje tylko request przed wys艂aniem.
val addHeader = KNETMiddleware.request { request ->
request.copy(
headers = request.headers + ("X-Custom" to "value")
)
}
val addQuery = KNETMiddleware.request { request ->
request.copy(
query = request.query + ("api_version" to "v2")
)
}
Modyfikuje tylko response po otrzymaniu.
val logStatus = KNETMiddleware.response { response ->
println("Status: ${response.statusCode}")
response // zwr贸膰 bez modyfikacji
}
val mapErrors = KNETMiddleware.response { response ->
if (!response.isSuccessful) {
// Transform error response
response.copy(bodyString = "Error: ${response.statusCode}")
} else response
}
Pe艂na kontrola nad request i response.
val timingMiddleware = KNETMiddleware.full { request, next ->
val start = System.currentTimeMillis()
val response = next(request)
val duration = System.currentTimeMillis() - start
println("${request.method} ${request.url} took ${duration}ms")
response
}
val retryMiddleware = KNETMiddleware.full { request, next ->
var lastResponse: KNETResponse? = null
repeat(3) { attempt ->
val response = next(request)
if (response.isSuccessful) return@full response
lastResponse = response
delay(1000L * (attempt + 1))
}
lastResponse!!
}
Obserwuje bez modyfikacji.
val analytics = KNETMiddleware.tap(
onRequest = { request ->
analytics.logRequest(request.url)
},
onResponse = { request, response ->
analytics.logResponse(request.url, response.statusCode)
}
)
// Pojedynczy header
KNETMiddleware.Builtin.addHeader("X-Custom", "value")
// Dynamiczny header
KNETMiddleware.Builtin.addHeader("X-Timestamp") {
System.currentTimeMillis().toString()
}
// Wiele headers
KNETMiddleware.Builtin.addHeaders(mapOf(
"X-App-Name" to "MyApp",
"X-App-Version" to "1.0.0"
))
// Bearer token
KNETMiddleware.Builtin.bearerToken { authRepository.getToken() }
// Basic auth
KNETMiddleware.Builtin.basicAuth("username", "password")
// API Key w header
KNETMiddleware.Builtin.apiKey("X-API-Key", "your-api-key")
// API Key w query
KNETMiddleware.Builtin.apiKeyQuery("api_key", "your-api-key")
// Simple logging
KNETMiddleware.Builtin.logging()
// Custom logger
KNETMiddleware.Builtin.logging(
logRequest = true,
logResponse = true,
logger = { Log.d("KNET", it) }
)
// Timing
KNETMiddleware.Builtin.timing { request, response, duration ->
metrics.recordLatency(request.url, duration)
}
// Simple retry
KNETMiddleware.Builtin.retry(maxAttempts = 3)
// Custom retry logic
KNETMiddleware.Builtin.retry(
maxAttempts = 5,
delayMs = 2000,
shouldRetry = { response ->
response.statusCode in listOf(502, 503, 504)
}
)
// Request ID
KNETMiddleware.Builtin.requestId()
// Custom request ID generator
KNETMiddleware.Builtin.requestId { "req_${UUID.randomUUID()}" }
// User-Agent
KNETMiddleware.Builtin.userAgent("MyApp/1.0.0 Android/12")
// Accept header
KNETMiddleware.Builtin.accept("application/json")
// Content-Type
KNETMiddleware.Builtin.contentType("application/json")
// No cache
KNETMiddleware.Builtin.noCache()
// Cache control
KNETMiddleware.Builtin.cacheControl("max-age=3600")
val pipeline = KNETMiddlewarePipeline()
.use(KNETMiddleware.Builtin.accept())
.use(KNETMiddleware.Builtin.requestId())
.use(KNETMiddleware.Builtin.bearerToken { getToken() })
.use(KNETMiddleware.Builtin.logging())
// Wykonaj request
val response = pipeline.execute(client, request)
// Lub z extension
val response = request.through(pipeline, client)
pipeline.useIf(
condition = { request -> request.url.contains("/admin") },
middleware = KNETMiddleware.Builtin.addHeader("X-Admin-Token", adminToken)
)
// Tylko dla POST/PUT
pipeline.useForMethods("POST", "PUT") {
KNETMiddleware.Builtin.contentType("application/json")
}
// Z middleware
val pipeline = KNETMiddlewarePipeline.of(
authMiddleware,
loggingMiddleware,
retryMiddleware
)
// Z auth
val pipeline = KNETMiddlewarePipeline.withAuth { getToken() }
// Z logging
val pipeline = KNETMiddlewarePipeline.withLogging { Log.d("KNET", it) }
// Z retry
val pipeline = KNETMiddlewarePipeline.withRetry(maxAttempts = 3)
// Standardowy dla API
val pipeline = KNETMiddlewarePipeline.standard(
tokenProvider = { authRepository.getToken() },
userAgent = "MyApp/1.0.0",
enableLogging = BuildConfig.DEBUG
)
// 艁膮czenie middleware
val combined = authMiddleware.then(loggingMiddleware)
// Kopiowanie pipeline
val copy = pipeline.copy()
// Merge dw贸ch pipeline
val merged = basePipeline.merge(additionalPipeline)
class ApiClient(
private val client: KNETClient,
private val authRepository: AuthRepository
) {
private val pipeline = KNETMiddlewarePipeline()
.use(KNETMiddleware.Builtin.accept("application/json"))
.use(KNETMiddleware.Builtin.contentType("application/json"))
.use(KNETMiddleware.Builtin.userAgent("MyApp/${BuildConfig.VERSION_NAME}"))
.use(KNETMiddleware.Builtin.requestId())
.use(KNETMiddleware.Builtin.bearerToken { authRepository.getToken() })
.use(KNETMiddleware.Builtin.retry(maxAttempts = 3))
.apply {
if (BuildConfig.DEBUG) {
use(KNETMiddleware.Builtin.logging())
}
}
suspend fun get(url: String): KNETResponse {
val request = KNETRequest.get(url)
return pipeline.execute(client, request)
}
suspend fun post(url: String, data: Map<String, Any>): KNETResponse {
val request = KNETRequest.post(url, data)
return pipeline.execute(client, request)
}
}
class TenantMiddleware(private val tenantProvider: () -> String) {
fun create() = KNETMiddleware.request { request ->
val tenant = tenantProvider()
request.copy(
headers = request.headers + mapOf(
"X-Tenant-ID" to tenant,
"X-Tenant-Host" to "$tenant.example.com"
)
)
}
}
val pipeline = KNETMiddlewarePipeline()
.use(TenantMiddleware { currentTenant }.create())
.use(authMiddleware)
fun signingMiddleware(secretKey: String) = KNETMiddleware.request { request ->
val timestamp = System.currentTimeMillis()
val payload = "${request.method}|${request.url}|$timestamp"
val signature = hmacSha256(secretKey, payload)
request.copy(
headers = request.headers + mapOf(
"X-Timestamp" to timestamp.toString(),
"X-Signature" to signature
)
)
}
val pipeline = KNETMiddlewarePipeline()
.use(signingMiddleware(BuildConfig.API_SECRET))
class FeatureFlagMiddleware(private val featureFlags: FeatureFlags) {
fun create() = KNETMiddleware.request { request ->
val flags = featureFlags.getActiveFlags().joinToString(",")
request.copy(
headers = request.headers + ("X-Feature-Flags" to flags)
)
}
}
pipeline.use(FeatureFlagMiddleware(featureFlags).create())
fun abTestMiddleware(userId: String) = KNETMiddleware.request { request ->
val variant = abTestService.getVariant(userId, "api_experiment")
request.copy(
headers = request.headers + mapOf(
"X-Experiment-Variant" to variant,
"X-User-Segment" to userSegmentService.getSegment(userId)
)
)
}
KNETMiddleware| Factory | Opis |
|---|---|
request { } |
Modyfikuje tylko request |
response { } |
Modyfikuje tylko response |
full { request, next -> } |
Pe艂na kontrola |
tap(onRequest, onResponse) |
Side effects bez modyfikacji |
conditional(condition, middleware) |
Warunkowe wykonanie |
forMethods(methods, middleware) |
Tylko dla metod HTTP |
forUrls(pattern, middleware) |
Tylko dla URL matching |
KNETMiddlewarePipeline| Metoda | Opis |
|---|---|
use(middleware) |
Dodaje middleware |
useBefore(middleware) |
Dodaje na pocz膮tek |
useIf(condition, middleware) |
Warunkowe middleware |
useForMethods(methods, middleware) |
Dla metod HTTP |
execute(client, request) |
Wykonuje request |
copy() |
Kopiuje pipeline |
merge(other) |
艁膮czy dwa pipeline |
clear() |
Czy艣ci middleware |