初始化与启动

Vostok.Web.init() 是静态方法,返回 VostokWeb 实例,路由、中间件等注册方法均为链式实例方法。start() / stop() 是静态方法。

// 方式一:独立初始化(推荐)
Vostok.Web.init(new VKWebConfig()
    .port(8080)
    .ioThreads(2)
    .workerThreads(16)
)
.get("/hello", (req, res) -> res.text("Hello!"))
.post("/users", (req, res) -> {
    String body = req.bodyText();
    res.status(201).json("{\"created\":true}");
})
.gzip()
.cors();

Vostok.Web.start();

// 方式二:通过 Vostok.init() 统一初始化
Vostok.init(cfg -> cfg
    .webConfig(new VKWebConfig().port(8080))
    .webSetup(web -> web
        .get("/hello", (req, res) -> res.text("Hello!"))
        .gzip()
        .cors()
    )
    .webStart(true)
);

自定义 Server Engine

Vostok 核心当前默认内置的是 BUILTIN NIO 引擎,但 Web 语义层已经抽成可复用的 SPI。业务项目如果需要接入 Netty、Undertow 或其他实现,可以通过 serverFactory(...) 注入自定义引擎,而不需要改动 Vostok.Web 的公共 API。

import yueyang.vostok.web.spi.*;

class MyServerEngine implements VKWebServerEngine {
    private final VKWebRuntimeSupport runtime;
    private boolean started;

    MyServerEngine(VKWebRuntimeSupport runtime) {
        this.runtime = runtime;
    }

    @Override
    public void start() {
        // 1. 业务项目自己负责 socket accept / event loop / HTTP 编解码 / TLS
        // 2. 普通 HTTP 请求复用 Vostok 语义:
        //    VKWebDispatchResult result = runtime.dispatchHttp(req);
        // 3. WebSocket 元数据复用:
        //    runtime.findWebSocket(path) / runtime.wsRegistry()
        started = true;
    }

    @Override
    public void stop() { started = false; }

    @Override
    public boolean isStarted() { return started; }

    @Override
    public int port() { return 8080; }
}

Vostok.Web.init(new VKWebConfig()
    .port(8080)
    .serverFactory((cfg, runtime) -> new MyServerEngine(runtime))
)
.get("/ping", (req, res) -> res.text("pong"));
自定义引擎的职责边界
业务自定义引擎需要自己负责:socket accept / event loop / HTTP 编解码 / TLS / WebSocket / SSE / 文件传输与背压
可以直接复用 Vostok 的:routermiddlewarerateLimit@VKApi MVCVKWebResultmetricsVKWsRegistry

路由

HTTP 方法路由

VostokWeb web = Vostok.Web.init(new VKWebConfig().port(8080));

// 快捷方法:GET / POST
web.get("/items", handler);
web.post("/items", handler);

// 通用方法:任意 HTTP 方法
web.route("PUT", "/items/{id}", handler);
web.route("DELETE", "/items/{id}", handler);
web.route("PATCH", "/items/{id}", handler);

注解控制器(@VKApi)

除了函数式 VKHandler,也可以用注解方式定义 API。类上使用 @VKApi,方法上使用 @VKGet/@VKPost/@VKPut/@VKDelete/@VKPatch

import yueyang.vostok.Vostok;
import yueyang.vostok.web.http.VKResponse;
import yueyang.vostok.web.mvc.VKMvcConfig;
import yueyang.vostok.web.mvc.VKWebResult;
import yueyang.vostok.web.mvc.annotation.*;

class CreateUserReq {
    public String name;
}

@VKApi("/api/users")
class UserApi {
    @VKGet("/detail/{id}")
    public java.util.Map<String, Object> detail(
            @VKPath("id") long id,
            @VKQuery(value = "page", required = false, defaultValue = "1") int page) {
        return java.util.Map.of("id", id, "page", page);
    }

    @VKPost("/create")
    public VKWebResult<String> create(@VKBody CreateUserReq req) {
        return VKWebResult.of(201, null, "created-" + req.name);
    }

    @VKGet("/manual")
    public void manual(VKResponse res) {
        // void 返回代表手动响应模式,不自动包装 VKWebResult
        res.status(202).text("manual");
    }
}

Vostok.Web.init(8080)
    .mvcConfig(VKMvcConfig.defaults().exposeExceptionMessage(false))
    .controller(new UserApi())
    .controllers("com.example.api");
路径拼接规则
@VKApi("/api/users") + @VKGet("/list") => /api/users/list
@VKApi 不写前缀时,方法注解路径应写完整路径(如 /api/users/list)。

