请求与参数绑定
SmartWeb 使用 WebMethodInvoker 调用 Action、Before、After 与 Catch。它在 Controller 加载阶段分析每个方法参数,为参数保存一个 WebActionContext.() -> Any? getter;请求运行时直接调用 getter,不再重新遍历注解。
可直接注入的上下文类型
方法参数类型命中以下类型时无需注解:
fun inspect(context: WebActionContext, request: Request, response: Response, session: Session) = Unit| 类型 | 注入值 |
|---|---|
ActionContext / WebActionContext | 当前 Action 上下文 |
Request | 服务器适配后的请求 |
Response | 可写响应对象 |
Session | 当前请求 Session |
Cookie | 与参数同名的 Cookie 对象 |
UploadFile | 与参数同名的单个文件 |
List<UploadFile> | 与参数同名的全部文件 |
Exception | Catch 阶段的 runtimeError |
IUser 子类型 | WebUserProvider 解析出的当前用户 |
参数名参与映射。Kotlin 通常可通过反射元数据取得;Java 应开启 -parameters。
路径变量
@GetAction("users/{userId}/orders/{orderId}")
fun order(userId: Long, orderId: Long) = Unit参数名命中路径变量时自动读取,也可以显式标记 @PathVar。底层支持 {name:regex}:
@GetAction("users/{id:\\d+}")
fun user(@PathVar id: Long) = Unit静态路由优先于动态路由。动态 Matcher 把捕获值写入 Context saves,Invoker 再转换为目标简单类型。
查询与表单参数
简单类型默认按请求参数读取:
@GetAction("search")
fun search(keyword: String?, page: Int = 1) = Unit@RequestParam 可强制参数来源。数组和 List 从同名参数数组读取:
fun batch(ids: LongArray, tags: List<String>) = UnitreadParam 先查看 Context saves,再读取合并后的 params JSON。前置过程写入的同名上下文值可能覆盖请求值,应避免命名碰撞。
JSON 请求体
data class CreateUserRequest(val name: String, val age: Int)
@PostAction
fun create(@RequestBody request: CreateUserRequest) = UnitList 或数组:
fun createMany(@RequestBody requests: List<CreateUserRequest>) = Unit若尚未确定来源的参数只有一个、它不是简单类型且没有 @RequestParam,SmartWeb 自动视为 Body。多个复杂参数时会根据字段名是否出现在 params 中推断。公开 API 推荐显式标记,避免歧义。
String Body 会先尝试同名请求参数,找不到时再读 Body。
SessionValue
fun profile(@SessionValue userId: Long?) = Unit键名就是参数名。ReferenceValue<T> 提供读写引用,setter 写回 Session.saves:
fun switchTenant(@SessionValue tenantId: ReferenceValue<Long>) {
tenantId.value = 42
}CookieValue
fun locale(@CookieValue locale: String?) = Unit简单类型从 Cookie 字符串转换。ReferenceValue<T> 赋值时通过 Response 添加同名 Cookie:
fun updateTheme(@CookieValue theme: ReferenceValue<String>) {
theme.value = "dark"
}写回 null 当前会报错;删除 Cookie 应直接设置过期 Cookie。
ContextValue
Before 返回对象后,ActionInvoker 按类型简单名首字母小写保存:
@Before
fun buildTenant(): TenantContext = TenantContext(...)
@GetAction
fun list(@ContextValue tenantContext: TenantContext) = UnitController 可声明默认 Context 参数名:
@ContextValues("tenantContext", "requestTrace")
@WebController
class TenantController全局配置 smart.web.controller.contextValueKeys 也会加入所有 Controller。使用 ReferenceValue<T> 可读写 Context。
当前用户
实现 AutoBind 接口 WebUserProvider:
class TokenUserProvider(private val tokens: TokenService) : WebUserProvider {
override fun invoke(context: WebActionContext): IUser? =
context.req.header("Authorization")?.value?.let(tokens::resolve)
}Action 参数实现 IUser 时自动读取:
fun me(user: AppUser?) = user可空用户不等于已认证。权限边界应由 Before/ProcessProvider 明确检查。
文件上传
@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 的过程模型。