The Request Object
In CabinJ, the Request
object represents an incoming HTTP request. It provides a comprehensive API to access various parts of the request, such as headers, path, query parameters, path parameters, and the request body in different formats. The server automatically parses the incoming raw HTTP request and populates this object for you.
Core Components
A Request
object encapsulates:
- HTTP Method: e.g., GET, POST, PUT.
- Path: The requested URL path (e.g.,
/users/123
). - Headers: A map of HTTP request headers.
- Query Parameters: Parameters from the URL's query string (e.g.,
?name=cabin&version=1
). - Path Parameters: Parameters extracted from the route path (e.g.,
:id
in/users/:id
). These are typically populated by the Router. - Request Body: The payload of the request, accessible as a raw string, a JSON map, or a deserialized Java object.
- Form Fields: Data submitted via
application/x-www-form-urlencoded
ormultipart/form-data
. - Uploaded Files: Files submitted via
multipart/form-data
. - Client IP Address: The IP address of the client making the request, with support for proxy headers.
- Custom Attributes: A mechanism to store request-scoped data.
Thread Safety: Instances of the Request
class are not thread-safe. They are designed to be used by a single thread handling an individual HTTP request.
Accessing Request Information
Method and Path
// Inside a handler or middleware
String method = req.getMethod(); // "GET", "POST", etc.
String path = req.getPath(); // e.g., "/users/profile"
String baseUrl = req.getBaseUrl(); // e.g., "http://localhost:8080" (if set by server)
Headers
Headers are case-insensitive.
String contentType = req.getHeader("Content-Type");
String userAgent = req.getHeader("User-Agent");
Map<String, String> allHeaders = req.getHeaders();
allHeaders.forEach((key, value) -> {
System.out.println(key + ": " + value);
});
Query Parameters
Query parameters are extracted from the URL string after the ?
.
// For a request to /search?q=cabin&page=1
String searchTerm = req.getQueryParam("q"); // "cabin"
Integer pageNumber = req.getQueryParamAsInt("page", 1); // 1 (with default)
Long limit = req.getQueryParamAsLong("limit", 10L); // 10 (with default)
Map<String, String> allQueryParams = req.getQueryParams();
Path Parameters
Path parameters are defined in your routes (e.g., /users/:userId
) and populated by the Router.
// For a route like router.get("/users/:userId/orders/:orderId", ...)
// and a request to /users/123/orders/456
String userId = req.getPathParam("userId"); // "123"
Integer orderId = req.getPathParamAsInt("orderId", null); // 456
Long numericUserId = req.getPathParamAsLong("userId", null); // 123L
Working with the Request Body
CabinJ automatically attempts to parse the request body based on the Content-Type
header.
Raw Body as String
String rawBody = req.getBodyAsString();
System.out.println("Raw request body: " + rawBody);
JSON Body
If the Content-Type
is application/json
, the body is automatically parsed.
1. As a Map<String, Object>
:
// For a JSON body like: {"name": "CabinJ", "version": 1.0}
Map<String, Object> jsonBody = req.getBody(); // This is the default for getBody() with JSON
if (jsonBody != null) {
String name = (String) jsonBody.get("name");
Double version = (Double) jsonBody.get("version"); // Numbers are often Doubles
}
2. As a Custom Java Object (POJO):
You need a corresponding Java class (e.g., MyPayload
) and Gson (which CabinJ uses internally) must be able to deserialize into it.
// Assuming a class:
// public class MyPayload {
// private String name;
// private double version;
// // getters and setters
// }
MyPayload payload = req.getBodyAs(MyPayload.class);
if (payload != null) {
System.out.println("Payload Name: " + payload.getName());
}
// For generic types (e.g., List<MyObject>)
// import java.lang.reflect.Type;
// import com.google.gson.reflect.TypeToken;
// Type listType = new TypeToken<List<MyObject>>() {}.getType();
// List<MyObject> objects = req.getBodyAs(listType);
Form Data (application/x-www-form-urlencoded
or multipart/form-data
)
1. Form Fields:
String username = req.getFormField("username");
String password = req.getFormField("password", "defaultPassword"); // With default
List<String> allFieldNames = req.getFormFields(); // Get names of all submitted fields
2. Uploaded Files (multipart/form-data
):
Files are accessible as UploadedFile
objects, which contain the filename, content type, and file data as bytes. A single field name can have multiple files.
// Assuming a form field named "profileImage" used for file upload
List<UploadedFile> files = req.getUploadedFile("profileImage");
if (files != null && !files.isEmpty()) {
UploadedFile profilePic = files.get(0); // Get the first file if multiple
String fileName = profilePic.getFilename();
String contentType = profilePic.getContentType();
byte[] fileData = profilePic.getData();
// Process the fileData (e.g., save to disk)
// Files.write(Paths.get("./uploads/" + fileName), fileData);
}
The UploadedFile
class typically has methods like:
getFilename()
: Original filename from the client.getContentType()
: MIME type of the file.getData()
: File content asbyte[]
.getSize()
: Size of the file in bytes.
Client IP Address
CabinJ attempts to determine the client's true IP address, even when behind proxies, by checking common headers like X-Forwarded-For
.
String clientIp = req.getIpAddress();
The server should call req.setRemoteIpAddress(socket.getRemoteSocketAddress().getAddress().getHostAddress())
when the connection is first established to provide the direct connection IP as a fallback. The getIpAddress()
method then uses a list of standard headers to find a more accurate client IP if proxies are involved.
Custom Attributes
You can store and retrieve request-scoped data using attributes. This is useful for passing data between middleware or from middleware to handlers. Attributes are typed.
// In a middleware:
User authenticatedUser = userService.authenticate(req);
if (authenticatedUser != null) {
req.putAttribute(User.class, authenticatedUser);
}
// In a later middleware or handler:
User currentUser = req.getAttribute(User.class);
if (currentUser != null) {
System.out.println("Current user: " + currentUser.getUsername());
}
Internal Parsing
The Request
object is typically constructed by the server from a ByteArrayOutputStream
or byte[]
containing the raw HTTP request data. It then parses:
- The request line (method, full path, HTTP version).
- Headers.
- The path and query string from the full path.
- The body, based on
Content-Length
andContent-Type
(URL-encoded, multipart, JSON).
This parsing happens automatically before your handlers or middleware receive the Request
object.