参数绑定注解

注解来源示例
@VKPath("id")路径参数/users/{id}
@VKQuery("page")Query 参数(支持 defaultValue)?page=1
@VKHeader("X-Token")请求头X-Token: abc
@VKCookie("sid")CookieCookie: sid=s-001
@VKBodyJSON 请求体绑定到 POJO
@VKForm("name")表单字段multipart/form-data 或 form
@VKFile("file")单文件上传VKUploadedFile
@VKFiles("files")多文件上传List<VKUploadedFile>
VKRequest / VKResponse原生对象注入参数可直接声明类型,无需注解

自动返回与 VKWebResult<T>

@VKApi 路由默认启用自动返回包装。普通返回值会自动包装成 VKWebResult.ok(data),并补齐链路字段。

字段类型说明
statusCodeint业务状态码,同时作为 HTTP 状态码输出
errorMessageString错误信息;成功时一般为 null
dataT业务返回数据
requestCostMslong请求耗时(毫秒)
traceIdString链路追踪 ID(来自请求)
requestTimelong请求进入处理器时间(epochMillis)
responseTimelong响应写出前时间(epochMillis)
// 返回普通对象:自动包装为 VKWebResult.ok(...)
{
  "statusCode": 200,
  "errorMessage": null,
  "data": {"id": 9, "page": 1},
  "requestCostMs": 3,
  "traceId": "trace-abc",
  "requestTime": 1741075200000,
  "responseTime": 1741075200003
}

路径参数 & 查询参数

web.get("/users/{id}", (req, res) -> {
    String id = req.param("id");             // 路径参数
    res.json("{\"id\":" + id + "}");
});

web.get("/search", (req, res) -> {
    String q    = req.queryParam("q");          // 查询参数(String)
    String page = req.queryParam("page");       // 需自行转型
    int pageNum = page != null ? Integer.parseInt(page) : 1;
    res.text("q=" + q + " page=" + pageNum);
});

// 通配路径:匹配 /files/a/b/c
web.get("/files/{*path}", (req, res) -> {
    String path = req.param("path");
    res.text("path=" + path);
});

VKHandler 接口

// 函数式接口,可用 lambda 实现
@FunctionalInterface
public interface VKHandler {
    void handle(VKRequest req, VKResponse res);
}

VKRequest — 请求对象

方法返回值说明
method()StringHTTP 方法,如 "GET"、"POST"
path()String请求路径,如 "/users/1"
query()String原始查询字符串,如 "page=1&size=10"
version()StringHTTP 版本,如 "HTTP/1.1"
header(name)String请求头(大小写不敏感)
headers()Map<String,String>所有请求头(只读)
body()byte[]原始请求体字节
bodyText()String请求体 UTF-8 字符串
param(name)String路径参数,如 {id} 对应 param("id")
queryParam(name)String查询参数(URL 解码后)
queryParams()Map<String,String>所有查询参数
cookie(name)StringCookie 值
cookies()Map<String,String>所有 Cookie
traceId()String请求追踪 ID
remoteAddress()InetSocketAddress客户端地址(含 IP 和端口)
keepAlive()boolean是否为 Keep-Alive 连接
isMultipart()boolean是否为 multipart/form-data 请求
formField(name)String表单字段值
formFields()Map<String,String>所有表单字段
file(name)VKUploadedFile获取第一个上传文件
files(name)List<VKUploadedFile>获取同名所有上传文件
allFiles()Collection<VKUploadedFile>所有上传文件

VKResponse — 响应对象

方法返回值说明
status(int)VKResponse设置 HTTP 状态码(链式)
status()int获取当前状态码(默认 200)
header(name, value)VKResponse设置响应头(链式)
headers()Map<String,String>获取所有响应头
text(String)VKResponse设置纯文本响应体(Content-Type: text/plain; charset=utf-8)
json(String)VKResponse设置 JSON 字符串响应体(Content-Type: application/json; charset=utf-8)
body(byte[])VKResponse设置原始字节响应体
body()byte[]获取响应体字节
cookie(name, value)VKResponse设置简单 Cookie(链式)
cookie(VKCookie)VKResponse设置带完整属性的 Cookie(链式)
deleteCookie(name)VKResponse删除 Cookie(设置 Max-Age=0,链式)
file(Path, long)VKResponse以文件方式响应(零拷贝传输)
sseResponse(Consumer<VKSseEmitter>)VKResponse切换为 SSE 模式(框架内部使用)

