Missing request attribute 'projektId' of type String | Thymleaf Form with just a String - spring-boot

I'm working on a Projekt where you can add workers to projects with their ids.I am using springboot, thymeleaf and a database means you give a project and a worker Id and the programm adds the worker to the project.workerlist. The Problem ist that I get this error:
Required request parameter 'projektId' for method parameter type String is not present
My HTML Form looks like this
<form action="#" th:action="#{neuenMitarbeiterzuProjektHinzufuegen}" method="post">
Projekt ID: <input type="text" th:value="*{projektId}" required/><br>
Mitarbeiter ID: <input type="text" th:value="*{mitarbeiterId}" required/><br>
<br>
<input type="submit" value="Mitarbeiter hinzufügen"/>
<input type="reset" value="Clear"/>
</form>
My Post Route Handler Method looks like this
#PostMapping(value="/neuenMitarbeiterzuProjektHinzufuegen")
public String neuenMitarbeiterzuProjektHinzufuegen(#RequestAttribute(value = "projektId") String projektID, #RequestAttribute(value = "mitarbeiterId") String mitarbeiterID,Model m)
{
Optional<Projekt> projekt = projektRepository.findById(Long.parseLong(projektID));
projektRepository.findById(Long.parseLong(projektID)).get().mitarbeiterHinzufuegen(mitarbeiterRepository.findById(Long.parseLong(mitarbeiterID)).get());
return "redirect:Projekte";
}

Looking at your code example I think you should be using #RequestParam not #RequestAttribute. Param is for things posted from the user (web) side and attribute you can set on the server side.
This blog has some explanation on the difference of #RequestAttribute https://www.logicbig.com/tutorials/spring-framework/spring-web-mvc/request-attribute.html

Related

How to send String[] from Thymeleaf multiple select form to Controller

I have a template with a form:
<h2>Favorite States</h2>
<form action="#" th:action="#{/states}" name="states" method="post">
<select class="js-example-basic-multiple" multiple="multiple" style="width: 60%">
<option th:each="state: ${states}" th:utext="${state.name}"></option>
</select>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
I want to get a String[] back from this form, for example if user selected NY, CA, MA states, I want to get String[] selectedStates = {"NY", "CA", "MA"}; in my post map Controller. How can I do that?
I have tried
#GetMapping(path="/states")
public #ResponseBody String statesList (#RequestParam String[] name) {
}
But it says required parameter is not received.
Thank you!
You can convert the javascript array to JSON using below code.
JSON.stringify(array);
And in controller, accept it as String parameter and then convert it back to array using Jackson library as below:
ObjectMapper mapper = new ObjectMapper();
String [] array = mapper.readValue(jsonString, String[].class):
You can wrap it and pass as a model attribute

modelAttribute in mustache Templates (Spring-Boot App) / binding form data

I'm working on a Spring-Boot app which handles form data. My question is, if there is a possibility to bind the form data in case of validation errors for example.
My case:
form.mustache:
<form action="/form/basisdata" method="post" name="basisdata">
<label for="contactName">Kontakt / Ansprechpartner*:</label>
<input type="text" name="contactName"/>
<label for="emailAddress">E-Mail-Adresse*:</label>
<input type="text" name="emailAddress"/>
<input type="hidden" name="_csrf" value="{{_csrf.token}}" />
<button class="a-button" type="submit">Weiter</button>
</form
Controller method:
#PostMapping("/basisdata")
public ModelAndView setFormBasisdata(#Valid #ModelAttribute("basisdata") Basisdata basisdata, BindingResult bindingResult, Map<String, Object> model) {
if (bindingResult.hasErrors()) {
List<Error> errorList = getErrors(bindingResult);
model.put("errors" , errorList);
model.put("basisdata", basisdata);
return new ModelAndView("formBasisdata", model);
}
return new ModelAndView("formNextStep", model);
}
In case of validation errors we're returning correctly to the same form page and shows the list of errors. But the form stays empty. I would prefer it, if the form is prefilled with the values that were entered before.
Is returning ModelAndView in that case the wrong option?
In jsp templates using spring forms there is an attribute in the form, that specifies the model (modelAttribute) in thymeleaf it's th:object. Is there something that I need to change / add in my mustache template?

how to display validation errors from another form submit

