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

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

Related

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.

Spring MVC: #PathVariable form

I have a data base which contains some items. I want to create a form which edits item with some id. I did it, form opens fine. Adress is /itemproject/edit_item/{id} Problems start when I'm trying to activate POST method. Instead of directing me to page with item list (/itemproject/view_items) programm sends me to /itemproject/edit_item/edit_item. itemproject is context path (for example).
#RequestMapping(value = "/edit_item/{id}", method = RequestMethod.GET)
public String editItem(#PathVariable("id") Integer id, Model model) {
Item item;
item = dbService.findItem(item).get(0);
model.addAttribute("item", item);
return "edit_item";
}
#RequestMapping(value = "/edit_item/{id}", method = RequestMethod.POST)
public String editItemComplete(#PathVariable("id") Integer id, #ModelAttribute("item") Item item, Model model) {
dbService.updateItem(item);
model.addAttribute("items",dbService.findAllItems());
return "view_items";
}
dbService works with data base.
I want that programm sent me to list of all items after ediding chosen item and updating it in database.
Here is example of edit form (url: /itemproject/edit_item/{id}
<spring:url value="edit_item" var="formURL"/>
<form:form action="${formURL}"
method="post" cssClass="col-md-8 col-md-offset-2"
modelAttribute="item"
>
<div class="form-group">
<label for="item-stuff">Stuff</label>
<form:input id="item-stuff"
cssClass="form-control"
path="stuff"/>
</div>
<button type="submit" class="btn btn-default">Edit item</button>
</form:form>
This is how my item list page looks (url: /itemproject/view_items)
<body>
<table class="table table-hover">
<tbody>
<tr>
<th>Stuff</th>
</tr>
<c:forEach items="${items}" var="item">
<tr>
<td>${item.stuff}</td>
</tr>
</c:forEach>
</tbody>
</table>
</body>
From Spring docs:
In Spring MVC you can use the #PathVariable annotation on a method
argument to bind it to the value of a URI template variable
That means that #PathVariable annotation is suitable when you use the GET method because when you use GET method you can pass your query string.
Instead, try to use #RequestBody in order to try to bind your POST HTTP body message to your parameter
For example:
#RequestMapping(value = "/edit_item", method = RequestMethod.POST)
public String editItemComplete(#RequestBody String body) {
//in here you'll have to pull the body content
return "view_items";
}
Let's say that you're sending an Integer id on HTTP POST body, then you can pull the data from the body like this:
#RequestMapping(value = "/edit_item", method = RequestMethod.POST)
public String editItemComplete(#RequestBody String body) {
ObjectMapper objectMapper = new ObjectMapper();
try {
idJson = objectMapper.readTree(body).path("id").asInt();
} catch (IOException e) {
e.printStackTrace();
}
return "view_items";
}
assuming that you're sending json from client to service.
Rather than loading the items and returning the view_items template, you can return "redirect:/itemproject/view_items" and that will cause your handler for view_items to be invoked, which will load the items etc.

passing parameter using JSP URL to controller in spring mvc, returns 404

First I am getting a list of objects on JSP page, each has an hyperlink(anchor), then on click of anchor I have to send index of the list(surveyId which is also originally coming in surveyList) to controller but the controller is not getting called. It returns 404.
<body>
Create a new survey
<table border="1" cellpadding="5" cellspacing="5">
<tr>
<td><b>Existing Surveys</b></td>
</tr>
<c:forEach var="survey" items="${surveyList}" varStatus="status">
<tr>
<td>${survey.surveyTitle}</td>
<td><a id="byParameter"
href="<c:url value='/home.htm/${surveyList[status.index].surveyId}' />">Share</a>
</td>
</tr>
</c:forEach>
</table>
</body>
URL getting generated is what is need, for example: /project/home.htm/15
This is how the controller looks, I have tried with both #RequestParam and #PathVariable annotations, both return 404. Controller is not getting called.
#Controller
#RequestMapping("/home.htm/parameter/surveyId=1")
public class UserHomeController {
#RequestMapping(value = "/home.htm/{surveyId}", method = RequestMethod.GET)
protected String doSubmit(#PathVariable("surveyId") int surveyId, HttpServletRequest request,
HttpServletResponse response, #ModelAttribute("userSurvey") UserSurvey userSurvey, BindingResult result)
throws AdException {
System.out.println("inside home controller, surveyId:"+surveyId");
return null;
}
}
Could anyone please help me out.

OnClick Event Not Firing Before Spring MVC Controller

I am trying to fire onClick event and Spring controller one by one.
In the click event of the button, I set a hidden field and then I want the controller to be executed AND the hidden field to be available in it. Here is my code for the JSP and the Spring Controller
JSP
<html:hidden property="MenuID" name="MenuID" id="hidMenuID" />
<c:forEach items="${menus}" var="menu" >
<tr class="cartItem">
<td align="left" >${MenuId}</td>
td align="right"><input type="text" name="menu_name" value="${Name}"/></td>
<td align="right"><input type="text" name="menu_price" value="${MenuPrice}"/></td>
<td align="right"><input type="submit" class="button" name="add_menu" value="Add" onclick="return SetMenuID();"></td>
<td align="right"><input type="submit" class="button" name="remove_menu" value="Remove" onclick="return SetMenuID();"></td>
</tr>
</c:forEach>
JavaScript
<script type="text/javascript">
function SetMenuID()
{
var MenuID = document.getElementById('hidMenuID');
MenuID.value = 'The new value';
return true;
}
</script>
Spring MVC
#RequestMapping(params = "add_menu", method = RequestMethod.POST)
public ModelAndView AddMyMenu(#RequestParam("MenuID") String menu_id, Model model, #ModelAttribute("cart") ArrayList<Menu> mycart)
{
int nMenuId = Integer.parseInt(menu_id);
Menu menu = m_arrMenus.get(nMenuId);
model.addAttribute("menus", GetMenus());
mycart.add(menu);
return new ModelAndView("edit_menu");
}
As you might guess, I am populating data from database. I have an onClick function associated with each ADD button. I set hidden field in thid function and want it to be available in my controller. But a runtime exception comes that
Error Status 400 - Required string MenuID is not present
What should I do to achieve similar result ? Thanks for any input.
#RequestParam expects a parameter my name in the request url.
Since your form is submitted and there are no request param that the action recieves. This is a reason you are getting 400 http error from server.
See the difference between #RequestParam vs #PathVariable
So either change the #RequestParam annotation or add (required = false) to it.

How to obtain csrf token in a velocity macro when using spring security

I am trying to create a custom login screen for a spring web security enabled application, and I cannot figure out how to pass the csrf token to velocity (no, I cannot use JSP at the moment).
The model looks something like this:
#RequestMapping(value = "/login", method = RequestMethod.GET)
public ModelAndView login(
#RequestParam(value = "error", required = false) String error,
#RequestParam(value = "logout", required = false) String logout
ModelAndView model = new ModelAndView();
if (error != null) {
model.addObject("error", "Invalid username or password!");
}
if (logout != null) {
model.addObject("msg", "You've been logged out successfully.");
}
model.setViewName("login");
return model;
}
And the relevant section of the velocity template looks like (taken and modified from a jsp example):
<form name='loginForm' action="/login" method='POST'>
<table>
<tr>
<td>User:</td>
<td><input type='text' name='username' value=''></td>
</tr>
<tr>
<td>Password:</td>
<td><input type='password' name='password' /></td>
</tr>
<tr>
<td colspan='2'><input name="submit" type="submit" value="submit" /></td>
</tr>
</table>
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
</form>
Of course, the ${_csrf.parameterName} and ${_csrf.token} variables are empty, so this only works if I disable csrf protection. So my main question is: how do I fill them in the model (or anywhere else)?
I have found the solution, the main point is that the csrf token is injected into the HttpServletRequest by the CsrfFilter, and you can get the HttpServletRequest object by just adding a HttpServletRequest parameter to your method that handles the request mapping.
So the changes that needed to be done are:
#RequestMapping(value = "/login", method = RequestMethod.GET)
public ModelAndView login(
#RequestParam(value = "error", required = false) String error,
#RequestParam(value = "logout", required = false) String logout,
HttpServletRequest request
){
...
CsrfToken csrfToken = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
if (csrfToken != null) {
model.addObject("_csrf",csrfToken);
}
...
Just to share my little bit, I initially started by using #P.Péter's solution which was fine. but as my application grew to have so many forms, i decided it was too cumbersome using that snippet for every form that I needed to protect from csrf intrusions, so here's what I did so I don't have to repeat across my application.
#ControllerAdvice
public class CsrfControllerAdvice {
#Autowired
private HttpServletRequest request;
#ModelAttribute("_csrf")
public CsrfToken appendCSRFToken(){
//HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
return (CsrfToken) request.getAttribute(CsrfToken.class.getName());
}
}
POINT - The idea is to use a #ControllerAdvice which gets called on entering any Spring Controller to attach the CsrfToken to the resulting View using the #ModelAttribute("<attribute-name>") annotation.
NOTE 1 - This _csrf model attribute gets attached for all Views, hence if you want to limit the _csrf processing to selected URLs or Views, see this resource here for very nice samples on how to do that.
NOTE 2 - Notice how I commented out the following line?
//HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
That's because in my case the Autowired HttpServletRequest instance is sufficient for my scenario. However some situations may warrant that you use the commented out instance e.g when you need to obtain the request object from some part of your application that isn't necessarily request scoped... See #Samit G's answer as pointed out in this thread here for more information.

Categories