-
Notifications
You must be signed in to change notification settings - Fork 27
HTTP
A typical datamill application will be deployed and run as a standalone Java application. That means that when your application starts, you will typically create a HTTP server - for example, take a look at a simple server:
public static void main(String[] args) {
// Note the Server class here is foundation.stack.datamill.http.Server
Server server = new Server(
rb -> rb.ifMethodAndUriMatch(Method.GET, "/status", r -> r.respond(b -> b.ok()))
.elseIfMethodAndUriMatch(Method.GET, "/hello", r -> r.respond(b -> b.ok("Hello world!")))
.orElse(r -> r.respond(b -> b.notFound())));
server.listen(8081);
}This application will start a HTTP server at port 8081, that responds to GET requests at /status with an empty HTTP 200 OK response, and responds to GET /hello requests with an OK response with the content Hello world!. All other requests return a HTTP 404 response.
While explicitly describing routes in this manner makes it very obvious what the routes in your application are, it can become slightly difficult to manage. That's why you can have the server try to match incoming HTTP requests to a bean in your application that is annotated with the appropriate annotations:
import foundation.stack.datamill.http.Response;
import foundation.stack.datamill.http.ServerRequest;
import foundation.stack.datamill.http.annotations.GET;
import foundation.stack.datamill.http.annotations.PATCH;
import foundation.stack.datamill.http.annotations.POST;
import foundation.stack.datamill.http.annotations.Path;
import rx.Observable;
@Path("/users")
public class UserController {
@POST
public Observable<Response> addUser(ServerRequest request) {
}
@GET
@Path("/{userName}")
public Observable<Response> getUser(ServerRequest request) {
}
@PATCH
@Path("/{userName}/password")
public Observable<Response> updatePassword(ServerRequest request) {
}
@PATCH
@Path("/{userName}")
public Observable<Response> updateUser(ServerRequest request) {
}
}Doing it this way allows you to organize your request handling more cleanly. Java Enterprise developers would be familiar with similar annotations in other APIs and frameworks for creating HTTP services.
In datamill, a key focus is on making it explicit how requests flow to your application, and the handlers for those requests. To actually use the annotated class, you will have to pass in an instance when creating the Server:
public static void main(String[] args) {
OutlineBuilder outlineBuilder = new OutlineBuilder();
Server server = new Server(
rb -> rb.ifMethodAndUriMatch(Method.GET, "/status", r -> r.respond(b -> b.ok()))
.elseIfMatchesBeanMethod(outlineBuilder.wrap(new UserController()))
.orElse(r -> r.respond(b -> b.notFound())));
server.listen(8081);
}(The OutlineBuilder class used in this example is explained in the Reflection section.)
The HTTP requests are now routed to the UserController instance if they do not match the first check for a GET /status request. Note that you can use annotated classes for request handling while still having knowledge of when and where the request is matched against the annotated class.
Note that all request handlers must take a single parameter of type ServerRequest. The ServerRequest contains methods for accessing the request URI, parameters and entity. Finally, the ServerRequest also contains the respond(...) method to help build a response. Because the handlers are expected to take a non-trivial amount of time, they return a Observable<Response> - they would emit the Response once they are done.