Merge pull request #3 from Pixel-Services/flash/add/server-router
Added websockets.md and handler-default-implementation.md
This commit is contained in:
Generated
+7
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="DiscordProjectSettings">
|
||||||
|
<option name="show" value="ASK" />
|
||||||
|
<option name="description" value="" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
@@ -5,10 +5,11 @@
|
|||||||
"items": [
|
"items": [
|
||||||
{ "text": "Getting Started", "link": "/flash" },
|
{ "text": "Getting Started", "link": "/flash" },
|
||||||
{ "text": "Installation", "link": "/flash/installation" },
|
{ "text": "Installation", "link": "/flash/installation" },
|
||||||
{ "text": "Server Types", "link": "/flash/server-types" },
|
|
||||||
{ "text": "RequestHandler", "link": "/flash/request-handler"},
|
{ "text": "RequestHandler", "link": "/flash/request-handler"},
|
||||||
{ "text": "Request and Response", "link": "/flash/request-response"},
|
{ "text": "Request and Response", "link": "/flash/request-response"},
|
||||||
{ "text": "Server Router", "link": "/flash/server-router" }
|
{ "text": "Server Router", "link": "/flash/server-router" },
|
||||||
|
{ "text": "Handler Default Implementation", "link": "/flash/handler-default-implementation" },
|
||||||
|
{ "text": "Websockets", "link": "/flash/websockets" }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -0,0 +1,143 @@
|
|||||||
|
---
|
||||||
|
banner_title: "Flash - Handler Default Implementations (HDI)"
|
||||||
|
banner_description: "Leverage HDI's for cleaner and more maintainable route logic."
|
||||||
|
---
|
||||||
|
|
||||||
|
# ⚡ Handler Default Implementations (HDI)
|
||||||
|
|
||||||
|
HDI's provide an elegant and reliable way to standardize common behaviors across multiple request handlers.
|
||||||
|
By defining base handlers that extend `RequestHandler` (or even chaining multiple base handlers), you can modularize your logic for aspects like authentication, user data retrieval, and rate limiting.
|
||||||
|
|
||||||
|
## 🔗 How It Works
|
||||||
|
|
||||||
|
Instead of implementing repeated logic in every handler, you create abstract handler classes that encapsulate common functionality. Your actual route handlers then extend these base classes, inheriting their behavior while focusing purely on request-specific logic.
|
||||||
|
|
||||||
|
### 🛠 Example: API Key Authentication
|
||||||
|
|
||||||
|
Imagine you want to protect API endpoints with an authentication key and track API usage in a database. You can create an abstract base handler that enforces API key validation:
|
||||||
|
|
||||||
|
```java
|
||||||
|
public abstract class APIKeyProtectedHandler extends RequestHandler {
|
||||||
|
protected String apiKey;
|
||||||
|
protected int remainingQuota;
|
||||||
|
|
||||||
|
public APIKeyProtectedHandler(Request req, Response res) {
|
||||||
|
super(req, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object handle() {
|
||||||
|
apiKey = req.header("X-API-Key");
|
||||||
|
|
||||||
|
if (apiKey == null || !isValidApiKey(apiKey)) {
|
||||||
|
res.status(403);
|
||||||
|
return "{\"error\":\"Invalid API Key\"}";
|
||||||
|
}
|
||||||
|
|
||||||
|
remainingQuota = getRemainingQuota(apiKey);
|
||||||
|
if (remainingQuota <= 0) {
|
||||||
|
res.status(429);
|
||||||
|
return "{\"error\":\"API quota exceeded\"}";
|
||||||
|
}
|
||||||
|
|
||||||
|
return handleAuthorized();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract Object handleAuthorized();
|
||||||
|
|
||||||
|
private boolean isValidApiKey(String key) {
|
||||||
|
// Implement key validation logic, e.g., checking against a database
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getRemainingQuota(String key) {
|
||||||
|
// Retrieve remaining quota from a database
|
||||||
|
return 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Now, your actual API handlers only need to extend APIKeyProtectedHandler, ensuring every request has a valid API key and available quota before executing its logic:
|
||||||
|
|
||||||
|
```java
|
||||||
|
@RouteInfo(method = HttpMethod.GET, path = "/data")
|
||||||
|
public class GetDataHandler extends APIKeyProtectedHandler {
|
||||||
|
public GetDataHandler(Request req, Response res) {
|
||||||
|
super(req, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Object handleAuthorized() {
|
||||||
|
return "{\"data\":\"Your API response here\"}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🏗️ Chaining HDIs for Modular Logic
|
||||||
|
|
||||||
|
HDIs can be chained together to compose multiple layers of behavior. For example:
|
||||||
|
|
||||||
|
- `ProtectedHandler` ensures authentication.
|
||||||
|
|
||||||
|
```java
|
||||||
|
public abstract class ProtectedHandler extends RequestHandler {
|
||||||
|
protected String authToken;
|
||||||
|
|
||||||
|
public ProtectedHandler(Request req, Response res) {
|
||||||
|
super(req, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object handle() {
|
||||||
|
authToken = req.header("Authorization");
|
||||||
|
if (authToken == null || !isValidToken(authToken)) {
|
||||||
|
res.status(401);
|
||||||
|
res.type("application/json");
|
||||||
|
return "{\"error\":\"Unauthorized\"}";
|
||||||
|
}
|
||||||
|
return handleAuthenticated();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract Object handleAuthenticated();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- `AuthenticatedHandler` extends ProtectedHandler to fetch user data from the database.
|
||||||
|
|
||||||
|
```java
|
||||||
|
public abstract class AuthenticatedHandler extends ProtectedHandler {
|
||||||
|
protected User user;
|
||||||
|
|
||||||
|
public AuthenticatedHandler(Request req, Response res) {
|
||||||
|
super(req, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Object handleAuthenticated() {
|
||||||
|
user = getUserFromDatabase(authToken);
|
||||||
|
if (user == null) {
|
||||||
|
res.status(403);
|
||||||
|
return "{\"error\":\"User not found\"}";
|
||||||
|
}
|
||||||
|
return handleWithUser();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract Object handleWithUser();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- Your actual implementation handler extends AuthenticatedHandler to work directly with user data.
|
||||||
|
|
||||||
|
```java
|
||||||
|
@RouteInfo(method = HttpMethod.GET, path = "/profile")
|
||||||
|
public class UserProfileHandler extends AuthenticatedHandler {
|
||||||
|
public UserProfileHandler(Request req, Response res) {
|
||||||
|
super(req, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Object handleWithUser() {
|
||||||
|
return "{\"username\":\"" + user.getUsername() + "\"}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -71,7 +71,7 @@ This page provides installation instructions for the latest version of the `flas
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
⚡ Latest version:
|
⚡ Latest version:
|
||||||
<a href="'https://maven.pixel-services.com/#/releases/com/pixelservices/flash'" style="text-decoration: underline; color: #007bff;">
|
<a href="https://maven.pixel-services.com/#/releases/com/pixelservices/flash" style="text-decoration: underline; color: #007bff;">
|
||||||
<strong>{{ latestVersion }}</strong>
|
<strong>{{ latestVersion }}</strong>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -11,19 +11,14 @@ In this section, we illustrate the powerful concept of `RequestHandler` in Flash
|
|||||||
|
|
||||||
To create a custom request handler, you need to extend the `RequestHandler` class and annotate the class with the `RouteInfo` annotation,
|
To create a custom request handler, you need to extend the `RequestHandler` class and annotate the class with the `RouteInfo` annotation,
|
||||||
specifying the HTTP method that the handler will respond to and the relative path that the handler will be registered to.
|
specifying the HTTP method that the handler will respond to and the relative path that the handler will be registered to.
|
||||||
The `enforceNonNullBody` attribute is used to specify whether the handler expects a non-null request body: by default, it is set to `false`.
|
|
||||||
After that, you must override the `handle` method;
|
After that, you must override the `handle` method;
|
||||||
The `handle` method is where you define the logic for processing the request and generating the response.
|
The `handle` method is where you define the logic for processing the request and generating the response.
|
||||||
The `req` (request) and `res` (response) objects are available in the handler to access the request data and send the response back to the client.
|
The `req` (request) and `res` (response) objects are available in the handler to access the request data and send the response back to the client.
|
||||||
|
|
||||||
You must call the super constructor with the `req` and `res` objects to initialize the handler.
|
You must call the super constructor with the `req` and `res` objects to initialize the handler.
|
||||||
|
|
||||||
```java{5,8}
|
```java{1,4}
|
||||||
import flash.Request;
|
@RouteInfo(method = HttpMethod.GET, path = "/hello")
|
||||||
import flash.Response;
|
|
||||||
import flash.RequestHandler;
|
|
||||||
|
|
||||||
@RouteInfo(method = HttpMethod.GET, path = "/hello", enforceNonNullBody = false)
|
|
||||||
public class MyHandler extends RequestHandler {
|
public class MyHandler extends RequestHandler {
|
||||||
public MyHandler(Request req, Response res) {
|
public MyHandler(Request req, Response res) {
|
||||||
super(req, res);
|
super(req, res);
|
||||||
|
|||||||
@@ -50,13 +50,8 @@ The `ExpectedRequestParameter`, `ExpectedBodyField`, and `ExpectedBodyFile` inst
|
|||||||
The `ExpectedRequestParameter` object is used to get the expected parameters of the request.
|
The `ExpectedRequestParameter` object is used to get the expected parameters of the request.
|
||||||
You can use the getter methods to safely get the parameter value, such as `getString`, `getInt`, `getDouble`, and `getBoolean` methods to safely cast the parameter to the expected type.
|
You can use the getter methods to safely get the parameter value, such as `getString`, `getInt`, `getDouble`, and `getBoolean` methods to safely cast the parameter to the expected type.
|
||||||
|
|
||||||
```java{8,11,16}
|
```java{4,8,18,21}
|
||||||
import flash.Request;
|
@RouteInfo(method = HttpMethod.GET, path = "/hello")
|
||||||
import flash.Response;
|
|
||||||
import flash.models.RequestHandler;
|
|
||||||
import flash.models.ExpectedRequestParameter;
|
|
||||||
|
|
||||||
@RouteInfo(method = HttpMethod.GET, path = "/hello", enforceNonNullBody = false)
|
|
||||||
public class MyHandler extends RequestHandler {
|
public class MyHandler extends RequestHandler {
|
||||||
// Store the expected parameter in a private field
|
// Store the expected parameter in a private field
|
||||||
private final ExpectedRequestParameter myExpectedReqParam;
|
private final ExpectedRequestParameter myExpectedReqParam;
|
||||||
@@ -89,14 +84,8 @@ Visiting `/hello?myParam=John` from your browser, will return `Hello, John!`.
|
|||||||
The `ExpectedBodyField` object is used to get the expected fields of the request body.
|
The `ExpectedBodyField` object is used to get the expected fields of the request body.
|
||||||
You can use the getter methods to safely get the field value, such as `getString`, `getInt`, `getDouble`, and `getBoolean` methods to safely cast the field to the expected type.
|
You can use the getter methods to safely get the field value, such as `getString`, `getInt`, `getDouble`, and `getBoolean` methods to safely cast the field to the expected type.
|
||||||
|
|
||||||
```java{8,11,16}
|
```java{4,8,18,21}
|
||||||
import flash.Request;
|
@RouteInfo(method = HttpMethod.GET, path = "/helloBody")
|
||||||
import flash.Response;
|
|
||||||
import flash.models.RequestHandler;
|
|
||||||
import flash.models.ExpectedBodyField;
|
|
||||||
|
|
||||||
// Make sure to set enforceNonNullBody to true
|
|
||||||
@RouteInfo(method = HttpMethod.GET, path = "/helloBody", enforceNonNullBody = true)
|
|
||||||
public class MyHandler extends RequestHandler {
|
public class MyHandler extends RequestHandler {
|
||||||
// Store the expected field in a private field
|
// Store the expected field in a private field
|
||||||
private final ExpectedBodyField myExpectedBodyField;
|
private final ExpectedBodyField myExpectedBodyField;
|
||||||
@@ -134,14 +123,8 @@ The methods provided by this object are slightly different from the other two, b
|
|||||||
- `getFileName()` simply returns the name of the file specified by the client.
|
- `getFileName()` simply returns the name of the file specified by the client.
|
||||||
- `getInputStream()` returns an `InputStream` object containing the file's contents.
|
- `getInputStream()` returns an `InputStream` object containing the file's contents.
|
||||||
|
|
||||||
```java{8,11,16}
|
```java{4,8,18,21}
|
||||||
import flash.Request;
|
@RouteInfo(method = HttpMethod.POST, path = "/helloFile")
|
||||||
import flash.Response;
|
|
||||||
import flash.models.RequestHandler;
|
|
||||||
import flash.models.ExpectedBodyFile;
|
|
||||||
|
|
||||||
// Make sure to set enforceNonNullBody to true
|
|
||||||
@RouteInfo(method = HttpMethod.POST, path = "/helloFile", enforceNonNullBody = true)
|
|
||||||
public class MyHandler extends RequestHandler {
|
public class MyHandler extends RequestHandler {
|
||||||
// Store the expected file in a private field
|
// Store the expected file in a private field
|
||||||
private final ExpectedBodyFile myExpectedBodyFile;
|
private final ExpectedBodyFile myExpectedBodyFile;
|
||||||
|
|||||||
+7
-11
@@ -9,27 +9,23 @@ In this section, we discuss how to use the `FlashServer` router to manage our `R
|
|||||||
The router is used to define route endpoints and their corresponding handler, which are executed when a request is made to the server.
|
The router is used to define route endpoints and their corresponding handler, which are executed when a request is made to the server.
|
||||||
|
|
||||||
The `FlashServer` router is an instance of the `RouteController` class, each server instance has its own router instance.
|
The `FlashServer` router is an instance of the `RouteController` class, each server instance has its own router instance.
|
||||||
To access the router instance, you can call the `route()` method on either the internal server or your `FlashServer` instance.
|
To access the router instance, you can call the `route()` method on the `FlashServer` instance.
|
||||||
|
|
||||||
## Creating a Route
|
## Creating a Route
|
||||||
|
|
||||||
To create a route, you need to call the `route()` method on your server's instance (in this case for simplicity, on the InternalFlashServer)
|
To create a route, you need to call the `route()` method on your server's instance (in this case for simplicity, on the InternalFlashServer)
|
||||||
and specify the base path of the route, followed by your handler class and optionally a `RouteInterceptor` instance.
|
and specify the base path of the route, followed by your handler class,
|
||||||
|
|
||||||
(More on the concept of `RouteInterceptor` in the following sections).
|
```java{9}
|
||||||
|
|
||||||
```java{8,9}
|
|
||||||
// Example.java
|
// Example.java
|
||||||
import static flash.InternalFlashServer.*;
|
|
||||||
|
|
||||||
public class Example {
|
public class Example {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
port(8080);
|
FlashServer server = new FlashServer(8080);
|
||||||
|
|
||||||
route("/api")
|
server.route("/api")
|
||||||
.register(MyHandler.class);
|
.register(MyHandler.class);
|
||||||
|
|
||||||
start();
|
server.start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -40,7 +36,7 @@ import flash.Request;
|
|||||||
import flash.Response;
|
import flash.Response;
|
||||||
import flash.models.RequestHandler;
|
import flash.models.RequestHandler;
|
||||||
|
|
||||||
@RouteInfo(method = HttpMethod.GET, path = "/hello", enforceNonNullBody = false)
|
@RouteInfo(method = HttpMethod.GET, path = "/hello")
|
||||||
public class MyHandler extends RequestHandler {
|
public class MyHandler extends RequestHandler {
|
||||||
public MyHandler(Request req, Response res) {
|
public MyHandler(Request req, Response res) {
|
||||||
super(req, res);
|
super(req, res);
|
||||||
|
|||||||
@@ -1,99 +0,0 @@
|
|||||||
---
|
|
||||||
banner_title: "Flash - Server Types"
|
|
||||||
banner_description: "Illustrating the main differences between the internal server instance and the FlashServer instance."
|
|
||||||
---
|
|
||||||
|
|
||||||
# Server Types
|
|
||||||
|
|
||||||
In this section, we discuss the differences between the internal server instance `InternalFlashServer` and the `FlashServer` instance, and whether you should use one or the other. We will also provide examples of how to use each type of server.
|
|
||||||
|
|
||||||
## Internal Server
|
|
||||||
|
|
||||||
The internal server is a singleton instance that can be accessed by statically importing
|
|
||||||
methods from `InternalFlashServer`. This means that for each application, only one `InternalFlashServer` instance is created.
|
|
||||||
This allows you to use the server's functionality without creating and managing an instance of the server.
|
|
||||||
|
|
||||||
### Usage
|
|
||||||
|
|
||||||
To use the internal server, you can statically import the methods from `InternalFlashServer`:
|
|
||||||
|
|
||||||
```java{1}
|
|
||||||
import static flash.InternalFlashServer.*;
|
|
||||||
|
|
||||||
public class Example {
|
|
||||||
public static void main(String[] args) {
|
|
||||||
port(8080);
|
|
||||||
|
|
||||||
get("/hello", (req, res) -> {
|
|
||||||
res.status(200);
|
|
||||||
return "Hello, world!";
|
|
||||||
});
|
|
||||||
|
|
||||||
post("/submit", (req, res) -> {
|
|
||||||
// Handle POST request
|
|
||||||
});
|
|
||||||
|
|
||||||
// Other routes and configurations
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Server Instance
|
|
||||||
|
|
||||||
The `FlashServer` class is used to create an instance of the server.
|
|
||||||
Unlike the internal server, you can create multiple instances of the `FlashServer` class with different configurations.
|
|
||||||
|
|
||||||
### Usage
|
|
||||||
|
|
||||||
To create a server instance, you can simply call `new FlashServer()` and configure the server using the instance methods:
|
|
||||||
|
|
||||||
```java{5}
|
|
||||||
import flash.FlashServer;
|
|
||||||
|
|
||||||
public class Example {
|
|
||||||
public static void main(String[] args) {
|
|
||||||
FlashServer server = new FlashServer("My Server Instance");
|
|
||||||
|
|
||||||
server.port(8080);
|
|
||||||
|
|
||||||
server.get("/hello", (req, res) -> {
|
|
||||||
res.status(200);
|
|
||||||
return "Hello, world!";
|
|
||||||
});
|
|
||||||
|
|
||||||
server.post("/submit", (req, res) -> {
|
|
||||||
// Handle POST request
|
|
||||||
});
|
|
||||||
|
|
||||||
// Other routes and configurations
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The `InternalFlashServer` is a **pre-configured instance** of `FlashServer` that is managed internally by the library. Under the hood, it is instantiated as:
|
|
||||||
|
|
||||||
```java
|
|
||||||
new FlashServer("Internal");
|
|
||||||
```
|
|
||||||
|
|
||||||
This means:
|
|
||||||
- Its **name** is set to `"Internal"` for logging and internal management purposes.
|
|
||||||
- It serves as the default server used by the library, which is automatically initialized when the library is loaded.
|
|
||||||
|
|
||||||
Both the `InternalFlashServer` and any user-created `FlashServer` instances are of the same type (`FlashServer`). However, if you want to create and manage your own server instance, you must provide a **name** during initialization, e.g.:
|
|
||||||
|
|
||||||
```java
|
|
||||||
FlashServer server = new FlashServer("My Server Instance");
|
|
||||||
```
|
|
||||||
|
|
||||||
## Which Server Type Should You Use?
|
|
||||||
|
|
||||||
It all depends on your use case.
|
|
||||||
If you only need one server instance and want to keep your code concise, you can use the internal server.
|
|
||||||
However, if your application needs to handle multiple server instances each with different ports and configurations,
|
|
||||||
you should use `FlashServer` instances.
|
|
||||||
|
|
||||||
::: warning
|
|
||||||
From now on, and unless otherwise specified, every example in the documentation will use the `InternalFlashServer` to illustrate the usage of the library.
|
|
||||||
However, for your specific implementation, you can call the same methods on your `FlashServer` instance to achieve the same results.
|
|
||||||
:::
|
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
---
|
||||||
|
banner_title: "Flash - Websockets"
|
||||||
|
banner_description: "Learn how to create and manage server-side Websockets in Flash."
|
||||||
|
---
|
||||||
|
|
||||||
|
# 🌐 Websockets
|
||||||
|
|
||||||
|
In this section, we illustrate how to create and manage Websockets in Flash, which are used to establish a bidirectional communication channel between the client and the server.
|
||||||
|
|
||||||
|
## Understanding Websockets
|
||||||
|
|
||||||
|
Websockets are a communication protocol that provides full-duplex communication channels over a single TCP connection.
|
||||||
|
Unlike HTTP, which is a request-response protocol, Websockets allow for real-time, low-latency communication between the client and the server.
|
||||||
|
|
||||||
|
Imagine a scenario where you need to send real-time updates to the client, such as a chat application or a live feed: if you were to use HTTP,
|
||||||
|
you would need to poll the server at regular intervals to check for updates, which is inefficient and resource-intensive.
|
||||||
|
|
||||||
|
## Creating a Websocket
|
||||||
|
|
||||||
|
To create a Websocket in Flash, you need to extend the `WebsocketHandler` class and override the `onOpen`, `onMessage`, `onClose`, and `onError` methods.
|
||||||
|
|
||||||
|
```java
|
||||||
|
public class MyWebsocketHandler extends WebsocketHandler {
|
||||||
|
@Override
|
||||||
|
public void onOpen(WebSocketSession session) {
|
||||||
|
System.out.println("WebSocket connection opened");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClose(WebSocketSession session, int statusCode, String reason) {
|
||||||
|
System.out.println("WebSocket connection closed");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMessage(WebSocketSession session, String message) {
|
||||||
|
System.out.println("Received message: " + message);
|
||||||
|
session.sendMessage("I received your message!");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(WebSocketSession session, Throwable error) {
|
||||||
|
System.out.println("WebSocket error: " + error.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
To register your Websocket handler with the server, you can use the `server.ws()` method:
|
||||||
|
|
||||||
|
```java
|
||||||
|
public class Example {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
FlashServer server = new FlashServer(8080);
|
||||||
|
|
||||||
|
server.ws("/ws")
|
||||||
|
.register(MyWebsocketHandler.class);
|
||||||
|
|
||||||
|
server.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Interacting with Websockets sessions
|
||||||
|
|
||||||
|
The `WebSocketSession` object provides methods to interact with the Websocket session, such as sending messages, closing the connection, and getting the remote address and session ID.
|
||||||
Reference in New Issue
Block a user