Spring boot: view unable to reach controller and having "400 BAD_REQUEST" - spring

I try to do a java web application using:
SpringBoot
Mysql
JDBC
Design pattern: MVC, DAO
And Thymeleaf
And I'm trying to send a data from one of my views:
<td th:text="${Inj.sleepTest}"></td>
<td th:text="${Inj.sleepDose}"></td>
<td th:text="${Inj.nightTest}"></td>
<td th:text="${Inj.comment}"></td>
<td>
<form th:action="#{/delInj}" method="post">
<input type="hidden" id="id_injection" name="id_injection" value="${Inj.id_injection}">
<input class="btn btn-danger" type="submit" value="Submit">
</form>
</td>
to my controler:
#RequestMapping(value="/delInj", method= RequestMethod.POST)
public ModelAndView delinject(Injection inj){
ModelAndView mv = new ModelAndView();
mv.setViewName("userOnly/MyInjections");
int i = inj.getId_injection();
System.out.println(i);
return mv;
}
but i have the error "400 BAD_REQUEST - Bad Request" in my browser.
I tried with "#RequestMapping" and "PostMapping" but neither of them seams to work

You have not added #RequestBody annotation to the method.
Like,
public ModelAndView delinject(#RequestBody Injection inj){
ModelAndView mv = new ModelAndView();
mv.setViewName("userOnly/MyInjections");
int i = inj.getId_injection();
System.out.println(i);
return mv;
}

Bad Request In general means: that the structure of your Request Body does not match the JSON sent (may be Types, constraints, structure ...)
check the JSON you sent in the request and the expected Request Body "Model"

I ran into this sort of an issue using Java record classes. In my case I wasn't passing all of the record fields. Two ways to fix this. 1. Pass all the fields
2. Add a constructor to the record that defaults optional fields to a default value. It sure would be nice if spring would enable you to turn on some response body or logging to let you know what actually was wrong with the request.

Related

How to use spring mvc with thymeleaf to iterate over a list?

My goal is to cycle through a list of objects, some to be displayed on the screen, others to be passed into a form as an object of which I can define certain aspects and then return to the controller the object and attribute to be modified.
The problem with the following approach is that the object in the list is not passed correctly to the form and thus gives an error because it is trying to make changes to a non-existent object.
If, on the other hand, I try to pass it as an object via ModelAndView it obviously works but does not have all the characteristics of the object I passed via the list.
Controller
#GetMapping("/")
public ModelAndView home() throws IOException {
ModelAndView mv = new ModelAndView();
mv.setViewName("home");
List<Comics> allComics = cs.getAll();
mv.addObject("comics", allComics);
return mv;
}
#PostMapping("/update")
public ModelAndView update(Comics com, #RequestParam("attr") String attr) throws IOException {
ModelAndView mv = new ModelAndView();
com.setLastRead(attr);
cs.updateAttributes(com);
mv.setViewName("home");
List<Comics> allComics = cs.getAll();
mv.addObject("comics", allComics);
return mv;
}
home.html
<html xmlns:th="http://www.thymeleaf.org">
<tr th:each="comic : ${comics}">
<td th:text="${comic.title}"></td>
<td th:text="${comic.lastChapter}"></td>
<td>
<a th:href="${comic.lastChapterLink}" target="_blank"
role="button" class="btn btn-md btn-block btn-info"> Link
</a>
</td>
<td></td>
<td>
<form th:action="#{/update}" th:object="${comic}" method="post">
<input type="text" name="attr" id="attr"/>
<button type="submit">Sub</button>
</form>
</td>
</tr>
PS: I cut out the head of the html page because it was full of non-relevant CDNs
How can I integrate Spring MVC with Thymeleaf to achieve the result whereby passing a list of objects can be displayed on the screen and used for other purposes within the html page without throwing errors?
Obviously if you know of more efficient methods to achieve the result I'm listening; I only used this method because I didn't know of any others.
Thank you
Answer to #RafaeldaSilva:
I agree, but that does not solve the problem.
Let me explain: the attribute I am going to modify through the form already has its name to allow what you wrote.
But the object iterated through:
tr th:each="comic : ${comics}">
cannot be passed directly as input, as it is a value that is taken from a list and exists individually only in the html page.
One might think of passing it as hidden input, but in this case the result would be the same (I have tried):
<form th:action="#{/update}" th:object="${comic}" method="post">
<input type="hidden" value="${comic}" name="com"/>
<input type="text" name="attr" id="attr"/>
<button type="submit">Sub</button>
</form>
#PostMapping("/update")
public ModelAndView update(#RequestParam("com") Comics com, #RequestParam("attr") String attr) throws IOException {
ModelAndView mv = new ModelAndView();
com.setLastRead(attr);
System.out.println("Comic: " + com);
cs.updateAttributes(com);
mv.setViewName("home");
List<Comics> allComics = cs.getAll();
mv.addObject("comics", allComics);
return mv;
}
Error:
[org.springframework.web.bind.MissingServletRequestParameterException: Required request parameter 'com' for method parameter type Comics is present but converted to null]
try removing the type="hidden" to see what is present in this input, as I understand you are inserting an object by doing value="${comic}", this way the input should not send the value wanted..
change this: <input type="hidden" value="${comic}" name="com"/>
for this: <input type="text" value="${comic}" name="com"/>
so you can see what the form is sending to the controller, I believe it's the object's memory path, and not the data that exists in it.
in the input you must inform the attributes of the object, not the complete object..

