Search code examples
springspring-mvcdata-bindinghttp-status-code-400

400 the request sent by the client was syntactically incorrect


I have gone through so many examples of this nature and proposed solutions from this site, but none of the solutions provided thereon apply to my problem. I believe that this error message, 400, shows up when the information sent to the controller is mulformed. I spent the last two days cross referrencing to another project I worked on in the past, which works, but I cannot pick up the problem.

@RequestMapping(value = {"/", "/home"}, method = RequestMethod.GET)
    public String homePage(ModelMap model) {
       model.addAttribute("user", getPrincipal());
    Catalog catalog = catalogService.getCatalogByCategory(Catalog.CatalogCategory.ALL);

    model.addAttribute("catalog", catalog);
    return "welcome";
}

This sends the data to a JSTL Spring form on my JSP as follows:

<form:form method="POST" modelAttribute="catalog">
        <form:hidden path="id"/>
        <form:hidden path="name"/>
        <form:hidden path="category"/>
        <form:hidden path="orderItems"/>


        <div id="products" class="row list-group">
            <c:forEach var="orderItem" items="${catalog.orderItems}">

                <div class="item  col-xs-4 col-lg-4">
                    <div class="thumbnail">
                        <img class="group list-group-image" src="http://placehold.it/400x250/000/fff" alt=""/>

                        <div class="caption">
                            <h4 class="group inner list-group-item-heading">
                                    ${orderItem.name}</h4>

                            <p class="group inner list-group-item-text">
                                    ${orderItem.description}
                            </p>

                            <div class="row">
                                <div class="col-xs-12 col-md-6">
                                    <p class="lead">
                                        R ${orderItem.price}</p>
                                </div>
                                <div class="col-xs-12 col-md-6">
                                    <label for="${orderItem.id}" class="btn btn-primary">Add to Cart <input
                                            type="checkbox" id="${orderItem.id}" name="orderItem.addedToCart"
                                            class="badgebox"><span class="badge">&check;</span></label>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </c:forEach>
        </div>

        <div class="row">
            <div class="form-group">
                <div class="col-sm-12 pull-right">
                </div>
                <div class="col-sm-2 pull-right">

                    <input type="submit"
                           class="btn btn-default btn-block btn-primary"
                           value="Next" name="action" formmethod="POST"
                           formaction="confirmList"/>

                </div>
            </div>
        </div>
    </form:form>`

At this point I submit the form to the following listener in my controller:

@RequestMapping(value = "/confirmList", method = RequestMethod.POST)
public String confirmList(@ModelAttribute Catalog catalog, @ModelAttribute     String numberOfItemsAdded) {

     List<OrderItem> selectedItems = new ArrayList<OrderItem>();
    for (OrderItem orderItem : catalog.getOrderItems()) {
        if (orderItem.isAddedToCart()) {
            selectedItems.add(orderItem);
        }
    }
    //model.addAttribute("numberOfItemsAdded", selectedItems.size());
    return "welcome";
}

That's it, execution flow does NOT even reach back my controller. Exhausting bug because I really do not understand what I am doing wrong here. Thank you in advance

EDIT:

Catalog.java

 @Entity
 @Table(name="Catalogs")
 public class Catalog{

 private long id; //generated value using hibernate ...
 private String name; //column annotated by @Column
 private String category;// column also annotated by @Column
 private List<OrderItem> orderItems;// one to many mapping

  //getters and setters here
}

Solution

  • I tested your code and I got HTTP 400 too. The thing is that what browser sends does not match whith what the controller method confirmList expects:

    This is the form data I saw in Chrome's network tab:

    id:1
    name:the catalog
    category:category
    orderItems:[com.eej.ssba2.model.test.catalog.OrderItem@82ea8a, com.eej.ssba2.model.test.catalog.OrderItem@f441ae, com.eej.ssba2.model.test.catalog.OrderItem@40a13, com.eej.ssba2.model.test.catalog.OrderItem@1316c95, com.eej.ssba2.model.test.catalog.OrderItem@1cfc05a, com.eej.ssba2.model.test.catalog.OrderItem@5d725c, com.eej.ssba2.model.test.catalog.OrderItem@ff32b9, com.eej.ssba2.model.test.catalog.OrderItem@5b49a4, com.eej.ssba2.model.test.catalog.OrderItem@13faf31, com.eej.ssba2.model.test.catalog.OrderItem@6d64d]
    orderItem.addedToCart:on
    orderItem.addedToCart:on
    orderItem.addedToCart:on
    orderItem.addedToCart:on
    action:Next
    

    But controller cannot understand this, as OrderItems shows a toString() of each OrderItem instance and the addedToCart is not binded to any orderItem of the orderItems list.

    You must modify your jsp this way:

    <form:form method="POST" modelAttribute="catalog">
            <form:hidden path="id"/>
            <form:hidden path="name"/>
            <form:hidden path="category"/>
            <!-- form:hidden path="orderItems"/-->
    
    
            <div id="products" class="row list-group">
                <c:forEach var="orderItem" items="${catalog.orderItems}" varStatus="status">
    
                    <div class="item  col-xs-4 col-lg-4">
                        <div class="thumbnail">
                            <img class="group list-group-image" src="http://placehold.it/400x250/000/fff" alt=""/>
    
                            <div class="caption">
                                <h4 class="group inner list-group-item-heading">
                                        ${orderItem.name}</h4>
                                        <form:hidden path="orderItems[${status.index}].name" />
                                <p class="group inner list-group-item-text">
                                        ${orderItem.description}
                                        <form:hidden path="orderItems[${status.index}].description" />
                                </p>
    
                                <div class="row">
                                    <div class="col-xs-12 col-md-6">
                                        <p class="lead">
                                            R ${orderItem.price}</p>
                                            <form:hidden path="orderItems[${status.index}].price" />
                                    </div>
                                    <div class="col-xs-12 col-md-6">
                                        <label for="${orderItem.id}" class="btn btn-primary">Add to Cart <input
                                                type="checkbox" id="${orderItem.id}" name="catalog.orderItems[${status.index}].addedToCart"
                                                class="badgebox"><span class="badge">&check;</span></label>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </c:forEach>
            </div>
    
            <div class="row">
                <div class="form-group">
                    <div class="col-sm-12 pull-right">
                    </div>
                    <div class="col-sm-2 pull-right">
    
                        <input type="submit"
                               class="btn btn-default btn-block btn-primary"
                               value="Next" name="action" formmethod="POST"
                               formaction="confirmList"/>
    
                    </div>
                </div>
            </div>
        </form:form>
    

    If you do so, you could see the message changes in Chrome's network tab (or the browser you are using). This is the form data right now:

    id:1
    name:the catalog
    category:category
    orderItems[0].name:OrderItemName#0
    orderItems[0].description:OrderItemDesc#0
    orderItems[0].price:0.0
    orderItems[1].name:OrderItemName#1
    orderItems[1].description:OrderItemDesc#1
    orderItems[1].price:0.43645913001303904
    orderItems[2].name:OrderItemName#2
    orderItems[2].description:OrderItemDesc#2
    orderItems[2].price:1.7151992716801088
    orderItems[3].name:OrderItemName#3
    orderItems[3].description:OrderItemDesc#3
    orderItems[3].price:1.303683806806788
    orderItems[4].name:OrderItemName#4
    orderItems[4].description:OrderItemDesc#4
    orderItems[4].price:2.507039003743686
    orderItems[5].name:OrderItemName#5
    orderItems[5].description:OrderItemDesc#5
    orderItems[5].price:3.173744751378154
    orderItems[6].name:OrderItemName#6
    orderItems[6].description:OrderItemDesc#6
    orderItems[6].price:3.183771167856446
    catalog.orderItems[6].addedToCart:on
    orderItems[7].name:OrderItemName#7
    orderItems[7].description:OrderItemDesc#7
    orderItems[7].price:6.73370053587355
    catalog.orderItems[7].addedToCart:on
    orderItems[8].name:OrderItemName#8
    orderItems[8].description:OrderItemDesc#8
    orderItems[8].price:2.0266022634803216
    orderItems[9].name:OrderItemName#9
    orderItems[9].description:OrderItemDesc#9
    orderItems[9].price:5.251986962977732
    catalog.orderItems[9].addedToCart:on
    action:Next
    

    And you would see now it returns a HTTP 200 as the request in fact reaches your controller. Delete the @ModelAttribute in your controller method too, as you have been suggested to:

        @RequestMapping(value = "/confirmList", method = RequestMethod.POST)
        public String confirmList(Catalog catalog, String numberOfItemsAdded) {
    
            List<OrderItem> selectedItems = new ArrayList<OrderItem>();
            for (OrderItem orderItem : catalog.getOrderItems()) {
                if (orderItem.isAddedToCart()) {
                    selectedItems.add(orderItem);
                }
            }
            //model.addAttribute("numberOfItemsAdded", selectedItems.size());
            return "catalog";
        }