You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Http4K Kotlin中为不同ContractRoute路由应用不同过滤器?

在Http4K中实现部分路由认证、部分路由免认证的正确方式

你当前的写法是把过滤器直接绑定到单个路由的handler上,但Http4K的Contract路由机制下,这种方式可能不会按预期生效。正确的做法是先对路由组分别应用过滤器,再合并路由,而不是在单个路由的handler上叠加过滤器。

修改后的实现代码

// 1. 抽离所有路由通用的基础过滤器链
private val baseFilters = ResetMDCFilter()
    .then(EnvironmentMDCFilter(environment))
    .then(RequestLogFilter())
    .then(ResponseLogFilter())
    .then(CorrelationIdMDCFilter())
    .then(ErrorCodeCatchAllFilter(logger))
    .then(ErrorCodeLensFailureFilter(logger))

// 2. 定义带认证的过滤器链(基础过滤器+鉴权过滤器)
private val authFilters = AuthorisationFilter(jwtVerifier).then(baseFilters)

// 3. 拆分路由为两个Contract组:免认证路由、需认证路由
private fun noAuthRoutes() = contract {
    val apiDescription = "Some description for API"
    renderer = OpenApi3(
        ApiInfo(
            title = "Title",
            version = "v1.0.0",
            description = apiDescription
        ),
        CustomJackson
    )
    descriptionPath = "/docs/swaggerV2.json"
    routes += pingRoute()
}

private fun authRoutes() = contract {
    val apiDescription = "Some description for API"
    renderer = OpenApi3(
        ApiInfo(
            title = "Title",
            version = "v1.0.0",
            description = apiDescription
        ),
        CustomJackson
    )
    descriptionPath = "/docs/swaggerV2.json"
    routes += getCustomerRoute()
}

// 4. 为两组路由分别绑定过滤器,再合并为最终的HttpHandler
val routes: HttpHandler = 
    ("/account" bind baseFilters.then(noAuthRoutes())) + 
    ("/account" bind authFilters.then(authRoutes()))

// 单个路由简化定义,无需再绑定过滤器(已在路由组层面处理)
private fun pingRoute(): ContractRoute {
    return "/ping" meta {
        summary = "ping"
        description = "Ping Api"
        returning(Status.OK to "The result")
    } bindContract Method.GET to { _ -> Response(Status.OK).body("pong")}
}

private fun getCustomerRoute(): ContractRoute {
    return "/customer" / Path.string().map(::CustomerNumber).of("customerNumber") meta {
        summary = "Gets customer account information"
        description = "Gets customer account information"
        returning(
            status = Status.OK,
            body = CustomerAccountDataResponse.bodyLens to CustomerAccountDataResponse()
        )
    } bindContract Method.GET to accountEndpoints::getCustomerAccount
}

逻辑说明

  • 把所有路由共用的过滤器抽成baseFilters,避免重复代码
  • 将路由拆分为两个独立的Contract,分别对应免认证和需认证的路由集合
  • 对每个Contract路由组应用对应的过滤器链,再通过+运算符合并成最终的HttpHandler
  • 单个路由的handler不再需要叠加过滤器,逻辑更清晰,同时保证过滤器能正确作用到目标路由上

替代简化方案(无需拆分Contract)

如果路由数量不多,也可以直接按路径分组绑定过滤器:

// 基础过滤器和认证过滤器定义不变
private val baseFilters = ...
private val authFilters = ...

// 定义包含所有路由的Contract
private val allRoutesContract = contract {
    val apiDescription = "Some description for API"
    renderer = OpenApi3(
        ApiInfo(
            title = "Title",
            version = "v1.0.0",
            description = apiDescription
        ),
        CustomJackson
    )
    descriptionPath = "/docs/swaggerV2.json"
    routes += pingRoute()
    routes += getCustomerRoute()
}

// 按路径匹配分别应用过滤器
val routes: HttpHandler = 
    ("/account/ping" bind baseFilters.then(allRoutesContract)) + 
    ("/account/customer" bind authFilters.then(allRoutesContract))

内容的提问来源于stack exchange,提问作者Akshay Mahajan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 19:41:01