常用响应示例

// 200 纯文本
res.text("Hello World");

// 200 JSON 字符串
res.json("{\"code\":0,\"msg\":\"ok\"}");

// 自定义状态码 + JSON
res.status(201).json("{\"created\":true}");

// 返回二进制内容(图片等)
res.status(200)
   .header("Content-Type", "image/png")
   .body(imageBytes);

// 404
res.status(404).text("Not Found");

// 302 重定向
res.status(302).header("Location", "/login").text("");

Cookie 操作

// 简单 Cookie
res.cookie("token", "abc123");

// 带完整属性的 Cookie
res.cookie(new VKCookie("session", "xyz")
    .httpOnly(true)
    .secure(true)
    .maxAge(3600)
    .path("/")
    .sameSite(VKCookie.SameSite.LAX)
);

// 删除 Cookie
res.deleteCookie("session");

文件上传

web.post("/upload", (req, res) -> {
    if (!req.isMultipart()) {
        res.status(400).text("Not a multipart request");
        return;
    }

    // 获取单个文件
    VKUploadedFile file = req.file("avatar");
    if (file != null) {
        System.out.println("文件名: " + file.fileName());
        System.out.println("类型: "  + file.contentType());
        System.out.println("大小: "  + file.size());
        System.out.println("内存: "  + file.inMemory());

        // 读取内容
        byte[] bytes = file.bytes();

        // 保存到磁盘
        file.transferTo(Path.of("/uploads/" + file.fileName()));
    }

    // 同名多文件
    List<VKUploadedFile> images = req.files("images");

    // 表单字段
    String desc = req.formField("description");

    res.json("{\"uploaded\":" + (file != null) + "}");
});

中间件

// 全局中间件(按注册顺序执行)
web.use((req, res, chain) -> {
    String token = req.header("Authorization");
    if (token == null) {
        res.status(401).json("{\"error\":\"unauthorized\"}");
        return;   // 不调用 chain.next() 即拦截请求
    }
    chain.next(req, res);  // 放行到下一个中间件或处理器
});

// 请求日志中间件示例
web.use((req, res, chain) -> {
    long start = System.currentTimeMillis();
    chain.next(req, res);
    System.out.printf("%s %s → %d  (%dms)%n",
        req.method(), req.path(), res.status(),
        System.currentTimeMillis() - start);
});
说明
VKMiddleware 为函数式接口:void handle(VKRequest req, VKResponse res, VKChain chain)
调用 chain.next(req, res) 将请求传递给下一个处理器;不调用则直接截断请求。

CORS

// 快捷开启(允许所有来源,默认方法:GET/POST/PUT/DELETE/OPTIONS/PATCH)
web.cors();

// 自定义 CORS 配置
web.cors(new VKCorsConfig()
    .allowOrigins("https://example.com", "https://app.example.com")
    .allowMethods("GET, POST, PUT, DELETE")
    .allowHeaders("Content-Type, Authorization")
    .allowCredentials(true)
    .maxAge(3600)               // Preflight 缓存秒数
    .exposeHeaders("X-Request-Id")
);

GZIP 压缩

// 默认配置(响应 >= 256 字节,且 Content-Type 为 text/* / application/json / application/xml 时压缩)
web.gzip();

// 自定义配置
web.gzip(new VKGzipConfig()
    .minBytes(512)               // 触发压缩的最小字节数
    .compressibleTypes(List.of("text/", "application/json"))
);

限流(令牌桶)

// 全局限流:每秒最多 1000 个请求(按客户端 IP 区分)
web.rateLimit(new VKRateLimitConfig()
    .capacity(1000)              // 令牌桶容量
    .refillTokens(1000)          // 每次补充令牌数
    .refillPeriodMs(1000)        // 补充周期(ms)
    .keyStrategy(VKRateLimitKeyStrategy.IP)  // 限流 key 策略
    .rejectStatus(429)
    .rejectBody("Too Many Requests")
);

// 针对指定路由限流
web.rateLimit(VKHttpMethod.POST, "/login",
    new VKRateLimitConfig()
        .capacity(10)
        .refillTokens(10)
        .refillPeriodMs(60000)   // 每分钟 10 次
        .rejectStatus(429)
);

// 按自定义 key 限流(如用户 ID)
web.rateLimit(new VKRateLimitConfig()
    .capacity(100)
    .refillTokens(100)
    .refillPeriodMs(1000)
    .keyStrategy(VKRateLimitKeyStrategy.CUSTOM)
    .customKeyResolver(req -> req.header("X-User-Id"))
);

