0

I am trying to validate a form in my POST method which registers new users. In the case that errors arise such as a username that is already used, I want my View, which is retrieved from my GET method to be updated with the error. I am using Dropwizard Views and Freemarker. My code thus far:

 public class SignUpView extends View {

    List<String> errors;

    public SignUpView() {
        super("signup.ftl");
        errors = new ArrayList<>();

    }

    public SignUpView(List<String> errors) {
        super("signup.ftl");
        this.errors = errors;

    }

}

signup.ftl:

<#-- @ftlvariable name="" type="com.tm.resources.views.SignUpView" -->
<#include "include/head.html">
<#include "include/header.html">

<br><br><br>

<form action="/signup" method="post">
  <div class="container">
    <h1>Sign Up</h1>
    <p>Please fill in this form to create an account.</p>
    <hr>
    <#if errors?has_content>
        <ul class="errorMessages">
             <#list errors as error>
                 <li>${error}</li>
             </#list>
        </ul>
    </#if>    
    <label><b>Full name</b></label>
    <input type="text" placeholder="full name" name="full_name" required>

    <label><b>Email</b></label>
    <input type="email" placeholder="Enter Email" name="email" required>

    <label><b>Password</b></label>
    <input type="password" placeholder="Enter Password" name="psw" required>

    <label><b>Repeat Password</b></label>
    <input type="password" placeholder="Repeat Password" name="psw-repeat" required>

    <label>
      <input type="checkbox" checked="checked" style="margin-bottom:15px"> Remember me
    </label>

    <p>By creating an account you agree to our <a href="#" style="color:dodgerblue">Terms & Privacy</a>.</p>

    <div class="clearfix">
      <button type="button" class="cancelbtn btn-form">Cancel</button>
      <button type="submit" class="signupbtn btn-form">Sign Up</button>
    </div>
  </div>
</form>


<#include "include/footer.html">

And my resource class:

@Path("/signup")
@Produces(MediaType.TEXT_HTML)
public class SignUpResource {

    // UserService
    private UserService userService;

    public SignUpResource(UserService userService) {
        this.userService = userService;
    }

    @GET
    public SignUpView getView() {

            return new SignUpView();
    }

    @POST
    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
    @Timed
    @UnitOfWork
    public Response registerUserInfo(@FormParam("full_name") String name, 
            @FormParam("email") String email,
            @FormParam("psw") String pwd,
            @FormParam("psw-repeat") String pwdRepeat){

        String responseString = "Successfully added user details, name: "+
                name+" and pwd: "+pwd;

        try {
            userService.addUser(name,email, pwd);
        } catch (ShowAbleException e) {
            // if adding triggered an error it must be displayed on the page
            List<String> errors = new ArrayList<String>();
            errors.add(e.getMessage());

            // HOW to go back to GET method with errors?? Or just display them?
        }

        try {
            //return Response.created(new URI("/")).entity(response).build();
            return Response.seeOther(new URI("/")).build(); // redirect back to homepage...
        } catch (URISyntaxException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            throw new RuntimeException(e.getMessage(), e);
        }
    } 
}

I have came across this page which gives some guidance, but I failed to replicate it as the source is not fully provided and it does not use Dropwizard's Views functionality. I also considered adding parameters to my GET method and redirect from the POST method, but this seems like overkill to me?

Any help will be appreciated.

TM00
  • 1,330
  • 1
  • 14
  • 26
  • I don't know anything Dropwizard-specific about this, but the pattern that's the easiest to maintain is when a form POST-s to its own page URL. Then the error message and the values to re-display are right there, you don't need to pass them around through cookies and query parameters and what not. If the submission has succeeded, you can forward to the next page (an extra round trip, no doubt). – ddekany Jan 22 '18 at 18:29
  • Thanks, that makes a lot of sense! I took your advice and found a thread that showed that one can respond with a new View. So I updated the errors array and returned a new Response with a View as its entity. – TM00 Jan 25 '18 at 10:26

1 Answers1

1

I managed to solve this by coming across this thread which indicated that one can send a new View as a response. So if errors are encountered, a new View is created in a Response with the updated errors. Code below:

 try {
            userService.addUser(name,email, pwd);
        } catch (ShowAbleException e) {
            // if adding triggered an error it must be displayed on the page
            List<String> errors = new ArrayList<String>();
            errors.add(e.getMessage());

            return Response.ok(new SignUpView(errors)).build();
        }

        try {
            //return Response.created(new URI("/")).entity(response).build();
            return Response.seeOther(new URI("/")).build(); // redirect back to homepage...
        } catch (URISyntaxException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            throw new RuntimeException(e.getMessage(), e);
        }

I also picked up that my SignUpView class requires a getErrors method for the errors list to be accessible to the Freemarker file. So I added:

public List<String> getErrors() {
    return errors;
}
TM00
  • 1,330
  • 1
  • 14
  • 26