🎯 empfohlene Sammlungen
Balanced sample collections from various categories for you to explore
Android Kotlin Web-Funktionsbeispiele
Android Kotlin Web-Funktionsbeispiele einschließlich URL-Routing, Middleware-Verarbeitung und Bereitstellung statischer Dateien
💻 URL-Routing kotlin
🟡 intermediate
⭐⭐⭐⭐
URL-Routen mit Parametern, Platzhaltern und RESTful-Mustern parsen und verarbeiten
⏱️ 35 min
🏷️ kotlin, android, web, routing
Prerequisites:
Intermediate Kotlin, HTTP basics
// Android Kotlin URL Routing Examples
// Using custom routing implementation for handling URL patterns
import java.util.regex.Pattern
// 1. Basic Route Definition
data class Route(
val path: String,
val handler: (Map<String, String>) -> String
)
// 2. Route Parser
class RouteParser {
// Parse route path and extract parameters
fun parsePath(path: String): List<String> {
return path.split("/").filter { it.isNotEmpty() }
}
// Convert route pattern to regex
fun patternToRegex(pattern: String): Pattern {
val regexPattern = pattern.replace(Regex("\{([^}]+)\}"), "([^/]+)")
return Pattern.compile("^$regexPattern$")
}
// Extract parameter names from pattern
fun extractParamNames(pattern: String): List<String> {
val params = mutableListOf<String>()
val matcher = Pattern.compile("\{([^}]+)\}").matcher(pattern)
while (matcher.find()) {
params.add(matcher.group(1))
}
return params
}
// Match path against pattern and extract parameters
fun match(pattern: String, path: String): Map<String, String>? {
val regex = patternToRegex(pattern)
val matcher = regex.matcher(path)
if (!matcher.matches()) {
return null
}
val paramNames = extractParamNames(pattern)
val params = mutableMapOf<String, String>()
for (i in paramNames.indices) {
params[paramNames[i]] = matcher.group(i + 1)
}
return params
}
}
// 3. Simple Router
class SimpleRouter {
private val routes = mutableListOf<Route>()
private val parser = RouteParser()
// Add route
fun addRoute(path: String, handler: (Map<String, String>) -> String) {
routes.add(Route(path, handler))
}
// GET route
fun get(path: String, handler: (Map<String, String>) -> String) {
addRoute("GET:$path", handler)
}
// POST route
fun post(path: String, handler: (Map<String, String>) -> String) {
addRoute("POST:$path", handler)
}
// PUT route
fun put(path: String, handler: (Map<String, String>) -> String) {
addRoute("PUT:$path", handler)
}
// DELETE route
fun delete(path: String, handler: (Map<String, String>) -> String) {
addRoute("DELETE:$path", handler)
}
// Handle incoming request
fun handle(method: String, path: String): String? {
val fullPath = "$method:$path"
for (route in routes) {
val params = parser.match(route.path, fullPath)
if (params != null) {
return route.handler(params)
}
}
return null
}
// Resolve route
fun resolve(method: String, path: String): Pair<Map<String, String>, ((Map<String, String>) -> String)>? {
val fullPath = "$method:$path"
for (route in routes) {
val params = parser.match(route.path, fullPath)
if (params != null) {
return Pair(params, route.handler)
}
}
return null
}
}
// 4. RESTful Router
class RestfulRouter {
private val router = SimpleRouter()
// Resource routes
fun resource(name: String) {
val basePath = "/$name"
val idPath = "/$name/{id}"
// List all
router.get(basePath) { params ->
"Listing all $name"
}
// Get one
router.get(idPath) { params ->
"Getting $name with id: ${params["id"]}"
}
// Create
router.post(basePath) { params ->
"Creating new $name"
}
// Update
router.put(idPath) { params ->
"Updating $name with id: ${params["id"]}"
}
// Delete
router.delete(idPath) { params ->
"Deleting $name with id: ${params["id"]}"
}
}
// Handle request
fun handle(method: String, path: String): String? {
return router.handle(method, path)
}
}
// 5. Nested Router
class NestedRouter {
private val router = SimpleRouter()
// Group routes under prefix
fun group(prefix: String, block: (SimpleRouter) -> Unit) {
val originalRoutes = router.toList()
block(router)
// Add prefix to new routes
val newRoutes = router.toList().drop(originalRoutes.size)
for (route in newRoutes) {
val prefixedPath = "$prefix${route.path.substringAfter(":")}"
// Re-add with prefix
}
}
// Add nested routes
fun addNestedRoutes() {
// API v1 routes
router.get("/api/v1/users") { "API v1 users" }
router.get("/api/v1/posts") { "API v1 posts" }
// API v2 routes
router.get("/api/v2/users") { "API v2 users" }
router.get("/api/v2/posts") { "API v2 posts" }
}
// Handle request
fun handle(method: String, path: String): String? {
return router.handle(method, path)
}
private fun SimpleRouter.toList(): List<Route> {
// Implementation for getting routes list
return emptyList()
}
}
// 6. Route Builder
class RouteBuilder {
private val routes = mutableListOf<Route>()
// Create route with DSL
fun route(method: String, path: String, handler: (Map<String, String>) -> String) {
routes.add(Route("$method:$path", handler))
}
// Build router
fun build(): SimpleRouter {
val router = SimpleRouter()
for (route in routes) {
val method = route.path.substringBefore(":")
val path = route.path.substringAfter(":")
router.addRoute(route.path, route.handler)
}
return router
}
}
// 7. URL Matcher
class UrlMatcher {
// Match with wildcards
fun matchWildcard(pattern: String, path: String): Boolean {
val regexPattern = pattern
.replace(".", "\\.")
.replace("*", ".*")
.replace("?", ".")
return Regex(regexPattern).matches(path)
}
// Match with query parameters
fun matchWithQuery(pattern: String, path: String): Map<String, String>? {
val (pathPart, queryPart) = if (path.contains("?")) {
path.split("?", limit = 2)
} else {
listOf(path, "")
}
val params = mutableMapOf<String, String>()
if (queryPart.isNotEmpty()) {
val queryParams = queryPart.split("&")
for (param in queryParams) {
val (key, value) = param.split("=", limit = 2)
params[key] = value
}
}
return if (matchWildcard(pattern, pathPart)) params else null
}
}
// 8. Path Utilities
class PathUtils {
// Join path segments
fun join(vararg segments: String): String {
return segments
.joinToString("/")
.replace(Regex("/+"), "/")
.trimEnd('/')
}
// Normalize path
fun normalize(path: String): String {
return path
.replace(Regex("/+"), "/")
.trimEnd('/')
.ifEmpty { "/" }
}
// Get file extension from path
fun getExtension(path: String): String {
return path.substringAfterLast('.', "")
}
// Get filename from path
fun getFilename(path: String): String {
return path.substringAfterLast('/')
}
// Get directory from path
fun getDirectory(path: String): String {
return path.substringBeforeLast('/', "")
}
}
// 9. Query String Parser
class QueryStringParser {
// Parse query string
fun parse(queryString: String): Map<String, List<String>> {
val params = mutableMapOf<String, MutableList<String>>()
if (queryString.isEmpty()) {
return params
}
val pairs = queryString.split("&")
for (pair in pairs) {
val (key, value) = pair.split("=", limit = 2)
params.getOrPut(key) { mutableListOf() }.add(
java.net.URLDecoder.decode(value, "UTF-8")
)
}
return params
}
// Build query string
fun build(params: Map<String, Any>): String {
return params
.flatMap { (key, value) ->
when (value) {
is List<*> -> value.map { key to it.toString() }
else -> listOf(key to value.toString())
}
}
.joinToString("&") { (key, value) ->
"$key=${java.net.URLEncoder.encode(value, "UTF-8")}"
}
}
}
// 10. Route Middleware
data class RouteMiddleware(
val name: String,
val handler: (Map<String, String>, () -> String) -> String
)
class MiddlewareRouter {
private val routes = mutableListOf<Route>()
private val middlewares = mutableListOf<RouteMiddleware>()
// Add middleware
fun use(middleware: RouteMiddleware) {
middlewares.add(middleware)
}
// Add route
fun addRoute(method: String, path: String, handler: (Map<String, String>) -> String) {
routes.add(Route("$method:$path", handler))
}
// Handle with middleware
fun handle(method: String, path: String, params: Map<String, String> = emptyMap()): String {
var result: String? = null
// Create handler chain
val handlerChain: () -> String = {
val matchedRoute = routes.find { route ->
RouteParser().match(route.path, "$method:$path") != null
}
matchedRoute?.handler?.invoke(params) ?: "404 Not Found"
}
// Apply middlewares in reverse order
var chain = handlerChain
for (middleware in middlewares.reversed()) {
val currentChain = chain
chain = {
middleware.handler(params, currentChain)
}
}
return chain()
}
}
// Main demonstration
fun demonstrateUrlRouting() {
println("=== Android Kotlin URL Routing Examples ===\n")
// 1. Basic routing
println("--- 1. Basic Routing ---")
val router = SimpleRouter()
router.get("/users") { params -> "List all users" }
router.get("/users/{id}") { params -> "Get user ${params["id"]}" }
router.post("/users") { params -> "Create user" }
println(router.handle("GET", "/users"))
println(router.handle("GET", "/users/123"))
println(router.handle("POST", "/users"))
// 2. Route parameters
println("\n--- 2. Route Parameters ---")
val parser = RouteParser()
val params = parser.match("/users/{id}/posts/{postId}", "/users/123/posts/456")
println("Matched params: $params")
// 3. RESTful routes
println("\n--- 3. RESTful Routes ---")
val restRouter = RestfulRouter()
restRouter.resource("articles")
println(restRouter.handle("GET", "/articles"))
println(restRouter.handle("GET", "/articles/789"))
println(restRouter.handle("POST", "/articles"))
// 4. URL matching
println("\n--- 4. URL Matching ---")
val matcher = UrlMatcher()
println("Match wildcard: ${matcher.matchWildcard("/api/*", "/api/users")}")
println("Match with query: ${matcher.matchWithQuery("/search", "/search?q=kotlin&page=1")}")
// 5. Path utilities
println("\n--- 5. Path Utilities ---")
val pathUtils = PathUtils()
println("Join path: ${pathUtils.join("/api", "v1", "users")}")
println("Normalize: ${pathUtils.normalize("//api//v1//")}")
println("Get extension: ${pathUtils.getExtension("/files/image.png")}")
println("Get filename: ${pathUtils.getFilename("/files/image.png")}")
// 6. Query string parsing
println("\n--- 6. Query String Parsing ---")
val queryParser = QueryStringParser()
val parsed = queryParser.parse("q=kotlin&page=1&sort=desc")
println("Parsed query: $parsed")
println("Build query: ${queryParser.build(mapOf("q" to "kotlin", "page" to 1))}")
// 7. Middleware
println("\n--- 7. Middleware ---")
val middlewareRouter = MiddlewareRouter()
middlewareRouter.use(RouteMiddleware("logger") { params, next ->
println("Request: $params")
next()
})
middlewareRouter.addRoute("GET", "/test") { "Test response" }
println(middlewareRouter.handle("GET", "/test"))
println("\n=== All URL Routing Examples Completed ===")
}
💻 Middleware-Verarbeitung kotlin
🟡 intermediate
⭐⭐⭐⭐
Request-Processing-Middleware mit Chaining, Logging, Authentifizierung und Fehlerbehandlung implementieren
⏱️ 40 min
🏷️ kotlin, android, web, middleware
Prerequisites:
Intermediate Kotlin, HTTP concepts
// Android Kotlin Middleware Handling Examples
// Implement middleware pattern for request processing
// 1. Context Object
data class RequestContext(
val method: String,
val path: String,
val headers: Map<String, String> = emptyMap(),
val body: String? = null,
val queryParams: Map<String, List<String>> = emptyMap(),
val attributes: MutableMap<String, Any> = mutableMapOf()
)
// 2. Response Object
data class ResponseContext(
var statusCode: Int = 200,
var headers: MutableMap<String, String> = mutableMapOf(),
var body: String? = null
)
// 3. Middleware Interface
interface Middleware {
fun process(ctx: RequestContext, res: ResponseContext, next: () -> Unit)
}
// 4. Logging Middleware
class LoggingMiddleware : Middleware {
override fun process(ctx: RequestContext, res: ResponseContext, next: () -> Unit) {
val startTime = System.currentTimeMillis()
println("[REQUEST] ${ctx.method} ${ctx.path}")
next()
val duration = System.currentTimeMillis() - startTime
println("[RESPONSE] ${res.statusCode} - ${duration}ms")
}
}
// 5. Authentication Middleware
class AuthenticationMiddleware(private val apiKey: String) : Middleware {
override fun process(ctx: RequestContext, res: ResponseContext, next: () -> Unit) {
val authHeader = ctx.headers["Authorization"]
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
res.statusCode = 401
res.body = "Unauthorized: Missing or invalid token"
println("[AUTH] Failed: No token provided")
return
}
val token = authHeader.substringAfter(" ")
if (token != apiKey) {
res.statusCode = 403
res.body = "Forbidden: Invalid token"
println("[AUTH] Failed: Invalid token")
return
}
println("[AUTH] Success: Token validated")
ctx.attributes["userId"] = "user_123"
next()
}
}
// 6. CORS Middleware
class CorsMiddleware(
private val allowedOrigins: List<String> = listOf("*"),
private val allowedMethods: List<String> = listOf("GET", "POST", "PUT", "DELETE"),
private val allowedHeaders: List<String> = listOf("Content-Type", "Authorization")
) : Middleware {
override fun process(ctx: RequestContext, res: ResponseContext, next: () -> Unit) {
val origin = ctx.headers["Origin"] ?: "*"
// Set CORS headers
res.headers["Access-Control-Allow-Origin"] = if (allowedOrigins.contains("*")) {
"*"
} else {
if (allowedOrigins.contains(origin)) origin else allowedOrigins.first()
}
res.headers["Access-Control-Allow-Methods"] = allowedMethods.joinToString(", ")
res.headers["Access-Control-Allow-Headers"] = allowedHeaders.joinToString(", ")
res.headers["Access-Control-Max-Age"] = "86400"
// Handle preflight request
if (ctx.method == "OPTIONS") {
res.statusCode = 204
res.body = null
println("[CORS] Preflight request handled")
return
}
println("[CORS] Headers added for origin: $origin")
next()
}
}
// 7. Body Parser Middleware
class BodyParserMiddleware : Middleware {
override fun process(ctx: RequestContext, res: ResponseContext, next: () -> Unit) {
if (ctx.body != null && ctx.headers["Content-Type"]?.contains("application/json") == true) {
try {
// Parse JSON body
ctx.attributes["parsedBody"] = ctx.body
println("[BODY] JSON body parsed successfully")
} catch (e: Exception) {
res.statusCode = 400
res.body = "Invalid JSON: ${e.message}"
println("[BODY] Failed to parse JSON")
return
}
}
next()
}
}
// 8. Rate Limiting Middleware
class RateLimitMiddleware(
private val maxRequests: Int = 100,
private val windowMs: Long = 60000 // 1 minute
) : Middleware {
private val requestCounts = mutableMapOf<String, MutableList<Long>>()
override fun process(ctx: RequestContext, res: ResponseContext, next: () -> Unit) {
val clientId = ctx.headers["X-Client-ID"] ?: ctx.queryParams["ip"]?.firstOrNull() ?: "unknown"
val currentTime = System.currentTimeMillis()
// Clean old requests
requestCounts[clientId]?.removeIf { currentTime - it > windowMs }
// Get current request count
val requests = requestCounts.getOrPut(clientId) { mutableListOf() }
if (requests.size >= maxRequests) {
res.statusCode = 429
res.headers["Retry-After"] = "${(windowMs - (currentTime - requests.first())) / 1000}"
res.body = "Too many requests. Please try again later."
println("[RATE LIMIT] Blocked: $clientId (${requests.size}/$maxRequests)")
return
}
requests.add(currentTime)
println("[RATE LIMIT] Allowed: $clientId (${requests.size}/$maxRequests)")
next()
}
}
// 9. Error Handling Middleware
class ErrorHandlingMiddleware : Middleware {
override fun process(ctx: RequestContext, res: ResponseContext, next: () -> Unit) {
try {
next()
} catch (e: Exception) {
res.statusCode = 500
res.body = "Internal Server Error: ${e.message}"
println("[ERROR] ${e.javaClass.simpleName}: ${e.message}")
e.printStackTrace()
}
}
}
// 10. Compression Middleware
class CompressionMiddleware : Middleware {
override fun process(ctx: RequestContext, res: ResponseContext, next: () -> Unit) {
next()
// Compress response if applicable
val acceptEncoding = ctx.headers["Accept-Encoding"] ?: ""
if (acceptEncoding.contains("gzip") && res.body != null && res.body!!.length > 1024) {
// In real implementation, compress the body
res.headers["Content-Encoding"] = "gzip"
println("[COMPRESSION] Response compressed")
}
}
}
// 11. Cache Middleware
class CacheMiddleware(
private val defaultMaxAge: Int = 3600
) : Middleware {
private val cache = mutableMapOf<String, Pair<String, Long>>()
override fun process(ctx: RequestContext, res: ResponseContext, next: () -> Unit) {
val cacheKey = "${ctx.method}:${ctx.path}"
val currentTime = System.currentTimeMillis()
// Check cache for GET requests
if (ctx.method == "GET") {
val cached = cache[cacheKey]
if (cached != null && currentTime - cached.second < defaultMaxAge * 1000) {
res.statusCode = 200
res.body = cached.first
res.headers["X-Cache"] = "HIT"
println("[CACHE] HIT: $cacheKey")
return
}
}
next()
// Cache response for GET requests
if (ctx.method == "GET" && res.statusCode == 200 && res.body != null) {
cache[cacheKey] = Pair(res.body!!, currentTime)
res.headers["X-Cache"] = "MISS"
res.headers["Cache-Control"] = "max-age=$defaultMaxAge"
println("[CACHE] MISS: Stored $cacheKey")
}
}
}
// 12. Timeout Middleware
class TimeoutMiddleware(
private val timeoutMs: Long = 30000
) : Middleware {
override fun process(ctx: RequestContext, res: ResponseContext, next: () -> Unit) {
val thread = Thread {
try {
next()
} catch (e: Exception) {
res.statusCode = 504
res.body = "Gateway Timeout: Request took too long"
println("[TIMEOUT] Request exceeded ${timeoutMs}ms")
}
}
thread.start()
thread.join(timeoutMs)
if (thread.isAlive) {
thread.interrupt()
res.statusCode = 504
res.body = "Gateway Timeout"
println("[TIMEOUT] Interrupted request")
}
}
}
// 13. Security Headers Middleware
class SecurityHeadersMiddleware : Middleware {
override fun process(ctx: RequestContext, res: ResponseContext, next: () -> Unit) {
next()
// Add security headers
res.headers["X-Content-Type-Options"] = "nosniff"
res.headers["X-Frame-Options"] = "DENY"
res.headers["X-XSS-Protection"] = "1; mode=block"
res.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains"
res.headers["Content-Security-Policy"] = "default-src 'self'"
println("[SECURITY] Security headers added")
}
}
// 14. Middleware Pipeline
class MiddlewarePipeline {
private val middlewares = mutableListOf<Middleware>()
// Add middleware
fun use(middleware: Middleware): MiddlewarePipeline {
middlewares.add(middleware)
return this
}
// Process request through middleware chain
fun process(ctx: RequestContext, handler: (RequestContext, ResponseContext) -> Unit): ResponseContext {
val res = ResponseContext()
var index = 0
// Create chain
val chain: () -> Unit = {
if (index < middlewares.size) {
val middleware = middlewares[index++]
middleware.process(ctx, res, chain)
} else {
handler(ctx, res)
}
}
chain()
return res
}
// Clear all middlewares
fun clear(): MiddlewarePipeline {
middlewares.clear()
return this
}
}
// 15. Router with Middleware
class MiddlewareRouter {
private val pipeline = MiddlewarePipeline()
// Setup common middlewares
fun setup() {
pipeline
.use(ErrorHandlingMiddleware())
.use(LoggingMiddleware())
.use(CorsMiddleware())
.use(SecurityHeadersMiddleware())
}
// Handle request
fun handle(
method: String,
path: String,
headers: Map<String, String> = emptyMap(),
handler: (RequestContext, ResponseContext) -> Unit
): ResponseContext {
val ctx = RequestContext(
method = method,
path = path,
headers = headers
)
return pipeline.process(ctx, handler)
}
// Add custom middleware
fun use(middleware: Middleware): MiddlewareRouter {
pipeline.use(middleware)
return this
}
}
// 16. Async Middleware (conceptual)
class AsyncMiddleware : Middleware {
override fun process(ctx: RequestContext, res: ResponseContext, next: () -> Unit) {
// Simulate async operation
Thread {
println("[ASYNC] Processing background task")
Thread.sleep(100)
println("[ASYNC] Background task completed")
}.start()
next()
}
}
// 17. Conditional Middleware
class ConditionalMiddleware(
private val condition: (RequestContext) -> Boolean,
private val middleware: Middleware
) : Middleware {
override fun process(ctx: RequestContext, res: ResponseContext, next: () -> Unit) {
if (condition(ctx)) {
middleware.process(ctx, res, next)
} else {
println("[CONDITIONAL] Middleware skipped")
next()
}
}
}
// 18. Request Validation Middleware
class ValidationMiddleware(
private val validator: (RequestContext) -> Boolean,
private val errorMessage: String = "Validation failed"
) : Middleware {
override fun process(ctx: RequestContext, res: ResponseContext, next: () -> Unit) {
if (validator(ctx)) {
println("[VALIDATION] Passed")
next()
} else {
res.statusCode = 400
res.body = errorMessage
println("[VALIDATION] Failed: $errorMessage")
}
}
}
// Main demonstration
fun demonstrateMiddlewareHandling() {
println("=== Android Kotlin Middleware Handling Examples ===\n")
// 1. Basic middleware chain
println("--- 1. Basic Middleware Chain ---")
val pipeline = MiddlewarePipeline()
pipeline
.use(LoggingMiddleware())
.use { ctx, res, next ->
println("[CUSTOM] Processing request")
res.statusCode = 200
res.body = "Hello, World!"
next()
}
val ctx = RequestContext("GET", "/api/test")
val response = pipeline.process(ctx) { reqCtx, res ->
// Handler
}
println("Response: ${response.statusCode} - ${response.body}")
// 2. Authentication middleware
println("\n--- 2. Authentication Middleware ---")
val authPipeline = MiddlewarePipeline()
authPipeline.use(AuthenticationMiddleware("secret_key_123"))
val authCtxValid = RequestContext(
"GET", "/api/protected",
headers = mapOf("Authorization" to "Bearer secret_key_123")
)
val authResponseValid = authPipeline.process(authCtxValid) { _, res ->
res.statusCode = 200
res.body = "Protected content"
}
println("Valid token: ${authResponseValid.statusCode}")
val authCtxInvalid = RequestContext(
"GET", "/api/protected",
headers = mapOf("Authorization" to "Bearer wrong_key")
)
val authResponseInvalid = authPipeline.process(authCtxInvalid) { _, res -> }
println("Invalid token: ${authResponseInvalid.statusCode}")
// 3. CORS middleware
println("\n--- 3. CORS Middleware ---")
val corsPipeline = MiddlewarePipeline()
corsPipeline.use(CorsMiddleware(allowedOrigins = listOf("https://example.com")))
val corsCtx = RequestContext(
"OPTIONS", "/api/data",
headers = mapOf("Origin" to "https://example.com")
)
val corsResponse = corsPipeline.process(corsCtx) { _, res -> }
println("CORS headers: ${corsResponse.headers}")
// 4. Rate limiting
println("\n--- 4. Rate Limiting Middleware ---")
val rateLimitPipeline = MiddlewarePipeline()
rateLimitPipeline.use(RateLimitMiddleware(maxRequests = 3))
for (i in 1..5) {
val rateCtx = RequestContext("GET", "/api/data")
val rateRes = rateLimitPipeline.process(rateCtx) { _, res ->
res.statusCode = 200
res.body = "Response $i"
}
println("Request $i: ${rateRes.statusCode}")
}
// 5. Error handling
println("\n--- 5. Error Handling Middleware ---")
val errorPipeline = MiddlewarePipeline()
errorPipeline.use(ErrorHandlingMiddleware())
val errorCtx = RequestContext("GET", "/api/error")
val errorResponse = errorPipeline.process(errorCtx) { _, res ->
throw RuntimeException("Something went wrong!")
}
println("Error response: ${errorResponse.statusCode} - ${errorResponse.body}")
// 6. Cache middleware
println("\n--- 6. Cache Middleware ---")
val cachePipeline = MiddlewarePipeline()
cachePipeline.use(CacheMiddleware(defaultMaxAge = 60))
for (i in 1..3) {
val cacheCtx = RequestContext("GET", "/api/data")
val cacheRes = cachePipeline.process(cacheCtx) { _, res ->
res.statusCode = 200
res.body = "Cached data"
}
println("Request $i: ${cacheRes.headers["X-Cache"]}")
}
// 7. Combined middlewares
println("\n--- 7. Combined Middlewares ---")
val router = MiddlewareRouter()
router.setup()
router.use(ValidationMiddleware({ it.method == "GET" }, "Only GET allowed"))
val combinedRes = router.handle("GET", "/api/data") { reqCtx, res ->
res.statusCode = 200
res.body = "Success"
}
println("Combined response: ${combinedRes.statusCode} - ${combinedRes.body}")
println("\n=== All Middleware Handling Examples Completed ===")
}
💻 Statische Dateien Bereitstellen kotlin
🟡 intermediate
⭐⭐⭐⭐
Statische Dateien mit MIME-Typerkennung, Caching und Komprimierungsunterstützung bereitstellen
⏱️ 35 min
🏷️ kotlin, android, web, static files
Prerequisites:
Intermediate Kotlin, File I/O
// Android Kotlin Static File Serving Examples
// Implementation for serving static files from Android assets or storage
import android.content.Context
import android.content.res.AssetManager
import java.io.File
import java.io.FileInputStream
import java.io.InputStream
import java.util.zip.GZIPOutputStream
import java.io.ByteArrayOutputStream
// 1. MIME Type Detector
class MimeTypeDetector {
private val mimeTypes = mapOf(
"html" to "text/html",
"css" to "text/css",
"js" to "application/javascript",
"json" to "application/json",
"xml" to "application/xml",
"pdf" to "application/pdf",
"zip" to "application/zip",
"txt" to "text/plain",
"jpg" to "image/jpeg",
"jpeg" to "image/jpeg",
"png" to "image/png",
"gif" to "image/gif",
"svg" to "image/svg+xml",
"ico" to "image/x-icon",
"woff" to "font/woff",
"woff2" to "font/woff2",
"ttf" to "font/ttf",
"eot" to "application/vnd.ms-fontobject",
"mp4" to "video/mp4",
"webm" to "video/webm",
"mp3" to "audio/mpeg",
"wav" to "audio/wav",
"ogg" to "audio/ogg"
)
// Get MIME type from file extension
fun getMimeType(filename: String): String {
val extension = filename.substringAfterLast('.', "").lowercase()
return mimeTypes[extension] ?: "application/octet-stream"
}
// Get MIME type from file
fun getMimeType(file: File): String {
return getMimeType(file.name)
}
// Check if file is binary
fun isBinary(filename: String): Boolean {
val mimeType = getMimeType(filename)
return !mimeType.startsWith("text/")
}
// Check if file is image
fun isImage(filename: String): Boolean {
val mimeType = getMimeType(filename)
return mimeType.startsWith("image/")
}
// Check if file is video
fun isVideo(filename: String): Boolean {
val mimeType = getMimeType(filename)
return mimeType.startsWith("video/")
}
// Check if file is audio
fun isAudio(filename: String): Boolean {
val mimeType = getMimeType(filename)
return mimeType.startsWith("audio/")
}
}
// 2. File Response
data class FileResponse(
val inputStream: InputStream,
val contentType: String,
val contentLength: Long,
val lastModified: Long,
val etag: String
)
// 3. Static File Server (Assets)
class AssetFileServer(private val context: Context) {
private val assetManager: AssetManager = context.assets
private val mimeTypeDetector = MimeTypeDetector()
// Serve file from assets
fun serveFile(path: String): FileResponse? {
return try {
val inputStream = assetManager.open(path.removePrefix("/"))
val fileInfo = getFileInfo(path)
FileResponse(
inputStream = inputStream,
contentType = fileInfo.contentType,
contentLength = fileInfo.size,
lastModified = fileInfo.lastModified,
etag = generateEtag(path, fileInfo.lastModified)
)
} catch (e: Exception) {
println("Error serving asset file: ${e.message}")
null
}
}
// List files in directory
fun listFiles(path: String = ""): List<String> {
return try {
assetManager.list(path.removePrefix("/"))?.toList() ?: emptyList()
} catch (e: Exception) {
emptyList()
}
}
// Check if file exists
fun fileExists(path: String): Boolean {
return try {
assetManager.open(path.removePrefix("/")).close()
true
} catch (e: Exception) {
false
}
}
// Get file info
private fun getFileInfo(path: String): FileInfo {
val extension = path.substringAfterLast('.', "")
val contentType = mimeTypeDetector.getMimeType(extension)
// Asset files don't provide size or last modified directly
// In real implementation, you might cache this info
return FileInfo(
size = -1L,
lastModified = System.currentTimeMillis(),
contentType = contentType
)
}
// Generate ETag
private fun generateEtag(path: String, lastModified: Long): String {
return "\""${path.hashCode()}-${lastModified}\"""
}
private data class FileInfo(
val size: Long,
val lastModified: Long,
val contentType: String
)
}
// 4. Static File Server (Storage)
class StorageFileServer {
private val mimeTypeDetector = MimeTypeDetector()
// Serve file from storage
fun serveFile(baseDir: File, path: String): FileResponse? {
val file = File(baseDir, path.removePrefix("/"))
return if (file.exists() && file.isFile) {
try {
FileResponse(
inputStream = FileInputStream(file),
contentType = mimeTypeDetector.getMimeType(file),
contentLength = file.length(),
lastModified = file.lastModified(),
etag = generateEtag(file)
)
} catch (e: Exception) {
println("Error serving file: ${e.message}")
null
}
} else {
null
}
}
// Serve file with range support
fun serveFileWithRange(baseDir: File, path: String, range: String?): FileResponse? {
val file = File(baseDir, path.removePrefix("/"))
if (!file.exists() || !file.isFile) {
return null
}
return try {
val inputStream = if (range != null) {
// Parse range header: "bytes=start-end"
val parts = range.removePrefix("bytes=").split("-")
val start = parts[0].toLongOrNull() ?: 0L
val end = if (parts.size > 1 && parts[1].isNotEmpty()) {
parts[1].toLong()
} else {
file.length() - 1
}
// In real implementation, return partial stream
FileInputStream(file)
} else {
FileInputStream(file)
}
FileResponse(
inputStream = inputStream,
contentType = mimeTypeDetector.getMimeType(file),
contentLength = file.length(),
lastModified = file.lastModified(),
etag = generateEtag(file)
)
} catch (e: Exception) {
println("Error serving file: ${e.message}")
null
}
}
// List directory
fun listDirectory(baseDir: File, path: String): List<File> {
val dir = File(baseDir, path.removePrefix("/"))
return if (dir.exists() && dir.isDirectory) {
dir.listFiles()?.toList() ?: emptyList()
} else {
emptyList()
}
}
// Generate ETag
private fun generateEtag(file: File): String {
return "\""${file.name}-${file.lastModified()}-${file.length()}\"""
}
}
// 5. Cache Control
class CacheControl {
// Get cache control header
fun getCacheControl(maxAge: Int = 3600, mustRevalidate: Boolean = false): String {
val parts = mutableListOf("max-age=$maxAge")
if (mustRevalidate) parts.add("must-revalidate")
return parts.joinToString(", ")
}
// Get cache control for static assets
fun getStaticAssetCache(): String {
return "public, max-age=31536000, immutable"
}
// Get cache control for HTML files
fun getHtmlCache(): String {
return "public, max-age=0, must-revalidate"
}
// Get cache control for API responses
fun getApiCache(): String {
return "no-cache, no-store, must-revalidate"
}
// Check if client has cached version
fun isCached(etag: String, ifNoneMatch: String?): Boolean {
return ifNoneMatch?.contains(etag) == true
}
// Check if resource has been modified
fun isModified(lastModified: Long, ifModifiedSince: Long): Boolean {
return lastModified > ifModifiedSince
}
}
// 6. Compression Handler
class CompressionHandler {
// Compress data if client accepts gzip
fun compressIfNeeded(
data: ByteArray,
acceptEncoding: String?
): Pair<ByteArray, String?> {
if (acceptEncoding?.contains("gzip") == true && data.size > 1024) {
val compressed = compress(data)
return compressed to "gzip"
}
return data to null
}
// Compress data with GZIP
private fun compress(data: ByteArray): ByteArray {
val outputStream = ByteArrayOutputStream()
GZIPOutputStream(outputStream).use { gzip ->
gzip.write(data)
}
return outputStream.toByteArray()
}
// Check if should compress based on content type
fun shouldCompress(contentType: String): Boolean {
val compressibleTypes = listOf(
"text/",
"application/json",
"application/javascript",
"application/xml",
"application/xhtml+xml"
)
return compressibleTypes.any { contentType.startsWith(it) }
}
}
// 7. Range Request Handler
class RangeRequestHandler {
// Parse range header
fun parseRange(rangeHeader: String, fileSize: Long): RangeInfo {
val range = rangeHeader.removePrefix("bytes=")
val parts = range.split("-")
val start = parts[0].toLongOrNull() ?: 0L
val end = if (parts.size > 1 && parts[1].isNotEmpty()) {
parts[1].toLong()
} else {
fileSize - 1
}
return RangeInfo(
start = start.coerceAtLeast(0),
end = end.coerceAtMost(fileSize - 1),
fileSize = fileSize
)
}
// Generate content range header
fun generateContentRange(range: RangeInfo): String {
return "bytes ${range.start}-${range.end}/${range.fileSize}"
}
data class RangeInfo(
val start: Long,
val end: Long,
val fileSize: Long
) {
val length: Long get() = end - start + 1
}
}
// 8. Static File Configuration
data class StaticFileConfig(
val baseDirectory: File,
val indexFile: String = "index.html",
val enableCompression: Boolean = true,
val enableCaching: Boolean = true,
val cacheMaxAge: Int = 3600,
val enableETag: Boolean = true,
val enableRange: Boolean = true
)
// 9. Complete Static File Server
class CompleteStaticFileServer(private val config: StaticFileConfig) {
private val storageServer = StorageFileServer()
private val cacheControl = CacheControl()
private val compressionHandler = CompressionHandler()
private val rangeHandler = RangeRequestHandler()
// Serve file with all features
fun serveFile(
path: String,
headers: Map<String, String> = emptyMap()
): Pair<FileResponse, Map<String, String>>? {
// Try index file if directory
var requestPath = path
if (requestPath.endsWith("/")) {
requestPath += config.indexFile
}
// Serve file
val fileResponse = storageServer.serveFile(config.baseDirectory, requestPath)
?: return null
// Build response headers
val responseHeaders = mutableMapOf<String, String>()
// Content type
responseHeaders["Content-Type"] = fileResponse.contentType
// Content length
responseHeaders["Content-Length"] = fileResponse.contentLength.toString()
// Last modified
responseHeaders["Last-Modified"] = formatDate(fileResponse.lastModified)
// ETag
if (config.enableETag) {
responseHeaders["ETag"] = fileResponse.etag
// Check if client has cached version
val ifNoneMatch = headers["If-None-Match"]
if (cacheControl.isCached(fileResponse.etag, ifNoneMatch)) {
return Pair(fileResponse.copy(inputStream = InputStream.nullInputStream()), mapOf("Status" to "304 Not Modified"))
}
}
// Cache control
if (config.enableCaching) {
val mimeType = fileResponse.contentType
val maxAge = if (mimeType.contains("html")) {
0
} else {
config.cacheMaxAge
}
responseHeaders["Cache-Control"] = cacheControl.getCacheControl(maxAge)
}
// Accept ranges
if (config.enableRange) {
responseHeaders["Accept-Ranges"] = "bytes"
}
// Compression
if (config.enableCompression) {
val acceptEncoding = headers["Accept-Encoding"]
if (compressionHandler.shouldCompress(fileResponse.contentType)) {
// In real implementation, compress the stream
responseHeaders["Vary"] = "Accept-Encoding"
}
}
return Pair(fileResponse, responseHeaders)
}
// Format date for HTTP header
private fun formatDate(timestamp: Long): String {
val sdf = java.text.SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", java.util.Locale.US)
sdf.timeZone = java.util.SimpleTimeZone(0, "GMT")
return sdf.format(java.util.Date(timestamp))
}
}
// 10. Directory Listing
class DirectoryListing {
private val mimeTypeDetector = MimeTypeDetector()
// Generate HTML directory listing
fun generateListing(files: List<File>, path: String): String {
val sb = StringBuilder()
sb.append("<!DOCTYPE html>\n")
sb.append("<html>\n")
sb.append("<head><title>Index of $path</title></head>\n")
sb.append("<body>\n")
sb.append("<h1>Index of $path</h1>\n")
sb.append("<hr>\n")
sb.append("<pre>\n")
// Parent directory link
if (path != "/") {
val parentPath = path.substringBeforeLast('/', "")
sb.append("<a href=\"$parentPath\">../</a>\n")
}
// Sort files: directories first, then files
val sortedFiles = files.sortedWith(compareBy({ !it.isDirectory }, { it.name }))
for (file in sortedFiles) {
val name = file.name
val size = if (file.isFile) formatSize(file.length()) else ""
val modified = formatDate(file.lastModified())
sb.append("<a href=\"$name\">$name</a>")
sb.append(" ".repeat(maxOf(0, 30 - name.length)))
sb.append(modified)
sb.append(" ".repeat(2))
sb.append(size)
sb.append("\n")
}
sb.append("</pre>\n")
sb.append("<hr>\n")
sb.append("</body>\n")
sb.append("</html>")
return sb.toString()
}
// Format file size
private fun formatSize(bytes: Long): String {
return when {
bytes < 1024 -> "$bytes B"
bytes < 1024 * 1024 -> "${bytes / 1024} KB"
bytes < 1024 * 1024 * 1024 -> "${bytes / (1024 * 1024)} MB"
else -> "${bytes / (1024 * 1024 * 1024)} GB"
}
}
// Format date
private fun formatDate(timestamp: Long): String {
val sdf = java.text.SimpleDateFormat("dd-MMM-yyyy HH:mm", java.util.Locale.US)
return sdf.format(java.util.Date(timestamp))
}
}
// 11. File Upload Handler (conceptual)
class FileUploadHandler(private val uploadDir: File) {
// Handle file upload
fun handleUpload(
filename: String,
inputStream: InputStream,
contentType: String
): File {
uploadDir.mkdirs()
val file = File(uploadDir, filename)
file.outputStream().use { output ->
inputStream.copyTo(output)
}
return file
}
// Generate unique filename
fun generateUniqueFilename(originalName: String): String {
val extension = originalName.substringAfterLast('.', "")
val baseName = originalName.substringBeforeLast('.')
val timestamp = System.currentTimeMillis()
return "${baseName}_$timestamp.$extension"
}
// Validate file type
fun isValidFileType(filename: String, allowedTypes: List<String>): Boolean {
val extension = filename.substringAfterLast('.', "").lowercase()
return allowedTypes.contains(extension)
}
}
// 12. Resource Bundles
class ResourceBundle(private val context: Context) {
// Serve resource from bundle
fun serveResource(resourceName: String): InputStream? {
return try {
context.resources.openRawResource(
context.resources.getIdentifier(
resourceName.substringBeforeLast('.'),
"raw",
context.packageName
)
)
} catch (e: Exception) {
null
}
}
// List all resources
fun listResources(): List<String> {
val fields = R.raw::class.java.fields
return fields.map { it.name }
}
}
// Main demonstration
fun demonstrateStaticFileServing(context: Context) {
println("=== Android Kotlin Static File Serving Examples ===\n")
// 1. MIME type detection
println("--- 1. MIME Type Detection ---")
val mimeTypeDetector = MimeTypeDetector()
println("HTML: ${mimeTypeDetector.getMimeType("index.html")}")
println("CSS: ${mimeTypeDetector.getMimeType("style.css")}")
println("JavaScript: ${mimeTypeDetector.getMimeType("app.js")}")
println("PNG Image: ${mimeTypeDetector.getMimeType("image.png")}")
println("Is image: ${mimeTypeDetector.isImage("photo.jpg")}")
println("Is binary: ${mimeTypeDetector.isBinary("video.mp4")}")
// 2. Asset file server
println("\n--- 2. Asset File Server ---")
val assetServer = AssetFileServer(context)
println("File exists 'index.html': ${assetServer.fileExists("index.html")}")
println("List files: ${assetServer.listFiles()}")
// 3. Storage file server
println("\n--- 3. Storage File Server ---")
val storageServer = StorageFileServer()
val baseDir = File(context.filesDir, "public")
println("Base directory: ${baseDir.absolutePath}")
// 4. Cache control
println("\n--- 4. Cache Control ---")
val cacheControl = CacheControl()
println("Static asset cache: ${cacheControl.getStaticAssetCache()}")
println("HTML cache: ${cacheControl.getHtmlCache()}")
println("API cache: ${cacheControl.getApiCache()}")
// 5. Compression
println("\n--- 5. Compression ---")
val compressionHandler = CompressionHandler()
println("Should compress HTML: ${compressionHandler.shouldCompress("text/html")}")
println("Should compress JSON: ${compressionHandler.shouldCompress("application/json")}")
println("Should compress PNG: ${compressionHandler.shouldCompress("image/png")}")
// 6. Range requests
println("\n--- 6. Range Requests ---")
val rangeHandler = RangeRequestHandler()
val rangeInfo = rangeHandler.parseRange("bytes=0-1023", 10000)
println("Range: ${rangeInfo.start}-${rangeInfo.end} (total: ${rangeInfo.fileSize})")
println("Content-Range: ${rangeHandler.generateContentRange(rangeInfo)}")
// 7. Complete static file server
println("\n--- 7. Complete Static File Server ---")
val config = StaticFileConfig(
baseDirectory = baseDir,
indexFile = "index.html",
enableCompression = true,
enableCaching = true,
enableETag = true
)
val completeServer = CompleteStaticFileServer(config)
println("Server configured with:")
println(" - Base directory: ${config.baseDirectory}")
println(" - Index file: ${config.indexFile}")
println(" - Compression: ${config.enableCompression}")
println(" - Caching: ${config.enableCaching}")
// 8. Directory listing
println("\n--- 8. Directory Listing ---")
val directoryListing = DirectoryListing()
println("Directory listing methods:")
println(" - generateListing(): Generate HTML listing")
println(" - formatSize(): Format file size")
println(" - formatDate(): Format file date")
// 9. File upload
println("\n--- 9. File Upload ---")
val uploadHandler = FileUploadHandler(File(context.cacheDir, "uploads"))
println("Upload handler methods:")
println(" - handleUpload(): Save uploaded file")
println(" - generateUniqueFilename(): Create unique name")
println(" - isValidFileType(): Validate file type")
println("\n=== All Static File Serving Examples Completed ===")
}