Imagine a form with a single field where the user provides their email. On POST, the controller action method sends a confirmation code to that email and displays a second form in which the user is supposed to enter the confirmation code they received. If the code does not match I then return the original view and I would like to display an error message next to the email ("email was not confirmed").
Sample code below:
1st view (asking the email)
<div>
Please provide the following information to sign-up:
<form:form modelAttribute="newAccountInfo" action="signup-submit.do" method="POST">
<div><form:label path="email">Email:</form:label>
<form:input type="text" path="email"/><form:errors path="email" cssClass="error" element="div"/>
</div>
...
1st view controller method
#RequestMapping(path="/signup-submit", method=RequestMethod.POST)
public String signupSubmit(HttpServletRequest request
, #ModelAttribute("newAccountInfo") #Valid NewAccountInfo newAccountInfo
, BindingResult result
, Model model) {
String confirmCode = generateRandomSecret();
// send confirmCode by email to newAccountInfo.email (omitted)
model.addAttribute("emailConfirmation" , new EmailConfirmation());
request.getSession().setAttribute("newAccountInfo", newAccountInfo);
request.getSession(false).setAttribute("email-code", confirmCode);
return View.SIGNUP_EMAIL_CONFIRMATION.name;
}
2nd view (asking the confirmation code)
<div>
Enter the confirmation code that was sent to your email:
<form:form modelAttribute="emailConfirmation" action="signup-email-confirmation-submit.do" method="POST">
<form:label path="code">Confirmation code:</form:label>
<form:input type="text" path="code"/>
<input type="submit" value="Submit" />
</form:form>
2nd view controller method
#RequestMapping(path="/signup-email-confirmation-submit", method=RequestMethod.POST)
public String signupEmailConfirmationSubmit(
#ModelAttribute("emailConfirmation") EmailConfirmation emailConfirmation
, BindingResult result
, Model model) {
if (emailConfirmation.getCode().equals(request.getSession(false).getAttribute("email-code")))
return View.SIGNUP_SUCCESS.name;
else {
model.addAttribute("newAccountInfo", request.getSession(false).getAttribute("newAccountInfo"));
request.getSession(false).invalidate();
// TODO - what should I do here ?
return View.SIGNUP.name;
}
Assuming the confirmation code was not correctly entered, what should I do in the second view controller method so that when the first view is displayed (for the second time), there is a field validation error message next to the email with description "email was not confirmed" ?
In the line marked with the TODO comment I 've tried the following:
result.rejectValue("email", null, "email was not confirmed");
… but that results in the following exception:
org.springframework.beans.NotReadablePropertyException: Invalid property 'email' of bean class [EmailConfirmation]: Bean property 'email' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?
(which makes sense as the email is not a field of EmailConfirmation).
However, the below also fails (silently, without an exception, the 1st view is displayed, I just don't see the validation error message):
result.addError(new FieldError("newAccountInfo", "email", "email could not be confirmed"));
In the end, the only way I could get this to work was to add a custom model property by adding the following (in the TODO line of the second view controller method always):
model.addAttribute("emailConfirmationError", true);
… and then modifying the 1st view as follows:
<div><form:label path="email">Email:</form:label>
<form:input type="text" path="email"/>
<form:errors path="email" cssClass="error" element="div"/>
<c:if test="${not empty emailConfirmationError}">
<span class="error">The email could not be confirmed</span>
</c:if>
</div>
The above succeeds but feels like a hack as I am not using the validation machinery of Spring MVC.
My questions are:
what is the idiomatic way to achieve the above
are there any other flawed mental models or misunderstandings present in the above code?

how to do binding in Spring annotated request parameter?

i have a controller that is using annotation for request mapping and requestParam.
the controller is working fine. However when submitting a command object with array, spring will crap out saying array index out of bound. i am guessing there is something wrong with binding but don't know how to fix it.
to be more specific, in eclipse i would set debugger at the beginning of the controller, and when submitting the form (by hitting a input submit button) eclipse debugger will not trigger and i will see array index out of bound error in console.
the controller is something like this:
#RequestMapping(value = {"/internal/pcsearch.dex", "/external/pcsearch.dex"},
method = {RequestMethod.POST, RequestMethod.GET})
public ModelAndView executeProductCatalogSearch(
HttpServletRequest request,
#RequestParam(value = "cat" ,required = false) String cat,
#RequestParam(value = "brand" ,required = false) String brand,
#ModelAttribute("command") ProductCatalogCommand cmd
){
[edit]
and the jsp is like:
<form name="pForm"
id="pForm"
action="<c:url value="psearch.dex"><c:param name="cat" value="${cat}"/></c:url>"
method="POST"
style="display:inline;">
...
...
<c:forEach var="model" items="${models}" varStatus="modelLinkStatus">
<script>
var modelImg<c:out value="${modelLinkStatus.index}"/>Src = '<c:out value="${model.altModelImage}"/>';
</script>
<spring:bind path="command.models[${modelLinkStatus.index}].modelSkusDisplayed">
<input type="hidden" name="<c:out value="${status.expression}"/>" id="<c:out value="${status.expression}"/>" value="<c:out value="${status.value}"/>"/>
</spring:bind>
<spring:bind path="command.updateCartButton">
<input type="submit" value="<spring:message code="orderEntryMessages.ecatalog.button.addToCart" text="Add to Cart" htmlEscape="yes" />" name="<c:out value="${status.expression}"/>" id="<c:out value="${status.expression}"/>" class="sub_buttons"/>
</spring:bind>
...
and the command object declare the model array as:
private List<ModelLink> models = new ArrayList<ModelLink>();
where modelLink is a custom ds.
the first foreach tag handle the the model command object and the 2nd part is the submit button i clicked on.
i think you should use AutoPopulatingList as models to bind list to view and controller. for example please refer link. This might resolve your problem of index.

Spring MVC spring:bind tag

Trying to get hold of <spring:bind> tag. I am trying to print a String value. The code looks something like:
mySimpleBean myBean = presentStudent.getSubjects().getTeacherName();
String firstName = myBean.getTeacherFirstName()
Where I get "myBean" from another bean "presentStudent". On jsp I am trying:
<spring:bind path="presentStudent.subjects.teacherName.teacherFirstName">
<input type="text" name="${status.expression}" value="${status.value}">
But it doesnt print anything.
Also "presentStudent" is commandObject for this form:
<form:form id="stuTeachForm" name="stuTeachForm" method="post" commandName="presentStudent"
action="getStuTeachData.html">
You can use the sping input tag instead of the bind tag
<form:form id="stuTeachForm" name="stuTeachForm" method="post" commandName="presentStudent" action="getStuTeachData.html">
<form:input type="text" path="subjects.teacherName.teacherFirstName"/>
Might also be worth outputting the desired value to check your bean has been properly populated
First name is ${presentStudent.subjects.teacherName.teacherFirstName}

Resources