diff --git a/jcommon/docean-plugin/docean-plugin-mongodb/pom.xml b/jcommon/docean-plugin/docean-plugin-mongodb/pom.xml index 95876db84..9c73b6bdb 100644 --- a/jcommon/docean-plugin/docean-plugin-mongodb/pom.xml +++ b/jcommon/docean-plugin/docean-plugin-mongodb/pom.xml @@ -1,39 +1,52 @@ - - 4.0.0 - - run.mone - docean-plugin - 1.6.0-jdk21-SNAPSHOT - - docean-plugin-mongodb - 1.5.0-jdk21-SNAPSHOT - - - - run.mone - docean-plugin-config - - - - org.mongodb - mongo-java-driver - 3.8.0 - - - - run.mone - catPlugin - 1.6.0-jdk21-SNAPSHOT - - - - dev.morphia.morphia - morphia-core - 2.4.13 - - - - + + 4.0.0 + + run.mone + docean-plugin + 1.6.0-jdk21-SNAPSHOT + + docean-plugin-mongodb + 1.5.0-jdk21-SNAPSHOT + + + + run.mone + docean-plugin-config + + + + org.mongodb + mongo-java-driver + 3.8.0 + + + + run.mone + catPlugin + 1.6.0-jdk21-SNAPSHOT + + + + dev.morphia.morphia + morphia-core + 2.4.13 + + + + org.mapstruct + mapstruct + 1.4.2.Final + + + + org.mapstruct + mapstruct-processor + 1.4.2.Final + + + + diff --git a/jcommon/docean-plugin/docean-plugin-mongodb/src/main/java/run/mone/bo/User.java b/jcommon/docean-plugin/docean-plugin-mongodb/src/main/java/run/mone/bo/User.java index 721b6498d..01f224d39 100644 --- a/jcommon/docean-plugin/docean-plugin-mongodb/src/main/java/run/mone/bo/User.java +++ b/jcommon/docean-plugin/docean-plugin-mongodb/src/main/java/run/mone/bo/User.java @@ -10,7 +10,7 @@ /** * 用户 * - * @author mone + * @author goodjava@qq.com */ @Data @Builder @@ -55,6 +55,8 @@ public class User implements MongoBo{ //版本(用于乐观锁) private int version; + private String token; + public User(String username, String password) { this.username = username; this.password = password; diff --git a/jcommon/docean-plugin/docean-plugin-mongodb/src/main/java/run/mone/controller/MongodbController.java b/jcommon/docean-plugin/docean-plugin-mongodb/src/main/java/run/mone/controller/MongodbController.java index de9b240c7..82090951c 100644 --- a/jcommon/docean-plugin/docean-plugin-mongodb/src/main/java/run/mone/controller/MongodbController.java +++ b/jcommon/docean-plugin/docean-plugin-mongodb/src/main/java/run/mone/controller/MongodbController.java @@ -38,7 +38,7 @@ public MongodbController(Class clazz) { //查询一条记录 //{"name":"$eq","field":"name","value":"bbb"} @Auth - @RequestMapping(path = "/one", method = "get") + @RequestMapping(path = "/one") public T one(Filter filter) { return this.datastore.find(this.clazz).filter(filter).first(); } @@ -50,7 +50,6 @@ public T getById(@RequestParam("id") String id) { } - @RequestMapping(path = "/getByIdAndUid", method = "get") public T getByIdAndUid(@ModelAttribute("user") User user, @RequestParam("id") String id) { return datastore.find(this.clazz).filter(Filters.and(Filters.eq("id", id), Filters.eq("uid", user.getId()))).first(); @@ -126,10 +125,8 @@ public boolean delete(T t) { } //删除 - @Auth(role = "user") - @RequestMapping(path = "/delete") - public boolean deleteWithUid(T t) { - User user = getCurrentUser(); + @RequestMapping(path = "/deleteWithUid") + public boolean deleteWithUid(@ModelAttribute("user") User user, T t) { t.setUid(user.getUid()); this.datastore.delete(t); return true; @@ -146,9 +143,8 @@ public boolean update(T t) { //更新 @Auth(role = "user") - @RequestMapping(path = "/update") - public boolean updateWithUid(T t) { - User user = getCurrentUser(); + @RequestMapping(path = "/updateWithUid") + public boolean updateWithUid(@ModelAttribute("user") User user, T t) { t.setUtime(System.currentTimeMillis()); t.setUid(user.getUid()); this.datastore.merge(t); @@ -168,9 +164,8 @@ public boolean add(T t) { } @Auth(role = "user") - @RequestMapping(path = "/add") - public boolean addWithUid(T t) { - User user = getCurrentUser(); + @RequestMapping(path = "/addWithUid") + public boolean addWithUid(@ModelAttribute("user") User user, T t) { long now = System.currentTimeMillis(); t.setState(0); t.setUid(user.getUid()); diff --git a/jcommon/docean-plugin/docean-plugin-mongodb/src/main/java/run/mone/controller/UserController.java b/jcommon/docean-plugin/docean-plugin-mongodb/src/main/java/run/mone/controller/UserController.java new file mode 100644 index 000000000..bd8ad309d --- /dev/null +++ b/jcommon/docean-plugin/docean-plugin-mongodb/src/main/java/run/mone/controller/UserController.java @@ -0,0 +1,117 @@ +package run.mone.controller; + +import com.xiaomi.youpin.docean.anno.Controller; +import com.xiaomi.youpin.docean.anno.ModelAttribute; +import com.xiaomi.youpin.docean.anno.RequestMapping; +import com.xiaomi.youpin.docean.anno.RequestParam; +import com.xiaomi.youpin.docean.mvc.ContextHolder; +import com.xiaomi.youpin.docean.mvc.MvcContext; +import run.mone.bo.User; +import run.mone.mapper.UserMapper; +import run.mone.service.UserService; +import run.mone.vo.UserVo; + +import javax.annotation.Resource; + +/** + * 用户管理控制器 + * + * @author goodjava@qq.com + */ +@Controller +@RequestMapping(path = "/user") +public class UserController extends MongodbController { + + @Resource + private UserService userService; + + public UserController() { + super(User.class); + } + + /** + * 根据用户名和密码查找用户 + * + * @param username 用户名 + * @param password 密码 + * @return 用户对象 + */ + @RequestMapping(path = "/findByUsernameAndPassword", method = "get") + public UserVo findUserByUsernameAndPassword(@RequestParam("username") String username, @RequestParam("password") String password) { + User user = userService.findUserByUsernameAndPassword(username, password); + return UserMapper.INSTANCE.userToUserVo(user); + } + + /** + * 更新用户密码 + * + * @param userId 用户ID + * @param newPassword 新密码 + * @return 是否更新成功 + */ + @RequestMapping(path = "/updatePassword") + public boolean updatePassword(String userId, String newPassword) { + return userService.updatePassword(userId, newPassword); + } + + /** + * 更新用户个人信息 + * + * @param user 新的个人信息 + * @return 是否更新成功 + */ + @RequestMapping(path = "/updateProfile") + public boolean updateProfile(@ModelAttribute("user") User user, User updateUser) { + updateUser.setId(user.getId()); + return userService.updateProfile(updateUser); + } + + + /** + * 处理用户注册请求的方法。 + * 调用userService中的registerUser方法,并返回注册结果。 + * + * @param user 用户对象,包含用户注册信息。 + * @return 返回注册操作的成功与否。 + */ + @RequestMapping(path = "/register") + public boolean registerUser(User user) { + return userService.registerUser(user); + } + + @RequestMapping(path = "/getByUserId") + public UserVo getByUserId(@RequestParam("userId") String userId) { + User user = userService.findById(userId); + return UserMapper.INSTANCE.userToUserVo(user); + } + + /** + * 该方法用于获取当前登录用户的信息 + * 通过@RequestMapping注解映射请求路径为"/getLoginUser" + * 使用@ModelAttribute注解将请求参数绑定到User对象 + * 返回绑定了请求参数的User对象 + */ + @RequestMapping(path = "/getLoginUser") + public User getLoginUser(@ModelAttribute("user") User user) { + return user; + } + + @RequestMapping(path = "/login") + public String login(User userReq) { + MvcContext context = ContextHolder.getContext().get(); + User user = userService.findUserByUsernameAndPassword(userReq.getUsername(), userReq.getPassword()); + if (null == user) { + return "error"; + } + user.setPassword(""); + context.session().setAttribute("user", user); + return "ok"; + } + + @RequestMapping(path = "/logout") + public String logout(MvcContext context) { + context.session().removeAttribute("user"); + return "ok"; + } + +} diff --git a/jcommon/docean-plugin/docean-plugin-mongodb/src/main/java/run/mone/mapper/UserMapper.java b/jcommon/docean-plugin/docean-plugin-mongodb/src/main/java/run/mone/mapper/UserMapper.java new file mode 100644 index 000000000..be3566419 --- /dev/null +++ b/jcommon/docean-plugin/docean-plugin-mongodb/src/main/java/run/mone/mapper/UserMapper.java @@ -0,0 +1,19 @@ +package run.mone.mapper; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; +import run.mone.bo.User; +import run.mone.vo.UserVo; + +/** + * @author goodjava@qq.com + * @date 2024/4/23 16:59 + */ +@Mapper +public interface UserMapper { + + UserMapper INSTANCE = Mappers.getMapper(UserMapper.class); + + UserVo userToUserVo(User user); + +} diff --git a/jcommon/docean-plugin/docean-plugin-mongodb/src/main/java/run/mone/service/UserService.java b/jcommon/docean-plugin/docean-plugin-mongodb/src/main/java/run/mone/service/UserService.java new file mode 100644 index 000000000..491b8c3a8 --- /dev/null +++ b/jcommon/docean-plugin/docean-plugin-mongodb/src/main/java/run/mone/service/UserService.java @@ -0,0 +1,67 @@ +package run.mone.service; + +import com.xiaomi.youpin.docean.anno.Service; +import dev.morphia.query.filters.Filter; +import dev.morphia.query.filters.Filters; +import run.mone.bo.User; + +/** + * @author goodjava@qq.com + * @date 2024/4/18 22:49 + */ +@Service +public class UserService extends MongoService { + + + public UserService() { + super(User.class); + } + + /** + * 根据用户名和密码查找用户 + */ + public User findUserByUsernameAndPassword(String username, String password) { + Filter filter = Filters.and( + Filters.eq("username", username), + Filters.eq("password", password) + ); + return findFirst(filter); + } + + /** + * 更新用户密码 + */ + public boolean updatePassword(String userId, String newPassword) { + User user = findById(userId); + if (user != null) { + user.setPassword(newPassword); + return update(user); + } + return false; + } + + /** + * 更新用户个人信息 + */ + public boolean updateProfile(User user) { + return update(user); + } + + + /** + * 注册用户 + *

+ * 该方法用于注册新用户。首先会根据用户名查找是否已存在相同的用户,如果存在则返回 false 表示注册失败。 + * 如果不存在相同用户,则保存新用户并返回 true 表示注册成功。 + * + * @param user 待注册的用户对象 + * @return 注册是否成功, 成功返回 true,失败返回 false + */ + public boolean registerUser(User user) { + User existingUser = findFirst(Filters.eq("username", user.getUsername())); + if (existingUser != null) { + return false; // 用户名已存在 + } + return save(user); + } +} diff --git a/jcommon/docean-plugin/docean-plugin-mongodb/src/main/java/run/mone/vo/UserVo.java b/jcommon/docean-plugin/docean-plugin-mongodb/src/main/java/run/mone/vo/UserVo.java new file mode 100644 index 000000000..5b416027e --- /dev/null +++ b/jcommon/docean-plugin/docean-plugin-mongodb/src/main/java/run/mone/vo/UserVo.java @@ -0,0 +1,33 @@ +package run.mone.vo; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * @author goodjava@qq.com + * @date 2024/4/29 10:26 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class UserVo implements Serializable { + + private String id; + private String username; + private String role; + private String email; + private String mobile; + private String avatarUrl; + private String bio; + private long ctime; + private long utime; + private int state; + private int version; + + +} diff --git a/jcommon/docean/src/main/java/com/xiaomi/youpin/docean/Mvc.java b/jcommon/docean/src/main/java/com/xiaomi/youpin/docean/Mvc.java index f220225d0..7d5f6c201 100644 --- a/jcommon/docean/src/main/java/com/xiaomi/youpin/docean/Mvc.java +++ b/jcommon/docean/src/main/java/com/xiaomi/youpin/docean/Mvc.java @@ -286,6 +286,10 @@ public void callMethod(MvcContext context, MvcRequest request, MvcResponse respo private Object[] getMethodParams(MvcContext context, MvcRequest request, HttpRequestMethod method) { Object[] params = new Object[]{null}; + if (context.isWebsocket()) { + params[0] = new String(request.getBody()); + return params; + } //If there is only one parameter and it is a String, no further parsing is necessary; it can be used directly. if (isSingleStringParameterMethod(method) && request.getMethod().toUpperCase().equals("POST")) { params[0] = new String(request.getBody()); diff --git a/jcommon/docean/src/main/java/com/xiaomi/youpin/docean/config/HttpServerConfig.java b/jcommon/docean/src/main/java/com/xiaomi/youpin/docean/config/HttpServerConfig.java index 2b8780eb5..530c1d075 100644 --- a/jcommon/docean/src/main/java/com/xiaomi/youpin/docean/config/HttpServerConfig.java +++ b/jcommon/docean/src/main/java/com/xiaomi/youpin/docean/config/HttpServerConfig.java @@ -50,7 +50,7 @@ public class HttpServerConfig { */ private String uploadDir; - private boolean userWs; + private boolean useWs; public static int HTTP_POOL_SIZE = 500; diff --git a/jcommon/docean/src/main/java/com/xiaomi/youpin/docean/mvc/HttpHandler.java b/jcommon/docean/src/main/java/com/xiaomi/youpin/docean/mvc/HttpHandler.java index 8f89bdc4a..079d65042 100644 --- a/jcommon/docean/src/main/java/com/xiaomi/youpin/docean/mvc/HttpHandler.java +++ b/jcommon/docean/src/main/java/com/xiaomi/youpin/docean/mvc/HttpHandler.java @@ -42,7 +42,7 @@ public HttpHandler(HttpServerConfig config) { @Override protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) { - if (config.isUserWs() && Cons.WebSocketPath.equalsIgnoreCase(request.uri())) { + if (config.isUseWs() && Cons.WebSocketPath.equalsIgnoreCase(request.uri())) { ctx.fireChannelRead(request.retain()); return; } diff --git a/jcommon/docean/src/main/java/com/xiaomi/youpin/docean/mvc/MvcResponse.java b/jcommon/docean/src/main/java/com/xiaomi/youpin/docean/mvc/MvcResponse.java index ff9ac2bc5..b7ff608f3 100644 --- a/jcommon/docean/src/main/java/com/xiaomi/youpin/docean/mvc/MvcResponse.java +++ b/jcommon/docean/src/main/java/com/xiaomi/youpin/docean/mvc/MvcResponse.java @@ -64,13 +64,6 @@ public void writeAndFlush(MvcContext context, HttpResponseStatus status, String public void writeAndFlush(MvcContext context, String message) { - HttpResponseStatus responseStatus = HttpResponseStatus.OK; - String status = context.getResHeaders().get("x-status"); - if (null != status) { - responseStatus = HttpResponseStatus.valueOf(Integer.valueOf(status)); - } - writeAndFlush(context, responseStatus, message); - writeAndFlush(context, message, HttpResponseStatus.OK.code()); } diff --git a/jcommon/docean/src/main/java/com/xiaomi/youpin/docean/mvc/MvcRunnable.java b/jcommon/docean/src/main/java/com/xiaomi/youpin/docean/mvc/MvcRunnable.java index 56af4e867..778ad7299 100644 --- a/jcommon/docean/src/main/java/com/xiaomi/youpin/docean/mvc/MvcRunnable.java +++ b/jcommon/docean/src/main/java/com/xiaomi/youpin/docean/mvc/MvcRunnable.java @@ -1,6 +1,8 @@ package com.xiaomi.youpin.docean.mvc; import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; import com.xiaomi.youpin.docean.Mvc; import com.xiaomi.youpin.docean.bo.MvcConfig; import com.xiaomi.youpin.docean.common.Cons; @@ -104,9 +106,16 @@ public void run() { private void call() { if (context.isWebsocket()) { - WsRequest req = GsonUtils.gson.fromJson(new String(request.getBody()), WsRequest.class); - request.setPath(req.getPath()); - request.setBody(GsonUtils.gson.toJson(req.getParams()).getBytes()); + String reqBody = new String(request.getBody()); + JsonElement element = GsonUtils.gson.toJsonTree(reqBody); + if (element.isJsonPrimitive()) { + request.setPath(Cons.WebSocketPath); + } + if (element.isJsonObject()) { + WsRequest req = GsonUtils.gson.fromJson(new String(request.getBody()), WsRequest.class); + request.setPath(req.getPath()); + request.setBody(GsonUtils.gson.toJson(req.getParams()).getBytes()); + } } //Directly returning not found when searching for favicon.ico. diff --git a/jcommon/docean/src/main/java/com/xiaomi/youpin/docean/mvc/TextWebSocketHandler.java b/jcommon/docean/src/main/java/com/xiaomi/youpin/docean/mvc/TextWebSocketHandler.java index 93035ae47..5668d5f40 100644 --- a/jcommon/docean/src/main/java/com/xiaomi/youpin/docean/mvc/TextWebSocketHandler.java +++ b/jcommon/docean/src/main/java/com/xiaomi/youpin/docean/mvc/TextWebSocketHandler.java @@ -17,6 +17,8 @@ package com.xiaomi.youpin.docean.mvc; import com.xiaomi.youpin.docean.Mvc; +import com.xiaomi.youpin.docean.mvc.context.WebSocketContext; +import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; @@ -57,7 +59,16 @@ protected void channelRead0(final ChannelHandlerContext ctx, TextWebSocketFrame @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { super.channelInactive(ctx); + String id = ctx.channel().remoteAddress().toString(); + WebSocketContext.ins().remove(id); } + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + super.channelActive(ctx); + String id = ctx.channel().remoteAddress().toString(); + Channel channel = ctx.channel(); + WebSocketContext.ins().put(id, channel); + } } diff --git a/jcommon/docean/src/main/java/com/xiaomi/youpin/docean/mvc/context/WebSocketContext.java b/jcommon/docean/src/main/java/com/xiaomi/youpin/docean/mvc/context/WebSocketContext.java new file mode 100644 index 000000000..9bd6a0aad --- /dev/null +++ b/jcommon/docean/src/main/java/com/xiaomi/youpin/docean/mvc/context/WebSocketContext.java @@ -0,0 +1,47 @@ +package com.xiaomi.youpin.docean.mvc.context; + +import io.netty.channel.Channel; +import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * @author goodjava@qq.com + * @date 2024/4/29 13:48 + */ +@Slf4j +public class WebSocketContext { + + + @Getter + private ConcurrentMap channelMap = new ConcurrentHashMap<>(); + + public void put(String id, Channel channel) { + this.channelMap.put(id, channel); + } + + public void remove(String id) { + this.channelMap.remove(id); + } + + + private static final class LazyHolder { + private static final WebSocketContext ins = new WebSocketContext(); + } + + public static final WebSocketContext ins() { + return LazyHolder.ins; + } + + public void sendMessage(String id, String message) { + Channel channel = channelMap.get(id); + if (null != channel) { + TextWebSocketFrame frame = new TextWebSocketFrame(message); + channel.writeAndFlush(frame); + } + } + +} diff --git a/jcommon/docean/src/test/java/com/xiaomi/youpin/docean/test/HttpServerTest.java b/jcommon/docean/src/test/java/com/xiaomi/youpin/docean/test/HttpServerTest.java index 4c7fd66e0..bce62cebd 100644 --- a/jcommon/docean/src/test/java/com/xiaomi/youpin/docean/test/HttpServerTest.java +++ b/jcommon/docean/src/test/java/com/xiaomi/youpin/docean/test/HttpServerTest.java @@ -79,7 +79,7 @@ public void testHttpServer() throws InterruptedException { } - Ioc.ins().putBean("$response-original-value", "true").putBean("$openStaticFile","true").putBean("$staticFilePath","/tmp/"); + Ioc.ins().putBean("$response-original-value", "true").putBean("$openStaticFile", "true").putBean("$staticFilePath", "/tmp/"); // Ioc.ins().putBean("$ssl_domain", "zzy.com"); // Ioc.ins().putBean("$ssl_self_sign", "false"); // Ioc.ins().putBean("$ssl_certificate","/Users/zhangzhiyong/key/zzy.com/certificate.crt"); @@ -96,6 +96,56 @@ public void testHttpServer() throws InterruptedException { server.start(); } + @SneakyThrows + @Test + public void testWebSocketServer() { + Ioc ioc = Ioc.ins(); + ioc.putBean(ioc).init("com.xiaomi.youpin.docean"); + Mvc.ins(); + DoceanHttpServer server = new DoceanHttpServer(HttpServerConfig.builder() + .httpVersion(HttpServerConfig.HttpVersion.http1) + .port(8899) + .websocket(true) + .useWs(true) + .build()); + server.start(); + } + + //用OkHttp写一个websocket客户端,然后发一个ok过去 + @SneakyThrows + @Test + public void testWebSocketClient() { + OkHttpClient client = new OkHttpClient(); + Request request = new Request.Builder().url("ws://127.0.0.1:8899/ws").build(); + WebSocketListener listener = new WebSocketListener() { + @Override + public void onOpen(WebSocket webSocket, Response response) { +// webSocket.send("ok"); + } + + @Override + public void onMessage(WebSocket webSocket, String text) { + System.out.println("Received: " + text); + } + + @Override + public void onClosing(WebSocket webSocket, int code, String reason) { + webSocket.close(1000, null); + System.out.println("Closing: " + code + " / " + reason); + } + + @Override + public void onFailure(WebSocket webSocket, Throwable t, Response response) { + System.err.println("Error: " + t.getMessage()); + } + }; + + WebSocket ws = client.newWebSocket(request, listener); + ws.send("ok2"); + System.in.read(); +// client.dispatcher().executorService().shutdown(); + } + @Test public void testClient() { diff --git a/jcommon/docean/src/test/java/com/xiaomi/youpin/docean/test/demo/DemoController.java b/jcommon/docean/src/test/java/com/xiaomi/youpin/docean/test/demo/DemoController.java index e2322945e..df3c6d0f1 100644 --- a/jcommon/docean/src/test/java/com/xiaomi/youpin/docean/test/demo/DemoController.java +++ b/jcommon/docean/src/test/java/com/xiaomi/youpin/docean/test/demo/DemoController.java @@ -20,9 +20,11 @@ import com.xiaomi.youpin.docean.anno.Controller; import com.xiaomi.youpin.docean.anno.RequestMapping; import com.xiaomi.youpin.docean.anno.RequestParam; +import com.xiaomi.youpin.docean.common.Safe; import com.xiaomi.youpin.docean.mvc.ContextHolder; import com.xiaomi.youpin.docean.mvc.MvcContext; import com.xiaomi.youpin.docean.mvc.MvcResult; +import com.xiaomi.youpin.docean.mvc.context.WebSocketContext; import com.xiaomi.youpin.docean.test.anno.TAnno; import com.xiaomi.youpin.docean.test.bo.M; import lombok.SneakyThrows; @@ -31,6 +33,7 @@ import javax.annotation.Resource; import java.nio.file.Files; import java.nio.file.Paths; +import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; /** @@ -46,6 +49,14 @@ public class DemoController { public void init() { log.info("init controller"); + + Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(() -> { + Safe.runAndLog(() -> WebSocketContext.ins().getChannelMap().forEach((k, v) -> { + WebSocketContext.ins().sendMessage(k, "server_ping"); + })); + }, 0, 5, TimeUnit.SECONDS); + + } @RequestMapping(path = "/test") @@ -56,6 +67,11 @@ public DemoVo test() { return vo; } + @RequestMapping(path = "/ws") + public String test(String req) { + return "ws:" + req; + } + @RequestMapping(path = "/header") public DemoVo header(MvcContext context) { DemoVo vo = new DemoVo();