Skip to content

路由与终结点

路由中间件是 Web 框架中用于将 HTTP 请求分发到对应处理逻辑(终结点)的核心组件。它负责解析请求路径、方法等信息,并根据预定义的路由规则,将请求映射到对应的处理函数或控制器。

路由中间件典型时序图

说明

  • Client 代表前端或第三方应用发起的 HTTP 请求。

  • Server 首先接收到请求,并将其传递给路由中间件。

  • 路由中间件 负责根据请求的路径和方法查找是否有匹配的终结点(处理函数)。

    • 如果匹配到终结点,则将请求分发给对应的终结点处理器,处理后返回响应。

    • 如果未匹配到终结点,则直接返回 404 Not Found。

该机制实现了请求的自动分发和处理,是 Web 框架的核心能力之一。

完整流程示例

下面以一个典型的 Spire Web 项目为例,展示路由中间件的完整流程:

cangjie
import spire_web_http.*
import spire_web_hosting.*
import spire_web_routing.*
import spire_extensions_injection.*

main() {
    // 1. 创建主机构造器
    let builder = WebHost.createBuilder()

    // 2. 注册路由服务
    builder.services.addRouting()

    // 3. 构建主机
    let host = builder.build()

    // 4. 注册路由终结点
    host.useEndpoints { endpoints: EndpointRouteBuilder =>

        // GET 静态路由示例
        endpoints.mapGet("hello") { context =>
            context.response.write("Hello Spire!")
        }

        // Get 动态路由路由示例
        endpoints.mapGet("api/{id}") { context =>
            context.response.write("id=" + context.request.routeValues["id"].toString())
        }
    }

    // 5. 启动主机
    host.run()
    return 0
}

流程说明

  • 注册路由服务: builder.services.addRouting() 向依赖注入容器注册路由相关服务,为后续路由匹配做准备。
  • 注册路由中间件: host.useRouting()(由 useEndpoints 自动注册)将路由中间件加入请求管道。该中间件会在每次请求时解析 URL 路径、HTTP 方法,并查找匹配的终结点。
  • 注册终结点: host.useEndpoints { ... } 用于注册具体的路由规则和处理逻辑。每个终结点(如 mapGet("hello") )定义了请求路径和对应的处理函数。
  • 请求分发:当有 HTTP 请求到达时,路由中间件会遍历所有注册的终结点,查找与请求路径和方法匹配的处理函数,并将请求交由其处理。
  • 响应输出:处理函数通过 context.response.write(...) 输出响应内容,最终返回给客户端。

注册顺序

路由中间件通常应在中间件之后,授权中间件之前注册,因为它依赖于请求的身份信息进行资源访问控制。例如:

cangjie
host.useAuthentication()
host.useRouting()      // 路由中间件
host.useAuthorization()
host.useEndpoints { ... }

常用扩展

动态路由(参数化路径)

cangjie
host.useEndpoints { endpoints: EndpointRouteBuilder =>
    // 支持参数化路径 /api/{id}
    endpoints.mapGet("api/{id}") { context =>
        context.response.write("id=" + context.request.routeValues["id"].toString())
    }
}

控制器路由

cangjie
// 用户控制器,路由前缀 /user
@Route["user"]
public class UserController <: Controller {
    // GET /user/{name}
    @HttpGet["{name}"]
    public func getUser(@FromRoute name: String): String {
        context.response.write("User: " + name)
        return "User: " + name
    }
}

main() {
    let builder = WebHost.createBuilder()
    builder.services.addControllers()
        .addApplicationPart("default", TypeInfo.of<UserController>())
    let host = builder.build()
    host.useEndpoints { endpoints =>
        endpoints.mapControllers()
    }
    host.run()
}

中间件组合

cangjie
main() {
    let builder = WebHost.createBuilder()
    builder.services.addRouting()
    let host = builder.build()
    // 异常处理中间件(lambda表达式)
    host.use((context, next) => {
        // 这里可以加异常处理逻辑
        context.response.write("error")
        next()
    })
    // 日志中间件(lambda表达式)
    host.use((context, next) => {
        // 这里可以加日志逻辑
        context.response.write("log")
        next()
    })
    // 路由中间件
    host.useRouting()
    host.useEndpoints { endpoints =>
        endpoints.mapGet("test") { context =>
            context.response.write("middleware test")
        }
    }
    host.run()
}

最佳实践

  • 路由设计应遵循 RESTful 原则,路径简洁、语义清晰。
  • 参数化路由建议命名规范,避免歧义,业务逻辑中要做好参数校验。
  • 路由注册顺序很重要,建议先注册认证、授权等安全相关中间件,再注册路由。
  • 控制器路由适用于复杂业务,便于分层管理和单元测试。
  • 动态路由和静态路由应合理区分,避免路径冲突。
  • 对于高频访问的路由,可结合缓存、限流等中间件提升性能和安全性。
  • 路由表建议集中管理,便于维护和扩展。
  • 生产环境建议关闭未使用的调试或测试路由,减少攻击面。