gene b.
gene b.

Reputation: 11974

Ajax POST Array of Objects to Spring MVC Controller - Multiple Errors

I need to submit a JS-formed Array of Objects to a Spring MVC Controller. All the property names match.

@PostMapping("/addAdmin")
public void addAdmin(@RequestParam List<UserRolesGUIBean> userRolesGUIBeans)
{
    // ...      
}   

JS:

 var entries = [];
 //...
 // entries is an array of objects of the form {id: "..", role: ".."}
 // verified to be correct before submission

 $.ajax({
        type : "post",
        dataType : "json", 
        url : 'addAdmin',  
        data : JSON.stringify(entries)
 })

Bean

public class UserRolesGUIBean implements Serializable {
    private String id;
    private String role;
    // + Constructors (Empty + Full), Getters and setters
}

Error:

Required List parameter 'userRolesGUIBeans' is not present]

Also tried this with ModelAttribute and an ArrayList,

PostMapping("/addAdmin")
    public void addAdmin(@ModelAttribute ArrayList<UserRolesGUIBean> userRolesGUIBeans)  {

Now there are no errors, but the list is empty, no data was received.

Tried everything -- arrays vs. lists, JSON.stringify(data) or a data object with data {"entries" : entries}, RequestBody doesn't work and gives UTF Errors; and RequestParam as above doesn't work either.

This is way too complicated for a simple task.

Upvotes: 2

Views: 7557

Answers (3)

Mr.J4mes
Mr.J4mes

Reputation: 9266

As promised, please go to https://start.spring.io/ and create a new project with a single depdendency for spring-boot-starter-web.

After that, you can create the following bean in your project.

import java.util.List;

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DemoController {
    @PostMapping("/request-body")
    public void getRequestBody(@RequestBody List<Person> list) {
        for (Person person : list) {
            System.out.println(person.name);
        }
    }

    public static class Person {
        private String name;
        private String phoneNo;

        /**
         * @return the name
         */
        public String getName() {
            return name;
        }
        /**
         * @param name the name to set
         */
        public void setName(String name) {
            this.name = name;
        }
        /**
         * @return the phoneNo
         */
        public String getPhoneNo() {
            return phoneNo;
        }
        /**
         * @param phoneNo the phoneNo to set
         */
        public void setPhoneNo(String phoneNo) {
            this.phoneNo = phoneNo;
        }
    }
}

Nothing special, just take in a list of Person and print out the names. You can right click and run the project directly from the IDE.

You can open Postman and make a POST request as following.

enter image description here enter image description here

This is what gets printed in the console.

enter image description here

If it works with Postman, you can make it work in JS. You just haven't figured out how. Instead of settling with that "workaround" you found, I think you should find out the proper way to submit a request in JS. In addition, some understanding of the Spring framework would help too. Otherwise, you will just keep randomly trying stuff like @ModelAttribute without getting anywhere.

Upvotes: 2

gene b.
gene b.

Reputation: 11974

SOLUTION:

1) In theory, if I was doing Form Submission (like $('#myForm').submit()), I could use @ModelAttribute to automatically bind my form to the bean. That's what @ModelAttribute does -- it's used for Form Submission. I don't have a real form; only my own custom values.

I could still "fake" a Form Submit by creating a Dynamic Form "on the fly," but I couldn't get the Arrayed-Field Form Submission (e.. obj[] with [] notation in the HTML Name) to map to a @ModelAttribute List<Bean>, so I disregarded this unusual Form Submit approach.

2) The real approach that worked is to just submit a custom JSON string which is my own. Not related to any form submission, so can't use @ModelAttribute. Instead, this is the @RequestBody approach. Then I have to parse the JSON RequestBody String myself -- and here we have to use Jackson, Java JSON, or GSON to parse the JSON Array.

In my case,

JS:

 $.ajax({
        type : "post",
        dataType : 'json', 
        url : 'addAdmin',  
        data : JSON.stringify(entries)
 })

Controller (note it takes a custom String only). Then it uses Jackson to parse the string manually, because Spring won't do it in this case. (Spring will only auto-parse if you're using @ModelAttribute form binding.)

@PostMapping("/addAdmin")
public boolean addAdmin(@RequestBody String json) throws Exception {

      String decodedJson = java.net.URLDecoder.decode(json, "UTF-8");
      ObjectMapper jacksonObjectMapper = new ObjectMapper(); // This is Jackson
      List<UserRolesGUIBean> userRolesGUIBeans =  jacksonObjectMapper.readValue(
              decodedJson, new TypeReference<List<UserRolesGUIBean>>(){});
      // Now I have my list of beans populated.

}

Upvotes: 2

Angelo Immediata
Angelo Immediata

Reputation: 6944

You are trying to send a JSON object by using a post. You should use @RequestBody annotation.

Try to change your method in this way:

@PostMapping("/addAdmin")
public void addAdmin(@RequestBody List<UserRolesGUIBean> userRolesGUIBeans)
{
    // ...      
} 

In this way Spring will intercept the Json and transform it in List of wished objects

Upvotes: 2

Related Questions