In this example we created an async servlet that adds contacts to the list, parses requests and processes form
validation with the help of Decoder
.
You can find full example sources on GitHub.
Here we will consider only the HttpDecoderExample class with AsyncServlet as it contains DataKernel-specific features.
Consider this example as a concise representation of the MVC pattern:
HttpDecoderExample
ClassLet’s create the HttpDecoderExample class which extends HttpServerLauncher. By extending the HttpServerLauncher we will take care of the server’s lifecycle and service management. Next, we provide two custom parsers based on HttpDecoder- ADDRESS_DECODER and CONTACT_DECODER - which will be used for validation.
public final class HttpDecoderExample extends HttpServerLauncher {
private final static String SEPARATOR = "-";
private final static Decoder<Address> ADDRESS_DECODER = Decoder.of(Address::new,
ofPost("title", "")
.validate(param -> !param.isEmpty(), "Title cannot be empty")
);
private final static Decoder<Contact> CONTACT_DECODER = Decoder.of(Contact::new,
ofPost("name")
.validate(name -> !name.isEmpty(), "Name cannot be empty"),
ofPost("age")
.map(Integer::valueOf, "Cannot parse age")
.validate(age -> age >= 18, "Age must be greater than 18"),
ADDRESS_DECODER.withId("contact-address")
);
Also, we need to create applyTemplate(Mustache mustache, Map<String, Object> scopes) method to fill the provided Mustache template with the given data:
private static ByteBuf applyTemplate(Mustache mustache, Map<String, Object> scopes) {
ByteBufWriter writer = new ByteBufWriter();
mustache.execute(writer, scopes);
return writer.getBuf();
}
Next, let’s provide a ContactDAOImpl factory method:
@Provides
ContactDAO dao() {
return new ContactDAOImpl();
}
Now we have everything needed to create AsyncServlet to handle requests:
@Provides
AsyncServlet mainServlet(ContactDAO contactDAO) {
Mustache contactListView = new DefaultMustacheFactory().compile("static/contactList.html");
return RoutingServlet.create()
.map("/", request ->
HttpResponse.ok200()
.withBody(applyTemplate(contactListView, map("contacts", contactDAO.list()))))
.map(POST, "/add", AsyncServletDecorator.loadBody()
.serve(request -> {
//[START REGION_3]
Either<Contact, DecodeErrors> decodedUser = CONTACT_DECODER.decode(request);
//[END REGION_3]
if (decodedUser.isLeft()) {
contactDAO.add(decodedUser.getLeft());
}
Map<String, Object> scopes = map("contacts", contactDAO.list());
if (decodedUser.isRight()) {
scopes.put("errors", decodedUser.getRight().toMap(SEPARATOR));
}
return HttpResponse.ok200()
.withBody(applyTemplate(contactListView, scopes));
}));
}
"/"
-
it simply displays a contact list.
The second one, "/add"
- is an HTTP POST
method that adds or declines adding new users. We will process this request parsing with the
help of the aforementioned HttpDecoder by using decode(request) method:Either<Contact, DecodeErrors> decodedUser = CONTACT_DECODER.decode(request);
Finally, write down the main() method which will launch our application:
public static void main(String[] args) throws Exception {
Launcher launcher = new HttpDecoderExample();
launcher.launch(args);
}
If you want to run the example, you need to clone DataKernel and import it as a Maven project. Check out branch v3.1. Before running the example, build the project (Ctrl + F9 for IntelliJ IDEA).
Then open HttpDecoderExample class, which is located at datakernel -> examples -> tutorials -> decoder and run its main() method. Open your favourite browser and go to localhost:8080.