Ended up adding the request-handler.md and request-response.md too
This commit is contained in:
@@ -5,7 +5,10 @@
|
||||
"items": [
|
||||
{ "text": "Getting Started", "link": "/flash" },
|
||||
{ "text": "Installation", "link": "/flash/installation" },
|
||||
{ "text": "Server Types", "link": "/flash/server-types" }
|
||||
{ "text": "Server Types", "link": "/flash/server-types" },
|
||||
{ "text": "RequestHandler", "link": "/flash/request-handler"},
|
||||
{ "text": "Request and Response", "link": "/flash/request-response"},
|
||||
{ "text": "Server Router", "link": "/flash/server-router" }
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
---
|
||||
banner_title: "Flash - Request Handler"
|
||||
banner_description: "Learn how to create and manage Request Handlers in Flash."
|
||||
---
|
||||
|
||||
# ⚙️ Request Handler
|
||||
|
||||
In this section, we illustrate the powerful concept of `RequestHandler` in Flash, which are used to handle incoming requests and generate responses.
|
||||
|
||||
## Creating a Request Handler
|
||||
|
||||
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.
|
||||
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;
|
||||
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.
|
||||
|
||||
You must call the super constructor with the `req` and `res` objects to initialize the handler.
|
||||
|
||||
```java{5,8}
|
||||
import flash.Request;
|
||||
import flash.Response;
|
||||
import flash.RequestHandler;
|
||||
|
||||
@RouteInfo(method = HttpMethod.GET, path = "/hello", enforceNonNullBody = false)
|
||||
public class MyHandler extends RequestHandler {
|
||||
public MyHandler(Request req, Response res) {
|
||||
super(req, res);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object handle() {
|
||||
String response = "Hello, world!";
|
||||
return response;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
::: warning
|
||||
Any logic that needs to be executed before the request handler is registered must be done within the constructor.
|
||||
:::
|
||||
|
||||
## Request Handler methods
|
||||
|
||||
The `RequestHandler` class provides several methods that can be used to interact with the request and response objects easily and safely.
|
||||
Following are listed the methods available in the `RequestHandler` class, with a brief description of their purpose:
|
||||
|
||||
| Method | Params | Description |
|
||||
|------------------------------|----------------------------|------------------------------------------------------------------------------------------------------|
|
||||
| `getRequestBody()` | `none` | Returns a `JSONObject` representation of the request body. |
|
||||
| `getSpecification()` | `none` | Returns an instance of `HandlerSpecification` containing all sorts of information about the handler. |
|
||||
| `expectedRequestParameter()` | `String name, description` | Returns an instance of `ExpectedRequestParameter` for the specified parameter name. |
|
||||
| `expectedBodyField()` | `String name, description` | Returns an instance of `ExpectedBodyField` for the specified field name. |
|
||||
| `expectedBodyFile()` | `String name, description` | Returns an instance of `ExpectedBodyFile` for the specified file name. |
|
||||
|
||||
(More on the `ExpectedRequestParameter`, `ExpectedBodyField`, and `ExpectedBodyFile` classes in the next section).
|
||||
@@ -0,0 +1,170 @@
|
||||
---
|
||||
banner_title: "Flash - Request and Response"
|
||||
banner_description: "Unlock the power of the Request and Response objects in Flash."
|
||||
---
|
||||
|
||||
# 📥 Request and Response
|
||||
|
||||
Generally speaking, in an HTTP request-response cycle, the client sends a request to the server, and the server generates a response based on the request.
|
||||
In this section, we illustrate the `Request` and `Response` objects in Flash, which are used to interact with the request data and generate responses.
|
||||
We will learn to read and interpret the request data, and model the correct response to send back to the client.
|
||||

|
||||
|
||||
## Request and Response Objects
|
||||
|
||||
The `Request` and `Response` objects are passed to the handler constructor and provide access to the request data and response methods.
|
||||
Under the hood, these objects are provided by the `RouteController` during the registration stage of the handler, and they are continously updated for each request on that specific handler's instance, but for now, you can consider them as magic.
|
||||
|
||||
These objects are a very powerful tool to interact with the lifecycle of the server's response, and they provide a wide range of methods that makes our lives as developers easier.
|
||||
|
||||
## RequestHandler context
|
||||
|
||||
Inside a `RequestHandler` class, you can access the `Request` and `Response` objects inside the `handle` method by simply typing `req` and `res`, respectively.
|
||||
You can use the methods provided by these objects both outside of a handler, and inside of it, to interact with the request and response objects.
|
||||
Although, the advantages of being inside a handler are that Flash supports out-of-the-box methods that can significantly clean up your code and improve
|
||||
the overall readability of your handlers.
|
||||
|
||||
Specifically, the `ExpectedRequestParameter`, `ExpectedBodyField`, and `ExpectedBodyFile` objects are used to get the expected properties of the request,
|
||||
and they are used by `Flash` to validate the request data before the `handle` method is even executed.
|
||||
|
||||
_**The developer can then simply assume that all the expected parameters are present and valid,
|
||||
without having to write a single line of validation code: Flash will do it for you.**_
|
||||
|
||||
The three objects mentioned above are fairly similar in their usage, providing getter methods which safely return the data in the expected format and type,
|
||||
thanks to Flash's built-in validation and casting system.
|
||||
|
||||
::: warning NOTE
|
||||
`Flash` will take care of informing the client of any missing or invalid parameters,
|
||||
parameters that are not in the expected format, or any other kind of error that might occur during the validation process.
|
||||
:::
|
||||
|
||||
::: danger REMEMBER
|
||||
The `ExpectedRequestParameter`, `ExpectedBodyField`, and `ExpectedBodyFile` instances are **_ONLY_** supposed to be retrieved by calling the respective
|
||||
`expectedRequestParameter()`, `expectedBodyField()`, and `expectedBodyFile()` methods **_INSIDE_** of the super constructor of your handler class.
|
||||
:::
|
||||
|
||||
## Example Usage
|
||||
|
||||
- `ExpectedRequestParameter`
|
||||
::: details Click to expand
|
||||
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.
|
||||
|
||||
```java{8,11,16}
|
||||
import flash.Request;
|
||||
import flash.Response;
|
||||
import flash.models.RequestHandler;
|
||||
import flash.models.ExpectedRequestParameter;
|
||||
|
||||
@RouteInfo(method = HttpMethod.GET, path = "/hello", enforceNonNullBody = false)
|
||||
public class MyHandler extends RequestHandler {
|
||||
// Store the expected parameter in a private field
|
||||
private final ExpectedRequestParameter myExpectedReqParam;
|
||||
public MyHandler(Request req, Response res) {
|
||||
super(req, res);
|
||||
// Get the expected parameter "myParam", and optionally provide a description
|
||||
myExpectedReqParam = expectedRequestParameter("myParam", "A description of the parameter");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object handle() {
|
||||
// OPTIONAL: specify the response status code and type
|
||||
res.status(200);
|
||||
res.type("text/plain");
|
||||
|
||||
// Safely get the parameter value as a String
|
||||
String myParamValue = myExpectedReqParam.getString();
|
||||
|
||||
// Return the response to the client
|
||||
return "Hello, " + myParamValue + "!";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Visiting `/hello?myParam=John` from your browser, will return `Hello, John!`.
|
||||
:::
|
||||
|
||||
- `ExpectedBodyField`
|
||||
::: details Click to expand
|
||||
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.
|
||||
|
||||
```java{8,11,16}
|
||||
import flash.Request;
|
||||
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 {
|
||||
// Store the expected field in a private field
|
||||
private final ExpectedBodyField myExpectedBodyField;
|
||||
public MyHandler(Request req, Response res) {
|
||||
super(req, res);
|
||||
// Get the expected field "myField", and optionally provide a description
|
||||
myExpectedBodyField = expectedBodyField("myField", "A description of the field");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object handle() {
|
||||
// OPTIONAL: specify the response status code and type
|
||||
res.status(200);
|
||||
res.type("text/plain");
|
||||
|
||||
// Safely get the field value as a String
|
||||
String myFieldValue = myExpectedBodyField.getString();
|
||||
|
||||
// Return the response to the client
|
||||
return "Field value: " + myFieldValue;
|
||||
}
|
||||
}
|
||||
```
|
||||
This time, since we are expecting a field in the request body, using our browser would not be enough to test the handler.
|
||||
Instead, you can use a tool like `Postman` to send a GET request to `/helloBody` with a multipart form data body containing a field named `myField`.
|
||||
You should receive a response like `Field value: <value>`.
|
||||
:::
|
||||
|
||||
- `ExpectedBodyFile`
|
||||
::: details Click to expand
|
||||
The `ExpectedBodyFile` object is used to get the expected files of the request body.
|
||||
The methods provided by this object are slightly different from the other two, but still extremely powerful and simple to use.
|
||||
|
||||
- `createFile(Path/String)` accepts either a Path or a String as input. It writes the file's contents to the specified location on the filesystem and returns a `File` object for further interaction.
|
||||
- `getFileName()` simply returns the name of the file specified by the client.
|
||||
- `getInputStream()` returns an `InputStream` object containing the file's contents.
|
||||
|
||||
```java{8,11,16}
|
||||
import flash.Request;
|
||||
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 {
|
||||
// Store the expected file in a private field
|
||||
private final ExpectedBodyFile myExpectedBodyFile;
|
||||
public MyHandler(Request req, Response res) {
|
||||
super(req, res);
|
||||
// Get the expected file "myFile", and optionally provide a description
|
||||
myExpectedBodyFile = expectedBodyFile("myFile", "A description of the file");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object handle() {
|
||||
// OPTIONAL: specify the response status code and type
|
||||
res.status(200);
|
||||
res.type("text/plain");
|
||||
|
||||
// Write the file to filesystem and get the File object
|
||||
File myFile = myExpectedBodyFile.createFile(Paths.get("path/to/save"));
|
||||
|
||||
// Return the response to the client
|
||||
return "File saved at: " + myFile.getAbsolutePath();
|
||||
}
|
||||
}
|
||||
```
|
||||
This time, you will need to use a tool like `Postman` to send a POST request to `/helloFile` with a multipart form data body containing a file named `myFile`.
|
||||
You should receive a response like `File saved at: <path>` where `<path>` is the location where the server saved the file.
|
||||
:::
|
||||
@@ -0,0 +1,61 @@
|
||||
---
|
||||
banner_title: "Flash - Server Router"
|
||||
banner_description: "Learn how to use the FlashServer router to create and manage RouteHandlers."
|
||||
---
|
||||
|
||||
# 🛣️ Server Router
|
||||
|
||||
In this section, we discuss how to use the `FlashServer` router to manage our `RequestHandler` instances.
|
||||
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.
|
||||
To access the router instance, you can call the `route()` method on either the internal server or your `FlashServer` instance.
|
||||
|
||||
## 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)
|
||||
and specify the base path of the route, followed by your handler class and optionally a `RouteInterceptor` instance.
|
||||
|
||||
(More on the concept of `RouteInterceptor` in the following sections).
|
||||
|
||||
```java{8,9}
|
||||
// Example.java
|
||||
import static flash.InternalFlashServer.*;
|
||||
|
||||
public class Example {
|
||||
public static void main(String[] args) {
|
||||
port(8080);
|
||||
|
||||
route("/api")
|
||||
.register(MyHandler.class);
|
||||
|
||||
start();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```java{6}
|
||||
// MyHandler.java
|
||||
import flash.Request;
|
||||
import flash.Response;
|
||||
import flash.models.RequestHandler;
|
||||
|
||||
@RouteInfo(method = HttpMethod.GET, path = "/hello", enforceNonNullBody = false)
|
||||
public class MyHandler extends RequestHandler {
|
||||
public MyHandler(Request req, Response res) {
|
||||
super(req, res);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object handle() {
|
||||
String response = "Hello, world!";
|
||||
return response;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In the example above, we create a route `/api` and register the `MyHandler` class to handle requests on the `/api/hello` endpoint.
|
||||
|
||||
This is because the `path` property of the `RouteInfo` annotation is relative to the base path of the route, which in this case is `/api`.
|
||||
|
||||
Visiting `/api/hello` from your browser will result in the response `Hello, world!`.
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
banner_title: "Flash - Server Types Guide"
|
||||
banner_title: "Flash - Server Types"
|
||||
banner_description: "Illustrating the main differences between the internal server instance and the FlashServer instance."
|
||||
---
|
||||
|
||||
@@ -82,7 +82,9 @@ This means:
|
||||
|
||||
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?
|
||||
|
||||
@@ -91,6 +93,7 @@ If you only need one server instance and want to keep your code concise, you can
|
||||
However, if your application needs to handle multiple server instances each with different ports and configurations,
|
||||
you should use `FlashServer` instances.
|
||||
|
||||
Example use cases for each server type:
|
||||
- Internal Server: Simple Backend for a webapp.
|
||||
- Server Instance: Applications where flexible deployment and scalability are required.
|
||||
::: 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.
|
||||
:::
|
||||
|
||||
Reference in New Issue
Block a user