Skip to content

请求与参数绑定

SmartWeb 使用 WebMethodInvoker 调用 Action、Before、After 与 Catch。它在 Controller 加载阶段分析每个方法参数,为参数保存一个 WebActionContext.() -> Any? getter;请求运行时直接调用 getter,不再重新遍历注解。

可直接注入的上下文类型

方法参数类型命中以下类型时无需注解:

kotlin
fun inspect(context: WebActionContext, request: Request, response: Response, session: Session) = Unit
类型注入值
ActionContext / WebActionContext当前 Action 上下文
Request服务器适配后的请求
Response可写响应对象
Session当前请求 Session
Cookie与参数同名的 Cookie 对象
UploadFile与参数同名的单个文件
List<UploadFile>与参数同名的全部文件
ExceptionCatch 阶段的 runtimeError
IUser 子类型WebUserProvider 解析出的当前用户

参数名参与映射。Kotlin 通常可通过反射元数据取得;Java 应开启 -parameters

路径变量

kotlin
@GetAction("users/{userId}/orders/{orderId}")
fun order(userId: Long, orderId: Long) = Unit

参数名命中路径变量时自动读取,也可以显式标记 @PathVar。底层支持 {name:regex}

kotlin
@GetAction("users/{id:\\d+}")
fun user(@PathVar id: Long) = Unit

静态路由优先于动态路由。动态 Matcher 把捕获值写入 Context saves,Invoker 再转换为目标简单类型。

查询与表单参数

简单类型默认按请求参数读取:

kotlin
@GetAction("search")
fun search(keyword: String?, page: Int = 1) = Unit

@RequestParam 可强制参数来源。数组和 List 从同名参数数组读取:

kotlin
fun batch(ids: LongArray, tags: List<String>) = Unit

readParam 先查看 Context saves,再读取合并后的 params JSON。前置过程写入的同名上下文值可能覆盖请求值,应避免命名碰撞。

JSON 请求体

kotlin
data class CreateUserRequest(val name: String, val age: Int)

@PostAction
fun create(@RequestBody request: CreateUserRequest) = Unit

List 或数组:

kotlin
fun createMany(@RequestBody requests: List<CreateUserRequest>) = Unit

若尚未确定来源的参数只有一个、它不是简单类型且没有 @RequestParam,SmartWeb 自动视为 Body。多个复杂参数时会根据字段名是否出现在 params 中推断。公开 API 推荐显式标记,避免歧义。

String Body 会先尝试同名请求参数,找不到时再读 Body。

SessionValue

kotlin
fun profile(@SessionValue userId: Long?) = Unit

键名就是参数名。ReferenceValue<T> 提供读写引用,setter 写回 Session.saves

kotlin
fun switchTenant(@SessionValue tenantId: ReferenceValue<Long>) {
    tenantId.value = 42
}

CookieValue

kotlin
fun locale(@CookieValue locale: String?) = Unit

简单类型从 Cookie 字符串转换。ReferenceValue<T> 赋值时通过 Response 添加同名 Cookie:

kotlin
fun updateTheme(@CookieValue theme: ReferenceValue<String>) {
    theme.value = "dark"
}

写回 null 当前会报错;删除 Cookie 应直接设置过期 Cookie。

ContextValue

Before 返回对象后,ActionInvoker 按类型简单名首字母小写保存:

kotlin
@Before
fun buildTenant(): TenantContext = TenantContext(...)

@GetAction
fun list(@ContextValue tenantContext: TenantContext) = Unit

Controller 可声明默认 Context 参数名:

kotlin
@ContextValues("tenantContext", "requestTrace")
@WebController
class TenantController

全局配置 smart.web.controller.contextValueKeys 也会加入所有 Controller。使用 ReferenceValue<T> 可读写 Context。

当前用户

实现 AutoBind 接口 WebUserProvider

kotlin
class TokenUserProvider(private val tokens: TokenService) : WebUserProvider {
    override fun invoke(context: WebActionContext): IUser? =
        context.req.header("Authorization")?.value?.let(tokens::resolve)
}

Action 参数实现 IUser 时自动读取:

kotlin
fun me(user: AppUser?) = user

可空用户不等于已认证。权限边界应由 Before/ProcessProvider 明确检查。

文件上传

kotlin
@PostAction("avatar")
fun upload(file: UploadFile) = storage.save(file.name, file.inputStream)

@PostAction("attachments")
fun uploadMany(files: List<UploadFile>) = files.map(storage::save)

参数名必须和 multipart field name 一致。单文件参数实际收到多个文件时会抛错。上传流应在 Action 内消费或转存,不要长期保存。

可空与默认参数

底层调用器识别 Kotlin nullable 和 optional:读取不到值时,可空参数接收 null,有默认值参数可省略;非空无默认值参数导致调用/转换错误。

SmartWeb 当前没有统一 Bean Validation。建议用 Before 或业务 ProcessProvider 校验,再用 Catch/Render 转换为 400。

与 Spring MVC 对照

Spring MVC 每种来源通常对应 HandlerMethodArgumentResolver;SmartWeb 在一个 initParam 中按优先级确定 getter。实现更短,但没有独立参数解析 SPI。

新增业务来源时,优先在 Before 中读取 Request 并返回上下文对象,Action 再用 @ContextValue 注入,这更符合 SmartWeb 的过程模型。

基于 Apache License 2.0 发布