Thymleaf: Request method 'POST' not supported - java

Consider I have the following form, which i send two parameters _id and poID to the controller:
<form style='float:left; padding:5px; height:0px' th:object="${plant}" th:method="post" th:action="#{/dashboard/DeliverPlant/{_id}(_id=${plant._id})/{po_id}(po_id=${plant.poID})}">
<button class="btn btn-default btn-xs" type="submit">Verify Plant Delivery</button>
</form>
And in the controller i have the following form:
#RequestMapping(method=POST, path="DeliverPlant/{_id}/{po_id}")
public String DeliverPlant(Model model,#PathVariable("_id") String id,#PathVariable("po_id") String po_id) throws Exception {
Long Id= Long.parseLong(id);
System.out.println("po_id is..................."+po_id+ "_id is: "+id);
return "dashboard/orders/ShowPOs";
}
When i send my request, there is no internal error but it complains that
There was an unexpected error (type=Method Not Allowed, status=405).
Request method 'POST' not supported
It seems that it cannot recognize the method.
So how can i fix it?
Update:
I also tried this
<form style='float:left; padding:5px; height:0px' th:object="${plant}" th:method="post" th:action="#{'/dashboard/DeliverPlant/'+{_id}(_id=${plant._id})+'/'+{po_id}(po_id=${plant.poID})}">
<button class="btn btn-default btn-xs" type="submit">Verify Plant Delivery</button>
</form>

Try
method="POST"
instead of
th:method="POST"
Also, in your Controller class try
#RequestMapping(method=RequestMethod.POST
where RequestMethod is org.springframework.web.bind.annotation.RequestMethod

You return controller in modelAndView.addobject "po", "plant", "??".
You take object value in return page(select page).
"_id" <- ???
...
Anyway
edit path
th:action="#{/dashboard/deliver/plant/__${??.id}__/__${plant.id}__/__${po.id}__ }"
edit controller
#RequestMapping(method = RequestMethod.POST, value ="/deliver/plant/{??.id}/{plant.id}/{po.id}")
public String DeliverPlant(#PathVariable("??.id") int id, #PathVariable("po.id") int poId, #PathVariable("plant.id") int plantId) {
return "dashboard/orders/ShowPOs"; // <-- break point.
}
You try debug.
See value.

Related

How to stay on the same page after form submit thymleaf and Spring boot?

