Exemplos de Funcionalidades Web Android Kotlin

Exemplos de funcionalidades web Android Kotlin incluindo roteamento URL, manipulação de middleware e serviço de arquivos estáticos

💻 Roteamento URL kotlin

🟡 intermediate ⭐⭐⭐⭐

Analisar e manipular rotas URL com parâmetros, curingas e padrões RESTful

⏱️ 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 ===")
}

💻 Manipulação de Middleware kotlin

🟡 intermediate ⭐⭐⭐⭐

Implementar middleware de processamento de requisições com encadeamento, logging, autenticação e tratamento de erros

⏱️ 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 ===")
}

💻 Serviço de Arquivos Estáticos kotlin

🟡 intermediate ⭐⭐⭐⭐

Servir arquivos estáticos com detecção de tipo MIME, cache e suporte a compressão

⏱️ 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 ===")
}