VKRateLimitKeyStrategy 枚举

枚举值说明
IP按客户端 IP 限流(默认)
TRACE_ID按请求追踪 ID 限流
HEADER按指定请求头值限流,配合 headerName() 使用
CUSTOM自定义 key,配合 customKeyResolver() 使用

静态资源

// 将 URL /static/** 映射到本地 ./public 目录
web.staticDir("/static", "./public");

// 示例:访问 /static/js/app.js → 读取 ./public/js/app.js

自动 CRUD API

扫描 @VKEntity 实体类,自动为每个实体生成标准 REST 接口(依赖 Data 模块已初始化)。

// 使用 Data 模块扫描的默认包(无需再传包名)
web.autoCrudApi();

// 指定包 + RESTful 风格(默认)
web.autoCrudApi(VKCrudStyle.RESTFUL, "com.example.entity");

// 传统风格(/entity/list、/entity/add 等)
web.autoCrudApi(VKCrudStyle.TRADITIONAL, "com.example.entity");

User 实体(表名 users)为例,RESTFUL 模式自动生成:

方法路径说明
GET/users查询全部
GET/users/{id}按主键查询
POST/users新增
PUT/users/{id}按主键更新
DELETE/users/{id}按主键删除

WebSocket

基本用法

web.websocket("/ws/chat", new VKWebSocketHandler() {
    @Override
    public void onOpen(VKWebSocketSession session) {
        System.out.println("connected: " + session.id());
        session.sendText("欢迎加入!");
    }

    @Override
    public void onText(VKWebSocketSession session, String text) {
        // 向当前路径所有连接广播
        Vostok.Web.websocketBroadcast("/ws/chat", text);
    }

    @Override
    public void onBinary(VKWebSocketSession session, byte[] data) {
        session.sendBinary(data);
    }

    @Override
    public void onClose(VKWebSocketSession session, int code, String reason) {
        System.out.println("closed: " + session.id() + " code=" + code);
    }

    @Override
    public void onError(VKWebSocketSession session, Throwable error) {
        System.err.println("error: " + error.getMessage());
    }
});

房间与分组广播

// 连接加入房间 / 分组(在 onOpen 或消息处理中调用)
session.joinRoom("room-1");
session.joinGroup("vip");

// 会话自行广播(返回发送成功的连接数)
session.broadcastRoom("room-1", "hello room");
session.broadcastGroup("vip", "vip msg");
session.broadcastRoomAndGroup("room-1", "vip", "msg");

// 全局静态广播(可在任意线程调用)
Vostok.Web.websocketBroadcast("/ws/chat", "全员通知");
Vostok.Web.websocketBroadcastRoom("/ws/chat", "room-1", "房间通知");
Vostok.Web.websocketBroadcastGroup("/ws/chat", "vip", "VIP 通知");
Vostok.Web.websocketBroadcastRoomAndGroup("/ws/chat", "room-1", "vip", "msg");

// 二进制广播
Vostok.Web.websocketBroadcastBinary("/ws/chat", data);
Vostok.Web.websocketBroadcastRoomBinary("/ws/chat", "room-1", data);
Vostok.Web.websocketBroadcastGroupBinary("/ws/chat", "vip", data);

WebSocket 握手鉴权

web.websocket("/ws/secure", new VKWebSocketConfig()
    .handshakeAuthenticator(ctx -> {
        String token = ctx.queryParam("token");
        if (token == null || !isValid(token)) {
            return VKWsAuthResult.reject(401, "Unauthorized");
        }
        // 通过后,attributes 会写入 session,可用 session.getAttribute() 获取
        return VKWsAuthResult.allow(Map.of("userId", parseUserId(token)));
    })
    .pingIntervalMs(30000)
    .idleTimeoutMs(120000),
    handler
);

VKWebSocketSession 方法

方法说明
id()会话唯一 ID
path()WebSocket 端点路径
traceId()请求追踪 ID
remoteAddress()客户端地址(InetSocketAddress)
isOpen()连接是否仍开放
sendText(String)向此会话发送文本帧
sendBinary(byte[])向此会话发送二进制帧
ping(byte[])发送 Ping 帧
close()正常关闭(code=1000)
close(int code, String reason)以指定 code 关闭连接
joinRoom(String)加入指定房间,返回是否成功
leaveRoom(String)离开指定房间
joinGroup(String)加入指定分组
leaveGroup(String)离开指定分组
broadcastRoom(room, msg)广播文本到指定房间,返回发送数
broadcastGroup(group, msg)广播文本到指定分组,返回发送数
broadcastRoomAndGroup(room, group, msg)广播到房间且属于分组的连接
setAttribute(key, value)设置会话属性(链式)
getAttribute(key)获取会话属性
getAttribute(key, Class<T>)获取会话属性并转型
removeAttribute(key)移除会话属性
hasAttribute(key)判断属性是否存在
attributes()获取所有属性(只读 Map)

