为所有ZIO-HTTP声明式Endpoint统一添加/api前缀时遇到路径解码错误的问题
我完全懂你碰到的这个坑——想用统一方式给所有声明式Endpoint加上/api前缀,不用一个个改路径,但用literal("api")把整个路由包起来后,反而触发了路径解码错误,这确实挺闹心的。
先给你拆解下问题原因:
ZIO-HTTP的声明式Endpoint是和你定义的路径强绑定的。你写GET / "activities"的时候,这个Endpoint已经把它的匹配路径固定成了/activities。当你用literal("api") / routes给整个路由集合加前缀,请求路径变成了/api/activities,但Endpoint依然在找/activities——它解析路径时,先匹配了api这个段,剩下的路径就空了,自然会报错说“Expected path segment 'activities' but found end of path”。而你那个sanity测试能正常工作,是因为它是普通路由,没有Endpoint那种强绑定的路径解码逻辑,只是简单的字符串匹配,所以加前缀没问题。
接下来给你几个靠谱的解决方案,按推荐程度排序:
1. 用EndpointGroup统一管理前缀(最推荐)
ZIO-HTTP专门给声明式Endpoint提供了分组功能,可以直接给整个组加前缀,所有组内的Endpoint都会自动带上这个前缀,完全不用逐个修改:
import zio.http._ import zio.http.endpoint._ // 创建带/api前缀的Endpoint组 val apiGroup = EndpointGroup(prefix = Path("api")) // 在组内定义Endpoint,路径自动包含/api前缀 val getActivities = apiGroup.endpoint(GET / "activities") .out[CollectionResponse[ActivitySummary]] .outError[ErrorResponse](Status.InternalServerError) .examplesOut(("Response", exampleActivitySummaryResponse())) // 组合路由,直接用就行,前缀已经自动加上了 val routes = Routes( getActivities.implement(_ => handleGetAll()) )
这种方式最符合ZIO-HTTP声明式Endpoint的设计理念,后续要改前缀也只需要改apiGroup的定义就行,非常灵活。
2. 给每个Endpoint的实现Route单独加前缀
如果不想重构现有的Endpoint定义,你可以先把每个Endpoint转换成Route,再给单个Route加前缀,最后组合成Routes:
// 先实现Endpoint得到Route val getActivitiesRoute = getActivities.implement(_ => handleGetAll()) // 给单个Route加前缀 val apiGetActivitiesRoute = literal("api") / getActivitiesRoute // 组合所有带前缀的Route val routes = Routes(apiGetActivitiesRoute)
如果有多个Endpoint,你可以把所有实现后的Route收集到一个列表里,统一处理前缀:
val allEndpointsRoutes = List( getActivities.implement(_ => handleGetAll()), // 其他Endpoint的实现Route ) // 给所有Route统一加前缀后组合 val apiRoutes = Routes(allEndpointsRoutes.map(literal("api") / _): _*)
3. 用统一常量定义前缀(最直接但繁琐)
如果你不想用上面两种方式,也可以定义一个前缀常量,手动给每个Endpoint的路径加上:
val API_PREFIX = "api" val getActivities = Endpoint((GET / API_PREFIX / "activities")) .out[CollectionResponse[ActivitySummary]] .outError[ErrorResponse](Status.InternalServerError) .examplesOut(("Response", exampleActivitySummaryResponse()))
这种方式虽然直接,但需要修改每个Endpoint的路径定义,后续改前缀也得逐个改,适合Endpoint数量不多的场景。
最后再验证下:用前两种方式,请求/api/activities时,Endpoint会正确识别带前缀的路径,不会再出现解码错误,同时还能保留声明式Endpoint的所有特性(比如自动生成文档)。
备注:内容来源于stack exchange,提问作者dbrown428

