I am building a very simple crud app using Spring boot, Jpa and Thymeleaf, but I am stuck at a "Request method 'GET' not supported" problem. I get this error whenever I want to access the /add page through which I can add a new student. The snippets associated with this error are as below:
Thymeleaf form:
<h1>Form</h1>
<form action="#" th:action="#{/add}" th:object="${addStudent}"
method="post">
<p>Full name: <input type="text" th:field="*{fname}" /></p>
<p>Major: <input type="text" th:field="*{major}" /></p>
<p><input type="submit" value="Submit" /> <input type="reset" value="Reset" /></p>
</form>
Controller addNewStudentMethod
#PostMapping("/add")
public String addNewStudent( #ModelAttribute StudentEntity studentEntity, Model model) {
model.addAttribute("addStudent",studentRepository.save(studentEntity) );
return "/allstudents";
}
The error I get:
There was an unexpected error (type=Method Not Allowed, status=405).
Request method 'GET' not supported
Thanks,
In your controller you only have a method which has mapped to POST request "/add". You have to have a GET request mapped to a different method OR change the #PostMapping("/add") to #RequestMapping("/add").
Please note:
#PostMapping is for mapping POST request only.
#GetMapping is for mapping GET request only.
#RequestMapping maps all request types
You have some issues with how you have it set up. What you may want is:
#GetMapping("/add")
public String addNewStudent(Model model) {
model.addAttribute("studentEntity", new StudentEntity()); //create a new bean so that your form can bind the input fields to it
return "add"; //let's say add.html this is the name of your form
}
#PostMapping("/add")
public String addNewStudent( #ModelAttribute StudentEntity studentEntity, Model model) {
//call any service methods to do any processing here
studentRepository.save(studentEntity);
return "redirect:/allstudents"; //this would be your confirmation page
}
Your add.html form would have something like:
<form th:object="${studentEntity}" th:action="#{/add}" method="post" action="allstudents.html">
<!-- input fields here --->
</form>
Note that the th:object is what you added to the model in the #GetMapping method.
change your Controller methods's #PostMapping("/any-url") to either #GetMapping("/any-url") or #RequestMapping("/any-url")
In simple words, change your above controller's method to
#RequestMapping("/add")
public String addNewStudent( #ModelAttribute StudentEntity studentEntity, Model model) {
model.addAttribute("addStudent",studentRepository.save(studentEntity) );
return "/allstudents";
}
Related
This is my login.jsp file.
<form:form method="POST" action="checklogin" modelAttribute="log">
<form:label path="username">UserName: </form:label>
<form:input path="username" id="username" /><br /><br />
<br />
<form:label path="pswd">Password: </form:label>
<form:password path="pswd" id="password" /><br /> <br />
<br />
<input type="submit" id="btnLogin" value=login class="login" />
</form:form>
This is my controller
#RequestMapping(value = "/checklogin", method=RequestMethod.POST)
public String chklogin(#ModelAttribute Login login, Model mod) {
if (login.getUsername().equals("subro") && login.getpswd().equals("ss")) {
mod.addAttribute("log",login);
return "Home";
}
else {
return "Login";
}
}
Still I am getting the error as
Neither BindingResult nor plain target object for bean name 'log' available as request attribute
Change
#ModelAttribute Login login
To
#ModelAttribute Login log
Two way data binding in spring allows user inputs to be dynamically bound to the beans. It is two-way in a sense that it can get the inputs from the beans and it can post the user inputs to the beans using GET and POST api.
Using the #ModelAttribute annotation you can bind the user inputs with the beans.
#RequestMapping(value = "/login", method=RequestMethod.GET)
public String login(#ModelAttribute Login log){ //one-way binding
return "Login";
}
#RequestMapping(value = "/checklogin", method=RequestMethod.POST)
public String chklogin(#ModelAttribute Login log, Model mod) { //two-way binding
if (login.getUsername().equals("subro") && login.getpswd().equals("ss")) {
mod.addAttribute("log",login);
return "Home";
}
else return "Error";
}
Earlier I haven't written the login method which is mapped with the url /login.
When you are getting an error as "Neither BindingResult nor plain target object for bean name 'log' available as request attribute", it means that the beans ('log' in my case) is not able to bind with the view page.
I am trying to learn spring mvc. I have trouble understanding how #ModelAttribute works. Below is my controller
#RequestMapping("/showForm")
public String showForm(Model theModel){
Student student = new Student();
theModel.addAttribute("testName",student);
return "student-form";
}
#RequestMapping("/processForm")
public String showForm(#ModelAttribute("hello") Student student){
return "student-confirmation";
}
When i go to student-form page i create a student object and add it to the model with attribute name "testName". Then i submit to form to "processForm" method. Using below form
<form:form action="processForm" modelAttribute="testName">
First Name: <form:input path="firstName" />
Second Name: <form:input path="lastName" />
<input type="submit" value="submit" />
</form:form>
Here is what i don`t understand the attribute names do not match at where i put the object to model and where i retrive it. But the result is shown correctly..
theModel.addAttribute("testName",student); //Here it is testName
#ModelAttribute("hello") Student student //Here it is hello
student-confirmation.jsp
First Name : ${hello.firstName}
Last Name : ${hello.lastName}
The Student model is populated automatically after submit the form because you have set testName as the model attribute:
theModel.addAttribute("testName",student);
JSP:
<form:form action="processForm" modelAttribute="testName">
<form:input path="firstName" />
So Spring matches Student field firstName with form text input with path: path="firstName" .
After submit:
#RequestMapping("/processForm")
public String showForm(#ModelAttribute("hello") Student student){
#ModelAttribute("hello") is not required, Student is matched
automatically.
You just change the reference name of the model. And then you can get values from the model using ${hello.firstName} at your JSP.
More info about #ModelAttribute you can read #ModelAttribute Annotation As a Method Argument
Hope it helps.
when using Spring framework and binding a form on a commandName object to add let's say a person with the following fields.
<form method="POST" action="addPerson.htm" commandName="person">
<input id="firstname" name="firstname"value="${person.firstname}"/>
<br>
<input id="name" name="name" value="${person.name}"/>
<br>
<input id="age" name="age" value="${person.age}"/>
</form>
On the server I've got the following code
#RequestMapping(value="/addPerson", method={RequestMethod.POST})
public String addPerson(#ModelAttribute(value="person") Person p){
service.addPerson(p);
return "redirect:/overview.htm";
}
How do I do error handling with this, so let's say for example the age must be a positive number and firstname can be left empty.
You need to create a new Validator class, implementing Validator interface.
Your validator can be something like this:
public class PersonValidator implements Validator {
public boolean supports(Class<?> clazz) {
return Person.class.equals(clazz);
}
public void validate(Object target, Errors errors) {
Person person=(Person)target;
if ( person.getAge() < 0 ) {
errors.rejectValue("age", "age_positive");
// Or you can use this approach as well to parametrize the error message:
//errors.rejectValue("age", "age_positive", ArrayParametersIfNeeded, "DefaultMessage");
}
}
}
You also need to have a error.properties file to hold the errors messages allowing i18n.
You can have all needed information about validators here: Validators
When you have it, you need to create the initBinder method in the controller:
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.addValidators(yourValidator);
}
Then, in the controller method, you can pass this additional parameter: BindingResult result and there you will have the information of the errors if you need it.
Also, you need to add this bit to the form:
<form:errors path="age"/></c:set>
Below your <input id="age" name="age" value="${person.age}"/>
I suggest you to change your <input> elements for the <form:input> tag.
In Spring MVC-3 , when we do a form submit, the form backing object does not get removed.
I thought in spring mvc, it removes the baking object after a form submit.
Am I correct or Can anyone explain what might have happened here ?
PS
<form:form id="id1" commandName="command1" modelAttribute="command1" method="post">
When we do a submit, that model attribute binned to the form get removed at some point from the session doesn't it?.
What I want to know is that point at where the command object get removed.
It would be helpful if you could provide some code. Might be a bug there.. MVC actually does not have a backing object. All you can do is put objects into the model map and they get deleted after each request. When you do a form post, all Spring MVC does is to map your form inputs to the object you have in your controller:
form:
<form action="/some-path" method="post">
<input type="text" name="some_property" />
<input type="submit" value="Submit" />
</form>
controller:
#RequestMapping(value="/some-path" method = RequestMethod.POST)
public ModelAndView createItem(SomeObject someObject, BindingResult result) {
// ...
}
mapping object:
public class SomeObject {
private String some_property;
// getter, setter
}
If you didn't declare your object as a Bean (#Named, #Component or something) or added it to the model map:
ModelAndView mav = new ModelAndView();
mav.addObject("someObject,someObject);
then it'll be gone when the controller is finished.
edit
Didn't notice the jsp tag.. Don't think it changes much.. Still, without any code it's hard to say anything for certain.
My application uses submit name as action name consistently. It has worked so far.
Enter multipart form...
html
<form:form modelAttribute="screenObject" enctype="multipart/form-data">
<input name="save" value="Save" type="submit" />
Controller
public static final String ACTION_SAVE="save";
#RequestMapping(method=RequestMethod.POST, params=ACTION_SAVE)
public ModelAndView save(#ModelAttribute("screenObject") FileHeaderEditScreenObject screenObject, BindingResult bindingResult, Model model, Locale locale) {
Error
message Request method 'POST' not supported
If I remove enctype="multipart/form-data", control flows right into the save method. I do need different actions on this multipart form. I want to stay consitent so I hope I do not have to introduce any hidden fields to represent actions or submit the form to different urls...
I suggest to remove params=ACTION_SAVE from the annotation.
It look like that it doesn't parse submit action from a multipart request.
You can handle that parameter as a request parameter:
#RequestMapping(method=RequestMethod.POST, params=ACTION_SAVE)
public ModelAndView save(
#RequestParam(value = "submit", required = true) String action,
#ModelAttribute("screenObject") ....
{
switch (action) {
case "action1": ...
case "action2": ...
}
}
This is not very nice. I would just use different URLs like this:
#RequestMapping("/action1"})
public void action1(
#RequestMapping("/action2"})
public void action2(