内容协商
内容协商(Content Negotiation)是 HTTP 的核心机制——客户端通过 Accept 请求头声明它能处理的媒体类型,服务端选择最合适的编码格式返回。ACE 提供了一套轻量的编解码注册表,零反射、编译期静态分派。
响应编码器
默认行为
框架内置 application/json 编码器,所有 @Get/@Post 等路由的返回值默认序列化为 JSON。
cangjie
@Get["/users/{id}"]
func getUser(@PathParam id: String): User {
return userService.findById(id)
// 默认返回 Content-Type: application/json
}注册自定义编码器
使用 registerBodyEncoder 向全局注册表添加新的媒体类型编码器:
cangjie
import ace_framework_runtime.*
// 注册 CSV 编码器
registerBodyEncoder("text/csv") { value: Any ->
match (value) {
case rows: Array<Array<String>> =>
rows.map { row -> row.joinToString(",") }.joinToString("\n")
case _ => value.toString()
}
}cangjie
// 注册 MessagePack 编码器(需自行引入 msgpack 库)
registerBodyEncoder("application/msgpack") { value: Any ->
MsgPack.encode(value)
}注册完成后,当客户端请求头包含对应类型时自动生效:
bash
curl -H "Accept: text/csv" http://localhost:8080/api/export/users手动调用编码器
在中间件或特殊场景中可直接调用 encodeBody:
cangjie
import ace_framework_runtime.*
let mw: Middleware = { ctx, next ->
await next()
let accept = ctx.header("Accept") ?? "application/json"
let encoded = encodeBody(accept, ctx.state["result"])
ctx.body(encoded)
}API 参考
| 函数 | 签名 | 说明 |
|---|---|---|
registerBodyEncoder | (contentType: String, encoder: (Any) -> String) -> Unit | 注册响应编码器 |
encodeBody | (acceptHeader: String, value: Any) -> String | 按 Accept 头选择编码器并序列化 |
Accept 头匹配规则
encodeBody 按 q 权重从高到低遍历 Accept 头的媒体类型列表,找到第一个已注册的编码器即返回。若全部未命中,退回 application/json。
请求参数类型转换器
路由参数(@PathParam/@Query)原始值均为字符串,框架内置 Int64、Float64、Bool 等基本类型的转换器。复杂类型需手动注册。
注册自定义转换器
cangjie
import ace_framework_runtime.*
// 注册 UUID 类型转换器
registerConverter("UUID") { raw: String ->
UUID.fromString(raw) // 格式不合法时抛出 BadRequestException
}
// 注册枚举转换器
registerConverter("UserStatus") { raw: String ->
match (raw.toUpperCase()) {
case "ACTIVE" => UserStatus.Active
case "INACTIVE" => UserStatus.Inactive
case _ => throw BadRequestException("无效的用户状态: ${raw}")
}
}注册后,@PathParam 与 @Query 绑定时自动调用:
cangjie
@Get["/users"]
func listUsers(@Query["status"] status: UserStatus): Array<User> {
return userService.findByStatus(status)
}手动调用转换器
cangjie
let raw = ctx.query("limit") ?? "20"
let limit = convertParam("Int64", raw) as Int64API 参考
| 函数 | 签名 | 说明 |
|---|---|---|
registerConverter | (typeName: String, converter: (String) -> Any) -> Unit | 注册类型转换器 |
convertParam | (typeName: String, raw: String) -> Any | 将字符串转换为目标类型 |
类型名称约定
typeName 需与仓颉类型名称精确一致(大小写敏感)。建议在应用启动时集中注册,避免在请求路径中调用 registerConverter(非线程安全)。
转换失败处理
转换器内部应在格式非法时抛出 BadRequestException,框架会将其映射为 HTTP 400 响应,并在 body 中附上错误信息。切勿静默吞掉异常后返回零值,这会导致业务逻辑静默错误。