<option> returning emptyin UI form:select of spring mvc3

I've a list of values to be passed from my controller to the jsp page. I've the below controller:
#RequestMapping(value="/addClient.do", method = RequestMethod.GET)
protected ModelAndView Submit(HttpServletRequest request, HttpServletResponse response) throws Exception {
MyForm = new MyForm();
MyForm.setClientList(MyService.getClientList(/*"30-JUN-15"*/));
System.out.println("Size of list : "+MyForm.getClientList().size()); //-- Displayed as 10 which is correct
ModelAndView model = new ModelAndView("feeMaintenance");
model.addObject("clientForm",MyForm);
model.addObject("selectedMenu", "MenuSelected");
model.addObject("clientsList",MyForm.getClientList());
return model;
}
And my jsp form is as below:
<body>
<form:form method="post" modelAttribute="clientForm" action="${userActionUrl}">
<tr> <td align="left">
<form:select path="clientList">
<form:option value="-" label="------Select Client ------">
<form:options items="${clientsLists}">
</form:options></form:option></form:select>
</tr> </td>
</form>
</body>
I've removed the additional unrelated code. The drop down only shows ----Select Client--- even though the controller shows the correct values of the clientList. Unable to figure out whats missing.
if MyForm.getClientList() is successfully return list of client data than
rewrite this line into jsp page.
you write key name into controller is clientsList and you write this into jsp page is clientsLists this is wrong.
I hope this is work.
try this code.
model.addObject("country", projectservice.getallCountry());
**Note:**itemLabel name is same as property name into pojo class and also itemValue name.

Spring MVC: How do I preserve model attributes in spring validation errors

