Welcome to the Treehouse Community

The Treehouse Community is a meeting place for developers, designers, and programmers of all backgrounds and skill levels to get support. Collaborate here on code errors or bugs that you need feedback on, or asking for an extra set of eyes on your latest project. Join thousands of Treehouse students and alumni in the community today. (Note: Only Treehouse students can comment or ask questions, but non-students are welcome to browse our conversations.)

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and a supportive community. Start your free trial today.

Java

Ronald Williams
seal-mask
.a{fill-rule:evenodd;}techdegree seal-36
Ronald Williams
Java Web Development Techdegree Graduate 25,021 Points

Why am I getting a 400 error? Java, Thymeleaf, Spring boot

BACKGROUND:

I am making a spring-boot web app with Thymeleaf. It is a Recipe site and I am working on the part where a user can add a Recipe. I am trying to make it so that a user can add an ingredient to a Recipe's ingredient list.

In the controllers I give the user a recipe form with a recipe object and one ingredient. If the user clicks a button to add an ingredient the form submits to /recipes/ingredient. Then in that controller I am trying to add one ingredient and redirect the user to the form.

Any help greatly appreciated!

PROBLEM:

When I click the add ingredient button I get a 400 error.

Code for the two controllers: (Note: once I get this part to work I am going to use a third controller for saving the recipe.)

@RequestMapping(value = "/recipes/add")
public String addRecipeForm(Model model, Principal principal) {
    checkForUser(model, (UsernamePasswordAuthenticationToken) principal);
    if (!model.containsAttribute("recipe")) {
        Recipe recipe = new Recipe();
        recipe.getIngredients().add(new Ingredient("", "", 0));
        model.addAttribute("recipe", recipe);
    }
    model.addAttribute("category", Category.values());
    model.addAttribute("action", String.format("/recipes/add"));
    model.addAttribute("heading", "Recipe Creator");
    return "form";
}

@RequestMapping(value = "/recipes/ingredient", method = RequestMethod.POST)
public String addIngredient(Recipe recipe, RedirectAttributes redirectAttributes) {
    recipe.getIngredients().add(new Ingredient("", "", 0));
    redirectAttributes.addFlashAttribute("recipe", recipe);
    return "redirect:/recipes/add";
}

// Third Controller used later to save Recipe
@RequestMapping(value = "/recipes/add", method = RequestMethod.POST)
public String addRecipe(Recipe recipe, ModelMap modelMap) {
    recipeService.save(recipe);
    return String.format("redirect:/recipes/details/%s", recipe.getId().toString());
}

Code for the Form.html template:

<!doctype html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head th:replace="layout :: common_header(~{::title})">
  <title>Recipe Editor | My Recipes</title>
</head>

<body>

<nav th:replace="layout :: nav"></nav>

<div class="grid-container">

  <div th:replace="layout :: logo"></div>

  <div class="grid-100">

    <div class="recipes">
      <form id="recipe-form" th:action="@{/recipes/ingredient}" method="post" th:object="${recipe}">

        <div class="grid-100 row controls">
          <div class="grid-50">
            <h2 th:text="${heading}"> Recipe Heading </h2>
          </div>
          <input type="hidden" th:field="*{id}" />
          <div class="grid-50">
            <div class="flush-right">
              <button type="submit" name="save">Save Recipe</button>
              <button class="secondary">Cancel</button>
            </div>
          </div>
        </div> <div class="clear"></div>

        <div class="grid-100 row">
          <div class="grid-20">
            <p class="label-spacing">
              <label> Photo </label>
            </p>
          </div>
          <div class="grid-40">
            <p>
              <input placeholder="Url"/>
            </p>
          </div>
        </div> <div class="clear"></div>

        <div class="grid-100 row">
          <div class="grid-20">
            <p class="label-spacing">
              <label> Name </label>
            </p>
          </div>
          <div class="grid-40">
            <p>
              <input type="text" th:field="*{name}"/>
            </p>
          </div>
        </div> <div class="clear"></div>

        <div class="grid-100 row">
          <div class="grid-20">
            <p class="label-spacing">
              <label> Description </label>
            </p>
          </div>
          <div class="grid-40">
            <p>
              <input type="textarea" th:field=="*{description}" />
            </p>
          </div>
        </div> <div class="clear"></div>

        <div class="grid-100 row">
          <div class="grid-20">
            <p class="label-spacing">
              <label> Category </label>
            </p>
          </div>
          <div class="grid-30">
            <p>
              <select th:field="*{category}">
                <option value="">All Categories</option>
                <div th:each="category : ${categories}">
                  <option th:value="${category}" th:text="${category}">Category</option>
                </div>
              </select>
            </p>
          </div>
        </div> <div class="clear"></div>

        <div class="grid-100 row">
          <div class="grid-20">
            <p class="label-spacing">
              <label> Prep Time </label>
            </p>
          </div>
          <div class="grid-20">
            <p>
              <input type="text" th:field="*{prepTime}"/>
            </p>
          </div>
        </div> <div class="clear"></div>

        <div class="grid-100 row">
          <div class="grid-20">
            <p class="label-spacing">
              <label> Cook Time </label>
            </p>
          </div>
          <div class="grid-20">
            <p>
              <input type="text" th:field=="*{cookTime}"/>
            </p>
          </div>
        </div> <div class="clear"></div>

        <div class="grid-100 row">

          <div class="grid-20">
            <p class="label-spacing">
              <label> Ingredients </label>
            </p>
          </div>
          <div class="grid-30"><p class="label-spacing">Item</p></div>
          <div class="grid-30"><p class="label-spacing">Condition</p></div>
          <div class="grid-20"><p class="label-spacing">Quantity</p></div>

          <div th:each="ingredient : *{ingredients}">

            <div class="grid-20"><p class="label-spacing"></p></div>

            <div class="grid-30">
              <p class="label-spacing">
                <input type="text" th:value="${ingredient.item}"/>
              </p>
            </div>

            <div class="grid-30">
              <p class="label-spacing">
                <input type="text" th:value="${ingredient.condition}"/>
              </p>
            </div>

            <div class="grid-20">
              <p class="label-spacing">
                <input type="text" th:value="${ingredient.quantity}"/>
              </p>
            </div>

          </div><!-- each ingredient -->

          <div class="prefix-20 grid-80 add-row">
            <p>
              <button type="submit">+ Add Another Ingredient</button>
            </p>
          </div>

        </div> <div class="clear"></div>

        <div class="grid-100 row">
          <div class="grid-20">
            <p class="label-spacing">
              <label> Steps </label>
            </p>
          </div>

          <div class="step-row">
            <div class="prefix-20 grid-80">
              <p>
                <input/>
              </p>
            </div>
          </div>

          <div class="prefix-20 grid-80 add-row">
            <p>
              <button>+ Add Another Step</button>
            </p>
          </div>
        </div>
        <div class="clear"></div>

        <div class="grid-100 row controls">
          <div class="grid-50"><h2></h2></div>
          <div class="grid-50">
            <div class="flush-right">
              <button type="submit">Save Recipe</button>
              <button class="secondary">Cancel</button>
            </div>
          </div>
        </div>

        <div class="clear"></div>
        <div class="row">&nbsp;</div>
      </form>
    </div> <!-- recipes -->
  </div> <!-- grid-100 -->
</div> <!-- grid-container -->
<div th:replace="layout :: scripts"></div>
</body>
</html>

1 Answer