Fork me on GitHub

Interactive HTTP Server Tutorial

This tutorial looks at handling a form submission.

The below example for this tutorial will implement a simple form submission. The form submission will validate a name was entered and provide a message if successfully entered. The simple form for this tutorial is as follows:

InteractiveHttpServer screen shot.

Download Tutorial Source

Template.woof.html

The below is the content of the Template.woof.html.

<html>
	<body>
		<form action="#{handleSubmission}" method="POST">
			<p>Name: <input type="text" name="name" value="${name}" /> ${nameIssue ${message} $}</p>
			<p>Description: <input type="text" name="description" value="${description}" /></p>
			<p><input type="submit" value="Add" /></p>
		</form>
		<p>${successMessage}</p>
	</body>
</html>

This is similar HTML to previous tutorials.

TemplateLogic Class

The logic to handle the form submission is the following:

public class TemplateLogic {

	@Data
	@HttpParameters
	public static class Parameters implements Serializable {

		private String name;

		private Issue nameIssue;

		private String description;

		private String successMessage;
	}

	@Data
	public static class Issue implements Serializable {

		private final String message;
	}

	/**
	 * Obtains the bean for rendering the template.
	 * 
	 * @param submittedParameters
	 *            Same {@link Parameters} that was constructed for
	 *            {@link #handleSubmission(Parameters)}. This allows the page to
	 *            be rendered with the values provided by the client.
	 * @return {@link Parameters} for rendering to page.
	 */
	public Parameters getTemplateData(Parameters submittedParameters) {
		return submittedParameters;
	}

	/**
	 * Reflectively invoked to handle form submission.
	 * 
	 * @param submittedParameters
	 *            {@link Parameters} which is dependency injected. It is
	 *            constructed via its default constructor and has the HTTP
	 *            parameters values loaded by corresponding names.
	 */
	public void handleSubmission(Parameters submittedParameters) {

		// Ensure have a name provided
		String name = submittedParameters.getName();
		if ((name == null) || (name.trim().length() == 0)) {
			submittedParameters.setNameIssue(new Issue("Must provide name"));
			return;
		}

		// TODO use values to undertake some business logic. Typically would
		// provider further dependencies as parameters to this method to allow
		// this.

		// Provide success message (and clear values)
		submittedParameters.setSuccessMessage("Thank you " + name);
		submittedParameters.setName(null);
		submittedParameters.setDescription(null);
	}
}

The aspects to notice are:

  • the method handleSubmission() matches in name to the #{handleSubmission} of the HTML form action. As the names are the same, WoOF will reflectively invoke the method to handle the form submission. By default WoOF will re-render the page after the method completes. Later tutorials will look at controlling navigation to other pages.
  • the Parameters inner class is annotated with @HttpParameters. As this is dependency injected into the form handling method, WoOF sees the annotation and will construct an instance of the object by its default constructor and load the HTTP parameters to its setter methods by corresponding names (e.g. name to setName(String name)). Note that this object is constructed once for each HTTP request so is the same object dependency injected into the getTemplateData(...) method - allowing entered values to be re-rendered to the page.
  • the Parameters inner class is serializable. By default WoOF will undertake the Post/Redirect/Get pattern after the handleSubmission() method completes and before the template is re-rendered. To enable the state of the Parameters object to be available across the redirect, it is added to the HttpSession and subsequently must be serializable.

OfficeFloor achieves this simple interactive programming model by substituting into the rendered page a unique url which it can map back to the corresponding method. The method is matched by its name and is free to have any parameters it requires (enabled by OfficeFloor's continuation injection and dependency injection). For example in more complex applications the handling methods may include a parameter for a DataSource or EntityManager to enable database interaction rather than just providing a message back to the client.

Unit Test

The unit test requests the various URL's exposed from the template.

	private final CloseableHttpClient client = HttpTestUtil.createHttpClient();

	public void testPageInteraction() throws Exception {

		// Start server
		WoofOfficeFloorSource.start();

		// Request the initial blank template
		this.doRequest("http://localhost:7878/example.woof");

		// Send form submission
		this.doRequest("http://localhost:7878/example-handleSubmission.woof?name=Daniel&description=founder");
	}

	private void doRequest(String url) throws Exception {
		HttpResponse response = this.client.execute(new HttpGet(url));
		assertEquals("Request should be successful", 200, response
				.getStatusLine().getStatusCode());
		response.getEntity().writeTo(System.out);
	}

Next

The next tutorial looks at controlling navigation between pages (templates).