Hello guys i have a question regarding what is mentioned in the title. Is it possible to stay on the same page and submit . I found something with javascript but it is not working for me because i m using thymleaf and spring boot. Or i just don't know how to adapt it to my case.
thymeleaf code:
<form th:action="#{/tweets/tweet}" th:object="${tweet}" method="post">
<div class="row">
<div class="col">
<input type="text" th:field="*{content}" class="form-control" placeholder="What's happening? Tell us!">
</div>
<div class="col">
<input class="form-control" type="submit" value="Submit" />
</div>
</div>
</form>
the controller class:
#Controller
#RequestMapping("tweets")
#Slf4j
public class TweetController {
private TweetService tweetService;
public TweetController(TweetService tweetService) {
this.tweetService = tweetService;
}
#PostMapping("/tweet")
#ResponseStatus(CREATED)
public Tweet tweet(#Valid #ModelAttribute("tweet") Tweet tweet, Principal
principal, BindingResult result) {
if(result.hasErrors()){
//do somethign
}
if (!tweet.getContent().equals(null) && !tweet.getContent().equals("") && !tweet.getContent().isEmpty()) {
tweetService.createTweet(tweet.getContent(), principal);
}
}
#GetMapping("/")
public String goToIndex(Model model){
model.addAttribute("tweet",new Tweet());
return "overview";
}
And i have server.context-path=/api
I have one more additional question to this topic. When i wanted to redirect it to another page i was getting a blank page. Not an error not an exception just a blank page. Any help ? I m new to this.
Yes, this is possible using ajax. I would recommend doing it using jQuery though. So, if you would like to submit your form and stay in the same page, you could do the following.
HTML
<form id="tweet-form" th:action="#{/tweets/tweet}" th:object="${tweet}" method="post">
<div class="row">
<div class="col">
<input type="text" th:field="*{content}" class="form-control" placeholder="What's happening? Tell us!">
</div>
<div class="col">
<input id="submit-form" class="form-control" type="button" value="Submit" />
</div>
</div>
</form>
Changes:
Added an id to the form.
Added an id to your input.
Change submit input's type for button.
jQuery
$('#submit-form').on('click', function() {
var form = $('#tweet-form');
$.ajax({
url: form.attr('action'),
data: form.serialize(),
type: post,
success: function(result) {
// Do something with the response.
// Might want to check for errors here.
}, error: function(error) {
// Here you can handle exceptions thrown by the server or your controller.
}
})
}
Controller
#PostMapping("/tweet")
#ResponseStatus(CREATED)
public Tweet tweet(#Valid #ModelAttribute("tweet") Tweet tweet, Principal
principal, BindingResult result) {
if(result.hasErrors()){
// Throw an exception or send a null Tweet.
}
if (!tweet.getContent().equals(null) && !tweet.getContent().equals("") && !tweet.getContent().isEmpty()) {
tweetService.createTweet(tweet.getContent(), principal);
}
// You are returning a Tweet, so you must return something.
return tweet;
}
Your controller pretty much stay the same. Just remember to return something.
Your example doesn't show what the tweet() method returns. It should return a Tweet object but doesn't have any return value. What are you attempting to do with that return value? If you're not handling it with Javascript someway, then get rid of #ResponseStatus(CREATED) and return a either a Model or a String pointing to your html file, like so:
#PostMapping("/tweet")
public String tweet(#Valid #ModelAttribute("tweet") Tweet tweet, Principal
principal, BindingResult result) {
if(result.hasErrors()){
//do somethign
}
if (!tweet.getContent().equals(null) && !tweet.getContent().equals("") && !tweet.getContent().isEmpty()) {
tweetService.createTweet(tweet.getContent(), principal);
}
return "redirect:/name-of-html-file";
}
reference
If you want thymeleaf to handle the tweet and the HttpStatus you could instead return something along the lines of
ModelAndView model = new ModelAndView("your-view");
model.addAttribute(tweet);
model.setStatus(HttpStatus.CREATED);
return model;

Request method GET not supported when deleting

I have a table for adding one object to another and deleting one object from another.
This is my controller:
#Controller
#RequestMapping("/proj/{pid}/coupling/{r1}")
public class CouplingController {
#RequestMapping(method = RequestMethod.GET)
public String getAllCouplings( ){
return "riskCoupling";
}
#RequestMapping(value = "/{r1}", method = RequestMethod.POST)
public String saveCoupling( ){
return "/projects";
}
#RequestMapping(value = "/{r2}", method = RequestMethod.DELETE)
public String removeCoupling(){
return "/projects";
}
}
This is my Thymeleaf view
<td>
<form action = "#" th:action="#{/proj/{pid}/coupling/{r1}/{r2} (pid=${projectID},r1=${r1ID},r2=${r2.id})}" method = "post">
<input type="submit" name="Couple" value="Couple" class="btn btn-info" />
</form>
</td>
<td">
<form action = "#" th:action="#{/proj/{pid}/coupling/{r1}/{r2} (pid=${projectID},r1=${r1ID},r2=${r2.id})}" method = "delete">
<input type="submit" name="RemoveCoupling" value="RemoveCoupling" class="btn btn-info" />
</form>
</td>
When I go to the url /proj/{pid}/coupling/{r1} I get the overview so the GET works.
When I press the Couple button it works. So the POST works.
When I press the RemoveCoupling button I get the error:
Request method "Get" not supported.
I really don't know why I get this error.
Since browsers unfortunately do not support any other verbs than POST and GET in HTML forms, they will just send POST/GET requests instead. This might be the reason why only that one does not work.
See more:
http://martinfowler.com/articles/richardsonMaturityModel.html
http://amundsen.com/examples/put-delete-forms/
http://www.quora.com/HTTP/Why-are-PUT-and-DELETE-no-longer-supported-in-HTML5-forms
I fixed this with Thymeleaf - Button click to call http delete method.
Basically putting a hidden field in my form.

Parameters not being passed in POST method REST Java

So, i am developing a web service where when i call api/update action on form submit, two of my inputs from my jsp, "data" and "id" are supposed to pass, on which i want to perform operations in the POST method block.
here is the form calling the method:
<form id="myForm" action="api/update" method="post">
id: <input type="text" id="id" name="id">
<br/>
<textarea id="data" name="data" rows="30" cols="100" placeholder="Empty File"></textarea>
<br/>
<button id="submit" type="submit">Submit</button>
</form>
Here is the REST code:
#Path("update")
public class UpdateResource {
#Context
private UriInfo context;
public UpdateResource() {
}
#Context
private HttpServletRequest request;
#POST
public void putHtml() {
String data = request.getParameter("data");
String id = request.getParameter("id");
System.out.println(id); //<---- line1
System.out.println(data); //<---- line2
//further operations
}
}
The problem is that line1 and line2 gives null as output, means somehow data is not being passed. Am i doing something wrong?
Here is the Header file captured from network tab of chrome browser:
Instead of trying to get the parameters from the HttpServletRequest, use #FormParam
#POST
public void putHtml(#FormParam("id") String id,
#FormParam("data") String data) {
}
The reason is that the entity body is already being read, resulting in the parameters being empty in the HttpServletRequest.

Trouble with getting form data using Spring

I am super new to front end development. I have a simple webapp where you can submit a form, and I want to be able to get the form data in a REST endpoint. I am using Spring boot which contains Spring MVC.
The HTML:
<div class="modal-header modal-header-info">
<button type="button" class="close" data-dismiss="modal"
aria-hidden="true">×</button>
<h4 class="modal-title">Add Entity</h4>
</div>
<div class="modal-body">
<form role="form" method="post" action="/createEntity">
<div class="form-group">
<label for="name">Name:</label> <input type="text"
class="form-control" id="name">
</div>
<button type="submit" class="btn btn-info">Submit</button>
</form>
</div>
The Java:
#RequestMapping(value = "/createEntity", method = RequestMethod.POST)
public void createEntity(#RequestBody String payload) {
System.out.println("Hello world!");
System.out.println(payload);
}
I get this error back:
Failed to read HTTP message:
org.springframework.http.converter.HttpMessageNotReadableException:
Required request body is missing: public void main.java.info.spring.data.neo4j.controllers.CreateEntityController.createEntity(java.lang.String)
How do I get the "name" value from the form? The "hello world" attribute prints out if I take out the #RequestBody part.
Edit: my configuration per request:
#EnableTransactionManagement
#Import(RepositoryRestMvcConfiguration.class)
#EnableScheduling
#EnableAutoConfiguration
#ComponentScan(basePackages = { "main.java.info.spring.data.neo4j.services",
"main.java.info.spring.data.neo4j.controllers" })
#Configuration
#EnableNeo4jRepositories(basePackages = "main.java.info.spring.data.neo4j.repositories")
public class MyNeo4jConfiguration extends Neo4jConfiguration {
public static final String URL = System.getenv("NEO4J_URL") != null ? System.getenv("NEO4J_URL")
: "http://localhost:7474";
#Override
public Neo4jServer neo4jServer() {
return new RemoteServer(URL, "neo4j", "LOL my pass");
}
#Override
public SessionFactory getSessionFactory() {
return new SessionFactory("main.java.info.spring.data.neo4j.domain");
}
}
Problem 1: HTML form controls are missing the name attribute
HTML forms are submitted in name-value pairs. Form controls that do not have a name attribute cannot be submitted because their names are not known. The id attribute is only for referencing controls on the client-side.
<input type="text" class="form-control" id="name">
is missing the name attribute. It should be:
<input type="text" class="form-control" id="name" name="name"/>
This will submit the control as a form parameter in the HTTP request body, something like:
name: [the value you specify for the control]
Problem 2: #RequestBody as a controller method argument will give you the entire HTTP request body
public void createEntity(#RequestBody String payload)
means that you want the entire HTTP request body to be passed as a String to the controller method createEntity. If your HTML form is indeed what you have posted, you will get the following value for payload (after you have addressed problem 1):
name: [the value you specify for the control]
If this is what you want, keep the #RequestBody annotation, but I suspect that you are only interested in the value of the name request parameter. If that is the case, you need to change your method declaration to (after you have addressed problem 1):
public void createEntity(#RequestParam String name)
This will map the HTTP request parameter name to the controller method argument name.
Problem 3: Having void as the return type for a controller method will force Spring MVC to look for a view with the same name as the method name
Since you have the controller method declared as:
public void createEntity(#RequestBody String payload)
Spring MVC will look for a view named createEntity after this method exits. If you have a view with that name, well and good. Otherwise, once you have fixed problems 1 and 2, you will get a 404 - Resource not found error.

HTTP Status 405 - Request method 'POST' not supported (Spring MVC)

Im getting this error: HTTP Status 405 - Request method 'POST' not supported
What I am trying to do is make a form with a drop down box that get populated based on the other value selected in another drop down box. For example when I select a name in the customerName box the onChange function in the .jsp page should be run and the page submitted then loaded again with the corresponding values in the customerCountry box.
however I'm getting this HTTP Status 405 error. I have searched the internet for a solution but haven't been able to find anything that helped. Here is the relevant parts of my code:
part of jsp page
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<style>
.error { color: red; }
</style>
<script>
function repopulate(){
document.deliveryForm.submit();
}
function setFalse(){
document.getElementById("hasId").value ="false";
document.deliveryForm.submit();
// document.submitForm.submit(); (This was causing the error)
}
</script>
</head>
<body>
<h1>Create New Delivery</h1>
<c:url var="saveUrl" value="/test/delivery/add" />
<form:form modelAttribute="deliveryDtoAttribute" method="POST" action="${saveUrl}" name="deliveryForm">
<table>
<tr>
<td><form:hidden id="hasId" path="hasCustomerName" value="true"/></td>
</tr>
<tr>
<td>Customer Name</td>
<td><form:select path="customerName" onChange="repopulate()">
<form:option value="" label="--- Select ---" />
<form:options items="${customerNameList}" />
</form:select>
</td>
<td><form:errors path="customerName" cssClass="error" /></td>
</tr>
<tr>
<td>Customer Country</td>
<td><form:select path="customerCountry">
<form:option value="" label="--- Select ---" />
<form:options items="${customerCountryList}" />
</form:select>
</td>
<td><form:errors path="customerCountry" cssClass="error" /></td>
</tr>
</form:form>
<form:form name="submitForm">
<input type="button" value="Save" onClick="setFalse()"/>
</form:form>
</body>
</html>
part of controller:
#RequestMapping(value = "/add", method = RequestMethod.GET)
public String getDelivery(ModelMap model) {
DeliveryDto deliveryDto = new DeliveryDto();
model.addAttribute("deliveryDtoAttribute", deliveryDto);
model.addAttribute("customerNameList",
customerService.listAllCustomerNames());
model.addAttribute("customerCountryList", customerService
.listAllCustomerCountries(deliveryDto.getCustomerName()));
return "new-delivery";
}
// I want to enter this method if hasId=true which means that a value in the CustomerName
// drop down list was selected. This should set the CountryList to the corresponding values
// from the database. I want this post method to be triggered by the onChange in the jsp page
#RequestMapping(value = "/add", method = RequestMethod.POST, params="hasCustomerName=true")
public String postDelivery(
#ModelAttribute("deliveryDtoAttribute") DeliveryDto deliveryDto,
BindingResult result, ModelMap model) {
model.addAttribute("deliveryDtoAttribute", deliveryDto);
model.addAttribute("customerNameList",
customerService.listAllCustomerNames());
model.addAttribute("customerCountryList", customerService
.listAllCustomerCountries(deliveryDto.getCustomerName()));
return "new-delivery";
}
// This next post method should only be entered if the save button is hit in the jsp page
#RequestMapping(value = "/add", method = RequestMethod.POST, params="hasCustomerName=false")
public String postDelivery2(
#ModelAttribute("deliveryDtoAttribute") #Valid DeliveryDto deliveryDto,
BindingResult result, ModelMap model) {
if (result.hasErrors()) {
model.addAttribute("deliveryDtoAttribute", deliveryDto);
model.addAttribute("customerNameList",
customerService.listAllCustomerNames());
model.addAttribute("customerCountryList", customerService
.listAllCustomerCountries(deliveryDto.getCustomerName()));
return "new-delivery";
} else {
Delivery delivery = new Delivery();
//Setters to set delivery values
return "redirect:/mis/home";
}
}
How come I get this error? Any help would be much appreciated! Thanks
EDIT: Changed hasId to hasCustomerName. I still get the HTTP Status 405 - Request method 'POST' not supported error though.
EDIT2: Commented out the line in the setFalse function that was causing the error
// D
I am not sure if this helps but I had the same problem.
You are using springSecurityFilterChain with CSRF protection. That means you have to send a token when you send a form via POST request. Try to add the next input to your form:
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
Check if you are returning a #ResponseBody or a #ResponseStatus
I had a similar problem. My Controller looked like that:
#RequestMapping(value="/user", method = RequestMethod.POST)
public String updateUser(#RequestBody User user){
return userService.updateUser(user).getId();
}
When calling with a POST request I always got the following error:
HTTP Status 405 - Request method 'POST' not supported
After a while I figured out that the method was actually called, but because there is no #ResponseBody and no #ResponseStatus Spring MVC raises the error.
To fix this simply add a #ResponseBody
#RequestMapping(value="/user", method = RequestMethod.POST)
public #ResponseBody String updateUser(#RequestBody User user){
return userService.updateUser(user).getId();
}
or a #ResponseStatus to your method.
#RequestMapping(value="/user", method = RequestMethod.POST)
#ResponseStatus(value=HttpStatus.OK)
public String updateUser(#RequestBody User user){
return userService.updateUser(user).getId();
}
You might need to change the line
#RequestMapping(value = "/add", method = RequestMethod.GET)
to
#RequestMapping(value = "/add", method = {RequestMethod.GET,RequestMethod.POST})
The problem is that your controller expect a parameter hasId=false or hasId=true, but you are not passing that. Your hidden field has the id hasId but is passed as hasCustomerName, so no mapping matches.
Either change the path of the hidden field to hasId or the mapping parameter to expect hasCustomerName=true or hasCustomerName=false.
I found the problem that was causing the HTTP error.
In the setFalse() function that is triggered by the Save button my code was trying to submit the form that contained the button.
function setFalse(){
document.getElementById("hasId").value ="false";
document.deliveryForm.submit();
document.submitForm.submit();
when I remove the document.submitForm.submit(); it works:
function setFalse(){
document.getElementById("hasId").value ="false";
document.deliveryForm.submit()
#Roger Lindsjö Thank you for spotting my error where I wasn't passing on the right parameter!
I was getting similar problem for other reason (url pattern test-response not added in csrf token)
I resolved it by allowing my URL pattern in following property in config/local.properties:
csrf.allowed.url.patterns = /[^/]+(/[^?])+(sop-response)$,/[^/]+(/[^?])+(merchant_callback)$,/[^/]+(/[^?])+(hop-response)$
modified to
csrf.allowed.url.patterns = /[^/]+(/[^?])+(sop-response)$,/[^/]+(/[^?])+(merchant_callback)$,/[^/]+(/[^?])+(hop-response)$,/[^/]+(/[^?])+(test-response)$
In my case the url was ending with /
paymentUrl(old)= /get-details/
i just removed the trailing /
paymentUrl(new)= /get-details
and it worked

Categories