Server-Sent Events (SSE)

// 注册 SSE 端点:handler 在建立连接时调用,emitter 可保存到集合用于后续推送
web.sse("/events", (req, emitter) -> {
    // 发送仅含 data 的事件
    emitter.send("connected");

    // 发送带 event 类型的事件
    emitter.send("update", "{\"count\":1}");

    // 发送完整事件(event + data + id)
    emitter.send("update", "{\"count\":2}", "evt-001");

    // 检查连接状态
    if (emitter.isOpen()) {
        emitter.send("alive", "ping");
    }

    // 主动关闭(幂等,多次调用安全)
    emitter.close();
});
SSE 事件格式说明
框架自动处理 SSE 协议格式,调用 emitter.send(data) 时不需要手动拼 "data: ...\n\n",框架会按 RFC 8895 格式化。多行 data 会自动按行分割。

HTTPS / TLS

Vostok.Web.init(new VKWebConfig()
    .port(8443)
    .tls(new VKTlsConfig()
        .keyStorePath("/path/to/server.p12")       // KeyStore 文件路径(必填)
        .keyStorePassword("changeit")              // KeyStore 密码(必填)
        .keyStoreType("PKCS12")                    // 默认 PKCS12,也支持 JKS
        .sslProtocol("TLS")                        // 默认 TLS
        .enabledProtocols("TLSv1.2", "TLSv1.3")   // 指定启用的协议版本
        .clientAuth(false)                          // 是否要求客户端证书
    )
);

健康检查 & Metrics

// 内置健康检查端点:GET /actuator/health
// 返回:{"status":"UP","connections":42}
web.health();

// 自定义路径
web.health("/_ping");

// 内置 Metrics 端点:GET /actuator/metrics
// 返回:{"requests":1000,"errors":2,"activeConnections":42,"avgResponseMs":5.2}
web.metrics();

// 自定义路径
web.metrics("/admin/metrics");

错误处理

// VKErrorHandler 签名:handle(Throwable error, VKRequest req, VKResponse res)
web.error((error, req, res) -> {
    System.err.println("Error on " + req.path() + ": " + error.getMessage());
    res.status(500).json(
        "{\"error\":\"" + error.getMessage() + "\"}"
    );
});
@VKApi 路由异常处理说明
@VKApi 路由内部会把绑定错误统一包装为 400、执行异常统一包装为 500 的 VKWebResult
传统 get/post/route 注册的函数式路由仍按原逻辑走全局 web.error(...)

生命周期

// 启动服务器(阻塞直到服务器绑定端口完成)
Vostok.Web.start();

// 检查是否已启动
boolean running = Vostok.Web.started();

// 获取实际监听端口(配置 port=0 时由 OS 分配)
int port = Vostok.Web.port();

// 停止服务器
Vostok.Web.stop();

配置参数

参数类型默认值说明
基础
portint8080监听端口(0 表示随机分配)
ioThreadsint1NIO Selector 线程数
workerThreadsintCPU核数×2(最小2)业务处理线程池大小
workerQueueSizeint10000Worker 任务队列容量
backlogint1024TCP 连接队列长度
maxConnectionsint10000最大并发连接数
serverFactory(VKWebServerFactory)VKWebServerFactorynull自定义 Server Engine 工厂;为空时使用内建 NIO 引擎
HTTP
readBufferSizeint16384读缓冲区大小(字节)
maxHeaderBytesint32768请求头最大字节数
maxBodyBytesint4194304 (4MB)请求体最大字节数
keepAliveTimeoutMsint30000Keep-Alive 连接超时(ms)
readTimeoutMsint15000读取超时(ms)
accessLogEnabledbooleantrue是否开启访问日志
accessLogQueueSizeint8192访问日志异步队列大小
文件上传(Multipart)
multipartEnabledbooleantrue是否允许 multipart 文件上传
multipartTempDirString系统临时目录/vostok-upload超内存阈值的文件临时目录
multipartInMemoryThresholdBytesint65536 (64KB)小于此值的文件保留在内存
multipartMaxPartsint128单请求最大 multipart 部分数
multipartMaxFileSizeByteslong16777216 (16MB)单个文件最大大小
multipartMaxTotalByteslong33554432 (32MB)所有文件总大小上限
限流
rateLimitLogEnabledbooleantrue是否记录限流日志
rateLimitCleanupIntervalMsint60000限流 key 定期清理间隔(ms)
WebSocket
websocketEnabledbooleantrue是否允许 WebSocket 升级
websocketMaxFramePayloadBytesint1048576 (1MB)单帧最大负载字节数
websocketMaxMessageBytesint4194304 (4MB)组合消息最大字节数
websocketMaxPendingFramesint1024发送队列最大帧数
websocketMaxPendingBytesint8388608 (8MB)发送队列最大字节数
websocketPingIntervalMsint30000Ping 心跳间隔(ms)
websocketPongTimeoutMsint10000Pong 超时(ms)
websocketIdleTimeoutMsint120000空闲连接超时(ms)
TLS
tls(VKTlsConfig)VKTlsConfignullTLS 配置,null 表示明文 HTTP

