2

Background and Context

I have a Spring Boot application that runs as an executable JAR in an embedded Tomcat container. The application exposes a variety of traditional RESTful services. In addition to the the traditional RESTful services I also need to create an endpoint that is more similar to a traditional servlet.

I define Traditional RESTful services as endpoints that use standard HTTP methods ( GET, POST, PUT, etc ) and accept JSON or XML request bodies ( when applicable method is used ) and returns JSON or XML. I define traditional in contrast with servlet-like endpoints that accept multipart/form-date and redirect the client after the method is called.

This endpoint signature is as follows

  1. Accepts a multipart/form-data
  2. POST method
  3. Redirects users to the base url of the value assigned to the referer header after submission

The form data sent to this endpoint is sent from a form that I do not own or control ( I have included the html form below ). Because the form has many oddly named key/value pairs I want to encapsulate the submission in a FormSubmission object.

Code

Below is the form used to send data to my endpoint

<form novalidate="" accept-charset="UTF-8" action="https://baseurl.com/brochure" enctype="multipart/form-data" id="hsForm_4b5eec88-e83b-474f-9c51-6204df71e5a5" method="POST" class="hs-form stacked hs-form-private hs-form-4b5eec88-e83b-474f-9c51-6204df71e5a5_bc44b3dd-63e5-4a6f-85d2-4c2c4b436bf8" data-form-id="4b5eec88-e83b-474f-9c51-6204df71e5a5" data-portal-id="482700" target="target_iframe_4b5eec88-e83b-474f-9c51-6204df71e5a5" data-reactid=".hbspt-forms-0">
   <input id="firstname-4b5eec88-e83b-474f-9c51-6204df71e5a5" type="text" name="firstname"></div>
   <input id="lastname-4b5eec88-e83b-474f-9c51-6204df71e5a5" type="text" name="lastname"></div>
   <input id="address-4b5eec88-e83b-474f-9c51-6204df71e5a5" type="text" name="address"></div>
   <select id="state-4b5eec88-e83b-474f-9c51-6204df71e5a5" name="state" >
      <option value="" disabled="" selected="" >- Please Select -</option>
      <option value="AL" >Alabama</option>
      <option value="AK">Alaska</option>
   </select>
   <select id="country_custom-4b5eec88-e83b-474f-9c51-6204df71e5a5"  name="country_custom" ">
      <option value="United States">United States</option>
      <option value="Canada">Canada</option>
   </select>
   <input id="zip-4b5eec88-e83b-474f-9c51-6204df71e5a5" class="hs-input" type="text" name="zip"></div>
   <input id="email-4b5eec88-e83b-474f-9c51-6204df71e5a5" class="hs-input" type="email" name="email"></div>
   <select id="want_to_talk_with_an_area_builder_-4b5eec88-e83b-474f-9c51-6204df71e5a5" name="want_to_talk_with_an_area_builder_">
      <option value="Yes">Yes, now please.</option>
      <option value="No">No thank you</option>
   </select>
   <select id="blog_pool_life_2_subscription-4b5eec88-e83b-474f-9c51-6204df71e5a5" name="blog_pool_life_2_subscription"
      <option value="instant">Instant</option>
      <option value="daily">Daily</option>
   </select>
</form>

The above form sends multipart/form-data to the following endpoint

@PostMapping("/brochure")
public RedirectView post( @ModelAttribute FormSubmission submission, RedirectAttributes attributes, @RequestHeader( value = "referer", required = true ) final String referer ) {

    String base = getBaseURL( referer );

    System.out.println( submission.getLastName() );

    return new RedirectView( base );
}

And the FormSubmission class is as follows

public class FormSubmission {

    private String firstName;
    private String lastName;
    private String street;
    private String city;
    private String state;
    private String zip;
    private String email;
    private String country;
    private Boolean mail;
    private String speakWithBuilder;

    @ModelAttribute("city")
    public String getCity() {
        return city;
    }

    @ModelAttribute("city")
    public void setCity(String city) {
        this.city = city;
    }

    @ModelAttribute("state")
    public String getState() {
        return state;
    }

    @ModelAttribute("state")
    public void setState(String state) {
        this.state = state;
    }

    @ModelAttribute("zip")
    public String getZip() {
        return zip;
    }

    @ModelAttribute("zip")
    public void setZip(String zip) {
        this.zip = zip;
    }

    @ModelAttribute("country_custom")
    public String getEmail() {
        return email;
    }

    @ModelAttribute("country_custom")
    public void setEmail(String email) {
        this.email = email;
    }

    @ModelAttribute("country_custom")
    public String getCountry() {
        return country;
    }

    @ModelAttribute("country_custom")
    public void setCountry(String country) {
        this.country = country;
    }

    @ModelAttribute("blog_pool_life_2_subscription")
    public Boolean isMail() {
        return mail;
    }

    @ModelAttribute("blog_pool_life_2_subscription")
    public void setMail( String mail ) {
        this.mail = mail.toLowerCase().contains( "yes" );
    }

    @ModelAttribute("firstname")
    public String getFirstName() {
        return firstName;
    }

    @ModelAttribute("firstname")
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    @ModelAttribute("lastname")
    public String getLastName() {
        return lastName;
    }

    @ModelAttribute("lastname")
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    @ModelAttribute("address")
    public String getStreet() {
        return street;
    }

    @ModelAttribute("address")
    public void setStreet(String street) {
        this.street = street;
    }

    @ModelAttribute("want_to_talk_with_an_area_builder_")
    public String getSpeakWithBuilder() {
        return speakWithBuilder;
    }

    @ModelAttribute("want_to_talk_with_an_area_builder_")
    public void setSpeakWithBuilder(String speakWithBuilder) {
        this.speakWithBuilder = speakWithBuilder;
    }


}

Problem Statement

As you can see my endpoint defines a @ModelAttribute FormSubmission submission, and my submission object notates each getter and setter with the name of elements from the HTML form. However, the print statements show as null. I swore that this was working a few days ago, and now all values are null. Please advice.

Post Script #1

For additional clarification, when I modify my endpoint signature as follows

@PostMapping("/brochure")
public RedirectView post( @ModelAttribute("firstname") String firstName, RedirectAttributes attributes, @RequestHeader( value = "referer", required = true ) final String referer ) {

    System.out.println( firstName );
    String base = getBaseURL( referer );


    return new RedirectView( base );
}

And call the endpoint the first name does print successfully. I hope this better illustrates that my question is specifically about encapsulating these form data key/value pairs inside an object.

Post Script #2

I was able to successfully encapsulate the data if I:

  1. Remove @ModelAttribute from the FormSubmission parameter, and
  2. Remove the @ModelAttribute from the getter and setter methods for the private member variables inside FormSubmission

This is better but I would still like to be able to name by variables consistent with Java's naming convention which I cannot do without ModelAttribute since I do not control the form

Chris Maggiulli
  • 3,375
  • 26
  • 39
  • 1
    I have the same problem, I got that object but is empty. [Look at this](https://stackoverflow.com/questions/2422468/how-can-i-upload-files-to-a-server-using-jsp-servlet?answertab=scoredesc#tab-top) – user2162180 Mar 14 '22 at 18:37

0 Answers0