We need to provide a way to let people register to our
spittr web application, this is done here relying on the support offered by the Spring framework, that easily support the forms, giving a natural way for processing them.
The Model
A registered user is defined as a Spitter:
public class Spitter {
private Long id;
private String username;
private String password;
private String firstName;
private String lastName;
private String email;
// ...
}
We'll need to check for equality on different Spitters, so we need to override both equals() and hashCode(). I did it using Objects.equals() and Objects.hash() in their implementions. I feel they helps to keep the code compact and readable without using external libraries.
We'll have a repository where storing our spitters, but we don't need to fix its type here, just the interface that each spitter repository has to implements:
public interface SpitterRepository {
Spitter save(Spitter spitter);
Spitter findByUsername(String username);
}
Actually, just to do some smoke testing, it is nice to have a concrete repository to play with:
@Repository
public class JdbcSpitterRepository implements SpitterRepository {
@Override
public Spitter save(Spitter spitter) {
// TODO: actual implementation
return spitter;
}
@Override
public Spitter findByUsername(String username) {
// TODO: actual implementation
return new Spitter(username, "password", "John", "Doe", "jdoe@somesite.dd");
}
}
I assumed it will be refactored in future to become a real JDBC repository, hence its name. Notice that the class is annotated as Spring repository.
Data Configuration
Having introduced a new repository in the web app, we should say to Spring how to get it. To do that I added a bean (in the sense of Spring Bean) in the already existing DataConfig java class, annotated as Spring configuration.
@Configuration
public class DataConfig {
// ...
@Bean
public SpitterRepository spitterRepository() {
return new JdbcSpitterRepository();
}
}
The View
The user enters data filling a simple HTML form, something like that:
<form method="POST">
First Name: <input type="text" name="firstName" /><br />
Last Name: <input type="text" name="lastName" /><br />
Email: <input type="email" name="email" /><br />
Username: <input type="text" name="username" /><br />
Password: <input type="password" name="password" /><br />
<input type="submit" value="Register" />
</form>
Being no action attribute specified in the form tag, the post is done referring to the same URL accessed originally. Not much more to say on this part, you could see the full registerForm.jsp file on GitHub.
We'll also have a way to show a registration, accessing profile.jsp. Our Spring web app should take care of putting in a variable named spitter, an instance of the above discussed Spitter class, that represents the currently registered user. So, to display it, using JSP EL, we'll write something like:
<c:out value="${spitter.username}" /><br />
<c:out value="${spitter.firstName}" />
<c:out value="${spitter.lastName}" /><br />
<c:out value="${spitter.email}" />
The Controller
As usual, the controller keeps model and view together. The Spitter controller reacts to calls based in "/spitter". We want it to convert a call to "/spitter/register" showing the register form view or redirecting it to /spitter/xxx, where xxx is the username, accordingly to the HTTP command used. GET is for the first one, POST for the second. Any "/spitter/xxx" GET call will drive to the profile view.
@Controller
@RequestMapping("/spitter")
public class SpitterController { // 1
private SpitterRepository spitterRepository; // 2
public SpitterController(SpitterRepository spitterRepository) {
this.spitterRepository = spitterRepository;
}
@RequestMapping(value = "/register", method = GET) // 3
public String showRegistrationForm() {
return "registerForm";
}
@RequestMapping(value = "/register", method = POST) // 4
public String processRegistration(Spitter spitter) {
spitterRepository.save(spitter);
return "redirect:/spitter/" + spitter.getUsername();
}
@RequestMapping(value = "/{username}", method = GET) // 5
public String showSpitterProfile(@PathVariable String username, Model model) { // 6
Spitter spitter = spitterRepository.findByUsername(username); // 7
model.addAttribute(spitter);
return "profile";
}
}
1. This class is annotated as Spring controller, and it maps calls rooted in "/spitter".
2. We are going to use a repository that has to implement the SpitterRepository.
3. Maps a GET to "/spitter/register" to the registerForm view.
4. Redirects a POST to "/spitter/register" to the "/spitter/xxx" view. A spitter is expected as parameter, and it is going to be saved to the repository before the redirect is performed.
5. Maps a GET to "/spitter/xxx" to the profile view.
6. The username is extracted from the path by the PathVariable Spring binding annotation.
7. The repository is asked to give the spitter associated with the username, then we push it to the model.
Testing
The controller design has been driven by a couple of mock JUnit tests, using Mockito, defined in the SpitterControllerTest class. Besides, having provided a (fake) concrete implementation for the spitter repository, it is possible to run the web app and see its general behavior.
So, GETting "/spitter/register", we'll see the input form:
As soon as we push the Register button, a POST will be generated for the same URL. As seen above, the controller will redirect to "/spitter/jdoe"
So. Our app works. At least in a sort of demo mode. It is easy to spot how weak is, however. We are going to improve its robustness in the next post.
Reference: Processing forms, from Spring in Action, Fourth Edition by Craig Walls. Chapter five, section four.
Full Java Spring code available on GitHub.
Go to the full post