API 速查

初始化与生命周期(静态方法)

方法返回值说明
Vostok.Web.init(int port)VostokWeb快速初始化,返回链式实例
Vostok.Web.init(VKWebConfig)VostokWeb完整初始化,返回链式实例
Vostok.Web.start()void启动服务器
Vostok.Web.stop()void停止服务器
Vostok.Web.started()boolean是否已启动
Vostok.Web.port()int实际监听端口

路由与中间件(实例方法,链式)

方法说明
get(path, VKHandler)注册 GET 路由
post(path, VKHandler)注册 POST 路由
route(method, path, VKHandler)注册任意 HTTP 方法路由
controller(Object)注册单个 @VKApi 控制器实例
controllers(String... basePackages)扫描包并注册 @VKApi 控制器
mvcConfig(VKMvcConfig)设置注解路由行为(自动包装/异常暴露等)
use(VKMiddleware)注册全局中间件
error(VKErrorHandler)注册全局错误处理器
cors()启用默认 CORS 中间件
cors(VKCorsConfig)启用自定义 CORS 中间件
gzip()启用默认 GZIP 压缩
gzip(VKGzipConfig)启用自定义 GZIP 压缩
rateLimit(VKRateLimitConfig)设置全局限流
rateLimit(VKHttpMethod, path, VKRateLimitConfig)设置单路由限流
staticDir(urlPrefix, directory)映射静态资源目录
autoCrudApi()自动生成 CRUD API(默认包)
autoCrudApi(String... packages)自动生成 CRUD API(指定包,RESTFUL)
autoCrudApi(VKCrudStyle, String... packages)自动生成 CRUD API(指定风格与包)
websocket(path, VKWebSocketHandler)注册 WebSocket 端点(默认配置)
websocket(path, VKWebSocketConfig, VKWebSocketHandler)注册 WebSocket 端点(自定义配置)
sse(path, VKSseHandler)注册 SSE 端点
health()注册 GET /actuator/health 端点
health(String path)注册自定义路径健康检查端点
metrics()注册 GET /actuator/metrics 端点
metrics(String path)注册自定义路径 Metrics 端点

MVC 配置(VKMvcConfig)

配置项默认值说明
autoWrapEnabledtrue是否对非 VKWebResult 返回值自动包装
internalErrorMessage"Internal Server Error"控制器异常的默认错误文案
exposeExceptionMessagefalse是否对外暴露异常原始 message(建议生产关闭)
failFastOnControllerLoadtrue控制器扫描/注册失败时是否立即抛错
bindErrorStatus400参数绑定失败的 HTTP 状态码
internalErrorStatus500控制器执行异常的 HTTP 状态码

WebSocket 全局广播(静态方法)

方法说明
websocketBroadcast(path, text)向路径所有连接广播文本
websocketBroadcastRoom(path, room, text)向指定房间广播文本
websocketBroadcastGroup(path, group, text)向指定分组广播文本
websocketBroadcastRoomAndGroup(path, room, group, text)向房间且属于分组的连接广播文本
websocketBroadcastBinary(path, data)向路径所有连接广播二进制
websocketBroadcastRoomBinary(path, room, data)向指定房间广播二进制
websocketBroadcastGroupBinary(path, group, data)向指定分组广播二进制
websocketBroadcastRoomAndGroupBinary(path, room, group, data)向房间且属于分组的连接广播二进制