I searched around on Stack Overflow, but could not find the solution to my query. I have a controller function that adds multiple model attributes on a GET request
#RequestMapping(method = RequestMethod.GET, value = "/showdeletesearchqueryform")
public String showDeleteSearchQuery(final Model model) {
if (LOG.isDebugEnabled()) {
LOG.debug("Fetching all the search query results.");
}
ImmutableList<ArtQueryResults> results = this.searchQueriesService
.getSearchQueries(APPNAME);
// Adding model attribute # 1
model.addAttribute("searchResults", results);
if (LOG.isDebugEnabled()) {
LOG.debug("\"searchResults\" model attribute has been intialized from "
+ results);
}
ArtDeleteQueryRequest request = new ArtDeleteQueryRequest();
request.setAppName(APPNAME);
if (LOG.isDebugEnabled()) {
LOG.debug("Model attribute initialized = " + request);
}
// Adding model attribute # 2
model.addAttribute("deletedAttributes", request);
return "deletesearchqueries";
}
My JSP
<div class="column-group">
<form:form method="POST" action="${pageContext.request.contextPath}/arttestresults/showdeletesearchqueryform" modelAttribute="deletedAttributes">
<form:errors path="*" cssClass="alert alert-danger column lg-units-5 units-2" element="div"/>
<form:hidden path="appName" id="appNameId" htmlEscape="true"/>
<div class = "units-1 column lg-units-12">
<!-- Hidden Key for app name. -->
<form:select path="idsToBeDeleted" id="IdsToBeDeletedSelectId">
<c:forEach items="${searchResults}" var="searchResult" varStatus="loop">
<form:option label="${searchResult.searchQuery}" value="${searchResult.id}" />
</c:forEach>
</form:select>
</div>
<div class="units-1 column lg-units-12">
<%-- This is a hack that make sure that form is submitted on a click. Not sure why form is not being submitted. --%>
<button class="button" type="submit" onclick="javascript:$('form').submit();">Delete Selected Queries</button>
</div>
</form:form>
My controller POST function
#RequestMapping(method = RequestMethod.POST, value = "/showdeletesearchqueryform")
public String deleteSearchQueries(
Model model,
#ModelAttribute(value = "deletedAttributes") #Valid final ArtDeleteQueryRequest request,
final BindingResult result) {
if (result.hasErrors()) {
LOG.warn("There are " + result.getErrorCount() + " validation errors.");
return "deletesearchqueries";
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("The ids to be deleted are " + request.getIdsToBeDeleted());
}
this.searchQueriesService.deleteSearchQueriesById(
ImmutableList.copyOf(request.getIdsToBeDeleted()));
return "redirect:/arttestresults/showdeletesearchqueryform";
}
}
If there is a validation failure, the model attribute searchResults is not being picked up when I return a view on error condition? Is there a way to preserve the other defined model attributes as well?
Seems that you need flash attributes which were added in spring 3.1. Please take a look at example/explanation:
http://viralpatel.net/blogs/spring-mvc-flash-attribute-example/
The get and the post are different requests. What you get in the post request, is only what comes from the form, so only the "deletedAttributes" model attribute and only the fields that are <input> in the JSP.
You need to put again the searchResults model attribute explicitely like you did in get method.
As suggested by M. Deinum, if one or more attribute(s) will be used by all methods in a controller, you can use a #ModelAttribute annotated method to put it (them) in model automatically.
You can also use SessionAttributes model attributes, that is attributes that are stored in session and not in request. But it is hard to have them properly cleaned from session if user do not post the form but go into another part of the application. You have an example of usage ofSessionAttributes` in Spring's Petclinic example.

Spring MVC : How to pass Model object from one method in controller to another method in same controller?

I have integrated Spring Security in my application , and would like to display an error message to the user in case of Bad credentials.
jsp:
<c:out value='${secerror}' /> //prints nothing on screen
<c:if test="${empty secerror}">
error is empty or null. //always prints this line on screen
</c:if>
<c:if test="${not empty secerror}">
<div class="errorblock">
Your login attempt was not successful, try again.<br /> Caused :
${sessionScope["SPRING_SECURITY_LAST_EXCEPTION"].message}
</div>
</c:if>
<c:set var = "url" value = "/j_spring_security_check" />
<form:form method="post" commandName="portalLogin" action="${pageContext.servletContext.contextPath}${url}" name="f">
[Update]: Sorry all, i realized that my Model object was getting overriden after i redirect to portalLogin.html as i had created a new model object created there previously.
But i tried few easy options by which i can pass Model object from one controller method to another method in the same controller. But nothing worked.
I tried using forward: prefix instead of redirect prefix. For this, i didn't get error message at all.
I tried below code in loginerror method.
return new ModelAndView("portalLogin","secerror","true");
I was getting following error for the above code:
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'portalLogin' available as request attribute
I did come across this link, but i found it to be a very lengthy solution and wasn't sure if iv'e to write that much code.
I wasn't sure if i can use Model object for #ModelAttribute annotations#ModelAttribute.
Please provide me with code snippets / examples which i can try out.
My controller method is like this:
#RequestMapping("/portalLogin")
public ModelAndView goToLogin(Model model) {
try {
System.out.println("Enter goToLogin************************");
} catch (Exception exception) {
}
return new ModelAndView("portalLogin", "portalLogin", new LoginModel());
//return new ModelAndView("portalLogin", model);
}
#RequestMapping(value="/loginfailed", method = RequestMethod.GET)
public ModelAndView loginerror(ModelMap model) {
//model.addAttribute("secerror", "true");
//return "redirect:portalLogin.html";
return new ModelAndView("portalLogin","secerror","true");
}
[Update]: As a work around i added goToLogin method logic inside loginerror method as my only intention is to get portalLogin page. Error was thrown as expected.
#RequestMapping(value="/loginfailed", method = RequestMethod.GET)
public ModelAndView loginerror(Model model) {
model.addAttribute("secerror", "true");
return new ModelAndView("portalLogin", "portalLogin", new LoginModel());
}
But still i would like to know if i can pass Model object from one controller method to another method in the same controller through some way.
You can also try something like this
<c:if test="${not empty secerror == 'true'}">
<tr>
<td colspan="2" align="center"><font style="color: red">Your login attempt was not successful, try again</font>
</td></tr></c:if>
Let`s make things easy, if you want to show a Bad credentials message, you can simply do something like this:
In your spring-security.xml:
<sec:form-login login-page="/login.jsp"
default-target-url="/default-url-when-login-correct"
authentication-failure-url="/login.jsp?error=true"
login-processing-url="/login" always-use-default-target="true" />
In your login.jsp:
<c:if test="${not empty param.error}">
<span>${sessionScope["SPRING_SECURITY_LAST_EXCEPTION"].message}</span>
</c:if>

