Merge pull request #4 from Pixel-Services/flash/add/server-router
This commit is contained in:
@@ -8,8 +8,9 @@
|
||||
{ "text": "RequestHandler", "link": "/flash/request-handler"},
|
||||
{ "text": "Request and Response", "link": "/flash/request-response"},
|
||||
{ "text": "Server Router", "link": "/flash/server-router" },
|
||||
{ "text": "Handler Default Implementation", "link": "/flash/handler-default-implementation" },
|
||||
{ "text": "Websockets", "link": "/flash/websockets" }
|
||||
{ "text": "Handler Default Implementations", "link": "/flash/handler-default-implementations" },
|
||||
{ "text": "Websockets", "link": "/flash/websockets" },
|
||||
{ "text": "Static Files Server", "link": "/flash/static-file-server" }
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
+15
-20
@@ -1,5 +1,5 @@
|
||||
---
|
||||
banner_title: "Flash - Handler Default Implementations (HDI)"
|
||||
banner_title: "Flash - Handler Default Implementations"
|
||||
banner_description: "Leverage HDI's for cleaner and more maintainable route logic."
|
||||
---
|
||||
|
||||
@@ -14,12 +14,11 @@ Instead of implementing repeated logic in every handler, you create abstract han
|
||||
|
||||
### 🛠 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:
|
||||
Imagine you want to protect API endpoints with an authentication key by checking it against a database. You can create an abstract `APIKeyProtectedHandler` that extends `RequestHandler` and implements the authentication logic:
|
||||
|
||||
```java
|
||||
public abstract class APIKeyProtectedHandler extends RequestHandler {
|
||||
protected String apiKey;
|
||||
protected int remainingQuota;
|
||||
|
||||
public APIKeyProtectedHandler(Request req, Response res) {
|
||||
super(req, res);
|
||||
@@ -34,30 +33,21 @@ public abstract class APIKeyProtectedHandler extends RequestHandler {
|
||||
return "{\"error\":\"Invalid API Key\"}";
|
||||
}
|
||||
|
||||
remainingQuota = getRemainingQuota(apiKey);
|
||||
if (remainingQuota <= 0) {
|
||||
res.status(429);
|
||||
return "{\"error\":\"API quota exceeded\"}";
|
||||
}
|
||||
|
||||
return handleAuthorized();
|
||||
}
|
||||
|
||||
// Implement this method in your actual handlers
|
||||
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:
|
||||
Now, your actual API handlers only need to extend APIKeyProtectedHandler, ensuring every request has a valid API key before executing its logic:
|
||||
|
||||
```java
|
||||
@RouteInfo(method = HttpMethod.GET, path = "/data")
|
||||
@@ -75,7 +65,8 @@ public class GetDataHandler extends APIKeyProtectedHandler {
|
||||
|
||||
## 🏗️ Chaining HDIs for Modular Logic
|
||||
|
||||
HDIs can be chained together to compose multiple layers of behavior. For example:
|
||||
HDIs can be chained together to compose multiple layers of behavior. For example,
|
||||
imagine you want to authenticate users with a token and fetch their data from a database. You can create two abstract handlers that extend one another:
|
||||
|
||||
- `ProtectedHandler` ensures authentication.
|
||||
|
||||
@@ -102,11 +93,12 @@ protected String authToken;
|
||||
}
|
||||
```
|
||||
|
||||
- `AuthenticatedHandler` extends ProtectedHandler to fetch user data from the database.
|
||||
- `AuthenticatedHandler` extends ProtectedHandler to fetch user data from the database, and overrides `handleAuthenticated` to ensure the user is authenticated before proceeding.
|
||||
|
||||
```java
|
||||
public abstract class AuthenticatedHandler extends ProtectedHandler {
|
||||
protected User user;
|
||||
private String authToken; // inherited from ProtectedHandler
|
||||
|
||||
public AuthenticatedHandler(Request req, Response res) {
|
||||
super(req, res);
|
||||
@@ -126,18 +118,21 @@ public abstract class AuthenticatedHandler extends ProtectedHandler {
|
||||
}
|
||||
```
|
||||
|
||||
- Your actual implementation handler extends AuthenticatedHandler to work directly with user data.
|
||||
- Your actual implementation handler `UserProfileHandler` extends `AuthenticatedHandler` and implements `handleWithUser` to ensure the user is authenticated and their profile data has been fetched before proceeding.
|
||||
|
||||
```java
|
||||
@RouteInfo(method = HttpMethod.GET, path = "/profile")
|
||||
@RouteInfo(endpoint = "/profile", method = HttpMethod.GET)
|
||||
public class UserProfileHandler extends AuthenticatedHandler {
|
||||
// inherited from AuthenticatedHandler
|
||||
private String username = user.getUsername();
|
||||
|
||||
public UserProfileHandler(Request req, Response res) {
|
||||
super(req, res);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object handleWithUser() {
|
||||
return "{\"username\":\"" + user.getUsername() + "\"}";
|
||||
return "{\"username\":\"" + username + "\"}";
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,70 @@
|
||||
---
|
||||
banner_title: "Flash - Static File Server"
|
||||
banner_description: "Learn how to serve static files in Flash."
|
||||
---
|
||||
|
||||
# 📁 Static File Server
|
||||
|
||||
Flash provides a built-in static file server that allows you to serve static files such with autoresolving MIME types and caching.
|
||||
|
||||
::: warning
|
||||
The static file server pre-registers literal routes for every file in the specified target directory.
|
||||
Creating/deleting files in the target directory will trigger the internal route registry to update accordingly.
|
||||
If you are planning to serve a compiled frontend application with a client-side router (think of react-router-dom),
|
||||
it is reccomended to use the Dynamic File Server instead,
|
||||
:::
|
||||
|
||||
## Usage
|
||||
|
||||
To serve static files in Flash, you need to call the `server.serveStatic()` method with the endpoint path and an instance of `StaticFileServerConfig`.
|
||||
The configuration object is instanced like so :
|
||||
|
||||
```java
|
||||
public StaticFileServerConfiguration(
|
||||
boolean enableFileWatcher,
|
||||
boolean enableIndexRedirect,
|
||||
String destinationPath,
|
||||
SourceType sourceType
|
||||
)
|
||||
```
|
||||
|
||||
- `enableFileWatcher` : If set to `true`, the server will watch for changes in the served files and reload them automatically.
|
||||
- `enableIndexRedirect` : If set to `true`, the server will redirect requests to directories to the `index.html` file.
|
||||
- `destinationPath` : The path to the directory containing the files to be served.
|
||||
- `sourceType` : The type of source to serve files from. It can be either `FILESYSTEM` or `RESOURCESTREAM`.
|
||||
|
||||
Registering the static file server is as simple as calling the `server.serveStatic()` method with the desired path and configuration object:
|
||||
|
||||
```java
|
||||
public class Example {
|
||||
public static void main(String[] args) {
|
||||
FlashServer server = new FlashServer(8080);
|
||||
|
||||
server.serveStatic("/static", new StaticFileServerConfiguration(
|
||||
true,
|
||||
true,
|
||||
"path/to/static/files",
|
||||
SourceType.FILESYSTEM
|
||||
));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now you can access the files in the specified directory by navigating to `http://localhost:8080/static/<file-name>`.
|
||||
|
||||
Similarly, you can serve files from the jar's resources folder by setting the `sourceType` to `RESOURCESTREAM`:
|
||||
|
||||
```java
|
||||
public class Example {
|
||||
public static void main(String[] args) {
|
||||
FlashServer server = new FlashServer(8080);
|
||||
|
||||
server.serveStatic("/static", new StaticFileServerConfiguration(
|
||||
true,
|
||||
true,
|
||||
"path/to/static/files",
|
||||
SourceType.RESOURCESTREAM
|
||||
));
|
||||
}
|
||||
}
|
||||
```
|
||||
+22
-1
@@ -18,6 +18,8 @@ you would need to poll the server at regular intervals to check for updates, whi
|
||||
## 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.
|
||||
These methods are called when the Websocket connection is opened, a message is received, the connection is closed, and an error occurs, respectively,
|
||||
and they provide you with an instance of the `WebSocketSession` object to be able to interact with it.
|
||||
|
||||
```java
|
||||
public class MyWebsocketHandler extends WebsocketHandler {
|
||||
@@ -34,6 +36,8 @@ public class MyWebsocketHandler extends WebsocketHandler {
|
||||
@Override
|
||||
public void onMessage(WebSocketSession session, String message) {
|
||||
System.out.println("Received message: " + message);
|
||||
|
||||
//optionally send a reponse back to the client
|
||||
session.sendMessage("I received your message!");
|
||||
}
|
||||
|
||||
@@ -52,7 +56,7 @@ public class Example {
|
||||
FlashServer server = new FlashServer(8080);
|
||||
|
||||
server.ws("/ws")
|
||||
.register(MyWebsocketHandler.class);
|
||||
.register(new MyWebsocketHandler());
|
||||
|
||||
server.start();
|
||||
}
|
||||
@@ -62,3 +66,20 @@ public class Example {
|
||||
## 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.
|
||||
|
||||
| Method | Params | Description |
|
||||
|--------------------|------------------|--------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `getChannel()` | `none` | Returns an instance of `AsynchronousSocketChannel` useful for retrieving info about the client . |
|
||||
| `getRequestInfo()` | `none` | Returns an instance of `RequestInfo` containing all sorts of information about the request (headers, method, path etc.) . |
|
||||
| `getPath()` | `none` | Returns the path to the websocket endpoint as a `String`. |
|
||||
| `getId()` | `none` | Returns the id of the websocket session as a `String`, useful if you want to keep track of the connected clients in a custom manager. |
|
||||
| `getBuffer()` | `none` | Returns the ByteBuffer for that session. |
|
||||
| `sendMessage()` | `String message` | Sends the `message` to the client as a `String`. it's up to the developer to stringify and de-stringify any data you want to send back and forth |
|
||||
| `close()` | `none` | Closes the websocket session. |
|
||||
|
||||
::: warning NOTE
|
||||
`WebsocketHandler` includes a `setId(String id)` method for overriding the default session ID. Unless you have a specific reason to change it, it's best to leave it as is.
|
||||
|
||||
Similarly, the `setBuffer(ByteBuffer buffer)` method allows you to override the default buffer. If you're unsure about this, it's recommended to keep the default setting.
|
||||
:::
|
||||
|
||||
|
||||
Reference in New Issue
Block a user