diff --git a/flash/config/sidebar.json b/flash/config/sidebar.json index 38bddae..a590fa1 100644 --- a/flash/config/sidebar.json +++ b/flash/config/sidebar.json @@ -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" } ] } ] diff --git a/flash/request-handler.md b/flash/request-handler.md new file mode 100644 index 0000000..6562c0a --- /dev/null +++ b/flash/request-handler.md @@ -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). \ No newline at end of file diff --git a/flash/request-response.md b/flash/request-response.md new file mode 100644 index 0000000..6c2a3f4 --- /dev/null +++ b/flash/request-response.md @@ -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. +![](https://miro.medium.com/v2/resize:fit:696/1*wzzLb_xz5Mlykcd1bBqDig.png) + +## 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: `. +::: + +- `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: ` where `` is the location where the server saved the file. +::: diff --git a/flash/server-router.md b/flash/server-router.md new file mode 100644 index 0000000..3f6cb11 --- /dev/null +++ b/flash/server-router.md @@ -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!`. \ No newline at end of file diff --git a/flash/server-types.md b/flash/server-types.md index 4a8d244..d769e7d 100644 --- a/flash/server-types.md +++ b/flash/server-types.md @@ -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. +:::