Spring 3.1 Form and List Binding

I have not been able to find a solution on this yet after searching and searching.
Using Spring 3.1
I have a form where I would like the user to be able to add multiple point of contacts (poc). In my JSP I have a submit button for adding a poc. It goes to a Spring Controller, gets the current list of pocs from the domain object (which will be empty at first), adds the poc to the list and then puts the domain object back into the model before returning to the same view. Then the user will ultimately submit the entire page which will go to the controller and save the entire object to persistence.
I have tried various attempts and come up with different results from only being able to add one poc with a new one overwriting the existing, to not being able to get the entered poc displayed on the form. I will put the code I currently have.
JSP:
<form:form method="post" commandName="request">
...
<h2>POC:</h2>
<input type="text" name="newPoc"/>
<input type="submit" name="addPoc" value="Add POC"/>
<table>
...
<c:forEach items="${request.pointOfContacts}" var="poc" varStatus="vs">
<tr>
<td><form:label path="pointOfContacts[${vs.index}].name/></td>
.....
</c:forEach>
......
</table>
</form:form>
Spring Controller:
#RequestMapping(value="/request", method=RequestMethod.POST, param="addPoc")
public Sring addPoc(#RequestParam String newPoc, MyRequest req, Model model) {
PointOfContact poc = new PointOfContact();
poc.setName(newPoc);
List<PointOfContact> pocs = req.getPointOfContacts();
pocs.add(poc);
req.setPointOfContacts(pocs);
model.addAttribute("request", req);
return "requestForm";
}
Domain Objects
#Entity
public class MyRequest {
...
#OneToMany(Cascade=CascadeType.ALL)
private List<PointOfContact> pointOfContacts = new ArrayList<PointOfContact>();
.....
}
#Entity
public class PointOfContact {
...
private String name;
....
}
Does anybody have any solutions? I have seen various posts about AutoPopulatingList, is this a solution? If so how would I use it in this example.
Yes you should use AutoPopulatingList, example. This will require change in MyRequest entity.

Resources